呕心沥血之pytorch框架从头到尾介绍

目录

前言

一、基本数据:Tensor

1.1 Tensor的创建

1.2 torch.FloatTensor

1.3 torch.IntTensor

1.4 torch.randn

1.5 torch.range

1.6 torch.zeros/ones/empty

二、Tensor的运算

2.1 torch.abs()

2.2 torch.add()

2.3 torch.clamp()

2.4 torch.div()

2.5 torch.pow()

2.6 torch.mm()

2.7torch.mul()

2.8 torch.mv()

三、神经网络工具箱

3.1 torch.nn

3.2 搭建简易神经网络

四、torch实现一个完整的神经网络

4.1 torch.autograd和Variable

4.2 自定义传播函数

4.3 PyTorch 之 torch.nn模块详细介绍

4.3.1 torch.nn.Linear

4.3.2 torch.nn.ReLU

4.3.3 torch.nn.Sequential

4.3.4 torch.nn.MSELoss

4.3.5 torch.nn.L1Loss

4.3.6 torch.nn.CrossEntropyLoss

4.3.7 使用torch.nn.MSELoss的神经网络

4.4 PyTorch 之 torch.optim

五、搭建神经网络实现

5.1 torchvision

5.1.1 torchvision.datasets

5.1.2 torchvision.models

5.1.3 torch.transforms

5.1.3.1 torchvision.transforms.Resize

5.1.3.2 torchvision.transforms.Scale

5.1.3.3 torchvision.transforms.CenterCrop

5.1.3.4 torchvision.transforms.RandomCrop

5.1.3.5 torchvision.transforms.RandomHorizontalFlip

5.1.3.6 torchvision.transforms.RandomVerticalFlip

5.1.3.7 torchvision.transforms.ToTensor

5.1.3.8 torchvision.transforms.ToPILImage:

5.1.3.9 torchvision.transforms.Compose(transforms)

5.1.4 torch.utils

5.2 模型搭建和参数优化

5.2.1 torch.nn.Conv2d

5.2.2 torch.nn.MaxPool2d

5.2.3 torch.nn.Dropout

5.3 参数优化

5.4完整代码

总结


前言

学习深度学习一个好的框架十分的重要,现在主流的就是PyTorch和TensorFlow,今天我们一起来学习PyTorch,PyTorch是使用GPU和CPU优化的深度学习张量库。本文通过详细且实践性的方式介绍了 PyTorch 的基础知识、张量操作、自动求导机制、神经网络创建、数据处理、模型训练、测试以及模型的保存和加载。


一、基本数据:Tensor

Torch定义了七种CPU tensor类型和八种GPU tensor类型:

Tensor,即张量,是PyTorch中的基本操作对象,可以看做是包含单一数据类型元素的多维矩阵。从使用角度来看,Tensor与NumPy的ndarrays非常类似,相互之间也可以自由转换,不过Tensor还支持GPU的加速。

1.1 Tensor的创建

1.2 torch.FloatTensor

torch.FloatTensor用于生成数据类型为浮点型的Tensor,传递给torch.FloatTensor的参数可以是维度值,也可以是一个列表。torch.Tensor是默认的tensor类型(torch.FlaotTensor)的简称。

import torch
a = torch.FloatTensor(2,3)
b = torch.FloatTensor([2,3,4,5])
a,b

得到的结果是:

(tensor([[1.0561e-38, 1.0102e-38, 9.6429e-39],
         [8.4490e-39, 9.6429e-39, 9.1837e-39]]),
 tensor([2., 3., 4., 5.]))

1.3 torch.IntTensor

torch.IntTensor用于生成数据类型为整型的Tensor,传递给传递给torch.IntTensor的参数可以是维度值,也可以是一个列表。

import torch
a = torch.IntTensor(2,3)
b = torch.IntTensor([2,3,4,5])
print(f"""{a}
{b}""")

得到的结果是:

tensor([[0, 0, 0],
        [0, 0, 0]], dtype=torch.int32)
tensor([2, 3, 4, 5], dtype=torch.int32)

1.4 torch.randn

用于生成数据类型为浮点数且维度指定的随机Tensor,和在numpy中使用的numpy.randn生成的随机数的方法类似,随机生成的浮点数的取值满足均值为0,方差为1的正态分布。

import torch
a = torch.randn(2,3)
print(a)

得到的结果是:

tensor([[-0.4916, -0.5392,  1.4072],
        [ 0.5161,  1.4514, -0.3777]])

1.5 torch.range

