当前位置: 首页 > article >正文

dl学习笔记:(4)简单神经网络

(1)单层正向回归网络

bx1x2z
100-0.2
110-0.05
101-0.05
1110.1

接下来我们用代码实现这组线性回归数据

import torch
x = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
z = torch.tensor([-0.2, -0.05, -0.05, 0.1])
w = torch.tensor([-0.2,0.15,0.15])
def LinearR(x,w):
    zhat = torch.mv(x,w)
    return zhat
zhat = LinearR(x,w)
zhat

下面对这段代码做一个详细说明:

(1)tensor的定义

这里我们需要定义成浮点数的原因,是因为torch中有很多函数不接受两个类型不同的参数传入,例如这里的mv函数,如果我们省略浮点的定义,就会出现报错如下:

这种静态的编程可以说既是pytorch的优点也是缺点,优点在于如果省略了对传入参数的检查处理,就会在运行速度上快很多,尤其是传入数据量大的时候,对每一个数据进行检查是一个很慢的过程。缺点在于你需要人为记住哪些函数是可以不同类型哪些不可以,pytorch没有一个统一的标准,唯一的办法就只有自己多试几次,所以建议大家在定义tensor的时候就默认定义成为float32

另外由于很多pytorch函数不接受一维张量,所以这里我们在定义的时候也养成两个[[的二维张量的习惯

(2)精度问题

如果我们想要查看运行结果是否和答案一致的时候,我们运行下面代码会发现奇怪的现象:

那就是为什么后面三个会是false,这其实就是精度的问题了。

所以当我们人为把精度调高到30位的时候就可以发现问题了:

当数据量大的时候,会因为精度问题产生较大的误差,如果想要控制误差大小,可以使用64位浮点数,唯一的缺点就是会比较占用内存。如果我们想要忽略较小的误差可以使用下面这个函数:

通过shift+tab快捷键来查看函数参数使用,我们也可以通过调整rtol和atol控制误差大小,可以看到这里的默认参数已经挺小的了

(2)torch.nn.Linear

在使用之前,我们先给出pytorch的架构图,包含了常用的类和库

我们先给出代码再进行解释:

import torch
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)
output = torch.nn.Linear(2,1)
zhat = output(X)
zhat

首先nn.Linear是一个类,在这里代表了输出层,所以使用output作为变量名,output的这一行相当于是类的实例化过程。里面输入的参数2和1分别指上一层的神经元个数和这一层的神经元个数。

由于nn.Module的子类会在实例化的同时自动生成随机的w和b,所以这里我们上一层就只有两个x,又由于这是回归类模型所以输出层只有一个。我们也可以调用查看随机生成的数:

如果我们不想要bias可以设置成false:output = torch.nn.Linear(2,1,bias = False)

如果我们不想都每次都随机生成不同的权重,可以设置随机数种子:torch.random.manual_seed(250)

(3)逻辑回归

我们学习过线性回归之后,都知道线性回归能够拟合直线的线性关系,但是现实生活中的数据关系往往不是线性的,所以为了更好的拟合曲线关系,统计学家就在线性回归的方程两边加上了联系函数,这种回归也被叫做广义线性回归。而在探索联系函数的过程中,就发现了sigmoid函数。

\sigma = sigmoid(z) = \frac{1}{1+e^{-z}}

我们可以运行下面代码来画出sigmoid图像:

import numpy as np
import matplotlib.pyplot as plt

# 定义Sigmoid函数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# 生成x轴数据
x = np.linspace(-10, 10, 400)

# 计算y轴数据
y = sigmoid(x)

# 绘图
plt.figure(figsize=(8, 6))
plt.plot(x, y, label="Sigmoid Function", color='b')
plt.title("Sigmoid Function")
plt.xlabel("x")
plt.ylabel("sigmoid(x)")
plt.grid(True)
plt.axhline(0, color='black',linewidth=0.5)
plt.axvline(0, color='black',linewidth=0.5)
plt.legend()
plt.show()

首先,我们可以观察函数图像,这是一个将所有数映射到(0,1)之间的函数,x趋于负无穷时,函数值趋近于0,x趋近于正无穷时,函数值趋近于1。

运行下面代码,我们可以画出sigmoid的导数图像:

import numpy as np
import matplotlib.pyplot as plt

# 定义Sigmoid函数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# 定义Sigmoid函数的导数
def sigmoid_derivative(x):
    s = sigmoid(x)
    return s * (1 - s)

# 生成x轴数据
x = np.linspace(-10, 10, 400)

# 计算Sigmoid函数的导数
y_derivative = sigmoid_derivative(x)

# 绘图
plt.figure(figsize=(8, 6))
plt.plot(x, y_derivative, label="Sigmoid Derivative", color='r')
plt.title("Derivative of Sigmoid Function")
plt.xlabel("x")
plt.ylabel("sigmoid'(x)")
plt.grid(True)
plt.axhline(0, color='black',linewidth=0.5)
plt.axvline(0, color='black',linewidth=0.5)
plt.legend()
plt.show()

可以发现在0处取到的导数值最大,往边上走迅速减小,所以它可以快速将数据从0的附近排开。这样的性质,让sigmoid函数拥有将连续型变量转化为离散型变量的力量,这也让它拥有了化回归类问题为分类问题的能力。

当我们将该函数以对数几率的形式表达出来的时候,会发现另一个有趣的地方。

\ln \frac{\sigma }{1-\sigma } =\ln \frac{\frac{1}{1+e^{-xw}}}{1-\frac{1}{1+e^{-xw}}} = \ln \frac{\frac{1}{1+e^{-xw}}}{\frac{e^{-xw}}{1+e^{-xw}}} = \ln\frac{1}{e^{-xw}} = \ln e^{xw} = xw

我们神奇地发现,让取对数几率后所得到的值就是我们线性回归的z,因为这个性质,在等号两边加sigmoid 的算法被称为“对数几率回归”,在英文中就是Logistic Regression,就是逻辑回归。

此时 \sigma1-\sigma之和为1,因此它们可以被我们看作是一对正反例发生的概率,即\sigma是某样本i的标签被预测为1的概率,而1-\sigma是i的标签被预测为0的概率,相比的结果就是样本i的标签被预测为1的相对概率。基于这种理解,虽然可能不严谨,逻辑回归、即单层二分类神经网络返回的结果一般被当成是概率来看待和使用。

(1)与门的实现:

x0x1x2andgate
1000
1100
1010
1111
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
andgate = torch.tensor([[0],[0],[0],[1]], dtype = torch.float32)
w = torch.tensor([-0.2,0.15,0.15], dtype = torch.float32)
def LogisticR(X,w):
    zhat = torch.mv(X,w)
    sigma = torch.sigmoid(zhat)
    #sigma = 1/(1+torch.exp(-zhat))
    andhat = torch.tensor([int(x) for x in sigma >= 0.5], dtype = torch.float32)
    return sigma, andhat
sigma, andhat = LogisticR(X,w)
andhat

这里我们求sigma的时候用的是库里面的函数,注释的是手动写的。

唯一需要额外解释的是andhat的列表推导式:

sigma >= 0.5 会对 sigma 中的每个元素进行比较,返回一个布尔型张量,并且形状与 sigma 相同举个例子,假设 sigma = torch.tensor([0.2, 0.7, 0.5, 0.3]),那么 sigma >= 0.5 的结果会是:tensor([False,  True,  True, False])
接下来是列表推导式部分,它会遍历 sigma >= 0.5 中的每个布尔值,并将每个布尔值转化为整数(True 转化为 1False 转化为 0)。也就是说,这个列表推导式的功能是将布尔值转换为二分类的预测值(0 或 1)。

上面的例子通过列表推导式 [int(x) for x in sigma >= 0.5],会得到:[0, 1, 1, 0]

当然除了sigmoid还有很多经典的函数,例如sign,ReLU,Tanh等函数,这里我们就不再一一列出来了,用到的时候再说。

(2)torch版本实现

import torch
from torch.nn import functional as F
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)
torch.random.manual_seed(250) #人为设置随机数种子
dense = torch.nn.Linear(2,1)
zhat = dense(X)
sigma = F.sigmoid(zhat)
y = [int(x) for x in sigma > 0.5]
y

