快速入门Pytorch

参考:https://mp.weixin.qq.com/s/u7fFDsGgMJDMM1B-MuCPQg

(一)Tensors


Tensors张量是一种特殊的数据结构,它和数组还有矩阵十分相似。在Pytorch中,我们使用tensors来给模型的输入输出以及参数进行编码。Tensors除了张量可以在gpu或其他专用硬件上运行来加速计算之外,其他用法类似于Numpy中的ndarrays。如果你熟悉ndarrays,您就会熟悉tensor的API。如果没有,请按照这个教程,快速了解一遍API。

# -*- coding: utf-8 -*-
"""
Created on Wed Feb 17 15:34:41 2021

@author: lll
"""
import torch
import numpy as np

#-----------------------------------------------------初始化Tensor
'''
直接从数据创建
可以直接利用数据创建tensor,数据类型会被自动推断出
'''
data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)


'''
从Numpy创建
Tensor 可以直接从numpy的array创建(反之亦然-参见bridge-to-np-label)
'''
np_array = np.array(data)
x_np = torch.from_numpy(np_array)

'''
从其他tensor创建
新的tensor保留了参数tensor的一些属性(形状,数据类型),除非显式覆盖
'''
x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")

'''
从常数或者随机数创建
shape是关于tensor维度的一个元组,在下面的函数中,它决定了输出tensor的维数。
'''
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

#-----------------------------------------------------Tensor的属性
'''
Tensor的属性
Tensor的属性包括形状,数据类型以及存储的设备
'''
tensor = torch.rand(3,4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

'''
Tensor的操作
Tensor有超过100个操作,包括 transposing, indexing, slicing, mathematical operations, linear algebra, random sampling,更多详细的介绍请点击这里
它们都可以在GPU上运行(速度通常比CPU快),如果你使用的是Colab,通过编辑>笔记本设置来分配一个GPU。
'''

# We move our tensor to the GPU if available
if torch.cuda.is_available():
  tensor = tensor.to('cuda')
  
'''
标准的numpy类索引和切片:
'''
tensor = torch.ones(4, 4)
tensor[:,1] = 0
print(tensor)


'''
合并tensors
可以使用torch.cat来沿着特定维数连接一系列张量。torch.stack另一个加入op的张量与torch.cat有细微的不同
'''
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)

'''
增加tensors
'''
# This computes the element-wise product
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
# Alternative syntax:
print(f"tensor * tensor \n {tensor * tensor}")

'''
它计算两个tensor之间的矩阵乘法
'''
print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
# Alternative syntax:
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")

'''
原地操作
带有后缀_的操作表示的是原地操作,例如:x.copy_(y), x.t_()将改变 x.
'''
print(tensor, "\n")
tensor.add_(5)
print(tensor)

'''
注意
原地操作虽然会节省许多空间,但是由于会立刻清除历史记录所以在计算导数时可能会有问题,因此不建议使用
'''
'''
Tensor转换为Numpt 数组
'''
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")

'''
tensor的变化反映在NumPy数组中。
'''
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")

'''
Numpy数组转换为Tensor
'''
n = np.ones(5)
t = torch.from_numpy(n)

'''
NumPy数组的变化反映在tensor中
'''
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")

(二)Autograd:自动求导


torch.autograd是pytorch自动求导的工具,也是所有神经网络的核心。我们首先先简单了解一下这个包如何训练神经网络。
背景介绍
神经网络(NNs)是作用在输入数据上的一系列嵌套函数的集合,这些函数由权重和误差来定义,被存储在PyTorch中的tensors中。
神经网络训练的两个步骤:
前向传播:在前向传播中,神经网络通过将接收到的数据与每一层对应的权重和误差进行运算来对正确的输出做出最好的预测。
反向传播:在反向传播中,神经网络调整其参数使得其与输出误差成比例。反向传播基于梯度下降策略,是链式求导法则的一个应用,以目标的负梯度方向对参数进行调整。
更加详细的介绍可以参照下述地址:
[3Blue1Brown]:
https://www.youtube.com/watch?v=tIeHLnjs5U8
Pytorch应用
来看一个简单的示例,我们从torchvision加载一个预先训练好的resnet18模型,接着创建一个随机数据tensor来表示一有3个通道、高度和宽度为64的图像,其对应的标签初始化为一些随机值。

import torch, torchvision
model = torchvision.models.resnet18(pretrained=True)
data = torch.rand(1, 3, 64, 64)
labels = torch.rand(1, 1000)

接下来,我们将输入数据向输出方向传播到模型的每一层中来预测输出,这就是前向传播。

prediction = model(data) # 前向传播

我们利用模型的预测输出和对应的权重来计算误差,然后反向传播误差。完成计算后,您可以调用.backward()并自动计算所有梯度。此张量的梯度将累积到.grad属性中。

loss = (prediction - labels).sum()
loss.backward() # 反向传播

接着,我们加载一个优化器,在本例中,SGD的学习率为0.01,momentum 为0.9。我们在优化器中注册模型的所有参数。

optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)

最后,我们调用.step()来执行梯度下降,优化器通过存储在.grad中的梯度来调整每个参数。

optim.step() #梯度下降

现在,你已经具备了训练神经网络所需所有条件。下面几节详细介绍了Autograd包的工作原理——可以跳过它们。
Autograd中的求导
先来看一下autograd是如何收集梯度的。我们创建两个张量a和b并设置requires_grad = True以跟踪它的计算。

import torch

a = torch.tensor([2., 3.], requires_grad=True)
b = torch.tensor([6., 4.], requires_grad=True)

接着在a和b的基础上创建张量Q
在这里插入图片描述

Q = 3*a**3 - b**2

