Pytorch入门
徐徐 抱歉选手

深度学习

机器学习研究如何使计算机系统利用经验改善性能。它是人工智能领域的分支,也是实现人工智能的一种手段,人工智能是最终目的

在机器学习的众多研究方向中,表征学习关注如何自动找出表示数据的合适方式,以便更好地将输入变换为正确的输出

深度学习是具有多级表示的表征学习方法。在每一级(从原始数据开始),深度学习通过简单的函数将该级的表示变换为更高级的表示。因此,深度学习模型也可以看作是由许多简单函数复合而成的函数。当这些复合的函数足够多时,深度学习模型就可以表达非常复杂的变换。

深度学习可以逐级表示越来越抽象的概念或模式

深度学习的一个外在特点是端到端的训练。也就是说,并不是将单独调试的部分拼凑起来组成一个系统,而是将整个系统组建好之后一起训练。

除端到端的训练以外,我们也正在经历从含参数统计模型转向完全无参数的模型

任务可以分为三种:回归Regression(线性模型),分类classification(非线性模型,包括深度学习、SVM等),结构化学习structured learning。

实现的手段可以有:监督学习、半监督学习、无监督学习、强化学习。相同点是统一都有输入,区别在于是否有输入的匹配输出。从前往后输入输出正确匹配对以此减少。

Tensor与Numpy

导入包

1
2
from __future__ import print_function
import torch

PyTorch操作inplace版本都有后缀_, 例如x.copy_(y), x.t_()

判断操作是否开辟新内存,使用Python自带的id函数:如果两个实例的ID一致,那么它们所对应的内存地址相同;反之则不同。

基本操作

torch.tensor()与np.array()用法类似,创建方法类似,二者可以转换,可接受基本运算法。

四种形式的加法(+, add(), add_(), +=)

操作 Torch Numpy 备注
创建 torch.tensor([0.1, 0.2, 0.3]) np.array([0.1, 0.2, 0.3])
转换 torch.from_numpy(x_numpy) x_torch.numpy() 这两个函数所产生的Tensor和NumPy中的数组共享相同的内存(所以他们之间的转换很快)。
四则运算 x_torch + y_torch x_numpy + y_numpy
范数 torch.norm(x_torch) np.linalg.norm(x_numpy)
正则化 torch.mean(x_torch, dim=0) np.mean(x_numpy, axis=0)
索引 我们还可以使用类似NumPy的索引操作(:)来访问Tensor的一部分,不会开辟内存。需要注意的是:索引出来的结果与原数据共享内存。

将NumPy中的array转换成Tensor的另一个方法就是torch.tensor(), 需要注意的是,此方法总是会进行数据拷贝(就会消耗更多的时间和空间),所以返回的Tensor和原来的数据不再共享内存。

函数item()可以将一个标量Tensor转换成一个Python number。

Reshape

Tensor Numpy 备注
Tensor.view() np.shape() Torch.view() can also automatically calculate the correct dimension if a -1 is passed in. This is useful if we are working with batches, but the batch size is unknown. Torch.view()返回的torch.Size其实就是一个tuple, 支持所有tuple的操作。

注意view()返回的新Tensor与源Tensor虽然可能有不同的size,但是是共享data的,也即更改其中的一个,另外一个也会跟着改变。(顾名思义,view仅仅是改变了对这个张量的观察角度,内部数据并未改变)

虽然view返回的Tensor与源Tensor是共享data的,但是依然是一个新的Tensor(因为Tensor除了包含data外还有一些其他属性),二者id(内存地址)并不一致。

想返回一个真正新的副本(即不共享data内存)该用clone创造一个副本然后再使用view。view和copy存在区别。

Broadcasting

遵循以下两个原则:

  • Each tensor has at least one dimension.
  • When iterating over the dimension sizes, starting at the trailing dimension, the dimension sizes must either be equal, one of them is 1, or one of them does not exist.

对应dimension要么相同,要么一个是1另一个是更大的数字。

计算图

为什么需要用到计算图?参考

计算图将函数表达式用树状结构表示。求偏导,联系链式法则,因子路径。