torch.range用于生成数据类型为浮点型且起始范围和结束范围的Tensor,所以传递给torch.range的参数有三个,分别为起始值,结束值,步长,左右都是闭区间,即1到20都可取。其中步长用于指定从起始值到结束值得每步的数据间隔。

import torch
a = torch.range(1,20,2)
print(a)

得到的结果是:

tensor([ 1.,  3.,  5.,  7.,  9., 11., 13., 15., 17., 19.])

1.6 torch.zeros/ones/empty

torch.zeros用于生成数据类型为浮点型且维度指定的Tensor,这个浮点型的Tensor中的元素值全部为0。也可以是torch.Tensor(2, 3).zero_()

import torch
a = torch.zeros(2,3)
print(a)

得到的结果是:

tensor([[0., 0., 0.],
        [0., 0., 0.]])

torch.ones生成全1的数组。

import torch
a = torch.ones(2,3)
print(a)

得到的结果是: 

tensor([[1., 1., 1.],
        [1., 1., 1.]])

torch.empty创建一个未被初始化数值的tensor,tensor的大小是由size确定。

size: 定义tensor的shape ,这里可以是一个list 也可以是一个tuple

import torch
a = torch.empty(2,3)
print(a)

得到的结果是: 

tensor([[7.5338e+28, 5.9288e+11, 6.1186e-04],
        [4.3988e+21, 7.5631e+28, 5.2839e-11]])

二、Tensor的运算

2.1 torch.abs()

将参数传递到torch.abs后返回输入参数的绝对值作为输出,输入参数必须是一个Tensor数据类型的变量,如:

import torch
a = torch.randn(2,3)
print(a)

得到的a是: 

tensor([[-0.2852,  1.0085, -1.0338],
        [-0.1877, -1.6604, -1.0330]])

对a进行abs处理:

b = torch.abs(a)
print(b)

得到的b是:  

tensor([[0.2852, 1.0085, 1.0338],
        [0.1877, 1.6604, 1.0330]])

2.2 torch.add()

将参数传递到torch.add后返回输入参数的求和结果作为输出,输入参数既可以全部是Tensor数据类型的变量,也可以一个是Tensor数据类型的变量,另一个是标量。

import torch
a = torch.randn(2,3)
print(a)
b = torch.randn(2,3)
print(b)
c = torch.add(a,b)
print(c)

得到的结果:

tensor([[0.0408, 1.1193, 1.2028],
        [0.6015, 0.4013, 2.1503]])
tensor([[-0.1892, -0.9570, -1.3704],
        [ 0.8400, -1.9080,  0.9387]])
tensor([[-0.1484,  0.1623, -0.1676],
        [ 1.4416, -1.5067,  3.0890]])

再看tensor和标量的情况:

import torch
a = torch.randn(2,3)
print(a)
b = 10
print(b)
c = torch.add(a,b)
print(c)

tensor([[ 0.8901,  0.0094,  0.7158],
        [ 0.4988, -0.2313,  0.0321]])
10
tensor([[10.8901, 10.0094, 10.7158],
        [10.4988,  9.7687, 10.0321]])

2.3 torch.clamp()

torch.clamp是对输入参数按照自定义的范围进行裁剪,最后将参数裁剪的结果作为输出,所以输入参数一共有三个,分别是需要进行裁剪的Tensor数据类型的变量、裁剪的下边界和裁剪的上边界,裁剪过程:使用变量中的每个元素分别与下边界和上边界的值进行比较,如果元素的值小于裁剪的下边界的值,该元素被重写成下边界的值;如果元素的值大于上边界的值,该元素就被重写成上边界的值。如果元素的值在下边界和上边界之间就不变。

import torch
a = torch.randn(2,3)
print(a)
b = torch.clamp(a,-1,1)
print(b)

得到的结果: 

tensor([[ 1.8599, -1.3503,  0.2013],
        [ 0.5805, -1.9710,  0.2958]])
tensor([[ 1.0000, -1.0000,  0.2013],
        [ 0.5805, -1.0000,  0.2958]])

2.4 torch.div()

torch.div是将参数传递到torch.div后返回输入参数的求商结果作为输出,同样,参与运算的参数可以全部是Tensor数据类型的变量,也可以是Tensor数据类型的变量和标量的组合。

import torch
a = torch.randn(2,3)
print(a)
b = torch.randn(2,3)
print(b)
c = torch.div(a,b)
print(c)

tensor([[ 1.1057,  0.3762,  0.2585],
        [-0.6597,  0.2669,  0.5578]])
