目录

 

一、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)优化方法选用不当

等等。