原文地址:https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html

什么是pytorch?

  pytorch是一个基于python语言的的科学计算包,主要分为两种受众:

  • 能够使用GPU运算取代NumPy
  • 提供最大灵活度和速度的深度学习研究平台

开始

Tensors

  Tensors与numpy的ndarray相似,且Tensors能使用GPU进行加速计算。

  

  创建5 * 3的未初始化矩阵:

  

  创建并随机初始化矩阵:

  

  创建一个类型为long且值全为0的矩阵:

  

  直接赋值创建tensor:

  

  使用已有的tensor创建一个tensor,这个方法能使用已有tensor的属性如类型:

  new_* 方法的参数是张量形状大小

  

  重写类型,结果具有相同的形状大小

  

  获取张量的尺寸大小:

  

  注:torch.Size实际上是一个元组,所以它支持所有的元组操作。

Operations(运算)

  pytorch有多种运算的语法。在下列例子中,我们看一下加法运算:

  加法1

  

  加法2:

  

  加法:提供一个输出tensor作为参数

  

  加法:in-place ( 相当于+= ,将结果直接赋值到左侧的变量)

  

  注:任何方法名中使用“_”的都是in-place操作,比如x.copy_(y),x.t_(),都会使x的值发生改变。

  同时,你也可以使用标准NumPy风格的索引操作:

  

  Resizing:如果你想要resize或reshape一个张量,可以使用方法 torch.view :

  

  如果张量只有一个元素,可以使用.item()来获取一个python数值

  

NumPy Bridge

  Torch的张量和NumPy数组的相互转换,他们共享底层的内存位置,更改一个另一个也会改变。

  张量转换为NumPy数组

  

  当张量的值改变时,numpy数组的值也同时改变:

  

  Numpy数组转换为张量

  

  除了字符张量,所有在CPU上的张量都能与NumPy数组相互转换。

CUDA Tensors

  张量能够被转移到任何设备通过使用 .to 方法。

  

AUTOGRAD: AUTOMATIC DIFFERENTIATION

  autograd包在pytorch中是所有神经网络的核心,我们先看一下然后将会训练自己的神经网络。

  autograd包给所有张量运算提供了自动微分。他是一个运行定义框架(define-by-run framework),意为着被你的代码如何运行所定义,每次的单个迭代都是不同的。

  让我们用简单的术语和一些例子来看下:

Tensor

  torch.Tensor是核心类。如果你设置了它的属性 .requires_grad为True, 它就开始跟踪所有的运算。当完成所有的计算可以通过 .backward() 然后就可以自动进行所有的梯度计算。该张量的所有梯度将会累加到.grad() 属性。

  通过调用 .detach() 方法可以停止一个张量对计算记录的追踪。

  另一种阻止张量计算追踪的方法是,将代码块写入 with torch.no_grad(): ,这在训练中评价测试一个模型的时候尤其有用,因为我们设置了requires_grad =True,而此时并不需要梯度。

  自动微分的另一个重要实现是类Function。

  Tensor和Function是相互联系的并建立了一个非循环图,记录了计算的完整历史。每个张量有.grad_fn属性,这个属性与创建张量的Function相关联(除了用户自己创建张量,该属性grad_fn is None)

  如果你想要计算导数,你可以调用张量的.backward()方法,如果张量是一个标量,如只有一个元素的数据,则backward()不需要指定任何参数;如果其有更多参数,则需要指定一个具有匹配张量大小的gradient参数。

  创建一个张量并设置 requires_grad = True来追踪计算:

  

  张量运算:

  

  y是作为运算的结果所创建的,所以它有属性 grad_fn。

  

  y的更多运算:

  

  .require_grad_()方法可以改变张量requires_grad的值,如果没指定的话,默认值为False。

  

  

Gradients

  开始反向传播。因为 out 包含一个单一的标量,out.backward() 相当于 out.backward((torch.tensor(1.))) 。  

  打印梯度d(out)/dx:

x = [ [1, 1], [1, 1] ]

y = x + 2

 z =  y * y * 3

out = z.mean

d(out)/dx = 3/2(x + 2)

  

  

  

  当.requires_grad=True 时,你也可以通过写入代码块with torch.no_grad()停止追踪历史和自动微分:

  