tensor([[ 0.0687,  0.6306, -0.4086],
        [ 0.7401,  0.6681, -0.6704]])
tensor([[16.1001,  0.5966, -0.6326],
        [-0.8914,  0.3995, -0.8320]])

2.5 torch.pow()

torch.pow:将参数传递到torch.pow后返回输入参数的求幂结果作为输出,参与运算的参数可以全部是Tensor数据类型的变量,也可以是Tensor数据类型的变量和标量的组合。

import torch
a = torch.randn(2,3)
print(a)
b = torch.pow(a,2)
print(b)

tensor([[-0.1936,  0.9489,  0.4453],
        [-0.9187, -1.0279,  0.3552]])
tensor([[0.0375, 0.9004, 0.1983],
        [0.8440, 1.0565, 0.1262]])

2.6 torch.mm()

torch.mm(a,b):正常矩阵相乘,要求a的列数与b的行数相同。torch.mm(a,b)等价于a@b.T

import torch
a = torch.randn(2,3)
print(a)
b = torch.randn(2,3)
print(b)
c = torch.mm(a,b.T)
print(c)
d = a@b.T
print(c == d)

tensor([[-0.0761, -2.0854,  0.4789],
        [-1.5470, -0.2247, -0.1333]])
tensor([[-1.2039, -0.3026, -0.3079],
        [ 0.4885, -0.0547, -0.4160]])
tensor([[ 0.5752, -0.1224],
        [ 1.9715, -0.6879]])
tensor([[ 0.5752, -0.1224],
        [ 1.9715, -0.6879]])
tensor([[True, True],
        [True, True]])

2.7torch.mul()

torch.mul(a,b)表示相同shape矩阵点乘,即对应位置相乘,得到矩阵有相同的shape。torch.mul(a,b)等同于a*b

import torch
a = torch.randn(2,3)
print(a)
b = torch.randn(2,3)
print(b)
c = torch.mul(a,b)
print(c)
d = a*b
print(c == d)

tensor([[-0.4312, -0.0642, -0.7170],
        [ 1.3519,  0.4870, -0.0252]])
tensor([[-0.1908, -0.7178, -0.5137],
        [ 1.7802,  0.6434, -0.7291]])
tensor([[0.0823, 0.0461, 0.3683],
        [2.4067, 0.3133, 0.0184]])
tensor([[True, True, True],
        [True, True, True]])

2.8 torch.mv()

torch.mv(a,b):是矩阵和向量相乘.第一个参数是矩阵,第二个参数只能是一维向量,等价于a乘于b的转置

import torch
a = torch.randn(2,3)
print(a)
b = torch.randn(3)
print(b)
c = torch.mv(a,b)
print(c)

tensor([[ 0.7834,  0.4062, -0.2497],
        [-0.0176,  1.7290,  0.8626]])
tensor([1.7972, 0.5609, 0.9367])
tensor([1.4018, 1.7461])

小技巧:torch.mm丶torch.mul和torch.mv如何快速分别,matrix矩阵,vector向量,multiply相乘,

torch.mm:后面两个m表示matrix

torch.mul:mul是multiply的缩写

touch.mv:m是matrix的缩写,v是vector的缩写

三、神经网络工具箱

torch.autograd库虽然实现了自动求导与梯度反向传播,但如果我们要完成一个模型的训练,仍需要手写参数的自动更新、训练过程的控制等,还是不够便利。为此,PyTorch进一步提供了集成度更高的模块化接口torch.nn,该接口构建于Autograd之上,提供了网络模组、优化器和初始化策略等一系列功能。

3.1 torch.nn

torch.nn是PyTorch提供的神经网络类(neural network),并在类中实现了网络各层的定义及前向计算与反向传播机制。在实际使用时,如果想要实现某个神经网络,torch.nn模块包含torch为我们准备好的各种层,方便我们调用以构建网络。比如:卷积层、池化层、激活函数层、循环层、全连接层等的相关使用方法。只需继承nn.Module,在初始化中定义模型结构与参数,在函数forward()中编写网络前向过程即可。

3.2 搭建简易神经网络

下面我们用torch搭一个简易神经网络:
1、我们设置输入节点为1000,隐藏层的节点为100,输出层的节点为10
2、输入100个具有1000个特征的数据,经过隐藏层后变成100个具有10个分类结果的特征,然后将得到的结果后向传播

import torch

