神经网络由对数据执行 *** 作的层/模块组成。 模块 torch.nn命名空间提供了建立自己的神经网络需要的所有构建 。pytorch中的每个模块都是 nn.Module 的子类。神经网络是一个是由其他模块(层)组成的模块。 这种嵌套结构允许轻松构建和管理复杂的架构。
在接下来的部分中,将构建一个神经网络来对 FashionMNIST 数据集中的图像进行分类。
1、导入训练神经网络所需要的包import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
2、获取训练设备
理想情况下是希望神经网络能够在 GPU 这样的硬件加速上训练模型, 如果GPU真的可用。 先查看下 torch.cuda 是否可用,否则我们 继续使用CPU。
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using {device} device")
3、定义类(1)通过子类化 nn.Module
定义我们的神经网络, 并且用__init__
方法初始化神经网络层. 每一个 nn.Module
子类用forward正向传播
方法实现对输入数据的 *** 作。
#定义类结构
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)#flatten()是对多维数据的降维函数
logits = self.linear_relu_stack(x)
return logits
其中flatten(dim)表示,从第dim个维度开始展开,将后面的维度转化为一维。也就是说,只保留dim之前的维度,其他维度的数据全都挤在dim这一维。
(2)创建一个实例 NeuralNetwork
, 并将其移至设备 device
中,并打印它的结构。
model = NeuralNetwork().to(device)#将实例化后的模型传递给设置好的设备并赋值给model
print(model)
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
)
)
(3)要使用模型,我们需要将输入数据传递给它。这将执行模型 forward 方法的正向 *** 作,以及一些后台 *** 作,不能直接调用model.forward() 。对输入调用模型将返回一个10维张量,其中包含每个类的原始预测值。我们通过一个神经网络实例nn.Softmax来获得预测概率。(注意此处获得的概率是未经训练的原始权重和偏置下得到的)。
#输出层,将数据经过模型迭代,打印出一个最大概率
X = torch.rand(1, 28, 28, device=device)#将一张28*28的图像数据输入到指定设备中
logits = model(X)#将张量数据传递给实例化模型
#print(logits)
#print(logits.shape)
pred_probab = nn.Softmax(dim=1)(logits)#将类别的概率归一化
#print(pred_probab)
y_pred = pred_probab.argmax(1)#返回最大值所在地索引
print(f"Predicted class: {y_pred}")#输出最大值索引
其中torch.rand返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数。张量的形状由参数sizes定义。
其中nn.Softmax(dim=1)是 Module 类,在实例化类后会初始化运算所需要的参数。Softmax函数应用于一个n维输入张量,对其进行缩放,使n维输出张量的元素位于[0,1]范围内,总和为1。dim (int) - Softmax是被计算的维度(当dim=0时,指的是在维度0上的元素相加等于1)。
本例中dim=1是因为数据经过模型训练输出的结果是一个形状为[1, 10]的二维张量。有一张很有趣的图片可以描述三维维度的选取:
pytorh中有一个torch.size()的函数来查看张量的大小 :
import torch
z = torch.ones(2,3,4)
print(z)
print(z.size())
print(z.size(0))
print(z.size(1))
print(z.size(2))
其中argmax()针对一维数组:返回数组中最大值元素的索引位置,针对二维数组:参数axis可取(0,1),默认是0,表示数组第几维的最大值。axis = 0,返回结果为数组中每一列最大值所在“行”索引值。axis = 1,返回结果为数组中每一行最大值所在“列”索引值。以下链接是关于argmax()具体用法:
python学习笔记(一)argmax()_Manduner_TJU的博客-CSDN博客_argmax(1)
输出结果如下:
torch.Size([1, 10])
tensor([[-0.0585, -0.1216, -0.0544, -0.2088, -0.0833, 0.1046, -0.0328, 0.0021,
0.0691, 0.0494]], grad_fn=)
tensor([[0.0971, 0.0912, 0.0975, 0.0836, 0.0948, 0.1144, 0.0997, 0.1032, 0.1104,
0.1082]], grad_fn=)
Predicted class: tensor([5])
可以看出在经过Softmax函数后,各个元素相加和为1,并在最后返回了最大值的索引。
4、模型层接下来分解 FashionMNIST 模型中的结构层。 为了说明这一点,抽取 3 张大小为 28x28 的小批量图像样本,看看通过网络传递它会发生什么。
#查看数据的格式
input_image = torch.rand(3,28,28)#随机生成三张图片
print(input_image.size())
经过torch后的结果变为张量形式,形状为(3,28,28):
torch.Size([3, 28, 28])
(1)nn.Flatten展平层
初始化nn.Flatten层的目的是将每个 2D 28x28 图像数据转换为28*28= 784 个像素值的连续数组( 保持小批量维度(dim=0 时)。
#将输入数据展平处理
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
其中函数torch.nn.Flatten(start_dim=1,end_dim=-1)中的参数:
start_dim与end_dim代表合并的维度,开始的默认值为1,结束的默认值为-1,因此常被使用在神经网络当中,默认把第0维保留下来,将每个batch的数据拉伸成一维。当函数内有一个参数时(代表开始合并的维度)。当有两个参数时(前一个参数代表开始合并的维度,后一个参数代表结束合并的维度)。类似torch.flatten默认是从0开始的,其余与torch.nn.Flatten()一样。
输出结果,经过Flatten展平层后的图像数据形状为(3,784):
torch.Size([3, 784])
(2)nn.Linear线性全连接层
线性层是一个使用存储的权重和偏差对输入的数据进行线性变换的模块。
#线性 *** 作
layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
其中nn.Linear()是用于设置网络中的全连接层的,在二维图像处理的任务中,全连接层的输入与输出一般都设置为二维张量,形状通常为[batch_size, size]
,不同于卷积层要求输入输出是四维张量(b,w,h,c)。
in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。
out_features指的是输出的二维张量的大小,即输出的二维张量的形状[batch_size,output_size],当然,它也代表了该全连接层的神经元个数。从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]的张量变换成了[batch_size, out_features]的输出张量。
输出结果,经过Linear线性全连接层后的图像数据形状为(3,20):
torch.Size([3, 20])
(3)nn.ReLU激活函数
非线性激活是在模型的输入和输出之间创建复杂映射的原因。 relu函数的作用就是增加了神经网络各层之间的非线性关系,否则,如果没有激活函数,无论神经网络有多少层,每一层的输出都是上层输入的线性函数,每层都相当于矩阵相乘,与没有隐藏层效果相当。在线性变换后引入非线性变换以帮助神经网络学习各种各样的现象。
在本例模型中,在线性层之间使用ReLU激活函数,当然其他类型的非线性激活函数也可以在模型中引入。
#激活函数
print(f"Before ReLU: {hidden1}\n\n")
print(hidden1.shape)
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
其中ReLU()函数是分段线性函数,把所有的负值都变为0,而正值不变,这种 *** 作被成为单侧抑制。类似的函数有nn.ReLU(inplace=True),把变量进行替换,直接把原来的变量覆盖掉,节省了内存。缺点是有时候出现梯度回传失败的问题,之前的变量被替换,找不到之前的变量。
输出结果(部分),可观察到之前的负值都变为0:
torch.Size([3, 20])
After ReLU: tensor([[0.1597, 0.5415, 0.2265, 0.0000, 0.6051, 0.8664, 0.0000, 0.0000, 0.0000,
0.1726, 0.1502, 0.0000, 0.4512, 0.5308, 0.2979, 0.0000, 0.0000, 0.3221,
0.6609, 0.2163],
[0.0703, 0.3304, 0.2813, 0.0415, 0.7798, 0.8213, 0.0000, 0.0000, 0.3287,
0.0000, 0.2881, 0.0000, 0.5848, 0.5451, 0.1254, 0.0000, 0.0000, 0.3244,
0.3276, 0.3021],
[0.3076, 0.4908, 0.1748, 0.0000, 1.0403, 0.5395, 0.0000, 0.0258, 0.0000,
0.0000, 0.3384, 0.0000, 0.1736, 0.5897, 0.6562, 0.3450, 0.0000, 0.2570,
0.7535, 0.0598]], grad_fn=)
(4)nn.Sequential结构层
nn.Sequential是有序的模块容器。 神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数,数据按照定义的结构顺序通过所有模块。 功能是可以使用顺序容器组成一个快速网络,例如 seq_modules
.
#顺序模块结构
seq_modules = nn.Sequential(
flatten,#展平 *** 作层
layer1,#线性全连接层
nn.ReLU(),#激活函数
nn.Linear(20, 10)#全连接层,输出十个分类
)
input_image = torch.rand(3,28,28)#定义3张28*28的图片数据
logits = seq_modules(input_image)#输入到网络中
(5)nn.Softmax 函数
神经网络的最后一个线性层返回logits(原始值在[-infty,infty]中),并将其传递给nn.Softmax模块。 Logit被缩放为数值[0,1],代表模型对每个类别的预测概率。 dim参数表示数值总和必须为1的维度。
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
5、模型参数
神经网络中的许多层都是参数化的,即具有相关的权重和偏差,这些权重和偏差在训练期间得到优化。 子类化 nn.module 模块自动跟踪模型对象中定义的所有字段,并使用模型的parameters()或named_parameters()方法访问所有参数。
在本例中,迭代每个参数并打印其大小及其值的预览。
print(f"Model structure: {model}\n\n")
for name, param in model.named_parameters():
print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
输出结果:
Model structure: NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(seq_modules): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
)
)
Layer: seq_modules.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[ 0.0010, 0.0185, -0.0102, ..., 0.0023, 0.0167, -0.0130],
[ 0.0345, 0.0235, 0.0289, ..., -0.0169, -0.0035, 0.0268]],
grad_fn=)
Layer: seq_modules.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0161, -0.0248], grad_fn=)
Layer: seq_modules.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[-0.0337, 0.0248, 0.0101, ..., -0.0397, -0.0437, 0.0109],
[-0.0058, -0.0323, 0.0332, ..., 0.0346, 0.0147, 0.0162]],
grad_fn=)
Layer: seq_modules.2.bias | Size: torch.Size([512]) | Values : tensor([ 0.0298, -0.0007], grad_fn=)
Layer: seq_modules.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[-0.0353, -0.0209, -0.0055, ..., 0.0197, -0.0314, 0.0225],
[ 0.0094, 0.0196, -0.0341, ..., 0.0256, 0.0062, -0.0084]],
grad_fn=)
Layer: seq_modules.4.bias | Size: torch.Size([10]) | Values : tensor([0.0344, 0.0038], grad_fn=)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)