简单的神经网络

前阵子540实验室的前辈们给我们出了一些题目,督促我们学习,以下是题目要求:

1.用pytorch 实现两层神经网络拟合,不使用model。题目:一个全连接的ReLU神经网络,一个隐藏层,没有bias,用来从x预测y。
(可自动求导)

2.用numpy 实现两层神经网络。题目:一个全连接的ReLU神经网络,一个隐藏层,没有bias,用来从x预测y。(不可自动求导)

3.用pytorch 实现两层神经网络,不使用model。题目:一个全连接的ReLU神经网络,一个隐藏层,没有bias,用来从x预测y。(不可自动求导)

这个三道好像是经典例题,算法和思想是一致的,题目之间的区别就只是所用工具,直接先去b站看吴恩达先生的视频,P44这里,重点看一下P51那个反向传播算法。

link

这是这个模型的图片如图所示有三层,分别是输入层、隐藏层,输出层(画技不行,别骂了。)
第一层是输入层X,样本数量为 N=64,样本维度为 D_in=1000;第二层为隐藏层H(因为在机器跑代码的时候我们看不到这个层上面的数据变化,看不见=隐藏。。。。。。),维度为 h=100;第三层为输出层Y,输出维度为 D_out=10.
输出层和隐藏层个点之间连线上的权为W1,矩阵的大小为(D_in,H),隐藏层和输出层之间的权为W2,大小为(H,D_out)

首先是数据准备,在torch使用.randn()生成,标准正态分布。

#数据准备
X = torch.randn(N,D_in)#输入
y = torch.randn(N,D_out)#输出
W1 = torch.randn(D_in,H,requires_grad=True)#权值
W2 = torch.randn(H,D_out,requires_grad=True)#权值

#自动求导才需要下面这段
W1 = Variable(W1,requires_grad=True)#封装
W2 = Variable(W2,requires_grad=True)#封装
#参与自动求导发生改变的数据封装后才能进行自动求导

设定学习率 LEARNING_RATE = 1e-6,训练轮次 EPOCH = 500。

先进行前向传播

h = X * W1#隐藏层输入为 NH
h_relu = relu(h)#激活函数,大于0的数字表述神经元兴奋,小于等于0表示神经元抑制
y_pred = h_relu * W2#输出维度为 N
D_in

#接下来的操作使用了BP算法的思想(根据预测值与真实值之间的误差来反向改变权重,神经元若有阈值也可以通过计算被改变,进而得到误差率更小的模型参数,优化模型)

计算损失值

loss = (y_pred - y)^2

计算反向传播
正数第二条式子就是 W2 的改变量,最后一条就是 W1 的改变量

// 反向传播代码段及各函数意义
grad_y_pred = 2.0 * (y_pred-y) # 64*10
grad_W2 = h_relu.t().mm(grad_y_pred)#torch.t() 矩阵转置  100*10
grad_h_relu = grad_y_pred.mm(W2.t()) # 64*100
grad_h = grad_h_relu.clone()#torch.clone() 复制一份tensor(新的内存空间) 64*100
grad_h[h<0] = 0#grad_h[h < 0] 小于0的值置为0 64*100
grad_W1 = X.t().mm(grad_h) #1000 *100

最后便是权重的更新,使用随机梯度下降

//update of weight
W1 = W1 - LEARNING_RATE * grad_W1
W2 = W2 - LEARNING_RATE * grad_W2

题目一的代码

// pytorch + 自动求导
import numpy
import torch
from torch.autograd import Variable

N,D_in,H,D_out = 64,1000,100,10 # N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度\n",

#数据的准备
X = torch.randn(N,D_in)#输入
y = torch.randn(N,D_out)#输出
W1 = torch.randn(D_in,H,requires_grad=True)#权值
W2 = torch.randn(H,D_out,requires_grad=True)#权值

W1 = Variable(W1,requires_grad=True)#封装
W2 = Variable(W2,requires_grad=True)#封装

#学习率和训练轮次
LEARNING_RATE = 1e-6
EPOCH = 500

for t in range(EPOCH):
    y_pred = X.mm(W1).clamp(min=0).mm(W2)#torch.mm() 矩阵相乘;torch.clamp() 将元素限制在【0,max】范围里,返回的是张量

    loss = (y_pred - y).pow(2).sum()#torch.pow() 每个元素平方;torch.sum() 所有元素的和
    print(t,loss.item())#torch.item() 将张量转换成标量

    loss.backward()#反向传播,计算当前梯度

    with torch.no_grad():#接下来的计算不被记录,不需要创建计算图
        W1 -= LEARNING_RATE * W1.grad
        W2 -= LEARNING_RATE * W2.grad

        W1.grad.zero_()#梯度清零,不然会累加
        W2.grad.zero_()#梯度清零,不然会累加

题目二的代码

// numpy + 手动求导
import numpy

N,D_in,H,D_out = 64,1000,100,10 # N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度\n",

