本文主要是介绍《PyTorch》Part4 PyTorch之小试牛刀,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《PyTorch》Part4 PyTorch之小试牛刀
- 1.基于numpy求导来实现反向传播
- 2.利用pytorch的非tensor相关函数求导来实现反向传播
- 3.利用pytorch的非tensor相关函数求导来实现反向传播
- 4.利用pytorch的非tensor相关函数求导来实现反向传播
- 5.基于TensorFlow静态图实现自动求导和反向传播
- 6.基于PyTorch的nn实现自动求导与反向传播
- 7.PyTorch的自动梯度下降优化器optim
- 8.PyTorch自定义nn模块实现反向传播
- 9. PyTorch:控制流和权重共享
环境配置:
tensorflow 1.2.1
torch 1.6.0+cu101
torchvision 0.7.0+cu101
1.基于numpy求导来实现反向传播
#-*- coding: utf-8 -*-# Example 1 利用numpy求导来实现反向传播
import numpy as np# N是批量大小,D_in是输入维度
# 49/5000 H是隐藏层维度; D_out是输出维度
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)
W2 = np.random.randn(H, D_out)learning_rate = 1e-6
for t in range(500):# 前向传递: 计算预测值yh = x.dot(W1)h_relu = np.maximum(h, 0)y_pred = h_relu.dot(W2)# 计算和打印损失lossloss = np.square(y_pred - y).sum()print(t, loss)# 反向传播, 计算W1和W2对loss的梯度grad_y_pred = 2.0 * (y_pred - y)grad_w2 = h_relu.T.dot(grad_y_pred)grad_h_relu = grad_y_pred.dot(W2.T)grad_h = grad_h_relu.copy()grad_h[h < 0] = 0grad_w1 = x.T.dot(grad_h)# 更新权重W1 -= learning_rate * grad_w1W2 -= learning_rate * grad_w2
运行结果:
...
480 3.285809972386525e-07
481 3.1147280390323234e-07
482 2.9525356973420686e-07
483 2.7988578469907816e-07
484 2.653185695193175e-07
485 2.515064903221421e-07
486 2.3841845592490285e-07
487 2.2601903652729718e-07
488 2.1425733687875495e-07
489 2.03108888139494e-07
490 1.925430272441955e-07
491 1.8252944612104411e-07
492 1.7303328148594878e-07
493 1.6403355832357957e-07
494 1.55505779208699e-07
495 1.47421866577345e-07
496 1.397587007156815e-07
497 1.3249193131515204e-07
498 1.256040713154569e-07
499 1.1907561933124292e-07进程已结束,退出代码 0
2.利用pytorch的非tensor相关函数求导来实现反向传播
#-*- coding: utf-8 -*-# Example 2 利用pytorch的非tensor相关函数求导来实现反向传播
# 该例子本质是替换numpy来实现反向传播,同时弥补numpy不能在cuda上加速的问题
import torchdtype = torch.float
device = torch.device("cpu") # 在CPU上运行
# device = torch.device("cuda:0") # 在GPU上运行# N是批量大小; D_in是输入维度;
# H是隐藏层的维度; D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 创建随机输入和输出数据
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)# 随机初始化权重
W1 = torch.randn(D_in, H, device=device, dtype=dtype)
W2 = torch.randn(H, D_out, device=device, dtype=dtype)learning_rate = 1e-6
for t in range(500):# 前向传递:计算预测yh = x.mm(W1) # mm->maximum h表示隐藏层h_relu = h.clamp(min=0) # relu函数作为输出函数y_pred = h_relu.mm(W2)# 计算和打印损失loss = (y_pred - y).pow(2).sum().item() # 求平方和print(t, loss)# Backprop计算W1和W2相对于损耗的梯度grad_y_pred = 2.0 * (y_pred - y) # loss 求导即为平方求导:(x^2)' = 2xgrad_W2 = h_relu.t().mm(grad_y_pred)grad_h_relu = grad_y_pred.mm(W2.t())grad_h = grad_h_relu.clone()grad_h[h < 0] = 0grad_W1 = x.t().mm(grad_h)# 使用梯度下降更新权重W1 -= learning_rate * grad_W1W2 -= learning_rate * grad_W2
运行结果:
...
480 3.9167625800473616e-05
481 3.874783214996569e-05
482 3.83115402655676e-05
483 3.780891711357981e-05
484 3.7070429243613034e-05
485 3.666989869088866e-05
486 3.630039645940997e-05
487 3.590686901588924e-05
488 3.5411609133007005e-05
489 3.4926386433653533e-05
490 3.464436667854898e-05
491 3.413881495362148e-05
492 3.374682637513615e-05
493 3.335313522256911e-05
494 3.302757249912247e-05
495 3.2601510611129925e-05
496 3.221363658667542e-05
497 3.175309393554926e-05
498 3.1339652196038514e-05
499 3.0954644898884e-05进程已结束,退出代码 0
3.利用pytorch的非tensor相关函数求导来实现反向传播
# -*- coding: utf-8 -*-
# Example 3 利用pytorch的非tensor相关函数求导来实现反向传播
# 利用pytorch的required_grad=True和loss.backward()实现自动求导和反向传播import torchdtype = torch.float
device = torch.device("cpu")
# device = torch.device("gpu:0") #取消注释在GPU上运行# N是批量大小; D_in是输入维度
# H是隐藏层的维度; D_out是输出维度
N, D_in, H, D_out = 64, 100, 1000, 10# 创建随机Tensor以保持输入和输出
# 设置requires_grad = False表示我们不需要计算渐变
# 在向后传播期间,对于这些Tensor
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)# 为权重创建随机Tensors
# 设置requires_grad = True表示我们想要计算梯度
# 在向后传播期间
W1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
W2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)learning_rate = 1e-6
for t in range(500):# 前向传播: 使用tensors的操作计算预测值y# 由于W1和W2有required_grad=True,涉及这些Tensors的操作将让pyTorch构建计算图# 从而允许自动计算梯度。由于我们不再手工实现反向传播, 所以不需要保留中间值的引用y_pred = x.mm(W1).clamp(min=0).mm(W2)# 使用Tensors上的操作计算和打印loss# loss是一个shape为()的张量# loss.item()得到这个张量对应的Python数值loss = (y_pred - y).pow(2).sum()print(t, loss.item())# 使用autograd计算反向传播。这个调用将计算loss对所有requires_grad=True的梯度# 这次调用后,W1.grad和W2.grad将分别是loss对W1和W2的梯度张量loss.backward()# 使用梯度下降更新权重。对于这一步,我们只想对W1和W2的值进行原地改变;不想为更新阶段构建计算图# 所以我们使用torch.no_grad()上下文管理器防止pytorch为更新构建计算图with torch.no_grad():W1 -= learning_rate * W1.gradW2 -= learning_rate * W2.grad# 反向传播后手动将梯度设置为0W1.grad.zero_()W2.grad.zero_()
运行结果:
...
480 1.7101149296649965e-06
481 1.7001423202600563e-06
482 1.7003992525133071e-06
483 1.698648134151881e-06
484 1.682349648035597e-06
485 1.674974328125245e-06
486 1.6609726571914507e-06
487 1.655122105148621e-06
488 1.6559454252274008e-06
489 1.6588630842306884e-06
490 1.6436761143268086e-06
491 1.625956656425842e-06
492 1.6106114344438538e-06
493 1.5901746337476652e-06
494 1.6021498367990716e-06
495 1.5953037291183136e-06
496 1.5847333543206332e-06
497 1.5747415318401181e-06
498 1.5617367807863047e-06
499 1.5503792383242399e-06进程已结束,退出代码 0
4.利用pytorch的非tensor相关函数求导来实现反向传播
该代码可在cuda上运行。
# -*- coding: utf-8 -*-
# Example 4 利用pytorch的非tensor相关函数求导来实现反向传播
# 利用pytorch定义自己的自动求导运算。之后就可以使用这个新的自动梯度运算符了。
# 然后,我们可以通过构造一个实例并像调用函数一样,传入包含输入数据的tensor调用它,
# 这样来使用新的自动求导运算。
# 这个例子中,我们自定义一个自动求导函数来展示ReLU的非线性。并用它实现我们的两层网络。import torchclass MyReLU(torch.autograd.Function):"""我们可以通过建立torch.autograd的子类来实现我们自定义的autograd函数,并完成张量的正向和反向传播。"""@staticmethoddef forward(ctx, x):"""在正向传播中,我们接收到一个上下文对象和一个包含输入的张量;我们必须返回一个包含输出的张量,并且我们可以使用上下文对象来缓冲对象,以便在反向传播中使用。"""ctx.save_for_backward(x)return x.clamp(min=0)@staticmethoddef backward(ctx, grad_output):"""在反向传播中,我们接收到上下文对象和一个张量,其包含了相对于正向传播过程中产生的输出的损失的梯度。我们可以从上下文对象中检索缓存的数据,并且必须计算并返回与正向传播的输入相关的损失的梯度"""x, = ctx.saved_tensorsgrad_x = grad_output.clone()grad_x[x < 0] = 0return grad_x
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)# N 是批处理大小; D_in是输入维度;
# H 是隐藏层维度; D_out是输出维度
N, D_in, H, D_out = 64, 100, 1000, 10# 产生输入和输出的随机张量
x = torch.randn(N, D_in, device=device)
y = torch.randn(N, D_out, device=device)# 产生随机权重的张量
W1 = torch.randn(D_in, H, device=device, requires_grad=True)
W2 = torch.randn(H, D_out, device=device,requires_grad=True)learning_rate = 1e-6
for t in range(500):# 正向传播: 使用张量上的操作来计算输出值y;# 我们通过调用 MyReLU.apply函数来使用自定义的ReLUy_pred = MyReLU.apply(x.mm(W1)).mm(W2)# 计算并输出lossloss = (y_pred - y).pow(2).sum()print(t, loss.item())# 使用autograd计算反向传播过程loss.backward()with torch.no_grad():# 用梯度下降更新权重W1 -= learning_rate * W1.gradW2 -= learning_rate * W2.grad# 在反向传播之后手动清零梯度W1.grad.zero_()W2.grad.zero_()
运行结果:
...
480 2.0872503228019923e-06
481 2.0793538624275243e-06
482 2.077500312225311e-06
483 2.0583531750162365e-06
484 2.0180125375190983e-06
485 2.0166357899142895e-06
486 2.034902081504697e-06
487 2.0492218482104363e-06
488 2.0555921764753293e-06
489 2.0511856746452395e-06
490 2.0325210243754555e-06
491 2.023186425503809e-06
492 1.9868443814630155e-06
493 1.994564172491664e-06
494 1.9885947040165775e-06
495 1.951446392922662e-06
496 1.9360254555067513e-06
497 1.9340623111929744e-06
498 1.9357069049874553e-06
499 1.917011786645162e-06进程已结束,退出代码 0
5.基于TensorFlow静态图实现自动求导和反向传播
import tensorflow as tf
import numpy as np# 首先我们建立计算图 computational graph# N是批大小; D是输入维度
# H是隐藏层维度; D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 为输入和目标数据创建placeholder
# 档执行计算图时, 他们将会被真实的数据填充
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))# 为权重创建Variable并用随机数据初始化
# TensorFlow的Variable在执行计算图时不会改变
W1 = tf.Variable(tf.random_normal((D_in, H)))
W2 = tf.Variable(tf.random_normal((H, D_out)))# 前向传播:使用TensorFlow的张量运算计算预测值y
# 注意这段代码实际上不执行任何数值运算
# 它只是建立了我们稍后将执行的计算图
h = tf.matmul(x, W1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, W2)# 使用TensorFlow的张量运算损失loss
loss = tf.reduce_sum((y - y_pred) ** 2.0)# 计算loss对于W1和W2的导数
grad_W1, grad_W2 = tf.gradients(loss, [W1, W2])# 使用梯度下降更新权重。为了实际更新权重,我们需要在执行计算图时计算new_W1和new_W2
# 注意:在TensorFlow中,更新权重值的行为是计算图的一部分
# 但在PyTorch中, 这发生在计算图形之外
learning_rate = 1e-6
new_W1 = W1.assign(W1 - learning_rate * grad_W1)
new_W2 = W2.assign(W2 - learning_rate * grad_W2)# 现在我们搭建好了计算图, 所以我们开始一个TensorFlow的会话(session)来执行计算图
with tf.Session() as sess:# 运行一次计算图来初始化Variable W1 和 W2sess.run(tf.global_variables_initializer())# 创建numpy数组来存储输入x和目标y的实际数据x_value = np.random.randn(N, D_in)y_value = np.random.randn(N, D_out)for _ in range(500):# 多次运行计算图。每次执行时,我们都用feed_dict参数# 将x_value绑定到x, 将y_value绑定到y,# 每次执行图形时,我们都要计算损失、new_W1和new_W2# 这些张量的值以numpy数组的形式返回loss_value, _, _ = sess.run([loss, new_W1, new_W2],feed_dict={x: x_value, y: y_value})print(loss_value)
运行结果:
...
0.000300851
0.0002942722
0.00028764148
0.00028208658
0.00027621523
0.00027057325
0.0002648923
0.00025931155
0.0002538307
0.00024925993
0.0002447182
0.00023910386
0.00023421564
0.0002299673
0.0002251084
0.00022098902
0.00021674071
0.00021248343
0.00020814096
0.00020463766
0.00020071122进程已结束,退出代码 0
6.基于PyTorch的nn实现自动求导与反向传播
# -*- coding: utf-8 -*-
import torch# N是批大小; D_in是输入维度
# H是隐藏层维度; D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 创建输入和输出随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)# 使用nn包将我们的模型定义为一系列的层
# nn.Sequential是包含其它模块的模块,并按顺序应用这些模块来产生其输出
# 每个线性模块使用线性函数从输入计算输出,并保存其内部的权重和偏差张量
# 在构造模型后,我们使用.to()方法将其移动到所需的设备(device)
model = torch.nn.Sequential(torch.nn.Linear(D_in, H),torch.nn.ReLU(),torch.nn.Linear(H, D_out),
)# nn包还包含常用的损失函数的定义;
# 在这种情况下,我们使用平均方误差(MSE)作为我们的损失函数
# 设置reductio='sum',表示我们计算的是平方误差的“和”,而不是平均值
# 这是为了与前面我们手工计算损失的例子保持一致
# 但是在实践中,通过设置reduction = 'elementwise_mean'来使用均方差作为损失更为常见
loss_fn = torch.nn.MSELoss(reduction='sum')
# loss_fn = torch.nn.MSELoss(reduction='elementwise_mean')learning_rate = 1e-4
for t in range(500):# 前向传播: 通过向模型传入x计算预测的y# 模块对象重载了__call__运算符, 所以可以像函数那样调用它们# 这么做相当于向模型传入了一个张量,然后它返回了一个输出张量y_pred = model(x)# 计算并打印损失# 传递包含y的预测值和真实值的张量,损失函数返回包含损失的张量loss = loss_fn(y_pred, y)print(t, loss.item())# 反向传播之前清零梯度model.zero_grad()# 反向传播: 计算模型的损失对所有可学习参数的导数(梯度)# 在内部, 每个模块的参数存储在requires_grad=True的张量中# 因此这个调用将计算模型中所有可学习参数的梯度loss.backward()# 使用梯度下降更新权重# 每个参数都是张量,所以我们可以像以前那样,得到她的数值和梯度with torch.no_grad():for param in model.parameters():param -= learning_rate * param.grad
运行结果:
...
480 8.447909181086288e-07
481 8.156128501468629e-07
482 7.873381946410518e-07
483 7.604432994412491e-07
484 7.339693866015295e-07
485 7.088103757268982e-07
486 6.844447284493071e-07
487 6.608780722672236e-07
488 6.382830406437279e-07
489 6.160664725030074e-07
490 5.948649572928844e-07
491 5.745329758610751e-07
492 5.546945658352342e-07
493 5.355688017516513e-07
494 5.174341595193255e-07
495 4.994170694772038e-07
496 4.821292236556474e-07
497 4.655937289044232e-07
498 4.494086738304759e-07
499 4.340374744060682e-07进程已结束,退出代码 0
7.PyTorch的自动梯度下降优化器optim
对于随机梯度下降(SGD/stochastic gradient descent)等简单的优化算法来说,这不是一个很大的负担,但在实践中,我们经常使用AdaGrad、RMSProp、Adam等更复杂的优化器来训练神经网络。
import torch# 1.输入
# N是批大小; D_in是输入维度
# H是隐藏层维度; D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 产生随机输入和输出张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)# 2.模型定义
# 使用nn包定义模型和损失函数
model = torch.nn.Sequential(torch.nn.Linear(D_in, H),torch.nn.ReLU(),torch.nn.Linear(H, D_out),
)
# 3.损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')# 4.优化器定义
# 使用optim包定义优化器(Optimizer).Optimizer将会为我们更新模型的权重
# 这里我们使用Adam优化方法;optim包还包含了许多别的优化算法
# Adam构造函数的第一个参数告诉优化器应该更新哪些张量
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)for t in range(500):# 前向传播: 通过向模型输入x计算预测的yy_pred = model(x)# 计算并打印lossloss = loss_fn(y_pred, y)print(t, loss.item())# 在反向传播之前, 将optimizer将它需要更新的所有张量的梯度清零# 这些张量是模型可学习的权重optimizer.zero_grad()#此处是优化器的置零,而不是model置零# 反向传播: 根据模型的参数计算loss的梯度loss.backward()# 调用Optimizer的step函数使他所有参数更新optimizer.step()#学习率在优化器中进行了设置
运行结果:
...
480 1.1322570259153508e-07
481 1.0503008240903e-07
482 9.762776898014636e-08
483 9.056345362523643e-08
484 8.404388296412435e-08
485 7.81737981014885e-08
486 7.239090393795777e-08
487 6.716422973340741e-08
488 6.229707594229694e-08
489 5.7813636544779e-08
490 5.361458832453536e-08
491 4.9780044975022975e-08
492 4.6198216807624704e-08
493 4.285558574679271e-08
494 3.967541317706491e-08
495 3.687521399342586e-08
496 3.424348093972185e-08
497 3.1632485075760997e-08
498 2.9320355920958718e-08
499 2.7226052523587896e-08进程已结束,退出代码 0
8.PyTorch自定义nn模块实现反向传播
import torchclass TwoLayerNet(torch.nn.Module):def __init__(self, D_in, H, D_out):"""在构造函数中,我们实例化了两个nn.Linear模块,并将它们作为成员变量"""super(TwoLayerNet, self).__init__()self.linear1 = torch.nn.Linear(D_in, H)self.linear2 = torch.nn.Linear(H, D_out)def forward(self, x):"""在向前传播的函数中,我们接收一个输入的张量,也必须返回一个输出张量我们可以使用构造函数中定义的模块以及张量上的任意的(可微分的)操作"""h_relu = self.linear1(x).clamp(min=0)y_pred = self.linear2(h_relu)return y_pred# N是批大小; D_in是输入维度
# H是隐藏层维度; D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 产生输入和输出的随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)# 通过实例化上面定义的类来构建我们的模型
model = TwoLayerNet(D_in, H, D_out)# 构造损失函数和优化器
# SGD构造函数中对model.parameters()的调用
# 将包含模型的一部分,即两个nn.Linear模块的学习参数
loss_fn = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):# 前向传播: 通过向模型传递x计算预测值yy_pred = model(x) #自动调用forward函数# 计算并输出lossloss = loss_fn(y_pred, y)print(t, loss.item())# 反向传播前清零梯度, 反向传播, 更新权重optimizer.zero_grad()loss.backward()optimizer.step()
运行结果:
...
480 1.4579343314835569e-06
481 1.4120196283329278e-06
482 1.3677002925760462e-06
483 1.3254004898044514e-06
484 1.283617507397139e-06
485 1.2436319138942054e-06
486 1.2044847608194686e-06
487 1.1670904314087238e-06
488 1.13036867332994e-06
489 1.0953788205370074e-06
490 1.0611396419335506e-06
491 1.0283911251462996e-06
492 9.96195353764051e-07
493 9.650908623370924e-07
494 9.35453158490418e-07
495 9.061046171154885e-07
496 8.777398079473642e-07
497 8.506697781740513e-07
498 8.244764444498287e-07
499 7.988149945958867e-07进程已结束,退出代码 0
9. PyTorch:控制流和权重共享
import random
import torchclass DynamicNet(torch.nn.Module):def __init__(self, D_in, H, D_out):"""在构造函数中,我们构造了三个nn.Linear实例,它们将在前向传播时被使用"""super(DynamicNet, self).__init__()self.input_linear = torch.nn.Linear(D_in, H)self.middle_linear = torch.nn.Linear(H, H)self.output_linear = torch.nn.Linear(H, D_out)def forward(self, x):"""对于模型的前向传播,我们随机选择0,1,2,3,并重用了多次计算隐藏层的middle_linear模块由于每个前向传播构建一个动态计算图,我们可以在定义mix的前向传播时使用常规Python控制流运算符,如循环或条件语句在这里,我们还看到,在定义计算图形时多次重用同一个模块是完全安全的这是Lua Torch的一大改进,因为Lua Torch中每个模块只能使用一次"""h_relu = self.input_linear(x).clamp(min=0)for _ in range(random.randint(0, 3)):h_relu = self.middle_linear(h_relu).clamp(min=0)y_pred = self.output_linear(h_relu)return y_pred# N是批处理大小;D是输入维度
# H是隐藏层维度;D_out是输出维度
N, D_in, H, D_out = 64, 1000, 100, 10# 产生输入和输出随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)# 实例化上面定义的类来构造我们的模型
model = DynamicNet(D_in, H, D_out)# 构造我们的损失函数(loss function)和优化器(optimizer)
# 用随机梯度下降训练这个奇怪的模型是困难的,所有我们使用了momentum方法
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):# 前向传播:通过向模型传入x计算预测的yy_pred = model(x)# 计算并打印损失loss = criterion(y_pred, y)print(t, loss.item())# 清零梯度,反向传播, 更新权重optimizer.zero_grad()loss.backward()optimizer.step()
运行结果:
...
480 0.8530825972557068
481 7.000678062438965
482 4.9826202392578125
483 1.8757514953613281
484 2.3885960578918457
485 1.0471543073654175
486 5.27086067199707
487 7.1531243324279785
488 3.232333183288574
489 0.9390440583229065
490 1.7408761978149414
491 2.718259811401367
492 0.5091014504432678
493 6.936823844909668
494 4.517251968383789
495 1.546199083328247
496 0.5939903259277344
497 1.854905366897583
498 3.927490234375
499 2.9751086235046387进程已结束,退出代码 0
这篇关于《PyTorch》Part4 PyTorch之小试牛刀的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!