《深度学习之pytorch实战计算机视觉》第6章 PyTorch基础(代码可跑通)

本文介绍了PyTorch的基础知识,包括Tensor的创建和运算、搭建简易神经网络、自动梯度计算、模型搭建与参数优化。通过示例展示了如何使用PyTorch构建和训练神经网络,以及使用torch.nn和torch.optim进行模型构建和参数优化。此外,还探讨了手写数字识别问题,涉及数据预处理、模型训练和评估。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章《深度学习之pytorch实战计算机视觉》第5章 Python基础讲了Python基础。接下来看看第6章 PyTorch基础。

目录

6.1 PyTorch中的Tensor

6.1.1 Tensor的数据类型

6.1.2 Tensor的运算

6.1.3 搭建一个简易神经网络

6.2 自动梯度

6.2.1 torch.autograd和Variable

6.2.2 自定义传播函数

6.3 模型搭建和参数优化

6.3.1 PyTorch之torch.nn

6.3.2 PyTorch之torch.optim

6.4 实战手写数字识别

6.4.1 torch和torchvision

6.4.2 PyTorch之torch.transforms

6.4.3 数据预览和数据装载

6.4.4 模型搭建和参数优化

PyTorch 是美国互联网巨头Facebook在深度学习框架 Torch 的基础上使用 Python 重写的一个全新的深度学习框架,它更像 NumPy 的替代产物,不仅继承了 NumPy 的众多优点,还支持GPUs计算,在计算效率上要比NumPy有更明显的优势;不仅如此,PyTorch还有许多高级功能,比如拥有丰富的API,可以快速完成深度神经网络模型的搭建和训练。所以PyTorch一经发布,便受到了众多开发人员和科研人员的追捧与喜爱,成为AI从业者的重要工具之一。

6.1 PyTorch中的Tensor

6.1.1 Tensor的数据类型

  • torch.FloatTensor:生成数据类型为浮点数的张量,入参为列表或者维度值
  • torch.IntTensor:生成数据类型为整型的张量,入参为列表或者维度值
  • torch.rand:随机生成数据类型为浮点型的张量,浮点数据在0-1区间均匀分布,入参为维度值
  • torch.randn:随机生成数据类型为浮点型的张量,浮点数据满足N(0,1)的正态分布,入参为维度值
  • torch.range:生成数据类型为浮点数且自定义起始范围和结束范围的张量,入参为起始点、结束点和步长
  • torch.zeros:生成浮点型且维度指定的张量,元素值全部为0
import torch

a = torch.range(1,20,1)
a.size()  #1 2 ... 19 20

输出:

torch.Size([20])

6.1.2 Tensor的运算

  • torch.abs() 求绝对值,入参是张量数据类型的变量
  • torch.add() 求和,入参都是张量,或者一个张量一个标量
  • torch.clamp() 裁剪,入参为张量、裁剪的上边界和下边界。每个元素都要和上边界和下边界进行比较,如果元素值小于下边界的值,则该元素被重写成下边界的值;元素值大于上边界同理。
  • torch.div() 求商,入参都是张量,或者一个张量一个标量
  • torch.mul() 求积,入参都是张量,或者一个张量一个标量
  • torch.pow() 求幂,入参都是张量,或者一个张量一个标量
  • torch.mm() 矩阵的乘法,入参的维度要满足矩阵乘法
  • torch.mv() 矩阵和向量的乘法,第一个参数是矩阵,第二个参数是向量

6.1.3 搭建一个简易神经网络

#导包
import torch

batch_n = 100   #一个批次输入的数据数量
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

#从输入层到隐藏层、从隐藏层到输出层的权重初始化
x = torch.randn(batch_n,input_data)
y = torch.randn(batch_n,output_data)

w1 = torch.randn(input_data,hidden_layer)
w2 = torch.randn(hidden_layer,output_data)

epoch_n = 20
learning_rate = 1e-6

#对参数优化,能够显示的写出求导公式
for epoch in range(epoch_n):
    h1 = x.mm(w1)  #100*1000
    h1 = h1.clamp(min=0)   #将小于0的值全部赋值为0,就相当于加了一个relu激活函数
    y_pred = h1.mm(w2)  #100*10
    
    loss = (y_pred - y).pow(2).sum()
    print("Epoch:{},Loss:{:.4f}".format(epoch,loss))
    
    grad_y_pred = 2*(y_pred - y)
    grad_w2 = h1.t().mm(grad_y_pred)
    
    grad_h = grad_y_pred.clone()
    grad_h = grad_h.mm(w2.t())
    grad_h.clamp(min=0)
    grad_w1 = x.t().mm(grad_h)
    
    w1 -= learning_rate*grad_w1
    w2 -= learning_rate*grad_w2

输出:

