前言
在进行深度学习的过程中,我们会使用到一个框架,现阶段主流的框架是pytorch和tensorflow,近些年来,pytorch的热度逐渐超过tensorflow,它具有很强的普适性,而功能也极其强大。
本文大部分都是参考这位大佬(写Bug那些事)的文章(狂肝两万字带你用pytorch搞深度学习!!!)进行编写的,代码基本都来自于这个大佬,因为刚刚开始深度学习,我不太会编写神经网络模型,对于中间各个层之间的处理无法完全理解,但是每一份代码我都自己手动敲了一遍,加入自己的理解的同时也进行了修改和优化,希望读者在不清楚里面的内容时可以多查资料,里面的很多用法都很活,很多库函数,万一查到了,可以直接调用,大大降低了我们的代码量,但是也希望可以先简要理解(不一定看得懂),还有就是官方教程真的是最好的学习资料,如果有不理解的函数,可以在pycharm里面进入相关函数的具体实现里面查看,虽然是英文,但是应该是最正确的理解。
本篇文章属于转载和原创两个结合,但是我标的转载,如果原作者介意,可以联系我删除相关内容
官方教程(最优秀的教程)
一、基本操作
- 数据类型
基本操作对象:tensor(张量)
实际上可看作一种数据矩阵,它与numpy的ndarrays很相似,二者之间可以进行相互转化,但是tensor最大的优点是支持使用GPU进行加速,可以简单了解一下CPU和GPU的区别,方便我们更好地理解深度学习
常用数据类型 CPU Tensor GPU Tendor 32位浮点数 torch.FloatTensor torch.cuda.FloatTensor 64位浮点数 torch.DoubleTensor torch.cuda.DoubleTensor 32位有符号整型 torch.IntTensor torch.cuda.IntTensor 64位有符号整型 torch.LongTensor torch.cuda.LongTensor
- Tensor的创建
Tensor创建有很多方法,创建的过程返回的都是Tensor数据类型
import torech
torch.Tensor(2,3,dtype=torch.Long)//指定类型
torch.FloatTensor(2,3)//传维度
torch.FloatTensor([2,3,4,5])//传入数据
torch.range(1,20,2)//一维矩阵,起始值,结束值,步长
torch.zeros(2,2)
torch.eye(2,2)//对角张量
- Tensor的运算
tensor的运算多种多样,多是直接对于矩阵数据进行处理
import torch
a = torch.randn(2,3)//生成随机矩阵,两行三列
b = torch.abs(a)//绝对值
c = torch.clamp(a,-0.1,0.1)//范围裁剪,使所有数据介于该范围
//参数:tensor,down,up
//数据运算:相当于点运输,对应行和列的元素进行运算
d = torch.add(a,10)//标量或矩阵相加
e = torch.div(a,10)//标量或矩阵相除
f = torch.pow(a,2)//求幂运算
//矩阵运算
g = torch.randn(3,2)
h = torch.mm(a,g)//矩阵乘法运算
i = torch.randn(3)
j = torch.mv(a,i)//矩阵与向量乘法运算
二、神经网络工具箱torch.nn
torch.autograd库虽然实现了自动求导与梯度反向传播,但如果我们要完成一个模型的训练,仍需要手写参数的自动更新、训练过程的控制等,还是不够便利。为此,PyTorch进一步提供了集成度更高的模块化接口torch.nn,该接口构建于Autograd之上,提供了网络模组、优化器和初始化策略等一系列功能。
1. nn.Module类
nn.Module是PyTorch提供的神经网络类,并在类中实现了网络各层的定义及前向计算与反向传播机制。在实际使用时,如果想要实现某个神经网络,只需继承nn.Module,在初始化中定义模型结构与参数,在函数forward()中编写网络前向过程即可。
编写自己的神经网络类。对该类进行继承,此时我们只需要编写两个函数,一个是初始化函数,定义在后续函数中你会用到的模型和方法,一个是forward函数,主要对你的输入数据进行处理
import torch
from torch import nn
//对输入数据进行加一处理
class MyModule(nn.Module):
def __init__(self):
super().__init__()
def forward(self, input):
output = input + 1
return output
mymodule = Mymodule()
x = torch.tensor(1.0)
output = mymodule(x)
print(output)
2. torch.nn
torch.nn是pytorch中自带的一个函数库,里面包含了神经网络中使用的一些常用函数,如具有可学习参数的nn.Conv2d(),nn.Linear()和不具有可学习的参数(如ReLU,pool,DropOut等)
最好的学习方法是去看看官方的解释
Module类是所有神经网络模块的基类,Module可以以树形结构包含其他的Module。Module类中包含网络各层的定义及forward方法
定义网络:
- 需要继承nn.Module类,并实现forward方法;
- 一般把网络中具有可学习参数的层放在构造函数__init__()中;
- 不具有可学习参数的层(如ReLU)可在forward中使用nn.functional来代替;
- 只要在nn.Module的子类中定义了forward函数,利用Autograd自动实现反向求导。
torch.nn的引入
快捷键简介
import torch
import torch.nn as nn
import torch.nn.functional as F
常用层简介:
torch.nn模块为我们准备好的各种层,方便我们调用以构建网络。这里主要介绍卷积层、池化层、激活函数层、循环层、全连接层等的相关使用方法。
1. 卷积层(Convolutional Layer):
卷积层是用于处理图像和其他二维数据的重要层。它通过滑动一个小的窗口(卷积核/滤波器)在输入上提取特征。在PyTorch中,可以使用torch.nn.Conv2d来创建一个二维卷积层。
原理:
主要学习了这篇文章
我只是简单介绍了一下,一定要去自己查资料理解一下
卷积窗口从输入数组的最左上方开始,按照从左往右,从上往下的顺序,依次在输入数组上滑动。当卷积窗口滑动到某一位置时,窗口中的输入子数组与核数组按元素相乘并求和。得到输出数组中对应位置的元素。(对应相乘,依次相加求和)
参数解释:
- padding:填充,边界的填充值,默认0
- stride:步幅,从左到右,从上到下,卷积窗口每次滑动的行数或者列数
- channel:通道
- in_chanels: 输入通道,输入数据和卷积核的通道相同,
这里默认卷积核个数为1,输出通道也为1- out_channels:输出通道
多输出通道:输入与单个卷积核进行互相关运算以后,得到输入通道数目个二维输出,对应相加,得到一个输出通道,如果有多个卷积核,每个卷积核对应一个二维输出,那么具有多个输出通道。- kernel_size:卷积核尺寸
kernel_size
跟卷积核不是一个东西。 kernel_size 可以看做是一个滑动窗口,这个窗口的大小由自己指定,如果输入是单个值,例如 3 ,那么窗口的大小就是 3 × 3 ,还可以输入元组,例如 (3, 2) ,那么窗口大小就是 3 × 2 。
# 创建一个卷积层
in_channels = 3 # 输入通道数(RGB图像为3通道)
out_channels = 64 # 输出通道数,也即卷积核的个数
kernel_size = 3 # 卷积核大小,可以是整数或元组 (height, width)
conv_layer = nn.Conv2d(in_channels, out_channels, kernel_size)
# 假设输入数据为 input_data,维度为 (batch_size, channels, height, width)
output_data = conv_layer(input_data)
2. 池化层(Pooling Layer):
池化层用于降低特征图的空间维度,减少计算量,并
保留重要的特征
。常用的池化操作包括最大池化和平均池化。在PyTorch中,可以使用torch.nn.MaxPool2d和torch.nn.AvgPool2d来创建最大池化层和平均池化层。
# 最大池化层
pool_layer = nn.MaxPool2d(kernel_size=2, stride=2)
pooled_data = pool_layer(output_data)
# 平均池化层
avg_pool_layer = nn.AvgPool2d(kernel_size=2, stride=2)
avg_pooled_data = avg_pool_layer(output_data)
3. 激活函数层(Activation Function Layer):
激活函数层用于引入非线性性质,使得神经网络可以学习复杂的函数映射。常用的激活函数有ReLU、Sigmoid和Tanh等。在PyTorch中,可以使用torch.nn.ReLU、torch.nn.Sigmoid和torch.nn.Tanh等来创建激活函数层。
- ReLU(Rectified Linear Unit)激活函数:
ReLU是目前最常用的激活函数,其定义为:f(x) = max(0, x)。即将输入小于0的值变为0,大于0的值保持不变。ReLU具有以下优点:
- 计算简单,只需判断输入是否大于0,没有指数运算等复杂操作。
- 避免了梯度消失问题,使得神经网络更易于训练。
- 保留了正部分信息,有助于网络学习更加稀疏的表示。
- Sigmoid激活函数:
Sigmoid函数定义为: f ( x ) = 1 1 + e x p ( − x ) f(x) = \frac{1}{1 + exp(-x)} f(x)=1+exp(−x)1
其输出值在0到1之间,可以将任意输入映射到一个概率值。Sigmoid的特点包括:
- 输出范围有限,导致在网络层数较多时,容易出现梯度消失问题,导致训练困难。
- 对于较大或较小的输入,其梯度接近于0,这也导致了梯度消失问题。
- Tanh(双曲正切)激活函数:
Tanh函数定义为: f ( x ) = exp ( x ) − exp ( − x ) exp ( x ) + exp ( − x ) f(x) = \frac{{\exp(x) - \exp(-x)}}{{\exp(x) + \exp(-x)}} f(x)=exp(x)+exp(−x)exp(x)−exp(−x)
其输出值在-1到1之间,比Sigmoid函数的输出范围更广。Tanh的特点包括:
- 输出均值为0,可以将数据规范化到均值为0的范围,有助于模型的训练。
- 仍然存在梯度消失问题,尤其是在输入接近于正负饱和区域时。
# 创建ReLU激活函数层
relu_layer = nn.ReLU()
# 创建Sigmoid激活函数层
sigmoid_layer = nn.Sigmoid()
# 创建Tanh激活函数层
tanh_layer = nn.Tanh()
# 假设输入数据为input_data
output_relu = relu_layer(input_data)
output_sigmoid = sigmoid_layer(input_data)
output_tanh = tanh_layer(input_data)
4. 循环层(Recurrent Layer):
循环层用于处理序列数据,其中每个时间步的输出依赖于前一个时间步的输出。常用的循环层是LSTM(长短期记忆网络)和GRU(门控循环单元)。在PyTorch中,可以使用torch.nn.LSTM和torch.nn.GRU等来创建循环层。
# LSTM循环层
input_size = 10 # 输入特征的大小
hidden_size = 20 # 隐状态特征的大小
num_layers = 2 # LSTM层的层数
lstm_layer = nn.LSTM(input_size, hidden_size, num_layers)
output_seq, (h_n, c_n) = lstm_layer(input_seq, (h_0, c_0))
5. 全连接层(Fully Connected Layer):
全连接层将输入的所有特征连接到输出,常用于最后一层用于分类任务。在PyTorch中,可以使用torch.nn.Linear来创建全连接层。
# 全连接层
in_features = 100 # 输入特征的大小
out_features = 50 # 输出特征的大小
fc_layer = nn.Linear(in_features, out_features)
output_data = fc_layer(input_data)
3. torch.nn.Sequential
torch.nn.Sequential 是 PyTorch 中一个方便的模型容器,用于按顺序组织神经网络的各个层。它允许用户按照顺序添加各种层,构建神经网络模型,而无需手动定义 forward 方法。Sequential 可以大大简化神经网络的创建过程。
参数:
每一层(layer)都作为一个元素添加到一个有序的列表中,并按照它们添加的顺序顺序执行。每个元素都是一个由 torch.nn 中的层组成的模块。层按照它们在列表中的顺序依次应用于输入数据,最终得到输出数据。
注意:
nn.Sequential
和nn.Linear
是 PyTorch 中两个不同的概念,它们分别用于不同的任务。
nn.Sequential 是一个容器,用于按顺序组织神经网络的各个层。它允许用户添加各种层,构建自己的神经网络模型,相当于对多个层次进行一个封装,按照用户定义时的各层的参数对数据进行处理。
我的理解是自定义构建一种新的层供自己使用,从而减少代码量。
nn.Linear 等等只是神经网络里面常用的各种层,按照一定的参数接收数据,如何返回处理后的数据,只是用于数据的处理。
import torch
import torch.nn as nn
# 构建一个简单的神经网络
model = nn.Sequential(
nn.Linear(in_features=784, out_features=128),
nn.ReLU(),
nn.Linear(in_features=128, out_features=64),
nn.ReLU(),
nn.Linear(in_features=64, out_features=10)
)
# 输出模型结构
print(model)
三、神经网络的搭建
1. 第一个简易神经网络
一个简单的单层隐藏层的神经网络示例,使用PyTorch实现了前向传播和反向传播过程,并用随机梯度下降(SGD)方法进行权重更新,来拟合随机生成的数据。
过程:
数据生成:输入数据,输出数据,权重矩阵
前向传播:输入数据处理,得到实际输出
损失计算:目标输出和实际输出
反向传播:计算权重矩阵的梯度
权重更新:随机梯度下降(SGD)方法
梯度:函数在某一点处的变化率或者斜率
对于一个多变量函数,梯度是一个向量,其中每个分量表示函数在相应变量上的偏导数。
梯度的方向指向函数在该点处增长最快的方向,而梯度的大小表示函数在该点处的变化率。在优化问题中,我们通常希望找到函数的最小值,因此梯度在优化算法中起到了关键作用。
在神经网络中,梯度在训练过程中非常重要。神经网络的目标是通过调整网络参数来最小化损失函数,而损失函数通常衡量了预测输出与真实标签之间的差距。通过计算损失函数对于网络参数的梯度,我们可以使用梯度下降等优化算法来更新参数,从而逐步改进网络的性能,使其更好地拟合训练数据。
Gradient简介
import torch
batch_n = 100 # 一个批次输入数据的数量,每个批次中有100个样本
hidden_layer = 100 # 隐藏层的大小,有100个神经元
input_data = 1000 # 每个数据的特征为1000维
output_data = 10 # 输出数据的维度为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) # (1000,100)
w2 = torch.randn(hidden_layer, output_data) # (100,10)
epoch_n = 20
lr = 1e-6
for epoch in range(epoch_n):
# 训练阶段
# ******前向传播******
# 第一层权重
h1 = x.mm(w1) # (100,1000)*(1000,100)-->(100,100)
print(h1.shape)
# 数据裁剪
h1 = h1.clamp(min=0)
# 第二层权重
y_pred = h1.mm(w2) # (100,100)*(100,10)-->(100,10)
# ******损失计算******
# 均方误差(Mean Squared Error, MSE)
loss = (y_pred - y).pow(2).sum()
print("epoch:{},loss:{:.4f}".format(epoch, loss))
# ******反向传播******
# 计算输出层权重 w2 的梯度
grad_y_pred = 2 * (y_pred - y) # 损失函数关于预测值求偏导
grad_w2 = h1.t().mm(grad_y_pred)
# 计算隐藏层权重 w1 的梯度
grad_h = grad_y_pred.clone()
grad_h = grad_h.mm(w2.t())
grad_h.clamp_(min=0) # 将小于0的值全部赋值为0,相当于sigmoid
grad_w1 = x.t().mm(grad_h)
# ******权重更新******
# 随机梯度下降(SGD)方法
w1 = w1 - lr * grad_w1
w2 = w2 - lr * grad_w2
w1 和 w2 分别是神经网络的两个权重矩阵,它们在神经网络中起着关键的作用:
w1:
w1 是输入层与隐藏层之间的权重矩阵。它决定了输入数据与隐藏层神经元之间的连接权重。在前向传播过程中,输入数据 x 通过与 w1 的矩阵相乘,得到隐藏层 h1 的输出。随后,h1 会经过激活函数(这里是ReLU),得到非线性的特征表示,再传递给输出层。w1 的维度是 (input_data, hidden_layer),其中 input_data 表示输入数据的特征数,hidden_layer 表示隐藏层神经元的数量。
w2:
w2 是隐藏层与输出层之间的权重矩阵。它决定了隐藏层神经元与输出数据之间的连接权重。在前向传播过程中,隐藏层 h1 通过与 w2 的矩阵相乘,得到神经网络的输出 y_pred。w2 的维度是 (hidden_layer, output_data),其中 hidden_layer 表示隐藏层神经元的数量,output_data 表示输出数据的维度(在这个示例中为10,因为有10个类别)。
2. torch.autograd
在训练神经网络时,最常用的算法是反向传播。在该算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整,从而得到更好的拟合效果。
它是自动微分引擎,通过跟踪所有执行的操作,并构建计算图来计算张量的导数(梯度)
- 在使用 torch.Tensor 时,默认情况下,PyTorch 会自动构建计算图,并允许通过调用 backward() 方法来计算张量的梯度。在执行反向传播(backward)时,torch.autograd 将根据计算图自动计算张量的梯度,这样就可以使用自动梯度更新优化模型参数。
神经网络模型的训练:(两大步)
- 前向传播:通过神经网络里面的各个层的处理,获得一个当前神经网络的预测结果。【输入层—隐藏层–输出层】
- 反向传播:通过建立损失函数,然后根据本次计算的误差结果对相应的权重进行调整。【输出层–隐藏层–输入层】
- 损失函数(均方误差、二元交叉熵等)
- 损失函数衡量了模型预测结果与真实标签之间的差异,是模型优化的目标
- 梯度计算
- 损失函数相对于各个参数的梯度(导数)
- 参数更新
- 在计算了每个参数对损失函数的梯度之后,使用优化算法(如梯度下降或其变体)来更新模型的参数。
- 优化算法使用学习率(learning rate)来控制参数更新的步长。学习率决定了每次更新参数的幅度大小。
- 重复训练:通过多次重复进行正向传播、反向传播和参数更新,模型逐渐调整参数,使得损失函数逐渐减小,从而达到优化模型的目标。
梯度下降算法简介级示例:有兴趣的朋友可以去了解一下,理解大概思想即可
import torch
x = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
计算图:
在这个神经网络中,w
和b
是需要我们进行优化的参数,所以需要能够计算损失函数关于这些变量的梯度,即初始化时设置它们的requires_grad
属性为True
,也可以在创建后通过x.requires_grad_(true)
设置该属性。
使用autograd实现一个二层结构的神经网络模型
这份代码是源自于这篇文章
我对代码进行了修改,加入了一些注解和更新,主要还是为了体现其中的一句代码loss.backward()
import torch
batch_n = 100 # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000 # 每个数据的特征为1000
output_data = 10
# requires_grad = False,表示该变量在进行自动梯度计算的过程中不会保留梯度值
x = torch.randn(batch_n, input_data, requires_grad=False)
y = torch.randn(batch_n, output_data, requires_grad=False)
w1 = torch.randn(input_data, hidden_layer, requires_grad=True)
w2 = torch.randn(hidden_layer, output_data, requires_grad=True)
# 学习率和迭代次数
epoch_n = 50
lr = 1e-6
# 迭代
for epoch in range(epoch_n):
# 正向传播
h1 = x.mm(w1) # (100,1000)*(1000,100)-->100*100
print(h1.shape)
h1 = h1.clamp(min=0)
y_pred = h1.mm(w2)
# y_pred = x.mm(w1).clamp(min=0).mm(w2)
# 损失函数
loss = (y_pred - y).pow(2).sum()
print("epoch:{},loss:{:.4f}".format(epoch, loss.data))
# 后向传播
# grad_y_pred = 2*(y_pred-y)
# grad_w2 = h1.t().mm(grad_y_pred)
loss.backward() # 简单一句代码实现梯度计算
# grad_h = grad_y_pred.clone()
# grad_h = grad_h.mm(w2.t())
# grad_h.clamp_(min=0)#将小于0的值全部赋值为0,相当于sigmoid
# grad_w1 = x.t().mm(grad_h)
# 参数更新
w1.data -= lr * w1.grad.data
w2.data -= lr * w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
# w1 = w1 -lr*grad_w1
# w2 = w2 -lr*grad_w2
3. 自定义传播函数
定义了一个继承自 torch.nn.Module 的 Model 类,包含了前向传播方法 forward。模型接受输入数据 x,经过两个全连接层(权重矩阵 w1 和 w2)以及ReLU激活函数,最终输出预测结果 y_pred。通过均方误差损失函数(MSE)计算预测结果与目标输出 y 之间的差异,并使用梯度下降法更新模型参数 w1 和 w2,优化模型。整个训练过程通过迭代进行。代码中的 backward 方法没有实际用途,因为PyTorch的自动微分机制会自动执行反向传播。
import torch
batch_n = 64 # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000 # 每个数据的特征为1000
output_data = 10
class Model(torch.nn.Module): # 定义一个继承自torch.nn.Module的模型类Model
def __init__(self):
super(Model, self).__init__() # 调用父类的初始化方法
def forward(self, input, w1, w2): # 定义前向传播方法
x = torch.mm(input, w1) # 矩阵相乘,得到隐藏层结果
x = torch.clamp(x, min=0) # 使用ReLU激活函数
x = torch.mm(x, w2) # 再次矩阵相乘,得到最终输出结果
return x
# 这里面的bakcward函数,一般不需要编写,使用默认的自动微分机制
def backward(self): # 定义后向传播方法(在此例中并未实际使用)
pass
model = Model() # 实例化模型类,得到一个模型对象
x = torch.randn(batch_n, input_data, requires_grad=False) # 生成随机输入数据
y = torch.randn(batch_n, output_data, requires_grad=False) # 生成随机目标输出数据
w1 = torch.randn(input_data, hidden_layer, requires_grad=True) # 随机初始化权重矩阵w1
w2 = torch.randn(hidden_layer, output_data, requires_grad=True) # 随机初始化权重矩阵w2
epoch_n = 30 # 迭代次数
lr = 1e-6 # 学习率
for epoch in range(epoch_n):
y_pred = model(x, w1, w2) # 调用模型的forward方法,得到预测结果y_pred
loss = (y_pred - y).pow(2).sum() # 计算损失函数,这里使用均方误差MSE
print("epoch: {}, loss: {:.4f}".format(epoch, loss.data)) # 输出当前迭代的损失值
loss.backward() # 后向传播,计算参数的梯度
w1.data -= lr * w1.grad.data # 根据梯度下降法更新参数w1
w2.data -= lr * w2.grad.data # 根据梯度下降法更新参数w2
w1.grad.data.zero_() # 清空参数w1的梯度值,为下一轮迭代准备
w2.grad.data.zero_() # 清空参数w2的梯度值,为下一轮迭代准备
4.使用损失函数的神经网络
均方差损失函数:简化了我们手动编写损失函数的过程,
torch.nn.MSELoss()
`注意:
使用了神经网络模型对输入数据进行处理时,我们不清楚它具体的参数,所以我们在对其中参数进行调整时,我们必须学会对神经网络模型中参数的遍历,这样才能更加方便我们根据反向传播获得的梯度对参数进行更新。
模型的参数是通过torch.nn.Parameter类来表示的,并存储在模型的parameters()方法返回的迭代器中。
参数优化
可以参考这篇文章学习一下
for param in models.parameters():
param.data -= param.grad.data * lr
示例
import torch
# 定义均方误差损失函数
loss_fn = torch.nn.MSELoss()
# 定义神经网络模型的参数
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)
# 构建神经网络模型
models = torch.nn.Sequential(
torch.nn.Linear(input_data, hidden_layer), # 输入层到隐藏层的线性变换
torch.nn.ReLU(), # 使用ReLU激活函数
torch.nn.Linear(hidden_layer, output_data) # 隐藏层到输出层的线性变换
)
# torch.nn.Sequential括号内就是我们搭建的神经网络模型的具体结构,Linear完成从隐藏层到输出层的线性变换,再用ReLU激活函数激活
# torch.nn.Sequential类是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络模型的搭建,
# 最主要的是,参数会按照我们定义好的序列自动传递下去。
# 定义训练超参数
epoch_n = 10000 # 训练的总轮数
lr = 0.01 # 学习率
# 迭代进行训练
for epoch in range(epoch_n):
y_pred = models(x) # 前向传播,计算预测值
loss = loss_fn(y_pred, y) # 计算均方误差损失
if epoch % 1000 == 0:
print("epoch:{}, loss:{:.4f}".format(epoch, loss.item()))
models.zero_grad() # 将模型参数的梯度清零,避免梯度累积
loss.backward() # 反向传播,计算梯度
with torch.no_grad():
for param in models.parameters():
param -= lr * param.grad # 根据梯度和学习率来更新模型参数
四、完整神经网络模型
1. torchvision
torchvision是PyTorch中专门用于计算机视觉任务的一个扩展库,提供了丰富的图像处理工具和常见的计算机视觉数据集。它主要包含以下模块和功能:
- 数据集加载和预处理:
- torchvision.datasets:包含常见的计算机视觉数据集,如CIFAR-10、CIFAR-100、MNIST、ImageNet等。可以使用这些数据集来进行图像分类、目标检测等任务。
- torchvision.transforms:提供了各种图像预处理和数据增强操作,例如裁剪、翻转、缩放、归一化等,用于在训练
过程中对图像数据进行预处理。- 模型和预训练模型:
- torchvision.models:提供了一些经典的计算机视觉模型,如AlexNet、VGG、ResNet、DenseNet等。可以使用这些模型来进行图像分类或迁移学习任务。
- torchvision.utils:包含了一些实用函数,例如加载预训练模型的函数等。
- 数据加载器:
- torchvision.datasets中的数据集类提供了加载图像数据集的功能,并返回包含图像和标签的数据。可以使用> - > - torch.utils.data.DataLoader将这些数据集加载为一个批次的数据用于训练或验证。
- 图像可视化:
- torchvision.utils中的一些函数,例如make_grid,用于将图像数据转换为网格形式,方便进行可视化。
- 工具函数:
- torchvision.io:提供了一些图像读取和保存的工具函数。
- 其他功能:
- torchvision.ops:包含一些计算机视觉任务中使用的操作函数。
- torchvision.transforms.functional:包含一些图像预处理的函数,可以直接用于对图像数据进行处理。
注意
导入必要的库:
import torch
import torchvision
import torchvision.transforms as transforms
import torchvision.models as models
1. torchviosion.datasets
torchvision.datasets
是torchvision库中用于加载计算机视觉数据集的模块。它提供了一些常见的计算机视觉数据集
参数解释:
- root:数据集存放的根目录,一般选择相对路径。
- train:true的对应训练集(默认),false对应测试集。
- transform: 对数据集进行预处理和增强的操作。
- download:是否下载数据集。
# 加载CIFAR-10数据集
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, transform=transform, download=True)
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, transform=transform, download=True)
2. torchviosion.transform
torchvision.transforms
是用于图像预处理和数据增强的模块。它提供了多种常用的图像变换操作,可以用于在深度学习任务中对图像数据进行预处理和增强,例如裁剪、缩放、翻转、归一化等
常见的图像变换操作:
- Compose(transforms):将多个变换操作组合成一个序列。
- 尺寸变换:
- Resize(size):将图像调整为指定的大小。
- CenterCrop(size):从图像中心裁剪出指定大小的区域。
- RandomCrop(size):随机裁剪图像为指定大小。
- Pad(padding):在图像边缘填充指定的像素值。
- 颜色变换:
- ToTensor():将图像转换为张量。
- Normalize(mean, std):对图像进行归一化操作。
- 翻转和旋转:
- RandomHorizontalFlip():以0.5的概率随机水平翻转图像。
- RandomVerticalFlip():以0.5的概率随机垂直翻转图像。
- RandomRotation(degrees):随机旋转图像。
- 亮度、对比度和饱和度变换:
- ColorJitter(brightness, contrast, saturation, hue):对亮度、对比度、饱和度和色调进行随机变换。
- 图像变换组合:
- RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(3/4, 4/3)):随机裁剪和缩放图像。
- RandomChoice(transforms):从一组变换中随机选择一个进行操作。
- RandomApply(transforms, p):以概率p应用一组变换。
- RandomOrder(transforms):随机顺序应用一组变换。
总结:
在正常使用的时候,我一般习惯性对这些操作进行封装,更加方便我们的使用,类比于torch.nn.Sequential
import torchvision.transforms as transforms
# 定义图像变换操作
transform = transforms.Compose([
transforms.Resize((256, 256)), # 将图像大小调整为256x256
transforms.RandomCrop((224, 224)), # 随机裁剪图像为224x224大小
transforms.RandomHorizontalFlip(), # 随机水平翻转图像
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 归一化操作
])
3. torchvision.models
torchvision.models
是PyTorch中用于加载预训练的计算机视觉模型的模块。它提供了一些经典的深度学习模型,如AlexNet、VGG、ResNet、DenseNet等,以及一些用于迁移学习的预训练模型。
1. 加载模型:
# 加载预训练的ResNet-18模型
model = models.resnet18(pretrained=True)
# 或者加载未训练的ResNet-18模型
model = models.resnet18(pretrained=False)
2. 修改模型结果(可选):(了解即可)
如果需要对模型进行微调或者适应特定任务,可以根据需要修改模型结构。但在绝大多数情况下,直接加载预训练的模型就可以满足一般的计算机视觉任务。
例如,可以修改全连接层的输出节点数,以适应不同的分类任务。
# 修改全连接层的输出节点数,适应不同的分类任务
num_classes = 10 # 假设有10个类别
model.fc = torch.nn.Linear(in_features=512, out_features=num_classes)
3. 使用模型进行预测或特征提取:
加载了预训练的模型后,可以使用模型进行图像分类、目标检测等任务。
# 假设有一张图像image,将其传递给模型进行分类预测
output = model(image)
# 也可以使用模型的前几层来提取特征向量
features = model.features(image)
4. torchvision.utils
torchvision.utils
是torchvision库中的一个模块,主要用于处理图像数据和进行数据可视化。
常用功能和用法的总结:
- 图像处理和数据可视化:
- make_grid: 将多张图像拼接成一张网格状的图像,便于数据可视化。
- save_image: 将多张图像保存到磁盘,方便保存和查看图像数据。
- save_image_grid: 将多个网格状的图像保存到磁盘,用于可视化多个网格图像。
- draw_bounding_boxes: 在图像上绘制边界框,适用于目标检测任务中的标注。
- draw_segmentation_masks: 在图像上绘制分割掩码,用于可视化分割任务中的掩码。
- 数据可视化:
- torchvision.utils.save_image: 用于将多张图像保存成一张网格图像并保存到磁盘。
- torchvision.utils.make_grid: 将多张图像拼接成一张网格状的图像,方便可视化。
- 辅助函数:
- torchvision.utils.save_image: 将多张图像保存到磁盘,可用于保存模型的输出结果或训练数据的预览。
- torchvision.utils.make_grid: 将多张图像拼接成一张网格状的图像,用于数据可视化和训练监控。
- torchvision.utils.save_image_grid: 将多个网格状的图像保存到磁盘,用于可视化多个网格图像。
拓展:
torch.utils
是PyTorch中用于辅助工具的模块,它提供了一些实用的函数和类,用于数据处理、模型保存和加载、学习率调整等常见任务。
常用的功能和用法:
- 数据加载与处理:
- torch.utils.data.Dataset:自定义数据集的基类,用于加载和处理自己的数据集。
- torch.utils.data.DataLoader:用于加载数据集的数据加载器,可以将数据集加载成批次,方便进行训练和验证。
- 学习率调整:
- torch.optim.lr_scheduler:包含了学习率调整的类,例如StepLR、MultiStepLR、ReduceLROnPlateau等,用于在训练过程中自动调整学习率。
- 参数保存与加载:
- torch.save(obj, filepath):将PyTorch模型或张量保存到文件中。
- torch.load(filepath):从文件中加载保存的PyTorch模型或张量。
- 并行计算:
- torch.nn.DataParallel:用于将模型在多个GPU上进行并行计算,加速模型训练过程。
- 数据处理工具:
- torch.utils.make_grid:将多张图像数据转换成一个网格形式,方便可视化。
- torch.utils.tensorboard:TensorBoard工具的接口,用于可视化训练过程和模型的性能。
- 辅助函数:
- torch.utils.data.get_worker_info:用于获取数据加载器中的worker信息,可以在多线程加载数据时使用。
数据加载器:
data_loader_train = torch.utils.data.DataLoader(
dataset=data_train, # 数据集对象,这里假设data_train是一个torch.utils.data.Dataset实例
batch_size=64, # 每个批次的样本数量,默认为1,这里设置为64
shuffle=True, # 是否在每个epoch前随机打乱数据
# num_workers=2 # 载入训练数据所需的子任务数(多线程加载数据),如果不需要多线程,可以不设置该参数
)
参数介绍:
- dataset:数据集对象
- 这个参数是必需的,它指定了要使用的数据集对象。数据集对象通常是torch.utils.data.Dataset类的实例,它负责加载和处理数据集中的样本。您需要根据您的数据集类型,创建相应的数据集对象,并将其传递给dataset参数。
- batch_size:每个批次的样本数量
- 这个参数指定了每个批次中包含的样本数量。较大的批次大小可能会提高训练速度,但同时也会增加内存消耗。较小的批次大小可能会增加训练过程中的随机性,但训练速度可能会变慢。根据您的硬件和数据集大小,可以根据需要调整批次大小。
- shuffle:是否在每个epoch前随机打乱数据
- 这个参数决定是否在每个epoch开始之前对数据进行随机打乱。如果将其设置为True,则在每个epoch之前都会对数据进行随机重排,以增加训练的随机性。这对于避免过拟合和提高模型的泛化能力很有帮助。
- num_workers:载入数据所需的子任务数(多线程加载数据)
- 这个参数用于指定在数据加载过程中使用的子任务数,即多线程加载数据。设置num_workers大于1可以加速数据加载过程,尤其在数据集较大时。但是,需要根据硬件和数据集大小,选择合适的子任务数,避免出现资源竞争和内存消耗过大的情况。
数据可视化:
torchvision.utils.make_grid
是torchvision库中的一个函数,用于将多张图像拼接成一张网格状的图像,方便进行数据可视化。它常用于将一个批次的图像数据组织成一张大图,以便查看图像数据的情况或在训练过程中监控模型的输入和输出。
import torch
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torchvision.utils as vutils
import matplotlib.pyplot as plt
# 假设有一个批次的图像数据,形状为 (batch_size, channels, height, width)
# 假设数据已经在 0 到 1 之间归一化了
batch_size = 16
channels = 3
height = 64
width = 64
# 数据预处理转换,包括将图像转换为张量和归一化
transform = transforms.Compose([
transforms.Resize((height, width)), # 调整图像大小
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 归一化,将像素值从 [0, 1] 变换到 [-1, 1]
])
# 加载数据集
data_train = datasets.CIFAR10(root='data', train=True, download=True, transform=transform)
data_test = datasets.CIFAR10(root='data', train=False, download=True, transform=transform)
# 创建数据加载器
data_loader_train = torch.utils.data.DataLoader(dataset=data_train, batch_size=batch_size, shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset=data_test, batch_size=batch_size, shuffle=False)
# 获取一个批次的图像数据
for images, _ in data_loader_train:
tensor_batch = images[:batch_size]
break
# 将张量数据转换为网格图像
grid_img = vutils.make_grid(tensor_batch, nrow=4, normalize=True)
# 显示网格图像
plt.imshow(grid_img.permute(1, 2, 0)) # 将 (C, H, W) 的 Tensor 转换为 (H, W, C) 并显示
plt.axis('off') # 关闭坐标轴
plt.show()
2. 模型搭建
在构建神经网络模型时,通常需要使用一些基本的层来定义网络的结构。以下是一些常用的层以及它们的作用:
- 卷积层(Convolutional Layer):用于提取输入数据中的特征。卷积层通过卷积操作在输入数据上滑动一组可学习的滤波器(也称为卷积核),从而产生输出特征图。
- 池化层(Pooling Layer):用于缩减特征图的尺寸并降低计算量。常见的池化操作包括最大池化和平均池化。
- 全连接层(Fully Connected Layer):也称为线性层或密集连接层,用于对提取的特征进行分类。全连接层将输入数据与权重矩阵相乘,并通过激活函数处理得到输出。
- 激活函数(Activation Function):用于引入非线性性质。常见的激活函数包括ReLU、Sigmoid、Tanh等。
- 批归一化层(Batch Normalization Layer):用于加速训练过程并提高网络的稳定性。批归一化对每个小批量的数据进行归一化处理。
- 丢弃层(Dropout Layer):用于防止过拟合。丢弃层在训练过程中随机地将一部分神经元的输出置为0,从而减少神经元之间的依赖关系。
- 损失函数(Loss Function):用于衡量模型预测结果与真实标签之间的差异。常见的损失函数包括交叉熵损失、均方误差等。
- 优化器(Optimizer):用于更新网络参数以最小化损失函数。常见的优化器包括随机梯度下降(SGD)、Adam、RMSprop等。
确定神经网络的层次结构通常需要根据任务的复杂性和数据集的特点来进行调整。一般情况下,可以先从简单的模型开始,然后逐渐添加更多的层和复杂性,观察模型的性能并进行调整。同时,可以参考相关领域的研究和论文,了解其他模型在类似任务上的表现,并尝试将其中的设计思想应用到自己的模型中。
通过实验和不断调整来优化神经网络的结构和参数,以获得更好的性能。同时,还可以尝试使用一些自动化的网络搜索算法(如超参数搜索、神经网络搜索等)来自动寻找最优的网络结构和参数配置。
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
3. 参数优化
# 创建模型实例并将其移到设备上
model = Model().to(device)
# 损失函数
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)
)
)
4. 模型训练
循环迭代,不断进行参数的调整,主要参考的是训练集的准确度
_epochs = 5
# 迭代训练
for epoch in range(n_epochs):
running_loss = 0.0 # 平均损失
running_correct = 0 # 正确分类数
# 当前epoch编号
print("Epoch {}/{}".format(epoch+1, n_epochs))
print("-" * 10)
# 循环遍历 data_loader_train 中的每个批次数据
for data in data_loader_train:
images, labels = data
images, labels = images.to(device), labels.to(device)
# 模型预测
outputs = model(images)
# 返回数据有两个,第一个值是最大值的张量 max_values,第二个值是最大值的索引张量 max_indices。
# 只需要用到了最大值的索引 max_indices,因为它表示了模型对每个图像的预测类别。
# ‘—’是一个占位符
_, pred = torch.max(outputs.data, 1)
# 梯度清零
optimizer.zero_grad()
# 损失计算
loss = cost(outputs, labels)
# 反向传播
loss.backward()
# 参数优化
optimizer.step()
# 累计所有批次的损失值
running_loss += loss.item() * images.size(0)
# 累计模型预测正确的样本数量
running_correct += torch.sum(pred == labels.data)
train_loss = running_loss / len(data_train)
train_accuracy = 100.0 * running_correct / len(data_train)
# 测试集里面正确分类数
testing_correct = 0
for data in data_loader_test:
images, labels = data
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == labels.data)
test_accuracy = 100.0 * testing_correct / len(data_test)
# 输出训练损失、训练准确率、测试准确率
print("Train Loss: {:.4f}, Train Accuracy: {:.2f}%, Test Accuracy: {:.2f}%".format(train_loss, train_accuracy, test_accuracy))
展示
Epoch 1/5
----------
Train Loss: 0.1292, Train Accuracy: 96.14%, Test Accuracy: 98.43%
Epoch 2/5
----------
Train Loss: 0.0475, Train Accuracy: 98.59%, Test Accuracy: 98.39%
Epoch 3/5
----------
Train Loss: 0.0291, Train Accuracy: 99.08%, Test Accuracy: 98.62%
Epoch 4/5
----------
Train Loss: 0.0233, Train Accuracy: 99.23%, Test Accuracy: 98.82%
Epoch 5/5
----------
Train Loss: 0.0184, Train Accuracy: 99.41%, Test Accuracy: 98.81%
5. 模型验证
从测试数据集里面随机挑选四张作为一个批次进行预测,如何进行输出查看,输出查看之前,一定记得要进行
反归一化处理
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=4,
shuffle=True)
# 模型预测
images, labels = next(iter(data_loader_test))
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, preds = torch.max(outputs.data, 1)
print("Predicted Labels:", preds)
print("Real Labels: ", labels)
# 显示预测结果的图像
img = torchvision.utils.make_grid(images.cpu())
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)
# 显示图像窗口
plt.show()
展示
预测结果是正确的,随机抽取的测试集数据是[8, 3, 6, 6]
Predicted Labels: tensor([8, 3, 6, 6], device='cuda:0')
Real Labels: tensor([8, 3, 6, 6], device='cuda:0')
6. 完整神经网络训练模型和代码
MINIST数据集简介:
- MNIST数据集是一个常用的手写数字数据集,它是机器学习和计算机视觉领域中最经典的数据集之一。MNIST数据集包含了一系列28x28像素大小的灰度图像,每张图像代表一个手写数字(0到9之间的数字)。数据集共有60000张训练图像和10000张测试图像,这些图像都已经过预处理和归一化。
- 对于MNIST数据集,训练集中的每个样本都有一个对应的标签,表示图像中的手写数字。这些标签用于监督学习,以便训练算法正确地预测图像中的数字。
import torch.utils.data
import torchvision
from torchvision import datasets,transforms
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
# 设置使用的设备,如果有可用的GPU则使用GPU,否则使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 1. 数据预处理
# 预处理操作
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转化为张量
# 将单通道的灰度图像复制为三通道的RGB图像
# 它的参数是自定义函数,x.repeat(3, 1, 1) 将单通道的图像张量在通道维度上复制三次,从而得到一个具有三个通道的 RGB 图像。
transforms.Lambda(lambda x: x.repeat(3, 1, 1)),
# 图像数据进行归一化操作
# transforms.Normalize(mean=(mean_channel1, mean_channel2, mean_channel3), std=(std_channel1, std_channel2, std_channel3))
# 表示将图像数据的每个通道(R、G、B)上的像素值减去 0.5,再除以 0.5,从而将图像数据归一化到范围 [-1, 1]
transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
])
# 加载MNIST数据集,并进行预处理操作
data_train = datasets.MNIST(root="./data/",
train=True,
transform=transform,
download=False)
data_test = datasets.MNIST(root="./data/",
train=False,
transform=transform,
download=False)
# 创建数据加载器
# 将数据集进行划分,分批次进行训练和测试
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)
# 从训练数据集加载器里面获取一个批次的数据,先将数据加载器转换为迭代器,然后通过next()函数回去容器里面的下一个元素
images, labels = next(iter(data_loader_train))
img = torchvision.utils.make_grid(images)
# 将网格图像 img 转换为 NumPy 数组,并进行维度转换,从 (C, H, W) 的张量格式转换为 (H, W, C) 的图像格式,以便于使用 matplotlib 进行显示
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)
# 2. 构建模型
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
# 3. 训练模型
# 创建模型实例并将其移到设备上
model = Model().to(device)
# 损失函数
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 # 正确分类数
# 当前epoch编号
print("Epoch {}/{}".format(epoch+1, n_epochs))
print("-" * 10)
# 循环遍历 data_loader_train 中的每个批次数据
for data in data_loader_train:
images, labels = data
images, labels = images.to(device), labels.to(device)
# 模型预测
outputs = model(images)
# 返回数据有两个,第一个值是最大值的张量 max_values,第二个值是最大值的索引张量 max_indices。
# 只需要用到了最大值的索引 max_indices,因为它表示了模型对每个图像的预测类别。
# ‘—’是一个占位符
_, pred = torch.max(outputs.data, 1)
# 梯度清零
optimizer.zero_grad()
# 损失计算
loss = cost(outputs, labels)
# 反向传播
loss.backward()
# 参数优化
optimizer.step()
# 累计所有批次的损失值
running_loss += loss.item() * images.size(0)
# 累计模型预测正确的样本数量
running_correct += torch.sum(pred == labels.data)
train_loss = running_loss / len(data_train)
train_accuracy = 100.0 * running_correct / len(data_train)
# 测试集里面正确分类数
testing_correct = 0
for data in data_loader_test:
images, labels = data
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == labels.data)
test_accuracy = 100.0 * testing_correct / len(data_test)
# 输出训练损失、训练准确率、测试准确率
print("Train Loss: {:.4f}, Train Accuracy: {:.2f}%, Test Accuracy: {:.2f}%".format(train_loss, train_accuracy, test_accuracy))
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=4,
shuffle=True)
# 模型预测
images, labels = next(iter(data_loader_test))
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, preds = torch.max(outputs.data, 1)
print("Predicted Labels:", preds)
print("Real Labels: ", labels)
# 显示预测结果的图像
img = torchvision.utils.make_grid(images.cpu())
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)
# 显示图像窗口
plt.show()
输出
[tensor(3), tensor(1), tensor(0), tensor(2), tensor(6), tensor(9), tensor(8), tensor(5), tensor(0), tensor(4), tensor(1), tensor(4), tensor(9), tensor(9), tensor(2), tensor(9), tensor(3), tensor(0), tensor(8), tensor(0), tensor(7), tensor(0), tensor(8), tensor(8), tensor(2), tensor(2), tensor(1), tensor(1), tensor(8), tensor(2), tensor(7), tensor(4), tensor(2), tensor(1), tensor(0), tensor(7), tensor(4), tensor(3), tensor(7), tensor(6), tensor(3), tensor(1), tensor(5), tensor(3), tensor(3), tensor(2), tensor(2), tensor(4), tensor(5), tensor(3), tensor(0), tensor(3), tensor(6), tensor(2), tensor(3), tensor(7), tensor(6), tensor(5), tensor(0), tensor(2), tensor(7), tensor(2), tensor(3), tensor(3)]
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)
)
)
Epoch 1/5
----------
Train Loss: 0.1292, Train Accuracy: 96.14%, Test Accuracy: 98.43%
Epoch 2/5
----------
Train Loss: 0.0475, Train Accuracy: 98.59%, Test Accuracy: 98.39%
Epoch 3/5
----------
Train Loss: 0.0291, Train Accuracy: 99.08%, Test Accuracy: 98.62%
Epoch 4/5
----------
Train Loss: 0.0233, Train Accuracy: 99.23%, Test Accuracy: 98.82%
Epoch 5/5
----------
Train Loss: 0.0184, Train Accuracy: 99.41%, Test Accuracy: 98.81%
Predicted Labels: tensor([8, 3, 6, 6], device='cuda:0')
Real Labels: tensor([8, 3, 6, 6], device='cuda:0')
参考资料: