查看原文
其他

详细介绍Tensors的使用

生信阿拉丁 生信阿拉丁 2022-05-16


tensor是一种类似于array和matrices的数据结构。PyTorch一般用tensors来表示输入和输出,同时也来表示模型的参数。tensor同Numpy中ndarray类似,然而tensors能够在GPU等其他硬件上运行。


1import torch
2import numpy as py



01

tensor的初始化


直接从数据转换


1data = [[1,2],[3,4]]
2x_data = torch.tensor(data)


从numpy转换


  • 使用tensor进行转换

1np_array = np.array(data)
2x_np = torch.tensor(np_array)
  • 使用from_numpy进行转换tensor

1  n = np.ones(5)
2  t = torch.from_numpy(n)
  • 使用numpy将tensor转换成ndarray

1  t = torch.ones(5)
2  print(f"t: {t}")
3  n = t.numpy()
4  print(f"n: {n}")
  • CPU的tensor和Numpy共享内存,修改一个会修改另一个。


从其他tensor转换

新的tensor保留着参数的相关性质,除非被明确的覆盖。

1x_ones = torch.one_like(x_data)
2
3x_rands = torch.rand_like(x_data , dtype = torch.float)


指定shape

shape是一个tuple,指定了维度。

1shape = (2,3,)
2rand_tensor = torch.rand(shape)
3ones_tensor = torch.ones(shape)
4zeros_tensor = torch.zeros(shape)


02

tensor的属性

tensor的属性有shape,datatype和 device。

1tensor = torch.rand(3,4)
2
3print(f"Shape of tensor: {tensor.shape}")
4print(f"Datatype of tensor: {tensor.dtype}")
5print(f"Device tensor is stored on: {tensor.device}")


03

tensor的操作

tensor的操作有很多,100多种,包括转置、检索、分片、数学计算、抽样、线性代数,以及其他的操作。这些操作也可以用在GPU上,可以用如下操作:

1if torch.cuda.is_available():
2  tensor = tensor.to('cuda')


类numpy的索引和切片


1tensor = torch.ones(4,4)
2tensor[:,1] = 0 


合并tensor


  • 使用torch.cat来在某一个维度上进行合并

1  t1 = torch.cat([tensor, tensor, tensor], dim=1)
  • 也可以使用torch.stack进行合并

1  t2 = torch.stack([tensor, tensor] ,dim =1 )


tensor的计算


  • 乘积运算

1  tensor.matmul(tensor.T)
2
3  tensor @ tensor.T
  • 点乘运算

1tensor.mul(tensor)
2
3tensor * tensor
  • 原位计算

    使用 “_” 为后缀的进行计算,例如 copy_ , t_ , 会覆盖原始值。例如:

1  tensor.add_(5)



Torch.autograd


“torch.autograd”是PyTorch的自动化微分引擎,从而用来方便进行神经网络训练。下面我们来介绍一下,autograd怎么来帮助神经网络进行训练。


01


背景信息

Neural networks(NNs)是封装了一系列的函数,来对输入数据进行计算。这些函数有各种参数(weights和biases),都存储在tensor的数据结构中。

训练一个NN有两个步骤:

  • Forward propagation:在前向传播中,NN使用各个函数,对输入进行计算,得到最终的结果。

  • Backward propagation:在后向传播中,NN通过对前向传播得到的结果计算误差,对参数进行成比例的调整。从最终的output出发,针对每一个函数的参数进行计算error的偏差,并且使用梯度下降来优化参数。


02


PyTorch中的用法

首先导入一个模型,然后获得一个随机的输入矩阵,随机生成一个label。

1import torch, torchvision
2model = torchvision.models.resnet18(pretrained=True)
3data = torch.rand(136464)
4labels = torch.rand(11000)

使用forwardpropagation来计算:

1prediction = model(data) # forward pass

对prediction和label来计算loss,然而反向传播loss到整个网络中,这个过程针对loss这个tensor使用backward()。Autograd会计算和存储每个模型的参数的梯度值,并且存在.grad性质中。

1loss = (prediction - lables).sum()
2loss.backward()

最后,我们加载一个optimizer,以SGD为例,首先定义一个。

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

之后,使用step() 来进行梯度下降,优化后的参数会存在.grad中。

1optim.step()

这样,一个模型就训练好了。


03


Autograd如何做微分

我们来看看autograd如何积累梯度,首先创建两个tensor,required_grad=True,表示会记录每一个操作。

1import torch
2a = torch.tensor([2.,3.],requires_grad = True)
3b = torch.tensor([6.,4.],requires_grad = True)
4## 构建一个tensor为Q
5Q=3*a**3 - b**2 

假设a和b是NN的参数,Q是error值。在NN训练中,我们来获得error反馈在各参数的梯度。

当我们调用.backward(),autograd计算梯度,并且存储在相应的.grad的属性中。当Q是一个vector时候,在使用时候,需要提供一个gradient的参数,长度同Q的shape一致,代表Q的梯度。假设为1 。

1external_grad = torch.tensor([1., 1.])
2Q.backward(gradient=external_grad)
3a.grad
4b.grad

也可以把Q变成一个标量,然后直接使用backward()。

1Q.sum().backward()



04


计算图

autograd会保存data和执行的操作在一个DAG的图形中,构成了Function的对象。在DAG中,输入是叶子,output是root。通过搜索root到leaves,可以计算梯度。

前向传播中,autograd会做两件事:

  • 计算得到结果

  • 维护DAG的操作函数