Epoch:0,Loss:61389692.0000
Epoch:1,Loss:160758480.0000
Epoch:2,Loss:545299456.0000
Epoch:3,Loss:589745280.0000
Epoch:4,Loss:5771187.5000
Epoch:5,Loss:3852597.2500
Epoch:6,Loss:2813506.2500
Epoch:7,Loss:2202821.2500
Epoch:8,Loss:1816049.7500
Epoch:9,Loss:1553005.6250
Epoch:10,Loss:1361722.5000
Epoch:11,Loss:1215317.7500
Epoch:12,Loss:1097719.7500
Epoch:13,Loss:999968.8750
Epoch:14,Loss:916233.5625
Epoch:15,Loss:843229.7500
Epoch:16,Loss:778861.1875
Epoch:17,Loss:721594.6875
Epoch:18,Loss:670533.8125
Epoch:19,Loss:624570.6250

6.2 自动梯度

通过torch.autograd包,使模型参数自动计算在优化过程中需要的梯度值,降低了后向传播代码的复杂度。

6.2.1 torch.autograd和Variable

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

#自动梯度例子
# import torch
from torch.autograd import Variable
batch_n = 100 
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

#x和y不保留梯度值,因为这两个变量不是我们模型需要优化的参数
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 = 20
learning_rate = 1e-6

for epoch in range(epoch_n):
    y_pred = x.mm(w1).clamp(min=0).mm(w2)
    loss = (y_pred - y).pow(2).sum()
    print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) #loss.data[0]报错
    
    loss.backward()
    
    w1.data -= learning_rate*w1.grad.data
    w2.data -= learning_rate*w2.grad.data
    
    w1.grad.data.zero_()
    w2.grad.data.zero_()

输出:

Epoch:0,Loss:38478596.0000
Epoch:1,Loss:64376108.0000
Epoch:2,Loss:226000160.0000
Epoch:3,Loss:615954176.0000
Epoch:4,Loss:260315120.0000
Epoch:5,Loss:11254304.0000
Epoch:6,Loss:6599379.5000
Epoch:7,Loss:4310198.0000
Epoch:8,Loss:3030261.0000
Epoch:9,Loss:2257996.2500
Epoch:10,Loss:1765981.0000
Epoch:11,Loss:1437985.2500
Epoch:12,Loss:1210078.5000
Epoch:13,Loss:1045272.9375
Epoch:14,Loss:921474.1875
Epoch:15,Loss:824932.1250
Epoch:16,Loss:747264.5625
Epoch:17,Loss:682944.3125
Epoch:18,Loss:628346.6875
Epoch:19,Loss:581212.0625

6.2.2 自定义传播函数

除了使用自动梯度函数,还可以通过构建一个继承了torch.nn.Module的类,来完成前向传播和后向传播函数的重写。

# import torch
from torch.autograd import Variable
batch_n = 100 
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

#定义类
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
    
#调用类
model = Model()

#模型的训练和参数优化
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 = 20
learning_rate = 1e-6

for epoch in range(epoch_n):
    y_pred = model(x,w1,w2)
    loss = (y_pred - y).pow(2).sum()
    print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) #loss.data[0]报错
    
    loss.backward()
    
    w1.data -= learning_rate*w1.grad.data
    w2.data -= learning_rate*w2.grad.data
    
    w1.grad.data.zero_()
    w2.grad.data.zero_()

输出:

Epoch:0,Loss:43075812.0000
Epoch:1,Loss:97484256.0000
Epoch:2,Loss:410335744.0000
Epoch:3,Loss:923601728.0000
Epoch:4,Loss:78289096.0000
Epoch:5,Loss:24612662.0000
Epoch:6,Loss:14282344.0000
Epoch:7,Loss:9647261.0000
Epoch:8,Loss:7035086.0000
Epoch:9,Loss:5384709.5000
Epoch:10,Loss:4264662.5000
Epoch:11,Loss:3462480.7500
Epoch:12,Loss:2866421.7500
Epoch:13,Loss:2410418.7500
Epoch:14,Loss:2053546.5000
Epoch:15,Loss:1768931.8750
Epoch:16,Loss:1538703.2500
Epoch:17,Loss:1349771.8750
Epoch:18,Loss:1193112.2500
Epoch:19,Loss:1062226.8750

6.3 模型搭建和参数优化

接下来看搭建复杂的神经网络模型,同时让参数的优化方法趋于高效。

如同使用PyTorch中的自动梯度方法一样,在搭建复杂的神经网络模型的时候,我们也可以使用PyTorch 中己定义的类和方法,这些类和方法覆盖了神经网络中的线性变换激活函数卷积层全连接层池化层等常用神经网络结构的实现

6.3.1 PyTorch之torch.nn

PyTorch 中的 torch.nn 包提供了很多与实现神经网络中的具体功能相关的类,这些类涵盖了深度神经网络模型在搭建和参数优化过程中的常用内容 ,比如神经网络中的卷积层、池化层、全连接层这类层次构造的方法、防止过拟合的参数归一化方法、 Dropout方法,还有激活函数部分的线性激活函数、非线性激活函数相关的方法,等等。在学会使用PyTorch的torch.nn进行神经网络模型的搭建和参数优化后,我们就会发现实现一个神经网络应用并没有我们想象中那么难。