#初始化参数
batch_n = 100  # 一个批次输入数据的数量
input_data = 1000  # 每个数据的特征为1000
hidden_layer = 100
output_data = 10
x = torch.randn(batch_n, input_data)     #输入数据:100个具有1000个特征的数据
y = torch.randn(batch_n, output_data)    #输出数据:100个具有10种分类的结果
w1 = torch.randn(input_data, hidden_layer)  #初始化输入层和隐层权重,tensor(1000,100)
w2 = torch.randn(hidden_layer, output_data)    #初始化隐层和输出层权重,tensor(100,10)
epoch_n = 100    #训练轮次
lr = 1e-6   #学习率

for epoch in range(epoch_n):
    h1 = x.mm(w1)  # (100,1000)@(1000,100)-->(100,100)
    h1 = h1.clamp(min=0)    #裁剪下边界
    y_pred = h1.mm(w2)   #预测的结果
    # 损失函数
    loss = (y_pred - y).pow(2).sum()
    #反向传播,更新权重w2和w1
    grad_y_pred = 2 * (y_pred - y)   #grad_y_pred:tensor(100,10)
    grad_w2 = h1.t().mm(grad_y_pred)    #grad_w2:tensor(100,10)
    grad_h = grad_y_pred.clone()    #clone():开辟新的内存,返回一个完全相同的tensor
    grad_h = grad_h.mm(w2.t())  #grad_h:tensor(100,100)
    grad_h.clamp_(min=0)  # 将小于0的值全部赋值为0,相当于sigmoid
    grad_w1 = x.t().mm(grad_h)    #x:tensor(100,1000)   x.t():tensor(1000,100)  grad_w1:(1000,100)
    w1 = w1 - lr * grad_w1   #w1:tensor(1000,100)
    w2 = w2 - lr * grad_w2  #w2:tensor(100,10)
    if epoch % 10 == 0:
        print(h1.shape)
        print("epoch:{},loss:{:.4f}".format(epoch, loss))

得到的结果:

torch.Size([100, 100])
epoch:0,loss:53135012.0000
torch.Size([100, 100])
epoch:10,loss:1620158.6250
torch.Size([100, 100])
epoch:20,loss:772108.7500
torch.Size([100, 100])
epoch:30,loss:455443.8750
torch.Size([100, 100])
epoch:40,loss:301568.7812
torch.Size([100, 100])
epoch:50,loss:215368.5000
torch.Size([100, 100])
epoch:60,loss:162041.8438
torch.Size([100, 100])
epoch:70,loss:126637.3047
torch.Size([100, 100])
epoch:80,loss:101764.2344
torch.Size([100, 100])
epoch:90,loss:83605.5938

四、torch实现一个完整的神经网络

4.1 torch.autograd和Variable

torch.autograd包的主要功能就是完成神经网络后向传播中的链式求导。

自动梯度的功能过程大致为:先通过输入的Tensor数据类型的变量在神经网络的前向传播过程中生成一张计算图,然后根据这个计算图和输出结果精确计算出每一个参数需要更新的梯度,并通过完成后向传播完成对参数的梯度更新。

完成自动梯度需要用到的torch.autograd包中的Variable类对我们定义的Tensor数据类型变量进行封装,在封装后,计算图中的各个节点就是一个Variable对象,这样才能应用自动梯度的功能。

下面我们使用autograd实现一个三层结构的神经网络模型
 

import torch
from torch.autograd import Variable

batch_n = 100  # 一个批次输入数据的数量
input_data = 1000  # 每个数据的特征为1000
hidden_layer = 100
output_data = 10
# 用Variable对Tensor数据类型变量进行封装的操作。requires_grad如果是False,表示该变量在进行自动梯度计算的过程中不会保留梯度值。
x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad=True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)

# 学习率和迭代次数
epoch_n = 100
lr = 1e-6

for epoch in range(epoch_n):
    h1 = x.mm(w1)  # (100,1000)*(1000,100)-->100*100
    h1 = h1.clamp(min=0)
    y_pred = h1.mm(w2)
    loss = (y_pred - y).pow(2).sum()
    loss.backward()  # 后向传播
    w1.data -= lr * w1.grad.data
    w2.data -= lr * w2.grad.data
    w1.grad.data.zero_()
    w2.grad.data.zero_()
    if epoch % 10 == 0:
        print(h1.shape)
        print("epoch:{},loss:{:.4f}".format(epoch, loss.data))

