跟我一起学PyTorch-03:PyTorch基础知识

PyTorch的许多函数在使用上和Numpy几乎一样,能够平滑地结合使用,Numpy的绝大多数操作同样可以用在PyTorch中。PyTorch的特色之一是提供构建动态计算图的框架,这样网络结构不再是一成不变的了,甚至可以在运行时修正它们。在神经网络方面,PyTorch的优点还在于使用了多GPU的强大加速能力、自定义数据加载器和极简的预处理过程等。尽管PyTorch与其他框架相比还算是新秀,仍然需要完善和改进,但不否认它一出现就得到了广泛的认同和运用。

1.Tensor简介

Tensor是PyTorch中的基本对象,意思为张量,表示多维的矩阵,是PyTorch中的基本操作对象之一。与Numpy中的多维数组ndarray类似。Tensor的声明和获取大小:

>>> import torch
>>> x = torch.Tensor(5,3)
>>> x.size()
torch.Size([5, 3])

Tensor的算术运算和选取操作与Numpy一样,因此Numpy相似的运算操作都可以迁移过来:

>>> x = torch.rand(5,3)
>>> y = torch.rand(5,3)
>>> x+y
tensor([[0.9579, 1.0589, 1.3667],
        [0.1667, 0.2882, 1.4379],
        [0.5773, 1.1986, 0.6230],
        [1.0630, 1.3136, 0.6978],
        [1.3252, 1.4158, 0.3060]])
>>> x[:,1]
tensor([0.3851, 0.1678, 0.8133, 0.6530, 0.8389])

Tensor与Numpy的array还可以进行相互转换,有专门的转换函数:

>>> x = torch.rand(5,3)
>>> y = x.numpy()
>>> z = torch.from_numpy(y)

2.Variable简介

Variable是PyTorch的另一个基本对象,可以把它理解为是对Tensor的一个封装。Variable用于放入计算图中进行前向传播、反向传播和自动求导,如下图所示。在一个Variable中有三个重要属性:data、grad和creator。其中,data表示包含的Tensor数据部分;grad表示传播方向的梯度,这个属性是延迟分配的,而且仅允许进行一次;creator表示创建这个Variable的Function的引用,该引用用户回溯整个创建链路。如果是用户创建的Variable,其creator为None,同时这种Variable称为Leaf Variable,autograd只会给Leaf Variable分配梯度。


image.png
>>> from torch.autograd import variable
>>> x = torch.rand(4)
>>> x = variable(x, requires_grad = True)
>>> y = x * 3
>>> grad_variables = torch.FloatTensor([1,2,3,4])
>>> y.backward(grad_variables)
>>> x.grad
tensor([ 3.,  6.,  9., 12.])

对于y.backward(grad_variables),grad_variables就是y求导时的梯度参数,由于autograd仅用于标量,因此当y不是标量而且在声明时使用了requires_grad = True时,必须指定grad_variables参数,然后将结果保存至Variable的grad中。grad_variables的长度要与y的长度一致。在深度学习中求导与梯度有关,因此grad_variables一般会定义类似为[1, 0.1, 0.01, 0.001],表示梯度的方向,求较小的值不会对求导效率有影响。

3.CUDA简介

如果安装了支持CUDA版本的PyTorch,就可以启用显卡运算了。torch.cuda用于设置和运行CUDA操作,它会记录当前选择的GPU,并且分配的所有CUDA张量将默认在上面创建,可以使用torch.cuda.device上下文管理器更改所选设备。

不过,一旦张量被分配,就可以直接进行操作,而不考虑所选择的设备,结果将始终放在与张量相关的设备上。默认情况下,不支持跨GPU操作,唯一例外的是copy_()。除非启用对等存储器访问,否则对于分布不同设备上的张量,任何启动操作的尝试都将引发错误。

>>> torch.cuda.is_available()
True

>>> x = x.cuda()
>>> y = y.cuda()
>>> x + y
tensor([2.7590, 2.7915, 2.8413, 1.1887], device='cuda:0',grad_fn=)

4.模型的保存于加载

Python中对于模型数据的保存和加载操作都是引用Python内置的pickle包,使用pickle.dump()和pickle.load()方法。在PyTorch中也有同样功能的方法提供。

>>> torch.save(model,'model.pkl') # 保存整个模型
>>> model = torch.load('model.pkl') #加载整个模型
>>> torch.save(alexnet.state_dict(),'params.pkl') # 保存网络中的参数
>>> alexnet.load_state_dict(torch.load('params.pkl')) # 加载网络中的参数