下面使用torch.nn包来简化之前的代码:

# import torch
from torch.autograd import Variable
batch_n = 100 
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

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

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

具体看看torch.nn中常用的类。

(1) torch.nn.Sequential: torch.nn.Sequential类是torch.nn 中的一种序列容器,通过在容器中嵌套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,最主要的是参数会按照定义好的序列自动传递下去。我们可以将嵌套在容器中的各个部分看作各种不同的模块,这些模块可以自由组合。

模块的加入一般有两种方式:一种是在以上代码中使用的直接嵌套,另一种是以 orderdict 有序字典的方式进行传入。这两种方式的唯一区别是,使用后者搭建的模型的每个模块都有我们自定义的名字,而前者默认使用从零开始的数字序列作为每个模块的名字。下面通过示例来直观地看一下使用这两种方式搭建的模型之间的区别。

方法一,直接使用嵌套搭建模型:

#直接使用嵌套搭建的模型代码:
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

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

打印输出:

Sequential(
  (0): Linear(in_features=1000, out_features=100, bias=True)
  (1): ReLU()
  (2): Linear(in_features=100, out_features=10, bias=True)
)

方法二,使用orderdict有序字典搭建模型:

#使用orderdict有序字典
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

from collections import OrderedDict
models2 = torch.nn.Sequential(OrderedDict([
    ("Line1",torch.nn.Linear(input_data,hidden_layer)),
    ("ReLU1",torch.nn.ReLU()),
    ("Line2",torch.nn.Linear(hidden_layer,output_data))])
)
print(models2)

输出:

Sequential(
  (Line1): Linear(in_features=1000, out_features=100, bias=True)
  (ReLU1): ReLU()
  (Line2): Linear(in_features=100, out_features=10, bias=True)
)

对两种方法进行比较,发现对模块使用自定义的名称可以更便捷地找到响应的模块进行操作。

(2) torch.nn.Linear: torch.nn.Linear 类用于定义模型的线性层 ,即完成前面提到的不同的层之间的线性变换。torch.nn.Linear 类接收的参数有3个,分别是输入特征数、输出特征数和是否使用偏置,设置是否使用偏置的参数是一个布尔值,默认为 True ,即使用偏置。在实际使用的过程中,我们只需将输入的特征数和输出的特征数传递给torch.nn.Linear类,就会自动生成对应维度的权重参数和偏置,对于自动生成的权重参数和偏置,我们的模型默认使用了一种比之前的简单随机方式更好的参数初始化方法。

根据我们搭建模型的输入、输出和层次结构需求,它的输入是在一个批次中包含100个特征数为1000的数据,最后得到100个特征数为10输出数据,中间需要经过两次线性变换,所以要使用两个线性层,两个线性层的代码分别是 torch.nn.Linear(input_data,hidden_layer)和 torch.nn.Linear(hidden_layer, output_data)。替代了之前使用矩阵乘法方式的实现,代码更精炼、简洁。

(3) torch.nn.ReLU: torch.nn.ReLU 类属于非线性激活分类,在定义时默认不需要传入参数。当然,在 torch.nn 中还有许多非线性激活函数类可供选择,比如之前讲到的PReLULeakyRuLUTanhSigmoidSoftmax

在掌握 torch.nn.Sequential、torch.nn.Linear和torch.nn.ReLU的使用方法后,快速搭建更复杂的多层神经网络模型变为可能,而且在整个模型的搭建过程中不需要对在模型中使用到的权重参数和偏置进行任何定义和初始化说明, 因为参数已经完成了自动生成。

#接下来对已经搭建好的模型进行训练并对参数进行优化,代码如下:
epoch_n = 10000   #增加训练次数
learning_rate = 1e-4  #增加学习速率
loss_fn = torch.nn.MSELoss()

torch.nn中的损失函数

(1) torch.nn.MSELoss: torch.nn.MSELoss 类使用均方误差函数对损失值进行计算,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算。示例如下:

# import torch
# from torch.autograd import Variable
loss_f1 = torch.nn.MSELoss()
x1 = Variable(torch.randn(100,100))  #随机生成维度是(100,100)的参数
y1 = Variable(torch.randn(100,100))
loss = loss_f1(x1,y1)
print(loss.data)

输出:

tensor(2.0312)

(2) torch.nn.L1Loss: torch.nn.L1Loss类使用平均绝对误差函数对损失值进行计算,同样,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数进行计算。示例如下:

# import torch
# from torch.autograd import Variable
loss_f2 = torch.nn.L1Loss()
x2 = Variable(torch.randn(100,100))  #随机生成维度是(100,100)的参数
y2 = Variable(torch.randn(100,100))
loss = loss_f2(x2,y2)
print(loss.data)