torch.Size([100, 100])
epoch:0,loss:46407064.0000
torch.Size([100, 100])
epoch:10,loss:4221967.5000
torch.Size([100, 100])
epoch:20,loss:936691.1250
torch.Size([100, 100])
epoch:30,loss:402065.7500
torch.Size([100, 100])
epoch:40,loss:237569.0781
torch.Size([100, 100])
epoch:50,loss:164869.8125
torch.Size([100, 100])
epoch:60,loss:124159.4922
torch.Size([100, 100])
epoch:70,loss:97910.7969
torch.Size([100, 100])
epoch:80,loss:79544.7188
torch.Size([100, 100])
epoch:90,loss:66041.4375

4.2 自定义传播函数

其实除了可以采用自动梯度方法,我们还可以通过构建一个继承了torch.nn.Module的新类,来完成对前向传播函数和后向传播函数的重写。在这个新类中,我们使用forward作为前向传播函数的关键字,使用backward作为后向传播函数的关键字。下面我们进行自定义传播函数:

import torch
from torch.autograd import Variable

class Model(torch.nn.Module):  # 完成类继承的操作
    def __init__(self):
        super(Model, self).__init__()  # 类的初始化

    def forward(self, input, w1, w2):
        x = torch.mm(input, w1)
        x = torch.clamp(x, min=0)
        x = torch.mm(x, w2)
        return x

    def backward(self):
        pass

batch_n = 64  # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000  # 每个数据的特征为1000
output_data = 10
epoch_n = 100
lr = 1e-6

model = Model()
# 用Variable对Tensor数据类型变量进行封装的操作。requires_grad如果是F,表示该变量在进行自动梯度计算的过程中不会保留梯度值。
x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad=True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)

for epoch in range(epoch_n):
    y_pred = model(x, w1, w2)
    loss = (y_pred - y).pow(2).sum()
    loss.backward()
    w1.data -= lr * w1.grad.data
    w2.data -= lr * w2.grad.data
    w1.grad.data.zero_()
    w2.grad.data.zero_()
    if epoch % 10 ==0:
        print("epoch:{},loss:{:.4f}".format(epoch, loss.data))

epoch:0,loss:44304884.0000
epoch:10,loss:1085179.2500
epoch:20,loss:254642.1250
epoch:30,loss:90138.6094
epoch:40,loss:38594.6016
epoch:50,loss:18416.0449
epoch:60,loss:9420.1582
epoch:70,loss:5071.0049
epoch:80,loss:2832.2644
epoch:90,loss:1626.8250

4.3 PyTorch 之 torch.nn模块详细介绍

4.3.1 torch.nn.Linear

torch.nn.Linear类用于定义模型的线性层,即完成前面提到的不同的层之间的线性变换。torch.nn.Linear类,会自动生成对应维度的权重参数和偏置,对于生成的权重参数和偏置,我们的模型默认使用一种比之前的简单随机方式更好的参数初始化方式。

class torch.nn.Linear(in_features, out_features, bias=True)

参数:

  • in_features : 每个输入样本的大小
  • out_features : 每个输出样本的大小
  • bias : 若设置为False,这层不会学习偏置。默认值:True

4.3.2 torch.nn.ReLU

torch.nn.ReLU属于非线性激活分类,在定义时默认不需要传入参数。当然,在torch.nn包中还有许多非线性激活函数类可供选择,比如PReLU、LeaKyReLU、Tanh、Sigmoid、Softmax等。

4.3.3 torch.nn.Sequential

torch.nn.Sequential类是torch.nn中的一种序列容器,模块将按照构造函数中传递的顺序添加到模块中。通俗的话说,就是根据自己的需求,把不同的函数组合成一个(小的)模块使用或者把组合的模块添加到自己的网络中。

import torch
import torch.nn as nn

class Net(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net, self).__init__()

        self.layer = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1),  # (18,15)
            nn.ReLU(True),
            nn.Linear(n_hidden_1, n_hidden_2),  # (15,10)
            nn.ReLU(True),
            nn.Linear(n_hidden_2, out_dim)  # (10,1)
        )

    def forward(self, x):
        x = self.layer(x)
        return x

net = Net(1000, 100, 10, 1)# instantiation
input = torch.randn(100, 1000)# output the predicted value
predict = net(input)# output the predicted value
print(predict.size())
print(net)

torch.Size([100, 1])
Net(
  (layer): Sequential(
    (0): Linear(in_features=1000, out_features=100, bias=True)
    (1): ReLU(inplace=True)
    (2): Linear(in_features=100, out_features=10, bias=True)
    (3): ReLU(inplace=True)
    (4): Linear(in_features=10, out_features=1, bias=True)
  )
)

4.3.4 torch.nn.MSELoss