#数据准备
X = numpy.random.randn(N, D_in)#输入
y = numpy.random.randn(N, D_out)#输出
W1 = numpy.random.randn(D_in, H)#权值
W2 = numpy.random.randn(H, D_out)#权值

#学习率和训练轮次
LEARNING_RATE = 1e-6
EPOCH = 500

for t in range(EPOCH):

    h = X.dot(W1)# h = W1 * X numpy.dot() 矩阵相乘
    h_relu = numpy.maximum(0, h)# h_relu = relu(h) numpy.maximu() 把元素限制在【0,max】范围内
    y_pred = h_relu.dot(W2)# y_pred = W2 * h_relu

    loss = numpy.square(y_pred - y).sum()#numpy.square() 元素平方
    print(t, loss)

    grad_y_pred = 2.0 * (y_pred - y)
    grad_W2 = h_relu.T.dot(grad_y_pred)#numpy.T() 矩阵转置
    grad_h_relu = grad_y_pred.dot(W2.T)
    grad_h = grad_h_relu.copy()#numpy.copy() 复制一份ndarray(新的内存空间)
    grad_h[h < 0] = 0#grad_h[h < 0] 小于0的值置为0
    grad_W1 = X.T.dot(grad_h)

    W1 = W1 - LEARNING_RATE * grad_W1
    W2 = W2 - LEARNING_RATE * grad_W2

题目三的代码

// pytorch + 手动求导
import torch

N,D_in,H,D_out = 64,1000,100,10 # N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度\n",

#数据准备
X = torch.randn(N,D_in)#输入
y = torch.randn(N,D_out)#输出
W1 = torch.randn(D_in,H,requires_grad=True)#权值
W2 = torch.randn(H,D_out,requires_grad=True)#权值

#学习率和训练轮次
LEARNING_RATE = 1e-6
EPOCH = 500

for t in range(EPOCH):

    h = X.mm(W1)# h = W1 * X
    h_relu = h.clamp(min=0)# h_relu = relu(h)
    y_pred = h_relu.mm(W2)# y_pred = W2 * h_relu

    loss = (y_pred-y).pow(2).sum().item()#平方求和再转换成标量
    print(t,loss)

    grad_y_pred = 2.0 * (y_pred-y) # 64*10
    grad_W2 = h_relu.t().mm(grad_y_pred)#torch.t() 矩阵转置  100*10
    grad_h_relu = grad_y_pred.mm(W2.t()) # 64*100
    grad_h = grad_h_relu.clone()#torch.clone() 复制一份tensor(新的内存空间) 64*100
    grad_h[h<0] = 0#grad_h[h < 0] 小于0的值置为0 64*100
    grad_W1 = X.t().mm(grad_h) #1000 *100

    #随机梯度下降
    W1 = W1 - LEARNING_RATE * grad_W1
    W2 = W2 - LEARNING_RATE * grad_W2


没有解决的问题
1.发现题目一梯度更新这一块其实可以写成

#梯度更新
#with torch.no_grad():#接下来的计算不被记录,不需要创建计算图
W1.data -= LEARNING_RATE * W1.grad.data
W2.data -= LEARNING_RATE * W2.grad.data

W1.grad.data.zero_()  # 梯度清零,不然会累加
W2.grad.data.zero_()  # 梯度清零,不然会累加

网上的解释为将variable参数从网络中隔离开,不参与参数更新,个人的理解是为了防止权重更新步骤进行反向传播。

心得
手动求导还行,自动求导这里磨了半天,我觉得不行
在这里插入图片描述

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
PyTorch提供了一种简洁的方式来构建神经网络,即使用torch.nn.Sequential类。这个类是torch.nn中的一种序列容器,它可以通过嵌套多个实现具体功能的类来构建神经网络模型[1]。 在torch.nn.Sequential中,我们可以按照顺序添加各个层和操作。例如,我们可以使用torch.nn.Linear类完成输入层到隐藏层的线性变换,并使用torch.nn.ReLU类作为激活函数,最后再添加另一个torch.nn.Linear类完成隐藏层到输出层的线性变换。下面是一个示例代码: ```python import torch import torch.nn as nn input_data = 10 hidden_layer = 20 output_data = 5 model = nn.Sequential( nn.Linear(input_data, hidden_layer), nn.ReLU(), nn.Linear(hidden_layer, output_data) ) # 这段代码定义了一个简单神经网络,包括一个输入层、一个隐藏层和一个输出层,其中输入数据的维度为10,隐藏层的维度为20,输出数据的维度为5。 print(model) ``` 在上面的示例中,我们首先导入了torch和torch.nn模块,然后定义了输入数据的维度为10,隐藏层的维度为20,输出数据的维度为5的神经网络模型。接着使用nn.Sequential类按照顺序添加了三个层,包括一个线性层和两个线性层之间的ReLU激活函数。最后,我们打印出了这个模型的结构。 需要注意的是,这个示例只是一个简单的示例,你可以根据自己的需求来修改神经网络的结构和参数。希望这个示例能对你有所帮助。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值