NEURAL NETWORKS

  torch.nn可以创建神经网络,nn依赖于 autograd 来定义模型和微分。nn.Moudle包括各种层和返回output的方法forward(input)。

  例如,以下为对数字图片进行分类的网络:

  

  一个简单的前馈网络,获得输入并经过一个个网络层,最终得到输出。

  以下是一个神经网络的训练步骤:

  • 定义一个具有可学习参数或权重的神经网络;
  • 对一个数据集的输入进行迭代;
  • 通过网络处理输入;
  • 计算损失
  • 将梯度反向传播给网络参数
  • 更新网络参数,通常的更新方法为 weight = weight - learning_rate* gradien

Define the network

#encoding:utf-8
import torch
import torch.nn as nn
import torch.nn.functional as F class Net(nn.Module): def __init__(self):
super(Net,self).__init__()
#定义2个卷积层
self.conv1=nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
self.conv2=nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5)
#定义了3个线性层,即y=wx+b
self.fc1=nn.Linear(in_features=16*5*5,out_features=120)
self.fc2=nn.Linear(120,84)
self.fc3=nn.Linear(84,10) def forward(self, x):
#在第一层卷积网络和第二层之间增加了relu激活函数和池化层
x=F.max_pool2d(input=F.relu(input=self.conv1(x)),kernel_size=(2,2))
#如果池化层是方块形的可以用一个number指定
x=F.max_pool2d(input=F.relu(input=self.conv2(x)),kernel_size=2)
x=x.view(-1,self.num_flat_features(x))
x=F.relu(input=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:]#切片0里面放的是当前训练batch的number,不是特征信息
num_features=1
for s in size:
num_features*=s
return num_features
net=Net()
print(net)
Net(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)

  你只要定义前向函数,当你使用 autograd 时,后向传播函数将会自动定义。在前向函数中你可以使用任何张量运算。

  模型的可学习参数由 net.parameters() 返回。

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

  这个网络适合32*32的数据集(1*32*32经过一个核心为5的卷积层,大小变为6*28*28;经过一个核心为2的池化层,大小变为6*14*14;再经过一个核心为5的卷积层,大小变为16*10*10;池化层,16*5*5,正好是下一个线性层的输入特征数),我们随机一个输入数据集,运行一下我们的模型吧:

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[-8.2934e-03, -1.9684e-02, -7.6836e-02,  5.3187e-02,  1.1083e-01,
-2.5156e-02, -6.1870e-05, -8.3843e-03, 4.1401e-02, -5.8330e-02]],
grad_fn=<AddmmBackward>)

  将所有参数的梯度设为0,使用随机梯度进行反向传播:

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

  注:torch.nn只支持批量数据的处理,而不是单个样本。例如,nn.Conv2d 将会使用 4D张量,nSamples x nChannels x Height x Width,如果只有单个样本,可以使用input.unsqueeze(0)来增加一个假的batch维度。

  回顾下刚学到的几个类:

  • torch.Tensor- 多维数组并支持自动微分运算如backward()
  • nn.Moudle-神经网络模型,帮助我们封装参数,移植到GPU,导出和加载等
  • nn.Parameter-张量,当你给Module定义属性时,参数会自动生成
  • autograd.Function-实现前向和反向定义的自动微分运算。每个张量运算至少创建一个Function结点,来连接创建张量和记录历史的函数

Loss Function

  损失函数计算输出和目标之间的值,来衡量两者的差距。在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)
tensor(0.9531, grad_fn=<MseLossBackward>)

  如果你沿着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
<MseLossBackward object at 0x7f6e48d5ee48>
<AddmmBackward object at 0x7f6e48d5eac8>
<AccumulateGrad object at 0x7f6e48d5eac8>

Backprop

  为了反向传播误差我们需要做的只要使用 loss.backward() ,同时也需要清除已有的梯度,否则梯度会累加到已经存在的梯度。

  调用 loss.backward() ,观察conv1 层bias的梯度反向传播前后的变化  

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)
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0022, 0.0022, -0.0139, 0.0072, 0.0029, 0.0035])

  现在我们已经知道了如何使用损失函数。

Update the weights

  在实际中使用的最简单的更新规则是随即梯度下降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等等,pytorch也提供了相关的包: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

  注:代码中我们手动将梯度设置为0,optimizer.zero_grad(),这是因为梯度不置0的话将会累加以前的值,在反向传播那一节我们也提到过。