torch.nn.MSELoss类:x作为输入时,模型预测输出和目标y之间均方误差作为误差计算标准。

                                                     loss(x,y)=1/n∑(xi−yi)2

定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算。如果在创建MSELoss的时候在构造函数中传入了size_average=False,那么求出的绝对值的和将不会除于n。

import torch
from torch.autograd import Variable
loss_f = torch.nn.MSELoss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
print(loss)

tensor(2.0111)

4.3.5 torch.nn.L1Loss

torch.nn.L1Loss类:x作为输入时,模型预测输出和目标y之间差的绝对值的平均值作为误差计算标准。

                                                          loss(x,y)=1/n∑|xi−yi|

使用实例时需要输入两个维度一样的参数方可进行计算,如果在创建L1Loss的时候在构造函数中传入了size_average=False,那么求出的绝对值的和将不会除于n。

import torch
from torch.autograd import Variable
loss_f = torch.nn.L1Loss()
x = Variable(torch.randn(200,200))
y = Variable(torch.randn(200,200))
loss = loss_f(x,y)
print(loss.data)

tensor(1.1312)

4.3.6 torch.nn.CrossEntropyLoss

torch.nn.CrossEntropyLoss类损失函数用于计算交叉熵,定义类的对象时不用传入任何参数,但在使用实例时需要输入两个满足交叉熵的计算条件的参数。

import torch
from torch.autograd import Variable
loss_f = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3,5))
y = Variable(torch.LongTensor(3).random_(5))#3个0-4的随机数字
loss = loss_f(x,y)
print(loss)

tensor(1.0468)

4.3.7 使用torch.nn.MSELoss的神经网络
import torch
from torch.autograd import Variable

batch_n = 100  # 一个批次输入数据的数量
input_data = 1000  # 每个数据的特征为1000
hidden_layer = 100
output_data = 10
epoch_n = 100
lr = 1e-6

x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)

loss_fn = torch.nn.MSELoss()
models = torch.nn.Sequential(
    torch.nn.Linear(input_data, hidden_layer),
    torch.nn.ReLU(),
    torch.nn.Linear(hidden_layer, output_data)
)

for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred, y)
    if epoch % 10 == 0:
        print("epoch:{},loss:{:.4f}".format(epoch, loss.data))
    models.zero_grad()
    loss.backward()
    for param in models.parameters():
        param.data -= param.grad.data * lr

epoch:0,loss:1.1110
epoch:10,loss:1.1110
epoch:20,loss:1.1110
epoch:30,loss:1.1110
epoch:40,loss:1.1110
epoch:50,loss:1.1110
epoch:60,loss:1.1110
epoch:70,loss:1.1109
epoch:80,loss:1.1109
epoch:90,loss:1.1109

4.4 PyTorch 之 torch.optim

torch.optim包提供非常多的可实现参数自动优化的类,如SGD、AdaGrad、RMSProp、Adam等
使用自动优化的类实现神经网络:

import torch
from torch.autograd import Variable

#初始化参数
batch_n = 100  # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000  # 每个数据的特征为1000
output_data = 10
epoch_n = 400
lr = 1e-4

x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)

loss_fn = torch.nn.MSELoss()
models = torch.nn.Sequential(
    torch.nn.Linear(input_data, hidden_layer),
    torch.nn.ReLU(),
    torch.nn.Linear(hidden_layer, output_data)
)

# 使用torch.optim.Adam类作为我们模型参数的优化函数,这里输入的是:被优化的参数和学习率的初始值。
# 因为我们需要优化的是模型中的全部参数,所以传递的参数是models.parameters()
optimzer = torch.optim.Adam(models.parameters(), lr=lr)

# 进行,模型训练的代码如下:
for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred, y)
    optimzer.zero_grad()  # 将模型参数的梯度归0
    loss.backward()
    optimzer.step()  # 使用计算得到的梯度值对各个节点的参数进行梯度更新。
    if epoch % 40 == 0:
        print("Epoch:{},Loss:{:.4f}".format(epoch, loss.data))

Epoch:0,Loss:1.0346
Epoch:40,Loss:0.4730
Epoch:80,Loss:0.2059
Epoch:120,Loss:0.0739
Epoch:160,Loss:0.0213
Epoch:200,Loss:0.0052
Epoch:240,Loss:0.0012
Epoch:280,Loss:0.0003
Epoch:320,Loss:0.0001
Epoch:360,Loss:0.0000

五、搭建神经网络实现

5.1 torchvision

torchvision 是PyTorch中专门用来处理图像的库。这个包中有四个大类,包含了目前流行的数据集,模型结构和常用的图片转换工具。