当调用backward()时,从root开始,autograd会:

  • 计算每一个.grad_fn的梯度

  • 将他们累加到.grad中

  • 利用DAG的链规则,回溯

设置requires_grad=False可以关闭梯度的计算。

在NN中,不计算梯度的参数称之为 frozen parameters。这个用于冻结某些参数,来调整另一些参数;也可以用于调整一个预先训练好的network。

1from torch import nn, optim
2
3model = torchvision.models.resnet18(pretrained=True)
4
5# Freeze all the parameters in the network
6for param in model.parameters():
7    param.requires_grad = False
8model.fc = nn.Linear(51210)
9optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)

也可以用装饰器torch.no_grad()来替代。

1x = torch.tensor([1], requires_grad=True)
2with torch.no_grad():
3    y = x * 2
4y.requires_grad
5
6@torch.no_grad()
7def doubler(x):
8     return x * 2
9z = doubler(x)
10z.requires_grad



神经网络

可以使用torch.nn 来构建神经网络。nn.autograd定义model和进行微分,nn.Module包含很多层,使用forward进行计算output。

一个神经网络包括以下步骤:

  • 定义一个NN,包含一些参数

  • 迭代inputs

  • 利用input进行计算

  • 计算loss

  • 反向传播到nn的参数

  • 调整nn的权重,例如weight = weight - learning_rate * gradient



定义网络

01


1import torch
2import torch.nn as nn
3import torch.nn.functional as F
4
5
6class Net(nn.Module):
7
8    def __init__(self):
9        super(Net, self).__init__()
10        # 1 input image channel, 6 output channels, 3x3 square convolution
11        # kernel
12        self.conv1 = nn.Conv2d(163)
13        self.conv2 = nn.Conv2d(6163)
14        # an affine operation: y = Wx + b
15        self.fc1 = nn.Linear(16 * 6 * 6120)  # 6*6 from image dimension
16        self.fc2 = nn.Linear(12084)
17        self.fc3 = nn.Linear(8410)
18
19    def forward(self, x):
20        # Max pooling over a (2, 2) window
21        x = F.max_pool2d(F.relu(self.conv1(x)), (22))
22        # If the size is a square you can only specify a single number
23        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
24        x = x.view(-1, self.num_flat_features(x))
25        x = F.relu(self.fc1(x))
26        x = F.relu(self.fc2(x))
27        x = self.fc3(x)
28        return x
29
30    def num_flat_features(self, x):
31        size = x.size()[1:]  # all dimensions except the batch dimension
32        num_features = 1
33        for s in size:
34            num_features *= s
35        return num_features
36
37
38net = Net()
39print(net)

当定义好forward的时候,backward自动也定义好了。

可以使用net.parametes()来调用模型中的参数。

1params = list(net.parameters())
2print(len(params))
3print(params[0].size())  # conv1's .weight

假设输入为32*32,代码如下:

1input = torch.randn(1, 1, 32, 32)
2out = net(input)
3print(out)

首先清零梯度,然后做后向传播。

1net.zero_grad()
2out.backward(torch.randn(1, 10))

回顾一下调用的模块:

  • torch.tensor

  • nn.Module()

  • nn.parameters()

  • autograd.Functions()


计算损失函数

02

损失函数为预测值和目标值之间的差异,用来估计预测值同目标的偏离程度。

损失函数有很多,最简单的是nn.MSELoss,计算平方根误差。

1output = net(input)
2target = torch.randn(10)  # a dummy target, for example
3target = target.view(1-1)  # make it the same shape as output
4criterion = nn.MSELoss()
5
6loss = criterion(output, target)
7print(loss)

当调用loss.backward()时候,整个DAG图都会进行微分,图中的所有tensor,如果requires_grad = True,那么.grad属性会累积梯度。

1print(loss.grad_fn)  # MSELoss
2print(loss.grad_fn.next_functions[0][0])  # Linear
3print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU


后向传播

03

使用loss.backward()来反向传播error。需要注意的是,需要首先清除已经存在的梯度,否则梯度会继续保存。

可以使用loss.backward(),来看前后的变化。

1net.zero_grad()     # zeroes the gradient buffers of all parameters
2
3print('conv1.bias.grad before backward')
4print(net.conv1.bias.grad)
5
6loss.backward()
7
8print('conv1.bias.grad after backward')
9print(net.conv1.bias.grad)


更新权重

04

最简单的权重更新方法是SGD,

w = w- learning_rate * gradient

简单的实现如下:

1learning_rate = 0.01
2for f in net.parameters():
3    f.data.sub_(f.grad.data * learning_rate)

当用nn的时候,可以使用不同的方法,比如SGD、Adam等,利用torch.optim来实现,使用方法如下:

1import torch.optim as optim
2
3# create your optimizer
4optimizer = optim.SGD(net.parameters(), lr=0.01)
5
6# in your training loop:
7optimizer.zero_grad()   # zero the gradient buffers
8output = net(input)
9loss = criterion(output, target)
10loss.backward()
11optimizer.step()    # Does the update



其他


保存模型


1PATH = './cifar_net.pth'
2torch.save(net.state_dict(), PATH)


加载模型


1net = Net()
2net.load_state_dict(torch.load(PATH))




参考资料

  • https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html



作者:童蒙

编辑:amethyst


往期精选


深度学习在scRNA-seq数据处理中的应用

差异可变剪接软件哪家强?

一款计算亲缘关系和祖先分析的软件-AKT

家系分析软件汇总


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存