【PyTorch与深度学习】6、PyTorch中搭建分类网络实例

课程地址

最近做实验发现自己还是基础框架上掌握得不好,于是开始重学一遍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

相关推荐
靴子学长3 小时前
基于字节大模型的论文翻译(含免费源码)
人工智能·深度学习·nlp
海棠AI实验室4 小时前
AI的进阶之路:从机器学习到深度学习的演变(一)
人工智能·深度学习·机器学习
IT古董5 小时前
【机器学习】机器学习的基本分类-强化学习-策略梯度(Policy Gradient,PG)
人工智能·机器学习·分类
落魄君子5 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
四口鲸鱼爱吃盐5 小时前
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
人工智能·pytorch·分类
落魄君子5 小时前
ELM分类-单隐藏层前馈神经网络(Single Hidden Layer Feedforward Neural Network, SLFN)
神经网络·分类·数据挖掘
leaf_leaves_leaf5 小时前
win11用一条命令给anaconda环境安装GPU版本pytorch,并检查是否为GPU版本
人工智能·pytorch·python
夜雨飘零15 小时前
基于Pytorch实现的说话人日志(说话人分离)
人工智能·pytorch·python·声纹识别·说话人分离·说话人日志
四口鲸鱼爱吃盐6 小时前
Pytorch | 从零构建MobileNet对CIFAR10进行分类
人工智能·pytorch·分类
苏言の狗6 小时前
Pytorch中关于Tensor的操作
人工智能·pytorch·python·深度学习·机器学习