输出:

tensor(1.1243)

(3)torch.nn.CrossEntropyLoss: torch.nn.CrossEntropyLoss 类用于计算交叉熵,在定义类的对象时不用传入任何参数,在使用实例时需要输入两个满足交叉熵的计算条件的参数,代码如下:

# import torch
# from torch.autograd import Variable
loss_f3 = torch.nn.CrossEntropyLoss()
x3 = Variable(torch.randn(3,5))   #随机生成维度是(3,5)的参数
y3 = Variable(torch.LongTensor(3).random_(5))  #3个范围是0-4的数字
loss = loss_f3(x3,y3)
print(loss.data)

输出:

tensor(3.0554)

看具体情况:

print(x3,y3)

输出:

tensor([[ 1.5224, -1.7120,  2.7553,  0.0077,  0.0938],
        [-0.2122,  1.3795, -1.6359, -1.2175, -1.2968],
        [-0.8271, -0.7797, -0.1743, -0.0701,  0.8936]]) 
tensor([1, 0, 1])

模型训练和参数优化:

#模型训练和参数优化
x4 = Variable(torch.randn(100,1000))  #随机生成维度是(100,100)的参数
y4 = Variable(torch.randn(100,10))
for epoch in range(epoch_n):
    y_pred = models2(x4)
    loss = loss_f(y_pred,y4)
    #if epoch%1000 == 0:
    print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item()))
    models2.zero_grad()
    
    loss.backward()
    
    for param in models.parameters():
        param.grad -= param.grad*learning_rate
#         param.data -= param.grad.data*learning_rate

减少训练时间,训练10个epochs的结果如下:

Epoch:0,Loss:1.0703
Epoch:1,Loss:1.0703
Epoch:2,Loss:1.0703
Epoch:3,Loss:1.0703
Epoch:4,Loss:1.0703
Epoch:5,Loss:1.0703
Epoch:6,Loss:1.0703
Epoch:7,Loss:1.0703
Epoch:8,Loss:1.0703
Epoch:9,Loss:1.0703

6.3.2 PyTorch之torch.optim

到目前为止,代码中的神经网络权重的参数优化和更新还没有实现自动化,并且目前使用的优化方法都有固定的学习速率,所以优化函数相对简单,如果我们自己实现一些高级的参数优化算法,则优化函数部分的代码会变得较为复杂。在PyTorch的torch.optim包中提供了非常多的可实现参数自动优化的类,比如 SGD、AdaGrad、RMSProp和Adam 等,这些类都可被直接调用,使用起来也非常方便。我们使用自动化的优化函数实现方法对之前的代码进行替换,新的代码如下:

# import torch
# from torch.autograd import Variable
batch_n = 100 
hidden_layer = 100   #隐藏层之后保留的数据特征个数
input_data = 1000  #每个数据的数据特征
output_data = 10   #分类结果值

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

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

epoch_n = 10000   #增加训练次数
learning_rate = 1e-4  #增加学习速率
loss_fn = torch.nn.MSELoss()

optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate) #Adam类入参是被优化的参数和学习速率的初始值

for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred,y)
    print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item()))
    optimzer.zero_grad()
    
    loss.backward()
    optimzer.step( )

 输出:

Epoch:0,Loss:1.0195
Epoch:1,Loss:0.9984
Epoch:2,Loss:0.9777
Epoch:3,Loss:0.9575
Epoch:4,Loss:0.9377
Epoch:5,Loss:0.9185
Epoch:6,Loss:0.8998
Epoch:7,Loss:0.8817
Epoch:8,Loss:0.8640
Epoch:9,Loss:0.8468
Epoch:10,Loss:0.8299
Epoch:11,Loss:0.8134
Epoch:12,Loss:0.7972
Epoch:13,Loss:0.7815
Epoch:14,Loss:0.7662
Epoch:15,Loss:0.7513
Epoch:16,Loss:0.7367
Epoch:17,Loss:0.7224
Epoch:18,Loss:0.7085
Epoch:19,Loss:0.6948
Epoch:20,Loss:0.6814

6.4 实战手写数字识别

我们现在已经学会基于 PyTorch 深度学习框架高效、快捷地搭建一个神经网络,并对模型进行训练和对参数进行优化的方法,接下来让我们小试牛刀,基于PyTorch框架使用神经网络来解决一个关于手写数字识别的计算机视觉问题,评价我们搭建的模型的标准是它能否准确地对手写数字图片进行识别

其具体过程是:

  1. 先使用己经提供的训练数据对搭建好的神经网络模型进行训练并完成参数优化
  2. 然后使用优化好的模型对测试数据进行预测
  3. 对比预测值和真实值之间的损失值,同时计算出结果预测的准确率

在将要搭建的模型中会用到卷积神经网络模型,下面让我们开始吧。

6.4.1 torch和torchvision