对于某一个结点往根结点去就是forward propagation,可以求该节点对它上面的所有节点的影响。对于某一个节点往叶子结点去就是backpropagation,可以求该节点受它下面的那些节点的影响。

记录了计算图,就能追踪每一次微分。

硬件支持

用方法to()可以将Tensor在CPU和GPU(需要硬件支持)之间相互移动。

1
2
gpu = torch.device("cuda:0")
x.to(gpu)

批量数据

主要涉及两个工具

1
from torch.utils.data import Dataset, DataLoader

custom dataset

Your custom dataset should inherit Dataset and override the following methods:

  • __len__ so that len(dataset) returns the size of the dataset.
  • __getitem__ to support the indexing such that dataset[i] can be used to get 𝑖\ th sample
1
2
3
4
5
6
7
8
9
10
11
class FakeDataset(Dataset):

def __init__(self, x, y):
self.x = x
self.y = y

def __len__(self):
return len(self.x)

def __getitem__(self, idx):
return self.x[idx], self.y[idx]

data loader

实现数据批量处理:batch/shuffling/parallel loading。

1
2
3
dataset = FakeDataset(x, y)
dataloader = DataLoader(dataset, batch_size=4,
shuffle=True, num_workers=4)

反向传播与自动求梯度

追踪梯度的步骤

  1. 对input tensor x的定义.requires_grad属性设置为True

  2. 调用反向传播y.backward(),来完成所有梯度计算。此input的Tensor的梯度将累积到Tensor类的.grad属性中。

  3. 随后用gradient descent更新x

  4. 然后需要把每一次反向传播后的梯度清零,因为.grad的数据不会被覆盖只会在前一个基础之上累加。

    We need to zero the grad variable since the backward call accumulates the gradients in .grad instead of overwriting.

    为了方便操作先调用调用.detach()将其从追踪记录中分离出来;再调用x.grad.zero_()清零梯度。

其他自动求梯度的设置

by using .detach() to get a new Tensor with the same content but that does not require gradients:

stop autograd from tracking history on Tensors with .requires_grad=True either by wrapping the code block in with torch.no_grad():

构造复杂模块

Many times, we want to compose Modules together. torch.nn.Sequential provides a good interface for composing simple modules.

1
2
3
4
5
6
model = torch.nn.Sequential(
nn.Linear(d_in, d_hidden),
nn.Tanh(),
nn.Linear(d_hidden, d_out),
nn.Sigmoid()
)

定义损失函数

两种常见的loss function。

1
2
mse_loss_fn = nn.MSELoss()
loss = nn.CrossEntropyLoss()

训练模型

训练模型需要用到求梯度的几种方法,比如SGD之类的。torch.optim提供了求梯度的一些方法,至少要传入模型参数和学习率

PyTorch implements a number of gradient-based optimization methods in torch.optim, including Gradient Descent. At the minimum, it takes in the model parameters and a learning rate.

在一个关于data_size/batch_size的循环,或者data_size的循环中,首先计算y_hat,其次计算loss,接下来求梯度(Optimizers do not compute the gradients for you, so you must call backward() yourself. ),要注意先清空梯度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
step_size = 0.01

linear_module = nn.Linear(d, 1)
loss_func = nn.MSELoss()
optim = torch.optim.SGD(linear_module.parameters(), lr=step_size)

for i in range(20):
# shuffling data
rand_idx = np.random.choice(n) # take a random point from the dataset
x = X[rand_idx]
y_hat = linear_module(x)
loss = loss_func(y_hat, y)
# You also must call the optim.zero_grad() function before calling backward() since by default PyTorch does and inplace add to the .grad member variable rather than overwriting it.
optim.zero_grad()
loss.backward()
optim.step()

在初始化参数的时候可以定一个momentum的初始值,之后当作参数传入torch.optim

参考

台大李宏毅机器学习助教补充课

Pytorch官方文档

  • 本文标题:Pytorch入门
  • 本文作者:徐徐
  • 创建时间:2020-10-22 18:50:03
  • 本文链接:https://machacroissant.github.io/2020/10/22/start-with-pytorch/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论