torchvision.datasets :一些加载数据的函数及常用的数据集接口;

torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNet、VGG、ResNet等;

torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;

torchvision.utils: 其他的一些有用的方法。

5.1.1 torchvision.datasets

torchvision.datasets中包含了以下数据集

  • MNIST
  • COCO(用于图像标注和目标检测)(Captioning and Detection)
  • LSUN Classification
  • ImageFolder
  • Imagenet-12
  • CIFAR10 and CIFAR100
  • STL10
data_train = datasets.MNIST(root="./data/",
                           transform=transform,
                           train = True,
                           download = True)
data_test = datasets.MNIST(root="./data/",
                          transform = transform,
                          train = False)
5.1.2 torchvision.models

torchvision.models 中为我们提供了已经训练好的模型,让我们可以加载之后,直接使用。

torchvision.models模块的 子模块中包含以下模型结构。如:

AlexNet

VGG

ResNet

SqueezeNet

DenseNet等

我们可以直接使用如下代码来快速创建一个权重随机初始化的模型:
 

import torchvision.models as models
resnet18 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
densenet = models.densenet_161()

也可以通过使用 pretrained=True 来加载一个别人预训练好的模型:

import torchvision.models as models
resnet18 = models.resnet18(pretrained=True)
alexnet = models.alexnet(pretrained=True)
5.1.3 torch.transforms

torch.transforms中有大量数据变换类,如:

5.1.3.1 torchvision.transforms.Resize

用于对载入的图片数据按照我们需求的大小进行缩放。传递的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。h代表高度,w代表宽度,如果输入的是整型数据那么h和w都等于这个数。

5.1.3.2 torchvision.transforms.Scale

用于对载入的图片数据按照我们需求的大小进行缩放。和Resize类似。

5.1.3.3 torchvision.transforms.CenterCrop

用于对载入的图片以图片中心为参考点,按照我们需要的大小进行裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。

5.1.3.4 torchvision.transforms.RandomCrop

用于对载入的图片按照我们需要的大小进行随机裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。

5.1.3.5 torchvision.transforms.RandomHorizontalFlip

用于对载入的图片按随机概率进行水平翻转。我们通过传递给这个类的自定义随机概率,如果没有定义,则使用默认的概率为0.5

5.1.3.6 torchvision.transforms.RandomVerticalFlip

用于对载入的图片按随机概率进行垂直翻转。我们通过传递给这个类的自定义随机概率,如果没有定义,则使用默认的概率为0.5

5.1.3.7 torchvision.transforms.ToTensor

用于对载入的图片数据进行类型转换,将之前构成PIL图片数据转换为Tensor数据类型的变量,让PyTorch能够对其进行计算和处理。

5.1.3.8 torchvision.transforms.ToPILImage:

用于对Tensor变量的数据转换成PIL图片数据,主要为方便图片显示。

5.1.3.9 torchvision.transforms.Compose(transforms)

Compose类的作用是将多个变换组合在一起,参数transform多个变换组成的列表(list)。

transforms.Compose([
	transforms.CenterCrop(10),
	transforms.Pad(10, 0),
	transforms.ToTensor(),
	transform.Normalize(mean=[0.5, 0.5, 0.5], std=[0.1, 0.1, 0.1])
	transforms.ConvertImageDtype(torch.float),
])

上面的例子中,对一张图像以此进行了以下步骤的变换:

  1. 以输入图像的中心为裁切中心,对图像进行裁切
  2. 对上一步得到的图像的边缘进行Pad填充
  3. 将上一步得到的图像转换成torch.Tensor格式
  4. 将上一步得到的tensor图像进行标准化转换
  5. 将上一步得到的tensor图像转换成torch.float类型
5.1.4 torch.utils

关于torchvision.utils我们介绍一种用来对数据进行装载的类torch.utils.data.DataLoader

DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
           batch_sampler=None, num_workers=0, collate_fn=None,
           pin_memory=False, drop_last=False, timeout=0,
           worker_init_fn=None, *, prefetch_factor=2,
           persistent_workers=False)

dataset参数:指定我们载入的数据集的名称,

batch_size参数:设置每个包中图片的数量,

shuffle参数:设置为True代表在装载的过程会将数据随机打乱顺序并进行打包

5.2 模型搭建和参数优化

