最近做实验发现自己还是基础框架上掌握得不好,于是开始重学一遍PyTorch框架,这个是课程笔记,此节课很详细,笔记记的比较粗,这个视频课是需要有点深度学习数学基础的,如果没有数学基础,可以一边学一边查一查
1. Transforms
我们导入到数据集中的图片可能大小不一样,数据并不总是以训模型所需的最终处理形式出现。我们使用Transforms对数据进行一些操作,使其适合训练(比如统一图片的像素值)。一般Transforms函数都在Dataset中去定义好。target_transfrom是同样的道理,比如我们得到的分类结果是整型的,然后我们能够做一个target_transfrom处理变成One-hot这种数据格式。无论是Transforms还是target_transform,我们都要在Dataset中去定义好。最后通过get_item方法返回到正确的符合规范的标签或样本。
在深度学习中,One-hot编码是一种将分类变量转换成向量表示的方法。在One-hot编码中,向量的长度与类别总数相同,而每个类别对应一个位置,在该位置上One-hot编码表示为1,而其余位置则表示为0。这种表示方法确保了每个类别之间是相互独立的,不存在任何顺序或距离的关系。
One-hot编码在处理分类特征时非常有用,特别是当特征的取值类别非常多时。它能够为神经网络提供一个清晰的输入表示,有助于模型的学习和泛化。例如,在处理文本数据时,每个单词可以被视为一个单独的分类特征,而One-hot编码则可以将每个单词转换为一个向量,其中只有在该单词对应的索引位置上有一个1,其余位置都是0。
使用One-hot编码的主要步骤通常包括:
- 构建词汇表:收集所有可能的类别,并按照某种顺序进行排序。
- 将文本数据转换为整数序列:将文本中的每个单词或字符映射到词汇表中的相应索引。
- 获取词汇表大小:确定One-hot编码向量的长度,即类别总数。
- 将整数序列转换为One-hot编码:对于每个整数序列,创建一个长度与词汇表大小相同的向量,并在相应的位置上设置为1,其他位置设置为0。
需要注意的是,虽然One-hot编码在某些情况下非常有用,但在处理具有顺序或距离关系的数据时,它可能不是最佳选择。此外,当类别数量非常多时,One-hot编码可能会导致大量的零值,这可能会对模型的训练和性能产生影响。在这种情况下,可能需要考虑其他编码方法,如嵌入向量等。
python
# 导入必要的库和模块
import torch
from torchvision import datasets # torchvision 中的 datasets 模块,用于加载标准数据集
from torchvision.transforms import ToTensor, Lambda # torchvision 的 transforms 模块提供数据预处理的方法
# 加载 FashionMNIST 数据集
ds = datasets.FashionMNIST(
root="data", # 数据存储的根目录,如果数据未下载则会下载到这个目录下
train=True, # 指定这是训练数据集,若设为 False 则加载测试数据集
download=True, # 如果数据未在 'root' 指定的路径下,则下载数据
transform=ToTensor(), # 指定一个转换函数,将 PIL 图片或者 numpy.ndarray 转换为 FloatTensor,并且归一化到 [0, 1] 区间
target_transform=Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(0, torch.tensor(y), value=1))
# target_transform 是一个用于处理目标变量的转换函数,
# 这里使用 Lambda 创建一个匿名函数,它将整数标签 y 转换为一个 one-hot 编码的 tensor。
# 具体过程如下:
# 1. torch.zeros(10, dtype=torch.float) 创建一个长度为 10,类型为 float 的零向量,
# 对应于 FashionMNIST 数据集中的 10 个类别。
# 2. scatter_(0, torch.tensor(y), value=1) 将上述零向量的第 y 位置设为 1,实现 one-hot 编码。
# scatter_ 的第一个参数是维度,这里为 0,表示在零维上操作(这里向量只有一维)。
)
2. 构建一个神经网络模型
神经网络由对数据执行操作的层/模块组成。torch.nn命名空间提供了构建自己的神经网络所需的所有构建块。PyTorch中的每个模块都将nn子类化。单元神经网络是由其他模块(层)组成的模块本身。这种嵌套结构允许轻松地构建和管理复杂的体系结构。下面的代码讲解分类模型常用的东西,内容都在注释中:
python
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 选择训练设备(GPU还是CPU)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f"Using {device} device")
# 定义分类模型网络(所有的模型都要继承自nn.Module
class NeuralNetwork(nn.Module):
def __init__(self):
# 调用父类的方法
super().__init__()
# 包含很多子模块
# Flatten层,对特征进行铺平
self.flatten = nn.Flatten()
# nn.Sequential按照顺序传入一些层和模块
# 它会自动把这些层和模块串联起来
self.linear_relu_stack = nn.Sequential(
# 线性层+ReLU激活函数堆叠模块
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
# 不需要显示调用,前向传播操作
def forward(self, x):
# 前向传播首先将张量x展开
x = self.flatten(x)
# logits就是刚才堆叠的线性层+ReLU
logits = self.linear_relu_stack(x)
return logits
# 实例化网络模型(送入训练设备)
model = NeuralNetwork().to(device)
print(model) # 打印一下模块的子模块
# 使用模型,直接输入数据即可,调用的是模型的前向传播方法forward
X = torch.rand(1, 28, 28, device=device)
logits = model(X) # 将张量送入前向传播过程
# 最后经过模型后的张量送入Softmax层进行归一化(dim = 1, 对维度1归一化)
# 不选维度0是因为维度0是batch
pred_probab = nn.Softmax(dim=1)(logits)
# 经过Softmax层后会得到一个概率,去算概率的最大值
# 就能得到每个样本预测分类值
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")
# 从0-1直接生成均匀分布的张量,3X28X28
# 模拟3张尺寸为28x28的图像组成的mini-batch
input_image = torch.rand(3,28,28)
print(input_image.size())
# 我们将刚才的图片展平
# Flatten展平的时候,dim默认是1,batch那个维度不跟着一起展平
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
# nn.Linear线性层
# in_features:线性层所接受的输入的特征大小
# out_features:线性层所输出的隐含层大小
# 也是默认不看batch(维度0),从维度1开始
layer1 = nn.Linear(in_features=28*28, out_features=20)
# 把刚才展平的图片张量传入线性层
hidden1 = layer1(flat_image)
# 可以通过.weight和.bias去调用它的权重和偏置
# 这是打印传入图片张量后的权重和偏置,也可以打印之前的
print("weight:", layer1.weight)
print("bias:", layer1.bias)
print(hidden1.size())
# nn.ReLU(ReLU激活函数)
# 非线性函数通常是在线性变化之后加入到网络中的
# 打印经过激活函数前后的刚才经过线性层的张量
# 负数的值全被设置为0,正数不变
print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
# nn.Sequential模块的有序容器
# 将模型实例化以后,我们的数据会有序地经过这些模块
# 最终根据这个序列里面的层算出一个结果
seq_modules = nn.Sequential(
flatten,
layer1,
nn.ReLU(),
nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image) # 初始化后能自动调用forward方法
# nn.Softmax
# 分类任务最后都要进入Softmax层进行一个归一化
# dim=1表示归一化的维度是1,因为0是batch
# 我们要对第0维度即batch以外维度的那个维度的特征向量做归一化
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
# 模型参数
# 所有的模型类都能返回model.named_parameters()
# 它返回的是一个字典
# name是该层的命,Size是大小,Values是具体的值
# 打印后没有1和3层是因为该层是ReLU
# ReLU中没有参数,所以不进行打印
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")
运行结果:
Using cuda device
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)
)
)
Predicted class: tensor([5], device='cuda:0')
torch.Size([3, 28, 28])
torch.Size([3, 784])
weight: Parameter containing:
tensor([[ 0.0342, -0.0152, 0.0254, ..., -0.0246, -0.0062, -0.0317],
[ 0.0030, -0.0293, -0.0034, ..., 0.0288, 0.0014, -0.0131],
[-0.0094, 0.0055, 0.0309, ..., 0.0318, -0.0313, -0.0152],
...,
[ 0.0230, -0.0186, 0.0193, ..., -0.0334, 0.0090, -0.0037],
[ 0.0280, 0.0286, 0.0029, ..., 0.0046, -0.0111, 0.0051],
[ 0.0157, 0.0238, -0.0304, ..., -0.0299, -0.0107, -0.0055]],
requires_grad=True)
bias: Parameter containing:
tensor([ 0.0030, 0.0163, 0.0011, -0.0243, -0.0193, -0.0343, -0.0194, -0.0123,
0.0063, -0.0263, 0.0233, 0.0040, -0.0051, -0.0300, -0.0355, 0.0316,
-0.0066, -0.0073, 0.0135, 0.0297], requires_grad=True)
torch.Size([3, 20])
Before ReLU: tensor([[-0.6039, -0.0355, -0.5492, 0.0075, 0.0092, 0.4912, 0.0317, -0.1176,
-0.6009, -0.0215, 0.4660, -0.4432, 0.0967, -0.0203, -0.2747, -0.2946,
-0.2083, -0.1807, -0.3419, -0.2545],
[-0.4104, 0.0996, -0.3292, -0.2309, -0.1896, 0.0418, 0.2437, 0.2290,
-0.2958, 0.0917, 0.3528, -0.3920, -0.3850, 0.1246, -0.1306, -0.1945,
-0.0163, -0.5014, -0.5486, 0.1099],
[-0.5584, -0.0735, -0.3966, -0.1962, 0.0883, 0.0703, 0.1986, 0.0563,
-0.2369, 0.2135, 0.3497, 0.0497, -0.1771, 0.0996, -0.1119, -0.2216,
-0.0223, -0.2386, -0.7143, 0.1265]], grad_fn=<AddmmBackward0>)
After ReLU: tensor([[0.0000, 0.0000, 0.0000, 0.0075, 0.0092, 0.4912, 0.0317, 0.0000, 0.0000,
0.0000, 0.4660, 0.0000, 0.0967, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000],
[0.0000, 0.0996, 0.0000, 0.0000, 0.0000, 0.0418, 0.2437, 0.2290, 0.0000,
0.0917, 0.3528, 0.0000, 0.0000, 0.1246, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.1099],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0883, 0.0703, 0.1986, 0.0563, 0.0000,
0.2135, 0.3497, 0.0497, 0.0000, 0.0996, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.1265]], grad_fn=<ReluBackward0>)
Model structure: 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)
)
)
Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[ 0.0180, -0.0305, 0.0110, ..., -0.0276, -0.0140, 0.0109],
[ 0.0281, 0.0051, 0.0264, ..., -0.0076, 0.0145, -0.0119]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0239, -0.0077], device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0041, 0.0065, 0.0284, ..., -0.0378, -0.0441, 0.0140],
[-0.0358, 0.0067, 0.0360, ..., -0.0239, 0.0258, -0.0229]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([-0.0424, -0.0293], device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[ 0.0065, -0.0425, -0.0349, ..., -0.0188, -0.0355, 0.0037],
[ 0.0047, 0.0199, -0.0234, ..., 0.0271, 0.0346, -0.0122]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([-0.0207, -0.0225], device='cuda:0', grad_fn=<SliceBackward0>)
进程已结束,退出代码0