概述
Learning PyTorch with Examples
本教程通过自成包含的例子介绍了pytorch的基本概念,它的核心是两个主要的特性:
一个类似于numpy的n维张量,但可以在gpu上运行,用于建立和训练神经网络。
我们将使用一个完全连接的relu网络作为运行实例。该网络将有一个单一的隐藏层,
并将通过梯度下降来训练,通过最小化来适应随机数据。网络输出与真实输出之间的欧氏距离。
您可以在本页末尾浏览各个示例
张量热身:在引入PYTORCH之前,我们将首先使用numpy实现网络。
numpy提供了一个n维数组对象,许多用于操作这些数组的函数。
numpy是一个科学计算的通用框架;它不知道任何关于计算图、深入学习或梯度的知识。
然而,通过使用numpy操作手动实现前后两层网络随机数据,
我们可以很容易地使用numpy来适应两层网络的随机数据:
import numpy as np
import torch
import tensorflow as tf
import random
n是批大小,d_in是输入维数。;
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):
# 向前传球:计算预测y
h = x.dot(w1)
h_relu = np.maximum(h, 0)
y_pred = h_relu.dot(w2)
# 计算和打印损失
loss = np.square(y_pred - y).sum()
print(t, loss)
# 用于计算W1和W2相对于损失的梯度的反向支持
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] = 0
grad_w1 = x.T.dot(grad_h)
# 更新权重
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
PYTORCH:
numpy是一个很好的框架,但是它不能利用GPU加速它的数值计算。
对于现代的深层神经网络来说,GPU通常提供50倍或更高的速度,
因此不幸的是,numpy对现代深度学习来说是不够的。
这里我们介绍了最基本的pytorch概念:张量在概念上与numpy数组相同:
张量是一个n维数组,而pyorp张量在概念上是与numpy数组相同的。
提供了许多在这些张量上操作的函数。在幕后,张量可以跟踪计算图和梯度,
但它们也是科学计算的通用工具。此外,与numpy不同的是,
pytorch张量可以利用GPU加速它们的数值计算。要在GPU上运行Python张量,
只需将其转换为一个新的数据集。在这里,我们使用Python张量来加速它们的数值计算。
将两层网络安装到随机数据中。就像上面的numpy示例一样,我们需要手动实现向前和向后通过网络:
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on 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):
# 向前传球:计算预测y
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
# 计算和打印损失
loss = (y_pred - y).pow(2).sum().item()
print(t, loss)
# 用于计算W1和W2相对于损失的梯度的反向支持
grad_y_pred = 2.0 * (y_pred - y)
grad_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] = 0
grad_w1 = x.t().mm(grad_h)
# 使用梯度下降更新权重
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
自动传递:在上面的例子中,我们必须手动实现神经网络的向前和向后传递。
对于一个小的两层网络来说,手动实现反向传递并不是什么大不了的事情,
但是对于大型复杂网络来说,很快就会变得毛茸茸的。谢天谢地,
我们可以使用自动微分来自动计算神经网络中的反向传递。提供了这个功能。
当使用自动图时,网络的向前通过将定义一个计算图;图中的节点将是张量,
而边将是从输入张量产生输出张量的函数。通过此图反传可以方便地计算梯度。
这听起来很复杂,在实际中使用非常简单。如果每个张量代表计算图中的一个节点。
x是一个张量,具有x.requesgrad=true,那么x.grad是另一个保持x相对于某个标量值的梯度的张量。
在这里,我们使用pytorch张量和autograd来实现我们的两层网络;现在我们不再需要手动实现通过网络的反向传递:
dtype = torch.float
device = torch.device("cpu")
device = torch.device("cuda:0") # Uncomment this to run on GPU
n为批次尺寸,d_in为输入维数
h是隐维,d_out是输出维。
N, D_in, H, D_out = 64, 1000, 100, 10
创建随机张量以保存输入和输出。
设置Required_grad=false表示我们不需要计算梯度
相对于这些张量,在反向传播时。
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
为权重创建随机张量。
设置Required_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):
# 向前通过:使用张量运算计算预测y;这些
# 与我们使用的计算前传的操作完全相同。
# 张量,但是我们不需要保留对中间值的引用,因为
# 我们不是用手实现后传。
y_pred = x.mm(w1).clamp(min=0).mm(w2)
# 使用张量运算计算和打印损失。
# 现在损失是形状的张量(1,)
# Item()获取损失中持有的标量值。
loss = (y_pred - y).pow(2).sum()
print(t, loss.item())
# 使用autograd计算向后通行证。此调用将计算
# 对于所有需要GRAD=TRUE的张量的损失梯度
# 在这个调用之后,w1.grad和w2.grad将是保持梯度的张量。
# W1和W2的损失。
loss.backward()
# 手动更新权重使用梯度下降。包装在torch.no_grad()
# 因为权重要求_grad=true,但是我们不需要跟踪这个
# 在自动毕业。
# 另一种方法是对加权数据和加权梯度数据进行操作。
# 回想一下tensor.data给出了一个张量,该张量与
# 张量但不追踪历史
# 您也可以使用torch.Optim.sgd来实现这一点。
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 更新权重后手动对梯度进行零化
w1.grad.zero_()
w2.grad.zero_()
PITORCH:定义新的自梯度函数,每个原始自格拉德算子实际上是两个作用于张量的函数。
前向函数从输入张量中计算输出张量。向后函数接收输出张量相对于某个标量值的梯度,
并计算输入张量相对于同一个标量值的梯度。通过定义torch.autograd.function的子类
并实现前向和后向函数,我们可以使用新的autograd操作符,方法是构造一个实例
并像函数一样调用它,传递包含输入数据的张量。在这个示例中,
我们定义了自己的自定义Autograd函数来执行relu非线性,并使用它实现我们的两层网络:
class MyReLU(torch.autograd.Function):
"""
我们可以通过子类torch.autograd.function来实现我们自己的自定义自动梯度函数,并实现对张量操作的前后过。
"""
@staticmethod
def forward(ctx, input):
"""
在前向传递中,我们接收包含输入的张量,并返回包含输出的张量。
CTX是一个上下文对象,可用于存储信息以进行反向计算。
您可以使用ctx.Save_for_back方法缓存任意对象,以便在后传中使用。
"""
ctx.save_for_backward(input)
return input.clamp(min=0)
@staticmethod
def backward(ctx, grad_output):
"""
在反向传递中,我们得到一个包含相对于输出的损失梯度的张量,并且我们需要计算相对于输入的损失的梯度。
"""
input, = ctx.saved_tensors
grad_input = grad_output.clone()
grad_input[input < 0] = 0
return grad_input
dtype = torch.float
device = torch.device("cpu")
device = torch.device("cuda:0") # Uncomment this to run on 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, 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):
# 为了应用我们的函数,我们使用函数。应用方法。我们将它命名为‘relu’
relu = MyReLU.apply
# 向前通过:使用操作计算预测的y;我们计算
# 使用我们的自定义自动梯度操作。
y_pred = relu(x.mm(w1)).mm(w2)
# Compute and print loss
loss = (y_pred - y).pow(2).sum()
print(t, loss.item())
# 使用自动高分计算后传。
loss.backward()
# 使用梯度下降更新权重
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 更新权重后手动对梯度进行零化
w1.grad.zero_()
w2.grad.zero_()
TensorFlow:静态图PensorFlow看起来很像TensorFlow:在这两个框架中,
我们定义了一个计算图,并使用自动微分来计算梯度。两者最大的区别是
TensorFlow的计算图是静态的,pytorch使用动态计算图。在TensorFlow中,
我们定义计算图一次,然后一次又一次地执行相同的图,可能会向不同的输入数据
提供不同的输入数据。在PYTORCH中,每一次前向传递都定义了一个新的计算图。
静态图很好,因为您可以预先优化图形;例如,一个框架可能决定融合一些图操作以提高效率,
或者想出一种将图形分布在多个GPU或多台机器上的策略。如果您一次又一次地重复使用同一张图,
那么这种可能代价高昂的预先优化当同一张图被反复重复运行时,可以进行摊还。
静态图和动态图不同的一个方面是控制流。对于某些模型,我们可能希望对每个数据点执行不同的计算;
例如,对于每个数据点,可以对不同的时间步数展开递归网络;这种展开可以作为一个循环来实现。
需要成为图形的一部分;因此,TensorFlow提供了tf.scan之类的操作符,用于将循环嵌入到图形中。
对于动态图,情况更简单:由于我们为每个示例动态生成图,所以我们可以使用正常的命令式流控制
来执行针对每个输入的计算。与上面的pytorch自图示例相比,这里我们使用TensorFlow来拟合一个简单的两层网络:
N, D_in, H, D_out = 64, 1000, 100, 10
为输入和目标数据创建占位符;这些占位符将被填充
当我们执行图的时候。
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))
为权重创建变量,并使用随机数据初始化它们。
TensorFlow变量在图的执行过程中保持其值。
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 = tf.reduce_sum((y - y_pred) ** 2.0)
计算相对于W1和w2的损失梯度。
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])
使用梯度下降更新权重。实际更新权重
在执行图形时,我们需要计算newW1和neww2。
在TensorFlow中,更新权重值的行为是
计算图;在pytorch中,这发生在计算的范围之外
graph.
learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate * grad_w1)
new_w2 = w2.assign(w2 - learning_rate * grad_w2)
现在我们已经构建了计算图,所以我们输入了一个TensorFlow会话
实际执行图表。
with tf.Session() as sess:
# 运行图一次以初始化变量w1和w2。
sess.run(tf.global_variables_initializer())
# 创建numpy数组,保存输入x和目标的实际数据
x_value = np.random.randn(N, D_in)
y_value = np.random.randn(N, D_out)
for _ in range(500):
# 多次执行图形。每次执行时我们都想绑定
# x_value to x,y_value to y,使用feed_dict参数指定。
# 每次我们执行图时,我们想要计算损失的值,
# 新_w1和新_w2;这些张量的值作为numpy返回
# arrays.
loss_value, _, _ = sess.run([loss, new_w1, new_w2],
feed_dict={x: x_value, y: y_value})
print(loss_value)
nn计算图和自动图是定义复杂运算符和自动获取导数的一个非常强大的范例; 然而对于大型神经网络来说,原始的自动梯度可能有点过低。在构建神经网络时, 我们经常会考虑将计算安排成层,其中一些层具有可学习的参数,在学习过程中将进行优化。 在TensorFlow中,keras、tensorflow-瘦和tfLearning等软件包提供了更高层次的抽象。 在用于构建神经网络的原始计算图上,nn包用于此目的。nn包定义了一组模块, 这些模块大致等效于神经网络层。模块接收输入张量并计算输出张量,但也可以保持内部状态, 例如包含可学习参数的张量。nn包还定义了一组有用的损失函数。通常用于训练神经网络。 在这个例子中,我们使用nn包来实现我们的两层网络:
N, D_in, H, D_out = 64, 1000, 100, 10 Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
使用nn包将我们的模型定义为一个层次序列。nn.equence是一个包含其他模块的模块, 并按顺序应用它们来产生其输出。每个线性模块使用线性函数计算输入输出,并保持其权重和偏差的内部张量。
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
nn包还包含常用损失函数的定义;在本例中,我们将使用均方误差(MSE)作为损失函数。
loss_fn = torch.nn.MSELoss(reduction='sum')
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()
# 反向传递:计算模型中所有可学习参数的损失梯度。在内部,每个模块的参数都存储
# 在具有Required_grad=true的张量中,因此这个调用将计算模型中所有可学习参数的梯度。
loss.backward()
# 使用梯度下降来更新权重。每个参数都是张量,所以我们可以像以前一样访问它的梯度。
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad
到目前为止,我们已经通过手动修改包含可学习参数的张量来更新模型的权重 (用torch.no_grad()或.data来避免自动梯度下降这样的简单优化算法的跟踪历史)。 对于像随机梯度下降这样的简单优化算法来说,这并不是一个巨大的负担,但在实践中, 我们经常使用更复杂的优化器,如adagrad、rmsport、adam等来训练神经网络。 优化算法的思想,并提供了常用优化算法的实现。在这个示例中,我们将像以前一样使用 nn包来定义我们的模型,但是我们将使用OptimPackage提供的ADAM算法对模型进行优化: N, D_in, H, D_out = 64, 1000, 100, 10 Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# Use the nn package to define our model and loss function.
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')
Use the optim package to define an Optimizer that will update the weights of the model for us. Here we will use Adam; the optim package contains many other optimization algoriths. The first argument to the Adam constructor tells the optimizer which Tensors it should update.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
# Forward pass: compute predicted y by passing x to the model.
y_pred = model(x)
# Compute and print loss.
loss = loss_fn(y_pred, y)
print(t, loss.item())
# 在向后传递之前,使用优化器对象对它将更新的变量(即模型的可学习权重)的所有渐变进行零化。
# 这是因为在默认情况下,每当.back()被调用时,渐变都会在缓冲区(即,而不是覆盖)中累积起来。
# 签出torch.autograd.back的文档以了解更多细节。
optimizer.zero_grad()
# Backward pass: compute gradient of the loss with respect to model
# parameters
loss.backward()
# 调用优化器上的STEP函数对其参数进行更新。
optimizer.step()
PYTORCH:自定义nn模块有时需要指定比现有模块序列更复杂的模型;在这些情况下, 您可以通过子类nn.Module和定义一个转发来定义您自己的模块,它接收输入张量并使用其他模块 或其他自举操作生成输出张量。在本例中,我们将我们的两层网络实现为一个自定义模块子类:
class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
"""
在构造函数中,我们实例化了两个nn.lined模块,并将它们赋值为成员变量。
"""
super(TwoLayerNet, self).__init__()
self.linear1 = torch.nn.Linear(D_in, H)
self.linear2 = torch.nn.Linear(H, D_out)
def forward(self, x):
"""
I对于前向函数,我们接受输入数据的张量,并且必须返回输出数据的张量。
我们可以使用构造函数中定义的模块以及张量上的任意运算符。
"""
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred
N is batch size; D_in is input dimension; H is hidden dimension; D_out is output dimension. N, D_in, H, D_out = 64, 1000, 100, 10 Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
通过实例化上面定义的类来构造我们的模型
model = TwoLayerNet(D_in, H, D_out)
构造我们的损失函数和一个优化器。对ADM构造函数中的Model.Parameters() 的调用将包含作为模型成员的两个nn.线性模块的可学习参数。
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
for t in range(500):
# 向前传递:通过将x传递给模型来计算预测y
y_pred = model(x)
# Compute and print loss
loss = criterion(y_pred, y)
print(t, loss.item())
# 零梯度,执行反向传递,并更新权重。
optimizer.zero_grad()
loss.backward()
optimizer.step()
pytorch:控制张量权重分配 作为动态图和权重共享的一个例子,我们实现了一个非常奇怪的模型:一个完全连接的relu网络, 它在每个前向通道上选择一个随机数介于1到4之间,并使用那么多隐藏层, 多次重复使用相同的权重来计算最内部的隐藏层。对于该模型,我们可以使用常规python流控制来实现循环 ,并且可以实现权重共享。在最内部的层中,只需在定义前向密码时多次重用同一个模块。 我们可以轻松地将该模型实现为一个模块子类:
class DynamicNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
"""
在构造函数中,我们构造了三个nn.线性实例,我们将在前向传递中使用这些实例。
"""
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,并重复多次计算隐藏层表示的中间线性模块。
由于每一次向前传递都会生成一个动态计算图,所以在定义模型的前通过时,
我们可以使用像循环或条件语句这样的常规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 is batch size; D_in is input dimension; H is hidden dimension; D_out is output dimension. N, D_in, H, D_out = 64, 1000, 100, 10 Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
通过实例化上面定义的类来构造我们的模型
model = DynamicNet(D_in, H, D_out)
构造我们的损失函数和一个优化器。用香草随机梯度下降训练这个奇怪的模型很难,所以我们使用动量。
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
# Forward pass: Compute predicted y by passing x to the model
y_pred = model(x)
# Compute and print loss
loss = criterion(y_pred, y)
print(t, loss.item())
# Zero gradients, perform a backward pass, and update the weights.
optimizer.zero_grad()
loss.backward()
optimizer.step()
最后
以上就是甜蜜鸡为你收集整理的Pytorch(五)的全部内容,希望文章能够帮你解决Pytorch(五)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复