走出第一步
有了大小和方向,接下来我们就可以开始走出我们的第一步了。来看权重的迭代公式:
现在我们的偏导数部分已经计算出来了,就是我们使用backward求解出的结果。而学习率,或者步长,是我们在迭代开始之前就人为设置好的,一般是0.01-0.5之间的某个小数。因此现在我们已经可以无障碍地使用代码实现权重的迭代了
先定义模型并进行反向传播:
import torch
from torch.nn import functional as F
import torch.nn as nn
#随机生成数据
torch.manual_seed(212)
X=torch.rand((500,20),dtype=torch.float32)*100
y=torch.randint(low=0,high=3
,size=(500,) #交叉熵损失函数要求y是一维的
,dtype=torch.float32) #交叉熵损失函数要求y是int64类型,调用时再转int
#定义输入及输出的特征量
input_=X.shape[1]
output_=len(y.unique())
#定义神经网络架构
#3分类,500个样本,20个特征,共3层,第一层13个神经元,第二层8个神经元
#第一层激活函数ReLU,第二层sigmoid
class Model(nn.Module):
def __init__(self,in_features=40,out_features=2):
super().__init__()
self.linear1=nn.Linear(in_features,13,bias=False)
self.linear2=nn.Linear(13,8,bias=False)
self.output=nn.Linear(8,out_features,bias=True)
def forward(self,x):
sigma1=torch.relu(self.linear1(x))
sigma2=torch.sigmoid(self.linear2(sigma1))
zhat=self.output(sigma2)
return zhat
net=Model(in_features=input_,out_features=output_)
zhat=net.forward(X)
#定义损失函数,使用交叉熵CrossEntropyLoss
criterion=nn.CrossEntropyLoss()
loss=criterion(zhat,y.long()) #要求y是int64类型
loss.backward()
net.linear1.weight.grad #查看梯度值
走出第一步的代码
#w(t+1)=w(t)-步长*grad
lr=0.1 #定义学习率
w=net.linear1.weight.data
dw=net.linear1.weight.grad
w-=lr*dw
w #查看w变化
从第一步到第二步:动量法Momentum
在反向传播优化算法中,动量法(Momentum)是一种常用的梯度下降优化算法的改进版。它的目标是加速梯度下降的收敛过程,并借鉴前面走过的方向,利用历史梯度方向中的信息。
我们让上一步的梯度向量(的反方向) 与现在这一点的梯度向量 (的反方向)以加权的方式求和,求解出受到上一步大小和方向影响的真实下降方向,再让坐标点向真实下降方向移动。在坐标轴上,可以表示为:
其中,对上一步的梯度向量加上的权重被称为动量参数(也叫做衰减力度,通常使用进行表示),对这一点的梯度向量加上的权重就是步长(依然表示为),真实移动的向量为,被称为“动量”(Momentum)。将上述过程使用公式表示,则有:
在第一步中,没有历史梯度方向,因此第一步的真实方向就是起始点梯度的反方向,。其中代表了之前所有步骤所累积的动量和(其实也就代表着上一步的真真实方向)。在这种情况下,梯度下降的方向有了“惯性”,受到历史累计动量的影响,当新坐标点的梯度反方向与历史累计动量的方向一致时,历史累计动量会加大实际方向的步子,相反,当新坐标点的梯度反方向与历史累计动量的方向不一致时,历史累计动量会减小实际方向的步子。
在Pytorch中手工实现动量法:
#v(t)=gamma*v(t-1)-lr*dw
#w(t+1)=w(t)+v(t)
lr=0.1 #定义学习率
gamma=0.9
w=net.linear1.weight.data
dw=net.linear1.weight.grad
dw.shape #查看dw的形状
# 需要定义一个v0
v=torch.zeros(dw.shape[0],dw.shape[1])
v=gamma*v-lr*dw
w+=v
w
torch.optim实现带动量的梯度下降
步骤:
- 向前传播
- 本轮向前传播的损失函数值
- 反向传播-得到梯度
- 更新权重和动量
- 清空梯度-清除原来计算出来的,基于上一个点的坐标计算的梯度
import torch
from torch.nn import functional as F
import torch.nn as nn
import torch.optim as optim
#随机生成数据
torch.manual_seed(212)
X=torch.rand((500,20),dtype=torch.float32)*100
y=torch.randint(low=0,high=3
,size=(500,) #交叉熵损失函数要求y是一维的
,dtype=torch.float32) #交叉熵损失函数要求y是int64类型,调用时再转int
#定义输入及输出的特征量
input_=X.shape[1]
output_=len(y.unique())
#定义神经网络架构
#3分类,500个样本,20个特征,共3层,第一层13个神经元,第二层8个神经元
#第一层激活函数ReLU,第二层sigmoid
class Model(nn.Module):
def __init__(self,in_features=40,out_features=2):
super().__init__()
self.linear1=nn.Linear(in_features,13,bias=False)
self.linear2=nn.Linear(13,8,bias=False)
self.output=nn.Linear(8,out_features,bias=True)
def forward(self,x):
sigma1=torch.relu(self.linear1(x))
sigma2=torch.sigmoid(self.linear2(sigma1))
zhat=self.output(sigma2)
return zhat
net=Model(in_features=input_,out_features=output_)
#定义损失函数,使用交叉熵CrossEntropyLoss
criterion=nn.CrossEntropyLoss()
然后定义优化算法
#定义优化算法
lr=0.1 #定义学习率
gamma=0.9
opt=optim.SGD(net.parameters()#需要进行迭代的权重
,lr=lr
,momentum=gamma)
以下这段每运行一次就可迭代更新一次权重,loss.item()的值会慢慢变小
zhat=net.forward(X)
loss=criterion(zhat,y.long()) #要求y是int64类型
loss.backward()
opt.step() #走一步
opt.zero_grad()
#print(net.linear1.weight.data) #查看权重值
print(loss.item()) #查看损失值