在PyTorch中有两个核心的包,分别是torch和torchvision。之前已经接触了torch包的一部分内容,比如使用了torch.nn 中的线性层加激活函数配合torch.optim完成了神经网络模型的搭建和模型参数的优化,并使用了torch.autograd实现自动梯度的功能,接下来会介绍如何使用torch.nn中的类来搭建卷积神经网络。torchvision包的主要功能是实现数据的处理、导入和预览等,所以如果需要对计算机视觉的相关问题进行处理,就可以借用在torchvision包中提供的大量的类来完成相应的工作。 代码中的开始部分如下:

import torch
from torchvision import datasets,transforms
from torch.autograd import Variable

#实现下载的代码是torchvision.datasets.MNIST。
#其他的数据集如COCO、ImageNet、CIFCAR等都可以通过这个方法快速下载和载入。
data_train = datasets.MNIST(root="./data/",transform = transform,train = True,download = False)
data_test = datasets.MNIST(root="./data/",transform = transform,train = False)

其中:

  • root 用于指定数据集在下载之后的存放路径,这里存放在根目录下的 data 文件夹中
  • transform 用于指定导入数据集时需要对数据进行哪种变换操作,在后面会介绍详细的变换操作类型,注意要提前定义这些变换操作
  • train 用于指定在数据集下载完成后需要载入哪部分数据,如果设置为True ,则说明载入的是该数据集的训练集部分;如果设置为False,则说明载入的是该数据集的测试集部分

6.4.2 PyTorch之torch.transforms

在torch.transforms 中有大量的数据变换类,其中有很大一部分可以用于实现数据增强( Data Argumentation)。若在我们需要解决的问题上能参与模型训练的图片非常有限,则要通过对有限的图片数据进行各种变换来生成新的训练集了,这些变换可以是缩小或者放大图片的大小、对图片进行水平或者垂直翻转等(数据增强十分重要)。不过在手写数字识别的问题上可以不使用数据增强的方法,因为可用于模型训练的数据已经足够了。对数据进行载入及有相应变化的代码如下:

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])
])

我们可以将transforms.Compose看做一种容器,它能够同时对多种数据变换进行组合。传入的参数是一个列表,列表中的元素就是对载入的数据进行的各种变换操作。上述代码进行了类型的转换变换和标准化变换(变换之后数据符合均值为0,标准差为1的正态分布)

下面看看在 torchvision. transforms 中常用的数据变换操作

  • (1) torchvision.transforms.Resize:用于对载入的图片数据按我们需求的大小进行缩放。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列,其中h代表高度,w代表宽度,但是如果使用的是一个整型数据,那么表示缩放的宽度和高度都是这个整型数据的值。
  • (2) torchvision.transforms.Scale:用于对载入的图片数据按我们需求的大小进行缩放,用法和 torchvision transforms.Resize 类似。
  • (3) torchvision.transforms.CenterCrop:用于对载入的图片以图片中心为参考点,按我们需要的大小进行裁剪。传递给这个类的参数可以是一个整型数据 ,也可以是一个类似于(h,w)的序列。
  • (4) torchvision.transforms.RandomCrop:用于对载入的图片按我们需要的大小进行随机裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。
  • (5) torchvision.transforms.RandomHorizontalFlip:用于对载入的图片按随机概率进行水平翻转。我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。
  • (6) torchvision.transforms.RandomVerticalFlip:用于对载入的图片按随机概率进行垂直翻转。 我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。
  • (7) torchvision.transforms.ToTensor:用于对载入的图片数据进行类型转换,将之前构成 PIL 图片的数据转换成 Tensor 数据类型的变量,让PyTorch能够对其进行计算和处理。
  • (8) torchvision.transforms.ToPILlmage:用于将Tensor变量的数据转换成 PIL 图片数据,主要是为了方便图片的展示。

6.4.3 数据预览和数据装载

在数据下载完成和载入之后,还需要对数据装载(DataLoader)。可以将数据的载入理解为对图片的处理,处理完成之后需要装载,就是将图片打包好送给模型进行训练。在装载时通过batch_size来确认每个包的大小,通过shuffle来确实是否打乱图片顺序。装载图片的代码如下:

data_loader_train = torch.utils.data.DataLoader(dataset = data_train,batch_size = 64,shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset = data_test,batch_size = 64,shuffle=True)

在装载完成后,选取其中一个批次的数据进行预览,代码如下:

import torchvision
import matplotlib.pyplot as plt

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)  #显示图片

输出结果:

