「深度学习一遍过」必修2:解读简化版模型代码

Posted 荣仔!最靓的仔!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「深度学习一遍过」必修2:解读简化版模型代码相关的知识,希望对你有一定的参考价值。

本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。
专栏地址:「深度学习一遍过」必修篇

目 录

1 总观整体代码

2 代码细致解读

2.1 训练

2.1.1 导入库

2.1.2 加载数据集

2.1.3 定义数据加载器 

2.1.4 创建模型

​ 2.1.5 选择优化函数

2.1.6 定义训练函数

2.1.7 定义测试函数

2.1.8 开始训练

2.2 保存

2.2.1 保存模型参数

2.3 测试

2.3.1 加载模型

2.3.2 获取预测结果

3 结果运行展示

3.1 模型结构

 3.2 训练轮次、Loss与Accuracy情况

3.3 预测结果


1 总观整体代码

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose

# 加载数据集
training_data = datasets.FashionMNIST(
    root='data',       
    train=True,      
    download=True,    
    transform=ToTensor(),  
)
test_data = datasets.FashionMNIST(
    root='data',
    train=False,
    download=True,
    transform=ToTensor(),
)

# 定义数据加载器
batch_size = 64 
training_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print("shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)    
    break

device = "cuda" if torch.cuda.is_available() else 'cpu'
print("Using {} device".format(device))

# 创建模型
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),
            nn.ReLU()
        )
    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)

# 选择优化函数
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 

# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        pred = model(X)
        loss = loss_fn(pred, y)
        optimizer.zero_grad() 
        loss.backward()      
        optimizer.step()   

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss:{loss:>7f} [{current:>5d}/{size:>5d}]")


# 定义测试函数
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, corrent = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            corrent += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    corrent /= size
    print(f"Test Error: \\n Accuracy: {(100 * corrent):>0.1f}%, Avg loss:{test_loss:>8f}\\n")

# 开始训练
epoch = 50
for t in range(epoch):
    print(f"Epoch {t + 1}\\n----------------------")
    train(training_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done!")

# 保存模型参数
torch.save(model.state_dict(), "model.pth")
print("Save PyTorch Model State to model.pth")

# 加载模型
model = NeuralNetwork() 
model.load_state_dict(torch.load("model.pth"))

# 获取预测结果
classes = [
    "T-shirt/top",
    "Trouser",
    "Pollover",
    "Dress",
    "coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]

model.eval()   
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "actual"') 

2 代码细致解读

2.1 训练

2.1.1 导入库

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose

① from torch import nn : 神经网络模块相关包 。

② from torch.utils.data import DataLoader : 实现一个数据加载器 。

2.1.2 加载数据集

training_data = datasets.FashionMNIST(
    root='data',
    train=True,
    download=True,
    transform=ToTensor(),
)
test_data = datasets.FashionMNIST(
    root='data',
    train=False,
    download=True,
    transform=ToTensor(),
)

① 加载 datasets 中的 FashionMNIST 数据集。

② root = 'data' :保存到 data 文件夹中;

③ train = True :设定为训练集 ;
     train = False :设定为测试集 ;

④ download = True :为 True 时若本地没有该数据集就从官网进行下载(源码中指定了官方下载路径,见下图)

⑤ transform = ToTensor() :此代码作用是对数据进行转换,直接把数据变为输入,相当于是进行了数据预处理;其他数据预处理常用的还有:resize()、尺度变换、随即翻转等。

2.1.3 定义数据加载器 

batch_size = 64

training_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print("shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

① batch_size = 64:用于设置一次传入的数据数量;

② training_dataloader = DataLoader(training_data, batch_size=batch_size)
     test_dataloader = DataLoader(test_data, batch_size=batch_size)
     用于给训练集、测试集分别创建一个数据集加载器;

③ for X, y in test_dataloader:
          print("shape of X [N, C, H, W]: ", X.shape)  
          print("Shape of y: ", y.shape, y.dtype)        
          break
     用于查看 dataloader 读取情况;
     X.shape 表示多个维度的图片,其中 N:代表传入64张图片
                                                               C:代表通道数(灰度图通道数为1)
                                                               H:图片的长
                                                               W:图片的宽 
      y 是指标签。

2.1.4 创建模型

device = "cuda" if torch.cuda.is_available() else 'cpu'
print("Using {} device".format(device))

 上面这段代码表示:如果显卡可用,则用显卡进行训练。

# 创建模型
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),
            nn.ReLU()
        )
    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)

① 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),
                nn.ReLU()
             )
        用于定义 linear_relu_stack,由多层神经网络构成;
        Sequential 意为其下定义的多层操作一个接一个按顺序进行,把它们前后全部拼接在一起。

③ nn.Linear 是全连接层
     28 * 28 :表示输入维度数量
     512 :表示目前维度数/下一层输出数量

③ nn.ReLU() :表示 ReLU 非线性激活函数 

④ def forward(self, x) :其中 x 为输入数据,forward 定义前向传播函数。

⑤ model = NeuralNetwork().to(device) :调用刚定义的模型,如果 cuda 可用将模型转到 GPU。

 2.1.5 选择优化函数

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

