【pytorch】pytorch学习笔记(一)

原文地址: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_rategradien

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 nChannels Height 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')

 

  

 

  

      

  

  

  

 

posted @ 2019-01-26 16:38  WhiteXie_zx  阅读(4112)  评论(0编辑  收藏  举报