[tensor(9), tensor(1), tensor(1), tensor(8), tensor(2), tensor(2), tensor(8), tensor(4), tensor(9), tensor(5), tensor(4), tensor(5), tensor(6), tensor(4), tensor(4), tensor(7), tensor(2), tensor(1), tensor(7), tensor(8), tensor(0), tensor(3), tensor(5), tensor(7), tensor(5), tensor(3), tensor(2), tensor(7), tensor(3), tensor(9), tensor(4), tensor(4), tensor(0), tensor(1), tensor(0), tensor(6), tensor(0), tensor(0), tensor(4), tensor(9), tensor(7), tensor(4), tensor(8), tensor(6), tensor(4), tensor(2), tensor(0), tensor(3), tensor(3), tensor(8), tensor(3), tensor(1), tensor(0), tensor(2), tensor(3), tensor(3), tensor(1), tensor(5), tensor(0), tensor(7), tensor(1), tensor(2), tensor(0), tensor(0)]
<matplotlib.image.AxesImage at 0x19c747873c8>

img = torchvision.utils.make_grid(images)中使用 torchvision.utils的make_grid()类方法将一个批次的图片构造成网格模式。需要传递的参数就是一个批次的装载数据,每个批次的装载数据都是4维的,维度的构成从前往后分别为batch_size、channel、height、weight(注意这是pytorch框架的格式,tensorflow略有不同,为batch_size、height、weight、channel,分别对应一个批次中的数据个数、每张图片的色彩通道数、每张图片的高度和宽度。输出的图片维度变成了(channel、height、weight),这个批次的图片全部被整合到一起,在这个维度中对应值和之前不一样, 但是色彩通道数保持不变。

如果想使用Matplotlib显示正常的图片形式,使用的必须是(height、weight、channel)的数组,即色彩通道数在最后面。

6.4.4 模型搭建和参数优化

在顺利完成数据装载后,可以开始编写卷积神经网络模型搭建和参数优化的代码。这里搭建一个包含卷积层、激活函数、池化层、全连接层的卷积神经网络,各个部分的功能实现依然是通过torch.nn 中的类来完成的,比如卷积层使用torch.nn.Conv2d 类方法来搭建;激活层使用 torch.nn.ReLU 类方法来搭建;池化层使用 torch.nn.MaxPool2d 类方法;全连接层使用torch.nn.Linear 类方法来搭建。代码如下:

class Model(torch.nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.conv1=torch.nn.Sequential(
        torch.nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1),#1---3
        torch.nn.ReLU(),
        torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
        torch.nn.ReLU(),
        torch.nn.MaxPool2d(stride=2,kernel_size=2))
        
        self.dense=torch.nn.Sequential(
        torch.nn.Linear(14*14*128,1024),
        torch.nn.ReLU(),
        torch.nn.Dropout(p=0.5),
        torch.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

对具体的使用方法补充说明:

  • (1) torch.nn.Conv2d:用于搭建卷积神经网络的卷积层,主要的输入参数有输入通道数、输出通道数、卷积核大小、卷积核移动步长和 Padding值。其中,输入通道数的数据类型是整型,用于确定输入数据的层数:输出通道数的数据类型也是整型,用于确定输出数据的层数;卷积核大小的数据类型是整型,用于确定卷积核的大小;卷积核移动步长的数据类型是整型,用于确定卷积核每次滑动的步长; Padding的数据类型是整型,为0表示不进行边界像素的填充,如果值大于0,那么增加数字所对应的边界像素层数。
  • (2) torch.nn.MaxPool2d:用于实现卷积神经网络中的最大池化层,主要的输入参数是池化窗口大小、池化窗口移动步长和 Padding值。同样,池化窗口大小的数据类型是整型 ,用于确定池化窗口的大小。池化窗口步长的数据类型也是整型,用于确定窗口每次移动的步长。Padding值的用法和意义同上。
  • (3) torch.nn.Dropout: torch.nn.Dropout 类用于防止卷积神经网络在训练的过程中发生过拟合,其工作原理简单来说就是在模型训练的过程中,以一定的随机概率将卷积神经网络模型的部分参数归零, 以达到减少相邻两层神经连接的目的。 正是因为选取方式的随机性,所以在模型的每轮训练中选择丢弃的神经连接也是不同的,这样做是为了让最后训练出来的模型对各部分的权重参数不过度依赖,从而防止过拟合。我们可以对随机概率值的大小进行设置,如果不做任何设置,就使用默认的概率值0.5。

最后看看代码中前向传播 forward 函数中的内容。首先经过 self.conv1卷积处理,然后进行 x.view (-1,14*14*128),对参数实现扁平化,因为之后紧接着的就是全连接,所以如果不进行扁平化,则全连接的实际输出的参数维度和其定义输入的维度将不匹配,程序会报错;最后,通过self.dense 定义的全连接进行最后的分类。

在编写完搭建卷积神经网络模型的代码后,我们就可以开始对模型进行训练和对参数进行优化了。首先,定义在训练之前使用哪种损失函数和优化函数:

model = Model()
cost = torch.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)
  )
)

最后,模型进行训练和参数优化的代码如下:

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)
        loss = cost(outputs,y_train)
        optimizer.zero_grad()
        
        loss.backward()
        optimizer.step()
        running_loss += loss.item()#loss.data[0]改成loss.item()
        running_correct += torch.sum(pred == y_train.data)  #输出是tensor!!!
        
    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("running_loss:",running_loss)
