目录
一、numpy实现
二、pytorch实现
1、手动求导
2、自动求导
三、torch.nn+torch.optim实现实现
1、torch.nn实现自动求导
2、optim选择并执行各种优化方法
3、继承torch.nn.Module类封装模型
四、模型优化效果差原因
一、numpy实现
用numpy写两层神经网络:一个全连接层,一个relu激活函数,没有bias,用x预测y,使用L2 Loss。意味着只有一个隐藏层。
- ℎ=𝑊1𝑋+𝐵1h=W1X+B1
- 𝑟=𝑚𝑎𝑥(0,ℎ)r=max(0,h)
- 𝑌_pred=𝑊2𝑟+𝑏2
- 这一实现完全使用numpy来计算前向神经网络,loss和反向传播
- forward pass
- loss
- backward pass
- numpy ndarray是一个普通的n维array。它不知道任何关于深度学习或者梯度(gradient)的知识,也不知道计算图(computation graph),只是一种用来计算数学运算的数据结构。
- 定义基本的信息:N:64个输入,D_in:1000维,H:中间的隐藏层 100维,D_out:输出10维。
#拟合X和Y,将x通过线性和非线性变换,成为与y相近的数据
import numpy as np
#随机创建训练数据
N,D_in,H,D_out=64,1000,100,10
x=np.random.randn(N,D_in)#矩阵常用大写,数据常用小写
y=np.random.randn(N,D_out)
w1=np.random.randn(D_in,H) #把(64,1000)的输入矩阵转为(64,100)的隐藏层,为(1000,100)的矩阵
w2=np.random.randn(H,D_out)#把(64,100)的隐藏层转为(64,10)的输出矩阵,为(100,10)的矩阵
learning_rate=1e-6#学习率 10 -6次方,不要太大也不要太小
for it in range(500):
#Forward pass 没有bias
h=x.dot(w1)#把(64,1000)的输入矩阵转x为(64,100)的隐藏层h
h_relu=np.maximum(h,0)
y_pred=h_relu.dot(w2)#把(64,100)的隐藏层h转为(64,10)的输出矩阵Y
#Compute Loss
loss=np.square(y_pred-y).sum()
print(it,loss)#t是第几次迭代训练
#Backward pass
#Compute the gradient
grad_y_pred=2.0*(y_pred-y)
grad_w2= h_relu.T.dot(grad_y_pred)#h_relu是(64,100)维的矩阵,grad_y_pred是(64,10)维的矩阵,要想相乘,把h_relu进行转置,即h_relu.T
grad_h_relu=grad_y_pred.dot(w2.T)
grad_h=grad_h_relu.copy()
grad_h[h<0]=0
grad_w1=x.T.dot(grad_h)
#Update weights of w1 and w2
w1-=learning_rate*grad_w1
w2=learning_rate*grad_w2
二、pytorch实现
一个Pytorch Tensor很像一个numpy懂得ndarray。但是它和numpy ndarray的最大区别是,Pytorch Tensor 可以在CPU或者GPU上运算。如果想要在GPU上运算,就要把tensor转换成cuda类型。改的地方:数据生成;矩阵乘法;矩阵转置;矩阵克隆;relu函数;L2损失;
1、手动求导
#拟合X和Y,将x通过线性和非线性变换,成为与y相近的数据
import torch
#随机创建训练数据
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)#要在GPU上运行的话,加上.to("cuda:0")或者.cuda(),第一个常用,指定cuda
y=torch.randn(N,D_out)
w1=torch.randn(D_in,H) #把(64,1000)的输入矩阵转为(64,100)的隐藏层,为(1000,100)的矩阵
w2=torch.randn(H,D_out)#把(64,100)的隐藏层转为(64,10)的输出矩阵,为(100,10)的矩阵
learning_rate=1e-6#学习率 10 -6次方,不要太大也不要太小
for it in range(500):
#Forward pass 没有bias
h=x.mm(w1)#把(64,1000)的输入矩阵转x为(64,100)的隐藏层h
h_relu=h.clamp(min=0)#clamp函数可以设置最大最小值,h.clamp(min=0,max=100)
y_pred=h_relu.mm(w2)#把(64,100)的隐藏层h转为(64,10)的输出矩阵Y
#Compute Loss
loss=(y_pred-y).pow(2).sum().item()#现在loss是一个tensor,要把它转为数字才能print
print(it,loss)#t是第几次迭代训练
#Backward pass
#Compute the gradient
grad_y_pred=2.0*(y_pred-y)
grad_w2= h_relu.t().mm(grad_y_pred)#h_relu是(64,100)维的矩阵,grad_y_pred是(64,10)维的矩阵,要想相乘,把h_relu进行转置,即h_relu.t()
grad_h_relu=grad_y_pred.mm(w2.t())
grad_h=grad_h_relu.clone()
grad_h[h<0]=0
grad_w1=x.t().mm(grad_h)
#Update weights of w1 and w2
w1-=learning_rate*grad_w1
w2=learning_rate*grad_w2
2、自动求导
# pytorch自动求导
# 拟合X和Y,将x通过线性和非线性变换,成为与y相近的数据
import torch
# 随机创建训练数据
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in) # 要在GPU上运行的话,加上.to("cuda:0")或者.cuda(),第一个常用,指定cuda
y=torch.randn(N,D_out) # x和y不需要梯度,计算也只是浪费内存
w1=torch.randn(D_in,H,requires_grad=True) # 把(64,1000)的输入矩阵转为(64,100)的隐藏层,为(1000,100)的矩阵,requires_grad=True声明自动求导时需要它的导数,未声明的不会对它自动求导
w2=torch.randn(H,D_out,requires_grad=True) # 把(64,100)的隐藏层转为(64,10)的输出矩阵,为(100,10)的矩阵
learning_rate=1e-6 # 学习率 10 -6次方,不要太大也不要太小
for it in range(500):
# Forward pass 没有bias
y_pred=x.mm(w1).clamp(min=0).mm(w2) # 把(64,1000)的输入X转为(64,10)的输出矩阵Y,更是简单,直接写成一个,方便后面自动求导
# Compute Loss
loss=(y_pred-y).pow(2).sum() # 现在loss是一个tensor,要把它转为数字才能print,先不能用item(),后面求导会出错。
print(it,loss.item()) # t是第几次迭代训练
# Backward pass
# Compute the gradient
loss.backward() # 自动求导,所有的变量导数都有,用的时候x.grad、y.grad即可
# Update weights of w1 and w2
w1=w1-learning_rate*w1.grad # torch变量带requires_grad 的不能进行+=,-=等操作
w1.retain_grad()#导数中间结果清零,不清零的话下一个导数会自动加上上一个导数再做运算,导致导数越来越大
w2=w2-learning_rate*w2.grad
w2.retain_grad()
三、torch.nn+torch.optim实现实现
1、torch.nn实现自动求导
Pytorch:nn库,内含各种定义neural network的方法。用pytorch autograd来构件计算图和计算gradients,然后PyTorch会帮助我们自动计算gradient
定义方法的model,进行实例化
import torch.nn as nn
import torch
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
model=torch.nn.Sequential(
torch.nn.Linear(D_in,H, bias=False),#x*w1+bias,x是model的输入,CLASS torch.nn.Linear(in_features, out_features, bias=True)
torch.nn.ReLU(),#对上面结果做
torch.nn.Linear(H,D_out,bias=False)
)
torch.nn.init.normal_(model[0].weight)#normal初始化权重,使得训练更加优化
torch.nn.init.normal_(model[2].weight)
#如果想要在cuda上面运行的话,要model=model.cuda()
loss_fn=nn.MSELoss(reduction='sum')#定义实例对象返回平方值的和,默认是def __init__(self, size_average=None, reduce=None, reduction: str = 'mean') -> None:
learning_rate=1e-6
for it in range(500):
y_pred=model(x)
loss=loss_fn(y_pred,y)#自动调用函数做L2损失
print(it,loss.item())
#模型里的中间参数全部清零,在下一次backward之前清零即可
model.zero_grad()
loss.backward()
#模型自动封装了中间产生的参数w1和w2,在model.parameters()内,w1,w2,b1,b2...
with torch.no_grad():
for param in model.parameters():#param包含一个tensor,一个grad,目的是tensor-grad
param-=learning_rate*param.grad
2、optim选择并执行各种优化方法
PyTorch:optim :这次我们不再手动更新模型的权重,而是使用optim这个包老帮助我们更新参数。optim这个pakage提供各种不同模型的优化方法,包括SGD(随机梯度下降)+momentum,RMSProp,Adam等。刚才的模型里是梯度下降方法来做优化,但是还需要手写,optim是一个含有多种优化方法的包,不用手写。
import torch.nn as nn
import torch
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
model=torch.nn.Sequential(
torch.nn.Linear(D_in,H, bias=False),#x*w1+bias,x是model的输入,CLASS torch.nn.Linear(in_features, out_features, bias=True)
torch.nn.ReLU(),#对上面结果做
torch.nn.Linear(H,D_out,bias=False)
)
#torch.nn.init.normal_(model[0].weight)
#torch.nn.init.normal_(model[2].weight)
#如果想要在cuda上面运行的话,要model=model.cuda()
loss_fn=nn.MSELoss(reduction='sum')#定义实例对象返回平方值的和,默认是def __init__(self, size_average=None, reduce=None, reduction: str = 'mean') -> None:
learning_rate=1e-4#一般取1e-4——1e-3
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)#Adam优化函数,传入所有的参数和学习率
#optimizer=torch.optim.SGD(model.parameters(),lr=learning_rate) 另一种优化方法
for it in range(500):
y_pred=model(x)
loss=loss_fn(y_pred,y)#自动调用函数做L2损失
print(it,loss.item())
#模型里的中间参数全部清零,在瞎子次backward之前清零有关
optimizer.zero_grad()
loss.backward()
optimizer.step()
3、继承torch.nn.Module类封装模型
1、定义数据、输出输出tensor、学习率(一般在[1e-4,1e-3])
2、定义class封装模型结构和前向传播,以计算y_pred,并实例化class对象(最重要的就是每个方法的输入和输出),torch.nn.Linear方法的需要的参数是输入维度和输出维度,实例化对象自动调用的方法需要Linear传入的tensor,如:w*x+b,其中x即为输入tensor
3、定义损失函数的实例化对象(torch.nn.MSELoss(reducion="sum)),设置加和还是计算平均
4、定义优化函数实例化对象(torch.optim.Adam(model.parameters(),lr=learning_rate)传入需要优化的桉树和学习率
5、进行迭代:
model(x)计算y_pred
计算损失:loss=loss_fn(y_pred,y)
参数清零:optim.zero_grad()
反向传播:loss.backward()
参数优化:optim.step()
#引入包
import torch
#定义比那里那个和输入输出
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)#模型输入
y=torch.randn(N,D_out)#真实结果
learning_rate=1e-4#学习率
#定义模型结构和前向传播,继承torch.nn.Module类,封装进一个类中
class TwoLayerNet(torch.nn.Module):
def __init__(self,D_in,H,D_out):#初始化函数中定义模型结构,两个线性层,分别需要输入维度和输出维度
super(TwoLayerNet,self).__init__()#运行父类的初始化方法,继承父类的一些初始化信息
self.linear1=torch.nn.Linear(D_in,H,bias=False)#对torch.nn.Linear方法进行实例化对象,初始化需要输入维度和输出维度两个参数,本次训练bias,设为False,默认诶我True
self.linear2=torch.nn.Linear(H,D_out,bias=False)
def forward(self,x):#定义前向传播方法,需要模型的初始输入进行前行传播
y_pred=self.linear2(self.linear1(x).clamp(min=0))#self.linear的实例化对象调用其内置函数进行linear运算,需要输入数据这个参数(w*x+b),x即为传入值,w和b是模型内置参数,封装在model.paramizes()内
return y_pred
#定义模型实例化对象
model=TwoLayerNet(D_in,H,D_out)
#定义损失函数实例化对象
loss_fn=torch.nn.MSELoss(reduction="sum")
#定义优化方法实例化对象,传入需要优化的参数和学习率
optimi=torch.optim.Adam(model.parameters(),lr=learning_rate)
#模型迭代
for it in range(500):
#运行model取得预测结果
y_pred=model(x)
#计算损失loss
loss=loss_fn(y_pred,y)
print(it,loss.item())
#进行参数清零,防止其叠加
optimi.zero_grad()
#进行反向传播
loss.backward()
#进行参数优化
optimi.step()
四、模型优化效果差原因
模型优化效果差可以从最后输出的损失结果中看,如果损失下降的慢且损失一直很大,则模型优化效果差。
原因有:
(1)参数初始化差,可以nomal初始化:
在模型定义之后加:torch.nn.init.normal_(model[0].weight)
torch.nn.init.normal_(model[2].weight)
(2)设置的学习率太低
(3)优化方法选用不当
等等。