研究僧的学习小站

神经网络

神经元:神经网络中的基本单元,相互连接组成神经网络。

单层神经网络

感知机:两层神经网络

多层神经网络:输入层、输出层、隐层、全连接层

\( a_0^{(1)}=\sigma (w_{0,0}a_0^{(0)}+w_{0,1}a_1^{(0)}+...+w_{0,n}a_n^{(0)}+b_0) \)

\( \sigma \left( \left[ \begin{array}{l} w_{0,0}& w_{0,1}& ...& w_{0,n}\\ w_{1,0}& w_{1,1}& ...& w_{1,n}\\ ...& ...& ...& ...\\ w_{k,0}& w_{k,1}& ...& w_{k,n}\\ \end{array} \right] \left[ \begin{array}{c} {a_0}^{\left( 0 \right)}\\ {a_1}^{\left( 0 \right)}\\ ...\\ {a_n}^{\left( 0 \right)}\\ \end{array} \right] +\left[ \begin{array}{c} b_0\\ b_1\\ ...\\ b_k\\ \end{array} \right] \right) =\left[ \begin{array}{c} {a_0}^{\left( 1 \right)}\\ {a_1}^{\left( 1 \right)}\\ ...\\ {a_k\\\\++}^{\left( 1 \right)}\\ \end{array} \right] \)

其中b0为偏置。

激活函数:增加模型的非线性分割能力,增加模型的稳健性,缓解梯度消失问题,加速模型收敛。

常见激活函数:

1. sigmoid:(0,1)

2. tanh:(-1,1)

3. relu:max(0,x)

4. ELU:a(e^x-1)

梯度下降

梯度:变化率最快的方向向量(学习的前进方向)。

对于某个数据x,由一个模型f,使之生成预测值\(f(x,w)=Y_{predict}\)。

为判断模型好坏,通过对比预测值与实际值,引入损失函数(均方误差)

\( loss=(Y_{predict}-Y_{true})^2 \qquad (回归损失) \\ loss=Y_{true} \cdot \log Y_{predict} \qquad (分类损失) \)

通过调整参数\(w\),尽可能降低\(loss\)。

选择起始点\(w_0\),调整\(w_0\)的位置,让\(loss\)不断减少。

\(w\)的更新方法

1. 计算w的梯度(导数)

\( \nabla w=\cfrac{f\left( w+0.0000001 \right) -f\left( w-0.0000001 \right)}{2\times 0.0000001} \)

2. 更新\(w\)

\( w=w-\alpha \nabla w \)

其中:

1. \(\nabla w < 0\),意味着\(w\)将增大;

2. \(\nabla w > 0\),意味着\(w\)将减小。

反向传播

计算图:描述函数的图形。

若有一个函数\(J(a,b,c)=3(a+bc)\),令\(u=a+v\),\(v=bc\),则可绘制计算图为:

此时可以清楚地对每个节点求偏导。

反向传播的过程即上图从右到左的过程,自变量a,b,c各自的偏导为连线上梯度的乘积。

\( \cfrac{\partial J}{\partial a}=\cfrac{\partial J}{\partial u}\cfrac{\partial u}{\partial a}=3\times 1 \\ \cfrac{\partial J}{\partial b}=3\times 1\times c \\ \cfrac{\partial J}{\partial c}=3\times 1\times b \)

神经网络中的反向传播

\(w_1,w_2,...,w_n\)表示网络第n层的权重,\(w_n[i,j]\)表示第n层第i个神经元连接到第n+1层第j个神经元的权重。

此时\(f_1=w_1[1,1]x_1+w_1[2,1]x_2\),\(f_2=w_1[1,2]x_2+w_1[2,2]x_2\),以此类推。因此可以画出此神经网络的计算图

其中函数A为激活函数。得到out后即可得到loss,调整\(w\),使得loss接近0。此处考察\(w_1[1,2]\)的变化对loss的影响

\( \cfrac{\partial loss}{\partial w_1\left[ 1,2 \right]}=x_1A^{\prime}\left( f_2 \right) \left( w_2\left[ 2,1 \right] A\prime\left( g_1 \right) w_3\left[ 1,1 \right] +w_2\left[ 2,2 \right] A\prime\left( g_2 \right) w_3\left[ 2,1 \right] \right) \times \nabla out \)

其中\(\nabla out = \cfrac{\partial loss}{\partial out}\).

如果要逐个计算\(\frac{\partial loss}{\partial w}\),需要的内存非常大。而反向传播的思想是从out向前逐层计算,先计算最后一层的梯度,再用最后一层的数据计算倒数第二层的梯度,以此类推。

Pytorch完成线性回归

对于pytorch中的一个tensor,需要设置属性.requires_grad=True,此时将追踪对张量的所有操作(即tensor的变化量),方便后续计算梯度。

tensor = torch.ones(n,m,requires_grad=True)

如果不想记录某个操作,请使用torch.no_grad()

with torch.no_grad():
	tensor = torch.randn(n,m)

在训练模型的代码中需要使用requires_grad=True,但在其他区域(如评估模型)不需要进行梯度计算,为防止跟踪历史记录以及过度使用内存,请在合适的区域使用torch.no_grad()。

向前计算:保留过去的值进行的计算,如求和(.sum),求平均值(.mean)等。

梯度计算:对out使用反向传播计算梯度\(\frac{\partial out}{\partial in}\)。