假设a和b是一个神经网络的权重,Q是它的误差,在神经网络训练中,我们需要w.r.t参数的误差梯度,即
在这里插入图片描述
当我们调用Q的.backward()时,autograd计算这些梯度并把它们存储在张量的 .grad属性中。我们需要在Q.backward()中显式传递gradient,gradient是一个与Q相同形状的张量,它表示Q w.r.t本身的梯度,即
在这里插入图片描述
同样,我们也可以将Q聚合为一个标量并隐式向后调用,如Q.sum().backward()。

external_grad = torch.tensor([1., 1.])
Q.backward(gradient=external_grad)

现在梯度都被存放在a.grad和b.grad中

# 检查一下存储的梯度是否正确
print(9*a**2 == a.grad)
print(-2*b == b.grad)

(三)神经网络


可以使用torch.nn包来构建神经网络. 你已知道autograd包,nn包依赖autograd包来定义模型并求导.一个nn.Module包含各个层和一个forward(input)方法,该方法返回output.
例如,我们来看一下下面这个分类数字图像的网络.

在这里插入图片描述
他是一个简单的前馈神经网络,它接受一个输入,然后一层接着一层的输入,直到最后得到结果。
神经网络的典型训练过程如下:

  1. 定义神经网络模型,它有一些可学习的参数(或者权重);
  2. 在数据集上迭代;
  3. 通过神经网络处理输入;
  4. 计算损失(输出结果和正确值的差距大小)
  5. 将梯度反向传播会网络的参数;
  6. 更新网络的参数,主要使用如下简单的更新原则:weight = weight - learning_rate*gradient
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 17 16:11:09 2021

@author: lll
"""

'''
神经网络的典型训练过程如下:
定义神经网络模型,它有一些可学习的参数(或者权重);
在数据集上迭代;
通过神经网络处理输入;
计算损失(输出结果和正确值的差距大小)
将梯度反向传播会网络的参数;
更新网络的参数,主要使用如下简单的更新原则:weight = weight - learning_rate * gradient
'''

#--------------------------------------------------------------定义网络
import torch
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 1 input image channel, 6 output channels, 3x3 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 6 * 6, 120)  # 6*6 from image dimension 
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features


net = Net()
print(net)

'''
你只需定义forward函数,backward函数(计算梯度)在使用autograd时自动为你创建.你可以在forward函数中使用Tensor的任何操作。
net.parameters()返回模型需要学习的参数。
'''
#--------------------------------------------------------------net.parameters()返回模型需要学习的参数。
params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight

'''
构造一个随机的32 32的输入,注意:这个网络(LeNet)期望的输入大小是3232.如果使用MNIST数据集来训练这个网络,请把图片大小重新调整到32*32.
'''
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

'''
将所有参数的梯度缓存清零,然后进行随机梯度的的反向传播.
'''
net.zero_grad()
out.backward(torch.randn(1, 10))

'''
注意
``torch.nn``只支持小批量输入,整个torch.nn包都只支持小批量样本,而不支持单个样本 例如,``nn.Conv2d``将接受一个4维的张量,每一维分别是(样本数*通道数*高*宽). 如果你有单个样本,只需使用`input.unsqueeze(0)`来添加其它的维数. 在继续之前,我们回顾一下到目前为止见过的所有类.
回顾
torch.Tensor-支持自动编程操作(如backward())的多维数组。同时保持梯度的张量。
nn.Module-神经网络模块.封装参数,移动到GPU上运行,导出,加载等
nn.Parameter-一种张量,当把它赋值给一个Module时,被自动的注册为参数.
autograd.Function-实现一个自动求导操作的前向和反向定义, 每个张量操作都会创建至少一个Function节点,该节点连接到创建张量并对其历史进行编码的函数。
现在,我们包含了如下内容:
定义一个神经网络
处理输入和调用backward
剩下的内容:
计算损失值
更新神经网络的权值
损失函数
一个损失函数接受一对(output, target)作为输入(output为网络的输出,target为实际值),计算一个值来估计网络的输出和目标值相差多少。
在nn包中有几种不同的损失函数.一个简单的损失函数是:nn.MSELoss,它计算输入和目标之间的均方误差。
'''
output = net(input)
target = torch.randn(10)  # a dummy target, for example
target = target.view(1, -1)  # make it the same shape as output
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

'''
现在,你反向跟踪loss,使用它的.grad_fn属性,你会看到向下面这样的一个计算图: input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss
所以, 当你调用loss.backward(),整个图被区分为损失以及图中所有具有requires_grad = True的张量,并且其.grad 张量的梯度累积。
为了说明,我们反向跟踪几步:
'''
print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

'''
反向传播
为了反向传播误差,我们所需做的是调用loss.backward().你需要清除已存在的梯度,否则梯度将被累加到已存在的梯度。
现在,我们将调用loss.backward(),并查看conv1层的偏置项在反向传播前后的梯度。
'''
net.zero_grad()     # zeroes the gradient buffers of all parameters

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

'''
更新权重
实践中最简单的更新规则是随机梯度下降(SGD).
weight=weight−learning_rate∗gradient
我们可以使用简单的Python代码实现这个规则。
'''

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)
'''
然而,当你使用神经网络是,你想要使用各种不同的更新规则,比如SGD,Nesterov-SGD,Adam, RMSPROP等.为了能做到这一点,我们构建了一个包torch.optim实现了所有的这些规则.使用他们非常简单:
'''    
import torch.optim as optim

# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)

# in your training loop:
optimizer.zero_grad()   # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # Does the update

'''
注意
观察如何使用optimizer.zero_grad()手动将梯度缓冲区设置为零。这是因为梯度是反向传播部分中的说明那样是累积的。
'''



    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值