(4)softmax

当我们遇到多分类问题的时候,往往使用softmax来解决

Softmax 是一种常用于多分类问题的激活函数,特别是在神经网络的输出层,它将一组实数转换为一个概率分布。Softmax 是“归一化指数函数”的一种形式,其主要功能是将任意实数向量转换成一个概率分布,使得每个元素的值介于0和1之间,且所有元素的和为1。这对于多分类问题来说非常重要,因为它可以帮助我们得到不同类别的概率,从而进行分类决策。

可是由于指数的缘故,经常会发生溢出的情况,如下:

所以一般我们不手动自己写softmax,一般会调用pytorch内置的函数

你可能会疑惑为什么这个函数不会出现溢出的问题,这是因为使用了一点小技巧。

为了避免溢出,我们可以将每个输入值z_{i}减去输入向量的最大值max(z),这样可以确保计算中的指数值不会太大,仍然可以保留同样结果的相对比例。通过以下变换,我们可以得到数值稳定的 Softmax:

另外还需要解释一点的是这里的0是什么,我们可以通过快捷键查看函数的参数发现,第二个参数是维度,也就是说该函数需要你指定沿着哪一个维度进行softmax操作。

这里由于是只有一行,所以就是沿着这一行做softmax,所以维度的索引值为0,下面举几个例子:

如果是二维张量dim=1就是对两个小的张量进行softmax

这意味着 Softmax 没有对每个样本独立处理,而是计算了每个类别在所有样本中的相对比例。

我们再举一个大一点的三维张量的例子:

import torch
import torch.nn.functional as F

# 输入一个 3D 张量 (batch_size, channels, height)
z = torch.tensor([[[1.0, 2.0, 3.0, 4.0],   # 第一个样本的 4 个特征值
                  [1.0, 2.0, 3.0, 4.0],   # 第二个样本的 4 个特征值
                  [1.0, 2.0, 3.0, 4.0]],  # 第三个样本的 4 个特征值
                 
                 [[5.0, 6.0, 7.0, 8.0],   # 第一个样本的 4 个特征值
                  [5.0, 6.0, 7.0, 8.0],   # 第二个样本的 4 个特征值
                  [5.0, 6.0, 7.0, 8.0]]]  # 第三个样本的 4 个特征值

# 打印输入张量
print("输入张量:")
print(z)
print("\n")

# 沿着 dim=2 计算 Softmax(计算每个特征维度的概率)
softmax_dim2 = F.softmax(z, dim=2)
print("沿着 dim=2 计算 Softmax:")
print(softmax_dim2)
print("\n")

# 沿着 dim=1 计算 Softmax(计算每个类别的概率)
softmax_dim1 = F.softmax(z, dim=1)
print("沿着 dim=1 计算 Softmax:")
print(softmax_dim1)
print("\n")

# 沿着 dim=0 计算 Softmax(计算每个样本的概率)
softmax_dim0 = F.softmax(z, dim=0)
print("沿着 dim=0 计算 Softmax:")
print(softmax_dim0)

由于这是一个shape为(2,3,4)的张量,所以dim=2也就是最后一个维度4的求和,所以是四个元素加起来等于1;dim=1也就是中间维度3,按照列加起来等于1;dim=0第一个维度2,所以是第一个小张量里面的加第二个小张量里面的等于1.


http://www.kler.cn/a/510217.html

相关文章:

  • Node.js 到底是什么
  • Java并发编程:线程安全的策略与实践
  • 深度学习项目--基于LSTM的火灾预测研究(pytorch实现)
  • HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (三、影视搜索页功能实现)
  • HTML中如何保留字符串的空白符和换行符号的效果
  • SpringBoot:RestTemplate与IllegalArgumentException
  • Java杂记:@TableField注解用法详解
  • HTTPS与HTTP:区别及安全性对比
  • Linux:指令大全(一)
  • Ubuntu把应用程序放到桌面
  • FastAPI 应用的容器化与 Docker 部署:提升性能与可扩展性
  • Java 静态导入:规则、实践与应用场景
  • 2,Linux文件基本属性(基于Ubuntu示例进行讲解)
  • 802.1协议讲解
  • leetcode刷题记录(六十八)——2. 两数相加
  • OpenAI第一个真正意义上的AI Agent:ChatGPT Tasks,使用指南1.0
  • latin1_swedish_ci(latin1 不支持存储中文、日文、韩文等多字节字符)
  • Shell控监Kafka积压
  • 210. 课程表 II【 力扣(LeetCode) 】
  • 【git】如何删除本地分支和远程分支?
  • Gateway与WebFlux
  • docker容器中运行了一个Ubuntu系统,如何把主机的一个文件拷贝到这个Ubuntu系统中
  • python json.dump 插入到json文件中,中文乱码问题
  • MCU中的LSB、MSB和大端模式、小端模式
  • Spring Boot+Vue
  • node.js项目依赖关系分析工具 Depazer 的使用