① loss_fn = nn.CrossEntropyLoss():用于定义损失函数,计算实际输出和真实相差多少。本句代码具体用到的是交叉熵损失函数,事实上,它就是做图片分类任务时常用的损失函数。

② optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) :作用是定义优化器,用来训练时候优化模型参数;其中,SGD表示随机梯度下降,用于控制实际输出y与真实y之间的相差有多大

③ lr=1e-3 :代表初始学习率。

2.1.6 定义训练函数

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        pred = model(X)
        loss = loss_fn(pred, y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss:{loss:>7f} [{current:>5d}/{size:>5d}]")

①  此处定义训练函数,把数据加载器、模型、损失,优化器等传到网络中去;

② size = len(dataloader.dataset) :用于查看 dataloader 有多少张图片;

③ for batch, (X, y) in enumerate(dataloader) :用于从数据加载器中读取 banch(一次读取多少张,即批次数),X(图片数据),y(图片真实标签);

④ X, y = X.to(device), y.to(device) :用于将数据存到显卡;

⑤ pred = model(X) :用于得到预测的结果 pred;

⑥ loss = loss_fn(pred, y) :用于计算预测的误差;

⑦ optimizer.zero_grad() :优化器工作之前先将梯度置0,进行归零操作;

⑧ loss.backward() :用相差多少这样一个损失值来对模型进行参数更新(反向传播,更新模型参数);

⑨ optimizer.step() :更新优化其中的参数;

⑩ if batch % 100 == 0 :每训练100次,输出一次当前信息
     :loss 值越低越好,预测值与真实值越来越靠近,这说明模型设计成功!

2.1.7 定义测试函数

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, corrent = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            corrent += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    corrent /= size
    print(f"Test Error: \\n Accuracy: {(100 * corrent):>0.1f}%, Avg loss:{test_loss:>8f}\\n")

① model.eval() :将模型转换为验证模式;

② test_loss, corrent = 0, 0 :初始化 test_loss、corrent 用来统计每次的误差;

③ with torch.no_grad() :测试时模型参数不用更新,所以 no_grad,整个模型参数正向推就ok,不反向更新参数;

④ for X, y in dataloader :加载数据加载器,得到里面的 X(图片数据)和 y(真实标签);

④ X, y = X.to(device), y.to(device) :将数据转换为 GPU;

⑤ pred = model(X) :将图片传图到模型中就得到预测的 pred;

⑥ test_loss += loss_fn(pred, y).item() :计算预测值pred和真实值y的差距;

⑦ corrent += (pred.argmax(1) == y).type(torch.float).sum().item() :统计预测正确的个数。

2.1.8 开始训练

epoch = 50
for t in range(epoch):
    print(f"Epoch {t + 1}\\n----------------------")
    train(training_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done!")

2.2 保存

2.2.1 保存模型参数

torch.save(model.state_dict(), "model.pth")
print("Save PyTorch Model State to model.pth")

2.3 测试

2.3.1 加载模型

model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))

① model = NeuralNetwork() :得到前面定义的模型框架

② model.load_state_dict(torch.load("model.pth")) :框架本身加载模型里的参数
     :此处要做到前后呼应,即先 torch.load 读取模型 pth,
                                                再用 load_state_dict 读取到里面所有参数

2.3.2 获取预测结果

classes = [
    "T-shirt/top",
    "Trouser",
    "Pollover",
    "Dress",
    "coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]

classes 用于定义整个标签类。
本身标签中有什么,模型并不知道,只是简化标签为 0、1、2、. . . ,之后再将 0、1、2、. . .  对应标签的名字。

model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "actual"')

① model.eval() :进入验证阶段,不会梯度更新,只会向前推;

② predicted, actual = classes[pred[0].argmax(0)], classes[y] :得到预测类别中最高的那一类,再把最高的这一类对应 classes 中的哪一个标签。

③ print(f'Predicted: "{predicted}", Actual: "actual"') :最终输出预测值与真实值。

3 结果运行展示

3.1 模型结构

 3.2 训练轮次、Loss与Accuracy情况

3.3 预测结果

欢迎大家交流评论,一起学习

希望本文能帮助您解决您在这方面遇到的问题

感谢阅读
END

版权声明:本文为CSDN博主「荣仔!最靓的仔!」的原创文章,遵循 CC 4.0 BY-SA 版权协议。
         转载请在醒目位置附上原文出处链接及本声明。

以上是关于「深度学习一遍过」必修2:解读简化版模型代码的主要内容,如果未能解决你的问题,请参考以下文章

「深度学习一遍过」必修6:利用迁移学习快速提升模型性能

「深度学习一遍过」必修18:基于pytorch的语义分割模型实现

「深度学习一遍过」必修3:Pytorch数据读取——使用Dataloader读取Dataset

「深度学习一遍过」必修7:模型训练验证与可视化

「深度学习一遍过」必修15:PyTorch模型分析

「深度学习一遍过」必修28:基于C3D预训练模型训练自己的视频分类数据集的设计与实现