#     print("len(data_train):",len(data_train))
    print("Loss is:{:.4f}, Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}%".format(running_loss/len(data_train),100*running_correct.numpy()/len(data_train),100*testing_correct.numpy()/len(data_test)))

输出:

Epoch 0/5
----------
Loss is:0.0005, Train Accuracy is:99.0517%, Test Accuracy is:98.5300%
Epoch 1/5
----------
Loss is:0.0003, Train Accuracy is:99.3083%, Test Accuracy is:98.6800%
Epoch 2/5
----------
Loss is:0.0003, Train Accuracy is:99.4067%, Test Accuracy is:98.7600%
Epoch 3/5
----------
Loss is:0.0002, Train Accuracy is:99.4950%, Test Accuracy is:98.9000%
Epoch 4/5
----------
Loss is:0.0002, Train Accuracy is:99.6367%, Test Accuracy is:98.4600%

为了验证我们训练模型是不是真的己如结果显示的一样准确,则最好的方法就是随机选取一部分测试集中的图片,用训练好的模型进行预测,看看和真实值有多大的偏差,并对结果进行可视化。测试过程的代码如下:

data_loader_test1 = torch.utils.data.DataLoader(dataset = data_test,batch_size=4,shuffle=True)
X_test1,y_test1 = next(iter(data_loader_test1))
inputs1 = Variable(X_test1)
pred1 = model(inputs1)
#dim=1表示输出所在行的最大值,若改写成dim=0则输出所在列的最大值
_,pred1 = torch.max(pred1,1)  #返回具体的value,和value所在的index(_可用其他变量替换)

print("Predict Label is:",[i for i in pred1.data])
print("Real Label is:",[i for i in y_test1])

#该方法可以用来直接将数据结构[batch,channel.height,width]形式的图像转化为图像矩阵,便于将多张图像进行可视化。
img1 = torchvision.utils.make_grid(X_test1)
img1 = img1.numpy().transpose(1,2,0)

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

输出如下:

Predict Label is: [tensor(6), tensor(2), tensor(0), tensor(5)]
Real Label is: [tensor(6), tensor(2), tensor(0), tensor(5)]
<matplotlib.image.AxesImage at 0x19c0003b908>

上述随机选取的测试结果表明模型识别正确。因为卷积神经网络在解决计算机视觉问题上有独特的优势,因此,采用简单的神经网络模型就能将识别的准确率达到很高的水平。不过,用来训练和测试模型的手写图片模型的特征非常明显,也很容易被模型捕获。

说明:记录学习笔记,如果错误欢迎指正!写文章不易,转载请联系我。