import torch
in = torch.randn(2,2,requires_grad=True)	#创建输入变量
var1 = in + 2		#中间变量1
var2 = var1*var1*3 	#中间变量2
out = var2.mean()	#输出
out.backward()		#反向传播
in.grad 		#输出对输入的梯度

当输出为标量时,可以调用backwad()方法,但输出为向量时,调用backward()时还需要传入其他参数。

手动实现线性回归

import numpy as np
import torch
import matplotlib.pyplot as plt

learning_rate = 0.01
#准备数据
#y = 3x + 0.8
x = torch.rand([50,1])
y_true = x*3 + 0.8

#通过模型计算y_predict
w = torch.rand([1,1],requires_grad=True)
b = torch.tensor(0,requires_grad=True,dtype=torch.float32)  #只有浮点型张量可以指定梯度

#通过循环反向传播更新参数
for i in range(2000):
    # 计算loss
    y_predict = torch.matmul(x, w) + b  # matmul为矩阵乘法
    loss = (y_true - y_predict).pow(2).mean()  #pow为幂次

    if w.grad is not None:      #.grad为累加型,需要归零
        w.data.zero_()
    if b.grad is not None:
        b.data.zero_()

    loss.backward()     #反向传播
    w.data = w.data - learning_rate*w.grad      #向梯度方向前进一个小量
    b.data = b.data - learning_rate*b.grad
    if i%500 == 0:
        print("w,b,loss",w.item(),b.item(),loss)    #每100次输出一个结果

plt.figure(figsize=(20,8))
plt.scatter(x.numpy().reshape(-1),y_true.numpy().reshape(-1))
plt.plot(x.numpy().reshape(-1),y_predict.detach().numpy().reshape(-1))
plt.show()

API

nn.Module

nn.modul是torch.nn的一个类,是自定义网络的一个基类。

自定义网络时,有两个方法需要特别注意

1. __init__需要调用super方法,继承父类的属性和方法

2. farward方法必须实现,用来定义网络的向前计算的过程

用y=wx+b模型举例如下

from torch import nn
class Lr(nn.Module):
	def __init__(self):
		super(Lr,self).__init__() #继承父类init参数
		self.linear = nn.Linear(1,1)
		
	def forward(self,x):
		out = self.linear(x)
		return out

nn.Linear为torch预定义的线性模型,即全链接层,传入的参数为输入的数量,输出的数量(in_features,out_features)。

nn.Module定义了__call__方法,实现的就是调用forward方法,即Lr的实例。

# 实例化模型
model = Lr()
# 传入数据,计算结果
predict = model(x)

优化器类

优化器(optimizer),可以理解为更新参数的方法,比如常见的随机梯度下降(stochastic gradient desent, SGD)

优化器类是由torch.optim提供的,例如

1. torch.optim.SGD(参数,学习率)

2. torch.optim.Adam(参数,学习率)

参数可以通过model.parameters来获取,获取模型中所有requires_grad=True的参数。

优化类的使用方法:

1. 实例化

2. 将所有参数的梯度值置为0

3. 反向传播计算梯度

4. 更新参数值

示例如下:

optimizer = optim.SGD(model.parameters(), lr=1e-3)	# 实例化
optimizer.zero_grad()	# 梯度置为0
loss.backward()		# 计算梯度
optimizer.step()	# 更新参数

损失函数

1. 均方误差:nn,MSEloss() # 常用于分类问题

2. 交叉熵损失:nn.CrossEntoptLoss() #常用于逻辑回归

使用方法

model = Lr()	# 实例化模型
criterion = nn.MSELoss()	# 实例化损失函数
optimizer = optim.SGD(model.parameters(), lr=1e-3)	# 实例化优化器类
for i in range(100)
	y_predict = model(x_true)	#向前计算预测值
	loss = criterion(y_true,y_predict)	#计算损失结果
	optimizer.zero_grad()	# 当前循环参数梯度置为0
	loss.backward()		# 计算梯度
	optimizer.step()	# 更新参数

调用实现线性回归

import torch
from torch import nn
import torch.optim as optim  # 修正拼写错误
import numpy as np
import matplotlib.pyplot as plt

# 1. 定义数据
x = torch.rand([50, 1])
y = x * 3 + 0.8

# 2. 定义模型
class Lr(nn.Module):
    def __init__(self):
        super(Lr, self).__init__()
        self.linear = nn.Linear(1, 1)  # 创建一个线性层,输入维度为1,输出维度也为1

    def forward(self, x):
        out = self.linear(x)
        return out

# 3. 实例化模型、损失函数和优化器
model = Lr()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)

# 4. 训练模型
for i in range(100):
    out = model(x)
    loss = criterion(y, out)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if (i + 1) % 20 == 0:
        print(f"Epoch [{i+1}/100], Loss: {loss.item():.6f}")

# 5. 模型评估
model.eval()
predict = model(x)
predict = predict.data.numpy()
plt.scatter(x.data.numpy(), y.data.numpy(), c="r")
plt.plot(x.data.numpy(), predict)  # 修正拼写错误
plt.show()

参考文献

哔哩哔哩:Python-知识库:【Pytorch入门到实战】卷积神经网络、循环神经网络、机器学习、深度学习、AI人工智能全套学习教程(附项目实战源码)一口气全都学完!