import math
import torch
import torch.nn as nn
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        
        #构建卷积层之后的全连接层以及分类器
        self.conv1 = nn.Sequential(
                nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.MaxPool2d(stride=2,kernel_size=2)
                )
        
        self.dense = torch.nn.Sequential(
                nn.Linear(14*14*128,1024),
                nn.ReLU(),
                nn.Dropout(p=0.5),
                nn.Linear(1024,10)
            )
        
    def forward(self,x):
        x=self.conv1(x)
        x=x.view(-1,14*14*128)
        x=self.dense(x)
        return x
5.2.1 torch.nn.Conv2d

用于搭建卷积神经网络的卷积层,主要参数是:

输入通道数、输出通道数、卷积核大小、卷积核移动步长和paddingde值(用于对边界像素的填充)

5.2.2 torch.nn.MaxPool2d

实现卷积神经网络的最大池化层,主要参数是: 

池化窗口的大小,池化窗口移动步长和paddingde值

5.2.3 torch.nn.Dropout

用于防止卷积神经网络在训练过程中发生过拟合,原理是以一定的随机概率将卷积神经网络模型的部分参数归零,以达到减少相邻两层神经连接的目的 

5.3 参数优化

搭完模型后,我们就可以对模型进行训练和参数优化了: 

model = Model()
cost = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
print(model)

 Model(
  (conv1): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU()
    (2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU()
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (dense): Sequential(
    (0): Linear(in_features=25088, out_features=1024, bias=True)
    (1): ReLU()
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=1024, out_features=10, bias=True)
  )
)

5.4完整代码

import torch
import torchvision
from torchvision import datasets, transforms
from torch.autograd import Variable
import matplotlib.pyplot as plt


transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Lambda(lambda x: x.repeat(3, 1, 1)),
    transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
])  

data_train = datasets.MNIST(root="./data/",
                            transform=transform,
                            train=True,
                            download=True)
data_test = datasets.MNIST(root="./data/",
                           transform=transform,
                           train=False)

data_loader_train = torch.utils.data.DataLoader(dataset=data_train,
                                                batch_size=64,  # 每个batch载入的图片数量,默认为1,这里设置为64
                                                shuffle=True,
                                                # num_workers=2#载入训练数据所需的子任务数
                                                )
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
                                               batch_size=64,
                                               shuffle=True)




images, labels = next(iter(data_loader_train))
img = torchvision.utils.make_grid(images)

img = img.numpy().transpose(1, 2, 0)
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean
print([labels[i] for i in range(64)])
plt.imshow(img)


import torch
import torch.nn as nn


class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()

        # 构建卷积层之后的全连接层以及分类器
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(stride=2, kernel_size=2)
        )

        self.dense = torch.nn.Sequential(
            nn.Linear(14 * 14 * 128, 1024),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(1024, 10)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(-1, 14 * 14 * 128)
        x = self.dense(x)
        return x


model = Model()
cost = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
print(model)

n_epochs = 5

for epoch in range(n_epochs):
    running_loss = 0.0
    running_correct = 0
    print("Epoch {}/{}".format(epoch, n_epochs))
    print("-" * 10)
    for data in data_loader_train:
        X_train, y_train = data
        X_train, y_train = Variable(X_train), Variable(y_train)
        outputs = model(X_train)
        _, pred = torch.max(outputs.data, 1)
        optimizer.zero_grad()
        loss = cost(outputs, y_train)

        loss.backward()
        optimizer.step()
        running_loss += loss.data
        running_correct += torch.sum(pred == y_train.data)
    testing_correct = 0
    for data in data_loader_test:
        X_test, y_test = data
        X_test, y_test = Variable(X_test), Variable(y_test)
        outputs = model(X_test)
        _, pred = torch.max(outputs.data, 1)
        testing_correct += torch.sum(pred == y_test.data)
    print("Loss is:{:4f},Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}".format(running_loss / len(data_train),
                                                                                   100 * running_correct / len(
                                                                                       data_train)
                                                                                   , 100 * testing_correct / len(
            data_test)))

data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
                                               batch_size=4,
                                               shuffle=True)
X_test, y_test = next(iter(data_loader_test))
inputs = Variable(X_test)
pred = model(inputs)
_, pred = torch.max(pred, 1)

print("Predict Label is:", [i for i in pred.data])
print("Real Label is:", [i for i in y_test])
img = torchvision.utils.make_grid(X_test)
img = img.numpy().transpose(1, 2, 0)

std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean
plt.imshow(img)
 

总结

PyTorch不仅是最受欢迎的深度学习框架之一,而且也是最强大的深度学习框架之一。它有许多独特的优点,使其在学术界和工业界都受到广泛的关注和使用。

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值