详细介绍Tensors的使用

作者:童蒙
编辑:amethyst

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

import torch
import numpy as py

1. tensor的初始化

直接从数据转换

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

从numpy转换

  • 使用tensor进行转换
np_array = np.array(data)
x_np = torch.tensor(np_array)
  • 使用from_numpy进行转换tensor
 n = np.ones(5)
 t = torch.from_numpy(n)
  • 使用numpy将tensor转换成ndarray
 t = torch.ones(5)
 print(f"t: {t}")
 n = t.numpy()
 print(f"n: {n}")
  • CPU的tensor和Numpy共享内存,修改一个会修改另一个。

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

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

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

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

2. tensor的属性

tensor的属性有shape,datatype和 device。

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}")

3. tensor的操作

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

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

类numpy的索引和切片

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

合并tensor

  • 使用torch.cat来在某一个维度上进行合并
    t1 = torch.cat([tensor, tensor, tensor], dim=1)
  • 也可以使用torch.stack进行合并
    t2 = torch.stack([tensor, tensor] ,dim =1 )

tensor的计算

  • 乘积运算
tensor.matmul(tensor.T)
tensor @ tensor.T
  • 点乘运算
tensor.mul(tensor)
tensor * tensor
  • 原位计算
    使用 “_” 为后缀的进行计算,例如 copy_ , t_ , 会覆盖原始值。例如:
    tensor.add_(5)

4.Torch.autograd

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

4.1背景信息

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

训练一个NN有两个步骤:

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

4.2 PyTorch中的用法

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

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

使用forwardpropagation来计算:
prediction = model(data) # forward pass

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

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

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

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

之后,使用step() 来进行梯度下降,优化后的参数会存在.grad中。
optim.step()
这样,一个模型就训练好了。

4.3 Autograd如何做微分

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

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

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

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

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

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

4.4 计算图

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。

from torch import nn, optim
model = torchvision.models.resnet18(pretrained=True)
# Freeze all the parameters in the network
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(512, 10)
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)

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

 x = torch.tensor([1], requires_grad=True)
 with torch.no_grad():
     y = x * 2
 y.requires_grad
 @torch.no_grad()
 def doubler(x):
      return x * 2
 z = doubler(x)
 z.requires_grad

5. 神经网络

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

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

  • 定义一个NN,包含一些参数
  • 迭代inputs
  • 利用input进行计算
  • 计算loss
  • 反向传播到nn的参数
  • 调整nn的权重,例如weight = weight - learning_rate * gradient

5.1 定义网络

 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自动也定义好了。

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

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

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

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

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

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

回顾一下调用的模块:

  • torch.tensor
  • nn.Module()
  • nn.parameters()
  • autograd.Functions()

5.2 计算损失函数

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

损失函数有很多,最简单的是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.backward()时候,整个DAG图都会进行微分,图中的所有tensor,如果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

5.3 后向传播

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

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

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)

5.4 更新权重

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

w = w- learning_rate * gradient

简单的实现如下:

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

当用nn的时候,可以使用不同的方法,比如SGD、Adam等,利用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

5.5 其他

保存模型

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

加载模型

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

6.参考资料

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容