<think>嗯,用户之前问过激光雷达的用途,现在转向了深度学习机器学习的学习路径。这可能意味着他们对技术领域有广泛的兴趣,或者正在考虑进入数据科学或AI领域。我需要确定他们的背景,是学生、转行者还是有其他目的。 首先,用户提到“新手如何学习”,所以需要提供从基础开始的指导。可能他们的主要需求是找到系统化的学习路线,避免在学习过程中感到迷茫。应该包括资源推荐、学习方法以及实践项目的重要性。 考虑到他们之前的问题关于激光雷达,可能对应用场景比较感兴趣,比如在自动驾驶或工业检测中的应用。所以在介绍学习路径时,可以结合这些实际应用案例,增强相关性。 需要区分机器学习深度学习,虽然深度学习机器学习的子领域,但初学者可能会混淆两者的学习顺序。应该建议先掌握机器学习基础,再过渡到深度学习,这样有助于建立扎实的理论基础。 用户可能没有明确说明是否有编程基础,但学习这两个领域常需要Python和数学知识。因此,在回答中需要强调编程和数学的重要性,并提供相应的学习资源。 另外,实践是关键。新手可能会陷入理论学习的泥潭,而缺乏实际项目经验。建议他们过Kaggle竞赛或开源项目来应用所学知识,同时培养解决问题的能力。 还要注意学习路径的阶段性,分步骤引导,避免信息过载。比如先学Python和数学,再学机器学习基础,接着是深度学习,最后是项目实战。同时,推荐社区和论坛,帮助他们解决学习中的问题,保持学习动力。 最后,可能用户担心学习难度大,需要鼓励他们保持耐心,持续实践,并参与社区交流。同时,提醒他们根据兴趣选择方向,比如计算机视觉、自然语言处理等,以保持学习的热情。</think>好的!对于深度学习机器学习的新手,学习路径需要结合**理论基础、工具实践**和**项目经验**。以下是一个分阶段的系统化学习指南,结合中文资源和学习建议: --- ### **一、基础准备:数学 + 编程** #### 1. **数学基础** - **线性代数**:矩阵运算、向量空间(推荐《线性代数的本质》系列视频)。 - **概率与统计**:概率分布、贝叶斯定理、假设检验(可参考《统计学导论》)。 - **微积分**:导数、梯度、最优化(重点理解梯度下降原理)。 *🔍 工具辅助*:如果数学薄弱,可用工具库(如NumPy、SymPy)边实践边补基础。 #### 2. **编程能力** - **Python**:掌握基础语法、数据结构(列表/字典/函数),推荐[廖雪峰Python教程](https://www.liaoxuefeng.com/wiki/1016959663602400)。 - **关键库**: - **NumPy**(数值计算)、**Pandas**(数据分析); - **Matplotlib/Seaborn**(数据可视化)。 *📌 目标*:能独立完成数据清洗、简单可视化分析。 --- ### **二、机器学习入门:理论与工具** #### 1. **经典算法理解** - **监督学习**:线性回归、逻辑回归、决策树、SVM、随机森林。 - **无监督学习**:K-Means、PCA、聚类评估。 - **模型评估**:交叉验证、ROC曲线、混淆矩阵、过拟合与正则化。 *📚 推荐书籍*: - 《机器学习》(周志华,西瓜书)——理论扎实,适合反复读; - 《Hands-On Machine Learning》(Scikit-Learn版)——代码实践友好。 #### 2. **工具实战** - **Scikit-Learn**:从调用API开始,理解参数调优(如GridSearchCV)。 - **项目练习**: - Kaggle入门赛(如泰坦尼克生存预测、房价预测); - 使用UCI数据集(如鸢尾花分类、手写数字识别)。 --- ### **三、深度学习进阶:框架与模型** #### 1. **深度学习基础** - **核心概念**:神经网络结构、激活函数、反向传播、损失函数。 - **框架选择**: - **PyTorch**(灵活,适合研究)或 **TensorFlow/Keras**(工程部署友好)。 *🎥 推荐课程*: - 吴恩达《深度学习专项课》(Coursera,中文字幕); - 李沐《动手学深度学习》(B站免费视频+代码)。 #### 2. **经典模型实践** - **计算机视觉**:CNN(LeNet、ResNet)、目标检测(YOLO)。 - **自然语言处理**:RNN、LSTM、Transformer。 - **实战工具**: - PyTorch Lightning(简化训练流程); - Hugging Face(快速调用预训练模型)。 #### 3. **调试技巧** - 学习使用TensorBoard或Weights & Biases可视化训练过程; - 掌握过拟合解决方法(数据增强、Dropout、早停法)。 --- ### **四、项目实战:从模仿到创新** #### 1. **复现经典项目** - 图像分类:CIFAR-10数据集训练ResNet; - 文本生成:用LSTM生成唐诗或新闻标题; - 目标检测:用YOLOv5训练自定义数据集(如检测交标志)。 #### 2. **参与竞赛** - **Kaggle**:从Featured竞赛中选择感兴趣的主题(如预测、图像分割); - **阿里天池/AI Studio**:国内平台,中文社区支持更友好。 #### 3. **开源贡献** - 在GitHub上复现论文代码(如搜索“PyTorch implementation of XXX”); - 参与开源项目(如Hugging Face模型库的文档优化或Bug修复)。 --- ### **五、持续提升:理论与工程结合** #### 1. **深入理论** - 阅读经典论文(如AlexNet、Transformer、BERT),配合代码实现; - 学习《深度学习》(花书)中的数学推导。 #### 2. **工程化能力** - 模型部署:学习ONNX、TensorRT或Flask/Django构建API; - 性能优化:掌握模型剪枝、量化、蒸馏等轻量化技术。 #### 3. **领域聚焦** - **CV方向**:学习OpenCV、MMDetection; - **NLP方向**:深入Hugging Face库、Prompt Engineering; - **强化学习**:从Gym环境到PPO算法。 --- ### **六、学习资源推荐** #### 1. **中文社区** - **论坛**:知乎(关注李沐、邱锡鹏等大V)、优快云(入门教程多); - **公众号**:AI科技评论、机器之心。 #### 2. **免费课程** - B站:跟李沐学AI、莫烦Python; - 百度AI Studio:提供免费GPU算力。 #### 3. **书籍** - 《机器学习实战:基于Scikit-Learn、Keras和TensorFlow》 - 《Python深度学习(第2版)》 --- ### **七、避坑指南** 1. **不要盲目追新**:先掌握经典模型(如ResNet、BERT),再跟进前沿论文。 2. **代码 > 理论**:初期以代码、理解流程为主,数学推导可逐步深入。 3. **学会提问**:遇到报错先查Stack Overflow、GitHub Issues,再向社区求助。 --- ### **总结** 新手学习的关键是**快速建立正反馈**: 1. 从工具(Python+框架)入手,先实现“能代码”; 2. 过项目理解理论(如反向传播),而非死磕公式; 3. 加入社群,避免闭门造车。保持耐心,半年可入门,1-2年能独立开发模型!
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值