TRAINING A CLASSIFIER

  我们已经知道了如何定义网络,计算损失和更新网络权重,那我们将会思考,那数据呢?

  通常来讲,当你处理图片,文本,语音和视频数据,可以使用标准的python包来加载数据为numpy数组,然后转换为张量torch.*Tensor。

  • 图片,通常可以使用Pillow, OpenCV
  • 语音,使用scipy 和 librosa
  • 文本,纯python或Cython,也可以使用NLTK和SpaCy

  对于视觉,我们专门创建了一个名为 torchvision 的包,其拥有加载普通数据集(Imagenet, CIFAR10, MNIST)的加载器和图片数据转换器,即torchvision.datasets 和 torch.utils.data.DataLoader。

  在本教程中,我们将使用CIFAR10数据集。它有10个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’. CIFAR10中的图像的大小为3*32*32,即大小为32*32的有3个通道的彩色图像。  

Training an image classifier

  接下来我们要做一步步完成以下内容:

  1. 使用torchvision加载和归一化CIFAR10的训练和测试数据集
  2. 定义一个卷积神经网络
  3. 定义一个损失函数
  4. 在训练数据上训练数据
  5. 在测试集上测试网络

1. Loading and normalizing CIFAR10

import torch
import torchvision
import torchvision.transforms as transforms

  torchvision数据集的输出是在[0,1]范围的]PILImage,我们将其转化为归一化范围[-1, 1]的张量。

transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Files already downloaded and verified

  使用matplotlib库查看训练数据:

import matplotlib.pyplot as plt
import numpy as np # functions to show an image def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show() # get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next() # show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

frog  bird truck  ship

2. Define a Convolutional Neural Network

  复制神经网络那一节的代码,然后将输入图像的通道改为3个,

import torch.nn as nn
import torch.nn.functional as F class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1=nn.Conv2d(3,6,5)
self.pool=nn.MaxPool2d(kernel_size=2,stride=2)
self.conv2=nn.Conv2d(6,16,5)
self.fc1=nn.Linear(16*5*5,120)
self.fc2=nn.Linear(in_features=120,out_features=84)
self.fc3 = nn.Linear(in_features=84, out_features=10)
def forward(self, x):
x=self.pool(F.relu(self.conv1(x)))
x=self.pool(F.relu(self.conv2(x)))
x=x.view(-1,16*5*5)
x=F.relu(self.fc1(x))
x=F.relu(self.fc2(x))
x=self.fc3(x)
return x
net=Net()
print(net)
Net(
(conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)

3. Define a Loss function and optimizer

  使用分类的交叉熵损失函数和带动量的随机梯度下降。

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

4. Train the network  

for epoch in range(2):#循环整个数据集多少遍

    running_loss=0.0
for i,data in enumerate(trainloader,start=0):
#get the inputs
inputs, labels = data # 梯度清0
optimizer.zero_grad() #forward + backword + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step() #打印统计信息
running_loss += loss.item()
if i % 2000 == 1999: #每2000个mini-batches打印一次
print("[%d, %d] loss: %.3f" %
(epoch+1, i+1, running_loss/2000))
running_loss=0.0
print('Finished Training')

  

  

      

  

  

  

最新文章

  1. mysql数据类型和列属性
  2. C#如何自定义DataGridViewColumn来显示TreeView
  3. HiveServer2 的jdbc方式创建udf的修改(add jar 最好不要使用),否则会造成异常: java.sql.SQLException: Error while processing statement: null
  4. IOS开发UI基础UILabel属性
  5. JavaScript的apply和call方法及其区别
  6. CentoS 下安装gitlab
  7. mac出现一个白条
  8. JsRender系列demo(7)compline
  9. Eval 表达式 GridView ItemCommand
  10. Python 学习入门(21)—— 线程
  11. 如何规避“Flash中国特供版”
  12. Docker数据卷持久化
  13. mysql之 表数据存放路径非datadir目录
  14. dojo:如何用MultiSelect实现类似ListBox风格的FromTo功能
  15. [转]OpenGL通过VBO实现顶点数组绘制顶点
  16. 读书笔记 C# Linq查询之group关键字浅析
  17. python3: 字符串和文本(4)
  18. LOJ #2585. 「APIO2018」新家
  19. 2016-2017 ACM-ICPC Southwestern European Regional Programming Contest (SWERC 2016) F dfs序+树状数组
  20. numpy 练习

热门文章

  1. angular学习总结
  2. MongoDB高级操作(2)
  3. Python之路:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy
  4. Codeforces Round #530 Div. 1 自闭记
  5. python_面向对象小试题
  6. idea log4j 用法
  7. MT【188】一个正切余切有关的恒等式
  8. SSH &amp; Git
  9. CentOS服务器配置SSH免密码登录
  10. 洛谷 P1993 小K的农场 解题报告