用numpy、PyTorch自动求导、torch.nn库实现两层神经网络
用numpy、PyTorch自动求导、torch.nn库实现两层神经网络
- 1 用numpy实现两层神经网络
- 2 用PyTorch自动求导实现两层神经网络
- 2.1 手动求导
- 2.2 gradient自动求导
- 3 用torch.nn库实现两层神经网络
- 3.1 参数未标准化
- 3.2 参数标准化
- 3.3 optim方法
- 3.4自己定义模型
- 4 总结
实现从手动求导到自动求导再到模型的一步步深入。
1 用numpy实现两层神经网络
一个全连接ReLU神经网络,一个隐藏层,没有bias,L2 Loss(h是隐藏层hidden,ReLU激活函数):
- h = W 1 X + b 1 h = W_1 X + b_1 h=W1X+b1
- h r e l u = m a x ( 0 , h ) h_relu = max(0, h) hrelu=max(0,h)
- y h a t = W 2 a + b 2 y_{hat} = W_2 a + b_2 yhat=W2a+b2
下面实现时 b 1 b_1 b1、 b 2 b_2 b2都为0,没有偏置bias。
这一实现完全使用numpy来计算前向神经网络,loss,和反向传播。
numpy ndarray是一个普通的n维array。它不知道任何关于深度学习或者梯度(gradient)的知识,也不知道计算图(computation graph),只是一种用来计算数学运算的数据结构。
import numpy as npN, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是100维,输出是10维'''随机创建一些训练数据'''
X = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)W1 = np.random.randn(D_in, H) #1000维转成100维
W2 = np.random.randn(H, D_out) #100维转成10维learning_rate = 1e-6for t in range(500): #做500次迭代'''前向传播(forward pass)'''h = X.dot(W1) # N * Hh_relu = np.maximum(h, 0) #激活函数,N * Hy_hat = h_relu.dot(W2) # N * D_out'''计算损失函数(compute loss)'''loss = np.square(y_hat - y).sum() #均方误差,忽略了÷Nprint(t, loss) #打印每个迭代的损失'''后向传播(backward pass)'''#计算梯度(此处没用torch,用最普通的链式求导,最终要得到 d{loss}/dX)grad_y_hat = 2.0 * (y_hat - y) # d{loss}/d{y_hat},N * D_outgrad_W2 = h_relu.T.dot(grad_y_hat) #看前向传播中的第三个式子,d{loss}/d{W2},H * D_outgrad_h_relu = grad_y_hat.dot(W2.T) #看前向传播中的第三个式子,d{loss}/d{h_relu},N * Hgrad_h = grad_h_relu.copy() #这是h>0时的情况,d{h_relu}/d{h}=1grad_h[h<0] = 0 # d{loss}/d{h}grad_W1 = X.T.dot(grad_h) #看前向传播中的第一个式子,d{loss}/d{W1}'''参数更新(update weights of W1 and W2)'''W1 -= learning_rate * grad_W1W2 -= learning_rate * grad_W2
0 36455139.29176882
1 35607818.495988876
2 36510242.60519045
3 32972837.109358862
4 23623067.52618093
5 13537226.736260608
6 6806959.784455631
7 3501526.30896816
8 2054356.1020693523
9 1400230.6793163505
...
490 3.4950278045838633e-06
491 3.3498523609301454e-06
492 3.210762995939165e-06
493 3.0774805749939447e-06
494 2.9500114328045522e-06
495 2.827652258736098e-06
496 2.710379907890261e-06
497 2.5980242077038875e-06
498 2.490359305069476e-06
499 2.387185101594446e-06
可以看到最后的loss越来越小,现在我们看一下预测值和真实值的接近程度
y_hat - y
array([[ 9.16825615e-06, -1.53964987e-05, 6.58365129e-06,-3.08909604e-05, 1.05735798e-05, 1.73376919e-05,2.63084233e-06, -1.11662576e-05, 1.06904464e-05,-1.71528894e-05],...[-5.79062537e-06, -1.74789200e-05, 5.27619647e-06,-7.82154474e-06, 3.39896752e-06, 1.08366770e-05,8.28712496e-06, -8.88009103e-06, 5.78585909e-06,-1.14913078e-05]])
可以看到他们的差值非常小,也就是我们这个是成功的。
2 用PyTorch自动求导实现两层神经网络
2.1 手动求导
此时 1 中的代码里面有如下改动:
- import numpy as np 改成 import torch
- np.random.randn 改成 torch.randn
- dot 改成 mm
- h_relu在numpy中是 np.maximum(h, 0),在torch中是 h.clamp(min=0),限制输入下限为0
- loss在numpy中是 np.square,在torch中是 .pow(2),且要用 .item()将tensor转为数值
- 转置在numpy中是 .T,在torch中是 .t()
- copy() 改成 clone()
import torchN, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是100维,输出是10维'''随机创建一些训练数据'''
X = torch.randn(N, D_in)
y = torch.randn(N, D_out)W1 = torch.randn(D_in, H) #1000维转成100维
W2 = torch.randn(H, D_out) #100维转成10维learning_rate = 1e-6for t in range(500): #做500次迭代'''前向传播(forward pass)'''h = X.mm(W1) # N * Hh_relu = h.clamp(min=0) #激活函数,N * Hy_hat = h_relu.mm(W2) # N * D_out'''计算损失函数(compute loss)'''loss = (y_hat - y).pow(2).sum().item() #均方误差,忽略了÷Nprint(t, loss) #打印每个迭代的损失'''后向传播(backward pass)'''#计算梯度(此处没用torch,用最普通的链式求导,最终要得到 d{loss}/dX)grad_y_hat = 2.0 * (y_hat - y) # d{loss}/d{y_hat},N * D_outgrad_W2 = h_relu.t().mm(grad_y_hat) #看前向传播中的第三个式子,d{loss}/d{W2},H * D_outgrad_h_relu = grad_y_hat.mm(W2.t()) #看前向传播中的第三个式子,d{loss}/d{h_relu},N * Hgrad_h = grad_h_relu.clone() #这是h>0时的情况,d{h_relu}/d{h}=1grad_h[h<0] = 0 # d{loss}/d{h}grad_W1 = X.t().mm(grad_h) #看前向传播中的第一个式子,d{loss}/d{W1}'''参数更新(update weights of W1 and W2)'''W1 -= learning_rate * grad_W1W2 -= learning_rate * grad_W2
0 28398944.0
1 27809498.0
2 32215128.0
3 37019776.0
4 36226528.0
5 27777396.0
6 16156263.0
7 7798599.0
8 3615862.0
9 1881907.25
...
490 5.404536932474002e-05
491 5.3628453315468505e-05
492 5.282810889184475e-05
493 5.204257831792347e-05
494 5.149881326360628e-05
495 5.084666554466821e-05
496 4.9979411414824426e-05
497 4.938142956234515e-05
498 4.8661189794074744e-05
499 4.8014146159403026e-05
2.2 gradient自动求导
此时 2.1 中的代码有如下改动:
- 用 requires_grad=True 给 W1、W2 声明可以求导,如果不传这个Ture就默认是False(X,y),以节省内存
- 前向传播中,为方便计算,直接用一行代码计算 y_hat
- loss此时应该是tensor的,把前面加的 item() 去掉,但是要把 .item() 放在下面打印损失函数的地方
- 把计算梯度的所有步骤去掉,直接用 loss.backward() 代替
- 把前面手动计算的 grad_W1 改为 W1.grad(W2同理)
- 在参数更新之后,用 W1.grad.zero_() 把W1的梯度清零(W2同理)
- 用 with torch.no_grad(): 避免电脑记住W1、W2的计算图,占据内存
import torchN, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是100维,输出是10维'''随机创建一些训练数据'''
X = torch.randn(N, D_in)
y = torch.randn(N, D_out)W1 = torch.randn(D_in, H, requires_grad=True) #1000维转成100维
W2 = torch.randn(H, D_out, requires_grad=True) #100维转成10维learning_rate = 1e-6for t in range(500): #做500次迭代'''前向传播(forward pass)'''y_hat = X.mm(W1).clamp(min=0).mm(W2) # N * D_out'''计算损失函数(compute loss)'''loss = (y_hat - y).pow(2).sum() #均方误差,忽略了÷N,loss就是一个计算图(computation graph)print(t, loss.item()) #打印每个迭代的损失'''后向传播(backward pass)'''loss.backward()'''参数更新(update weights of W1 and W2)'''with torch.no_grad():W1 -= learning_rate * W1.gradW2 -= learning_rate * W2.gradW1.grad.zero_()W2.grad.zero_()
0 28114322.0
1 22391836.0
2 19137772.0
3 16153970.0
4 12953562.0
5 9725695.0
6 6933768.5
7 4784875.0
8 3286503.0
9 2288213.25
...
490 3.3917171094799414e-05
491 3.35296499542892e-05
492 3.318845119792968e-05
493 3.276047937106341e-05
494 3.244510298827663e-05
495 3.209296482964419e-05
496 3.168126931996085e-05
497 3.1402159947901964e-05
498 3.097686203545891e-05
499 3.074205596931279e-05
3 用torch.nn库实现两层神经网络
3.1 参数未标准化
此时 2.2 中的代码有如下改动:
- import torch 改为 import torch.nn as nn(nn 就是 neural network)
- 不需要定义W1、W2了,直接定义model = torch.nn.Sequential(一串模型拼到一起)
- 后续的y_hat的计算只需要model(x)就可以了
- loss也不用写那么复杂,用函数 loss_fn = nn.MSELoss(reduction=‘sum’) 即可,下文loss就引用这个函数
- 参数更新中的参数也要可以直接用 for 循环从模型中得到
- 梯度清零也一样:model.zero_grad()
import torch.nn as nn #各种定义 neural network 的方法N, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是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=True), # W1 * X + b,默认Truetorch.nn.ReLU(),torch.nn.Linear(H, D_out)
)# model = model.cuda() #这是使用GPU的情况loss_fn = nn.MSELoss(reduction='sum')learning_rate = 1e-6for t in range(500): #做500次迭代'''前向传播(forward pass)'''y_hat = model(X) # model(X) = model.forward(X), N * D_out'''计算损失函数(compute loss)'''loss = loss_fn(y_hat, y) #均方误差,忽略了÷N,loss就是一个计算图(computation graph)print(t, loss.item()) #打印每个迭代的损失'''后向传播(backward pass)'''loss.backward()'''参数更新(update weights of W1 and W2)'''with torch.no_grad():for param in model.parameters():param -= learning_rate * param.grad #模型中所有的参数更新model.zero_grad()
0 686.78662109375
1 686.2665405273438
2 685.7469482421875
3 685.2279663085938
4 684.7101440429688
5 684.1931762695312
6 683.6768188476562
7 683.1609497070312
8 682.6456909179688
9 682.130859375
...
490 496.4220275878906
491 496.12548828125
492 495.82940673828125
493 495.533203125
494 495.2373046875
495 494.94171142578125
496 494.6462707519531
497 494.35101318359375
498 494.0567321777344
499 493.7628479003906
model
Sequential((0): Linear(in_features=1000, out_features=100, bias=True)(1): ReLU()(2): Linear(in_features=100, out_features=10, bias=True)
)
model[0]
Linear(in_features=1000, out_features=100, bias=True)
model[0].weight
Parameter containing:
tensor([[-0.0147, -0.0315, 0.0085, ..., 0.0039, 0.0254, -0.0308],[ 0.0046, 0.0125, 0.0128, ..., -0.0241, -0.0206, -0.0127],[-0.0162, 0.0051, 0.0152, ..., -0.0280, -0.0133, 0.0079],...,[ 0.0239, 0.0237, -0.0025, ..., 0.0290, -0.0192, 0.0187],[-0.0249, 0.0287, 0.0060, ..., -0.0198, 0.0007, 0.0209],[ 0.0238, -0.0157, -0.0156, ..., 0.0105, 0.0057, -0.0189]],requires_grad=True)
3.2 参数标准化
3.1 中的代码更新的很慢,可能是参数初始化不好,于是我们用 torch.nn.init.normal_ 对第0层和第2层(也就是Linear层)的weight标准化:
import torch.nn as nn #各种定义 neural network 的方法N, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是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=True), # W1 * X + b,默认Truetorch.nn.ReLU(),torch.nn.Linear(H, D_out)
)torch.nn.init.normal_(model[0].weight)
torch.nn.init.normal_(model[2].weight)# model = model.cuda() #这是使用GPU的情况loss_fn = nn.MSELoss(reduction='sum')learning_rate = 1e-6for t in range(500): #做500次迭代'''前向传播(forward pass)'''y_hat = model(X) # model.forward(), N * D_out'''计算损失函数(compute loss)'''loss = loss_fn(y_hat, y) #均方误差,忽略了÷N,loss就是一个计算图(computation graph)print(t, loss.item()) #打印每个迭代的损失'''后向传播(backward pass)'''loss.backward()'''参数更新(update weights of W1 and W2)'''with torch.no_grad():for param in model.parameters():param -= learning_rate * param.grad #模型中所有的参数更新model.zero_grad()
0 34311500.0
1 32730668.0
2 33845940.0
3 31335464.0
4 23584192.0
5 14068799.0
6 7252735.5
7 3674312.0
8 2069563.0
9 1346445.75
...
490 7.143352559069172e-05
491 7.078371709212661e-05
492 7.009323599049821e-05
493 6.912354729138315e-05
494 6.783746357541531e-05
495 6.718340591760352e-05
496 6.611335265915841e-05
497 6.529116944875568e-05
498 6.444999598897994e-05
499 6.381605635397136e-05
model[0].weight
Parameter containing:
tensor([[ 0.1849, -0.2587, 1.6247, ..., -0.8608, -2.2139, -1.3076],[-0.5197, 0.0600, 0.2141, ..., 0.0561, -0.1613, -0.3905],[-0.5303, -0.1129, -0.2974, ..., -0.6166, -3.4082, 0.0969],...,[-0.4742, 0.2449, -1.5979, ..., -0.6195, -0.2970, -1.3764],[-0.1131, 0.4973, 0.7679, ..., 0.1231, 0.6992, 0.4403],[-0.1557, 0.8185, 0.7784, ..., -0.9993, 0.3424, -1.1116]],requires_grad=True)
3.3 optim方法
前面的梯度下降方法还是很蠢,是手动更新模型的weights,3.3 中我们使用optim包来帮助我们更新参数,optim这个package提供了各种不同的模型优化方法,包括 SGD+momentum, RMSProp, Adam 等等。
在 3.2 的基础上继续改进:
- 用optim包一般 learning_rate 是1e-4
- 定义optimizer,使用 Adam 优化
- 参数更新整个部分可以用一句 optimizer.step() 代替,表示一步把所有参数全更新
- 用 optimizer.zero_grad() 给梯度清零
import torch.nn as nn #各种定义 neural network 的方法N, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是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=True), # W1 * X + b,默认Truetorch.nn.ReLU(),torch.nn.Linear(H, D_out)
)# torch.nn.init.normal_(model[0].weight)
# torch.nn.init.normal_(model[2].weight)# model = model.cuda() #这是使用GPU的情况loss_fn = nn.MSELoss(reduction='sum')
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)for t in range(500): #做500次迭代'''前向传播(forward pass)'''y_hat = model(X) # model.forward(), N * D_out'''计算损失函数(compute loss)'''loss = loss_fn(y_hat, y) #均方误差,忽略了÷N,loss就是一个计算图(computation graph)print(t, loss.item()) #打印每个迭代的损失optimizer.zero_grad() #求导之前把 gradient 清空'''后向传播(backward pass)'''loss.backward()'''参数更新(update weights of W1 and W2)'''optimizer.step() #一步把所有参数全更新
0 677.295166015625
1 660.0888061523438
2 643.3673095703125
3 627.08642578125
4 611.1599731445312
5 595.6091918945312
6 580.5427856445312
7 565.9138793945312
8 551.620849609375
9 537.651123046875
...
490 9.944045586962602e-09
491 9.147494317574001e-09
492 8.492017755656889e-09
493 7.793811818146423e-09
494 7.225093412444039e-09
495 6.644597760896431e-09
496 6.126881668677697e-09
497 5.687876836191208e-09
498 5.240272660245182e-09
499 4.8260742069317075e-09
在这里又需要把标准化的部分注释掉,否则会更新的非常糟糕。(这一点很奇怪,换了 learning_rate 和 优化方法又会变,可能又需要标准化了)
3.4自己定义模型
一般都是从 torch.nn.Module 里面继承新的模型。
import torch.nn as nn #各种定义 neural network 的方法N, D_in, H, D_out = 64, 1000, 100, 10 #64个训练数据(只是一个batch),输入是1000维,hidden是100维,输出是10维'''随机创建一些训练数据'''
X = torch.randn(N, D_in)
y = torch.randn(N, D_out)'''定义两层网络'''
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)self.linear2 = torch.nn.Linear(H, D_out, bias=False)def forward(self, x):y_hat = self.linear2(self.linear1(X).clamp(min=0))return y_hatmodel = TwoLayerNet(D_in, H, D_out)loss_fn = nn.MSELoss(reduction='sum')
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)for t in range(500): #做500次迭代'''前向传播(forward pass)'''y_hat = model(X) # model.forward(), N * D_out'''计算损失函数(compute loss)'''loss = loss_fn(y_hat, y) #均方误差,忽略了÷N,loss就是一个计算图(computation graph)print(t, loss.item()) #打印每个迭代的损失optimizer.zero_grad() #求导之前把 gradient 清空'''后向传播(backward pass)'''loss.backward()'''参数更新(update weights of W1 and W2)'''optimizer.step() #一步把所有参数全更新
0 713.7529296875
1 695.759033203125
2 678.2886352539062
3 661.2178344726562
4 644.5472412109375
5 628.3016357421875
6 612.5072021484375
7 597.1802978515625
8 582.385009765625
9 568.1029663085938
...
490 3.386985554243438e-07
491 3.155915919705876e-07
492 2.9405845225483063e-07
493 2.7391826051825774e-07
494 2.553651086145692e-07
495 2.379783694550497e-07
496 2.2159480295158573e-07
497 2.0649896725899453e-07
498 1.9220941283037973e-07
499 1.790194232853537e-07
此时这个Adam作用很好。
4 总结
其实步骤都是一样的:定义参数、定义模型、定义损失函数、交给optimizer优化、训练。
用numpy、PyTorch自动求导、torch.nn库实现两层神经网络相关推荐
- 【深度学习】pytorch自动求导机制的理解 | tensor.backward() 反向传播 | tensor.detach()梯度截断函数 | with torch.no_grad()函数
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.pytorch里自动求导的基础概念 1.1.自动求导 requires_grad=True 1.2.求导 requ ...
- PyTorch 笔记Ⅱ——PyTorch 自动求导机制
文章目录 Autograd: 自动求导机制 张量(Tensor) 梯度 使用PyTorch计算梯度数值 Autograd 简单的自动求导 复杂的自动求导 Autograd 过程解析 扩展Autogra ...
- 机器学习 标量、向量、矩阵的求导 PyTorch自动求导
1 说明 本文是学习Dive into Deep Learning中相应内容做出的总结和一些实现代码,原文链接:矩阵计算. 2 求导 学习PyTorch的自动求导之前首先需要知道求导的过程. 注意:可 ...
- autograd库测试笔记-(一个基于Numpy的自动求导库)
导入 autograd 库,同时导入这个库里的numpy(应该是作者自己把numpy放入了这个库的命名空间里面)以及逐项求导elementwise_grad. from autograd import ...
- pytorch自动求导-07
自动求导 假设我们想对函数 y=2x⊤xy = 2\mathbf{x}^{\top}\mathbf{x}y=2x⊤x关于列向量 x\mathbf{x}x求导 import torchx = torch ...
- pytorch自动求导机制
Torch.autograd 在训练神经网络时,我们最常用的算法就是反向传播(BP). 参数的更新依靠的就是loss function针对给定参数的梯度.为了计算梯度,pytorch提供了内置的求导机 ...
- pytorch自动求导
1.求导 params = torch.tensor([1.0, 0.0], requires_grad=True) 注意到了张量构造函数的 require_grad = True 吗?这个参数告诉P ...
- PyTorch基础(二)-----自动求导Autograd
一.前言 上一篇文章中提到PyTorch提供了两个重要的高级功能,分别是: 具有强大的GPU加速的张量计算(如NumPy) 包含自动求导系统的的深度神经网络 第一个特性我们会在之后的学习中用到,这里暂 ...
- pytorch自动求梯度—详解
构建深度学习模型的基本流程就是:搭建计算图,求得损失函数,然后计算损失函数对模型参数的导数,再利用梯度下降法等方法来更新参数.搭建计算图的过程,称为"正向传播",这个是需要我们自己 ...
最新文章
- wine安装lingoes
- 微软邮件系统Exchange 2013系列(二)先决条件
- dvi黑屏解决方法_赛博朋克2077黑梦黑屏怎么办 黑梦BUG全黑模式解决方法
- c++中基类与派生类中隐含的this指针的分析
- CoreLocation框架--监测方向/地磁传感器
- python装饰器详解-python装饰器详解
- mybatis中concat的用法
- Python 文件读写小结
- n阶方阵的蛇形排列java_排列组合的模板算法
- 如何在云服务器搭建虚拟主机,如何在云服务器搭建虚拟主机
- kindle索引_Kindle 有哪些鲜为人知的使用技巧?
- 最新卡巴斯基互联网安全套装7.0(kis7)系列激活码
- java 汉字转拼音缩写_用JAVA实现汉字转拼音缩写
- 为什么“家徒四壁”中的徒是仅仅,只有的意思?
- CentOS系列的绑定MAC(物理网卡地址)
- 【工具】-10 UML时序图(Sequence Diagram)学习笔记
- 如何把蓝奏云里的文件进行批量导出分享?蓝奏云批量分享的工具
- Centos 7 安装 OpenResty api 网关 Orange
- emplace 和 emplace_back
- 宾得常用镜头群[转自东河寒梅]
热门文章
- sql限定查询语句(where子句)
- 理工男一般不浪漫,一浪漫便值很多年
- 多项目同时进行该如何做好管理?
- ML之RL:基于MovieLens电影评分数据集利用强化学习算法(多臂老虎机+EpsilonGreedy策略)实现对用户进行Top电影推荐案例
- Error记录--make: ./libtool:命令未找到
- DN-DETR: Accelerate DETR Training by Introducing Query DeNoising阅读笔记
- C# MessageBox用法实例 (附效果图)
- ppt_第七章_类人DNA与神经元基于催化算子映射编码方式.
- sed命令详解+示例
- 全球与中国肥料用着色剂市场深度研究分析报告