在torchvision.models模块里,PyTorch提供了一些常用的模型:

  • AlexNet
  • VGG
  • ResNet
  • SqueezeNet
  • DenseNet
  • Inception v3

可以使用torch.util.model_zoo来预加载它们,具体设置通过参数pretrained=True来实现。

>>> import torchvision.models as models
>>> ResNet18 = models.ResNet(pretrained=True)
>>> alexnet = models.alexnet(pretrained=True)
>>> squeezenet = models.squeezenet(pretrained=True)
>>> vgg16 = models.vgg16(pretrained=True)
>>> densenet = models.densenet(pretrained=True)
>>> inception = models.inception(pretrained=True)

加载这类预训练模型的过程中,还可以进行微处理。

>>> from torch.utils import model_zoo
>>> pretrained_dict = model_zoo.load_url(model_urls['resnet134'])
>>> model_dict = model.state_dict()
>>> pretrained_dict = {k:v for k,v in pretrained_dict.items() if k in model_dict} # 将pretrained_dict里不属于model_dict的键剔除掉
>>> model_dict.update(pretrained_dict) # 更新现有的model_dict
>>> model.load_state_dict(model_dict)

5.第一个PyTorch程序

下面的这段程序是对线性回归模型的简单演练。该示例中先创建了一些随机训练样本,让其符合经典线性函数分布,并加了一点噪声处理,使得样本出现一些偏差。接着使用PyTorch创建一个线性回归的模型,在训练过程中对训练样本进行反向传播,求导后根据指定的损失边界结束训练。最后显示模型学习的结果与真实情况的对比示意图。

完整的代码如下:

#!/usr/bin/env python

from __future__ import print_function
from itertools import count
import numpy as np
import torch
import torch.autograd
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt

random_state = 5000
torch.manual_seed(random_state)
poly_degree = 4
W_target = torch.randn(poly_degree, 1) * 5
b_target = torch.randn(1) * 5

def make_features(x):
    """创建一个特征矩阵,结构为[x, x^2, x^3, x^4]"""
    x = x.unsqueeze(1)
    return torch.cat([x ** i for i in range(1, poly_degree + 1)], 1)

def f(x):
    """近似函数"""
    return x.mm(W_target) + b_target[0]

def poly_desc(W, b):
    """生成多项式描述内容"""
    result = 'y = '
    for i, w in enumerate(W):
        result += '{:+.2f} x^{} '.format(w, len(W) - i)
    result += '{:+.2f}'.format(b[0])
    return result

def get_batch(batch_size=32):
    """创建类似(x, f(x))批数据"""
    random = torch.from_numpy(np.sort(torch.randn(batch_size)))
    x = make_features(random)
    y = f(x)
    return Variable(x), Variable(y)

# 声明模型
fc = torch.nn.Linear(W_target.size(0), 1)

for batch_idx in count(1):
    # 获取数据
    batch_x, batch_y = get_batch()
    
    # 重置求导
    fc.zero_grad()

    # 前向传播
    output = F.smooth_l1_loss(fc(batch_x), batch_y)
    loss = output.item()

    # 后向传播
    output.backward()

    # 应用导数
    for param in fc.parameters():
        param.data.add_(-0.1 * param.grad.data)

    # 停止条件
    if loss < 1e-3:
        plt.cla()
        plt.scatter(batch_x.data.numpy()[:, 0], batch_y.data.numpy()[:, 0], label='real curve', color='b')
        plt.plot(batch_x.data.numpy()[:, 0], fc(batch_x).data.numpy()[:, 0], label='fitting curve', color='r')
        plt.legend()
        plt.show()
        break

print('Loss: {:.6f} after {} batches'.format(loss, batch_idx))
print('==> Learned function:\t' + poly_desc(fc.weight.data.view(-1), fc.bias.data))
print('==> Actual function:\t' + poly_desc(W_target.view(-1), b_target))

执行上述代码,输出如下:

Loss: 0.000518 after 760 batches
==> Learned function:   y = +6.07 x^4 +3.91 x^3 +0.90 x^2 +8.19 x^1 -1.64
==> Actual function:    y = +6.13 x^4 +3.89 x^3 +0.86 x^2 +8.20 x^1 -1.62
image.png

你可能感兴趣的:(跟我一起学PyTorch-03:PyTorch基础知识)