pytorch实战1:手把手教你基于pytorch实现LeNet5

pytorch实战1:手把手教你基于pytorch实现LeNet5

前言

​ 最近在看经典的卷积网络架构,打算自己尝试复现一下,在此系列文章中,会参考很多文章,有些已经忘记了出处,所以就不贴链接了,希望大家理解。

​ 完整的代码在最后。

本系列必须的基础

​ python基础知识、CNN原理知识、pytorch基础知识

本系列的目的

​ 一是帮助自己巩固知识点;

​ 二是自己实现一次,可以发现很多之前的不足;

​ 三是希望可以给大家一个参考。

目录结构

1. 数据集介绍与下载:

1.1 介绍:

​ LeNet5是1998年提出的,主要用来当时的手写数字识别,因此使用的数据集是MNIST数据集。

​ MNIST是一个经典的手写数字数据,也是一个公开的小型数据。 MNIST中的图像每个都是28*28=784的大小,并且为灰度图,值为0-255。

1.2 下载:

​ 数据集可以通过官网进行下载http://yann.lecun.com/exdb/mnist/(建议)也可以通过pytorch代码获取

# 导包
from torchvision.datasets import MNIST
import torchvision.transforms as transforms
# 下载数据集或者加载数据集
train_dataset = MNIST(root='../data/',train=True,transform=transforms.ToTensor(),download=True)
test_dataset = MNIST(root='../data/',train=False,transform=transforms.ToTensor())

注意,上面下载代码中root参数指明了下载后保存的地址,需要根据自己的文件夹路径进行修改。

2. LeNet5模型创建:

2.1 架构介绍:

​ LeNet5模型架构如下图所示:

在这里插入图片描述

其中,值得注意的地方是:

  • 输入大小为32*32,而数据集的图像大小为28*28,因此需要做出处理,方法是为原图像进行填充,即padding=2(这样大小为:28+2+2=32)。
  • 上图中全连接层写的不是特别清晰,比如FC1层120个神经元是指的是输出的神经元个数,输入神经元个数应该是5*5*16。
2.2 模型类创建:

​ 下面我们来创建这个模型类,首先根据pytorch创建模型的基本结构,写出:

# 创建模型
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        # 定义模型
        pass

    def forward(self,x):
		pass

​ 接着,我们来定义我们的模型,这里我们采取的思路是:先定义前面的卷积、池化层,再定义全连接层。

​ 好的,先定义卷积层(看注释):

self.features = nn.Sequential(
    # C1层,输入通道数为1是因为为灰度图,不是彩色图。其余的就是根据架构填写的参数,除去padding=2,
    # padding=2是为了让28*28的图片变为32*32
    nn.Conv2d(in_channels=1,out_channels=6,kernel_size=(5,5),stride=1,padding=2),
    nn.ReLU(),
    # Pool 1 层
    nn.MaxPool2d(kernel_size=2,stride=2),
    # C2层,输入通道数是上一层的输出通道数,其余同上
    nn.Conv2d(in_channels=6,out_channels=16,kernel_size=(5,5),stride=1),
    nn.ReLU(),
    # Pool 2 层
    nn.MaxPool2d(kernel_size=2,stride=2),
)

​ 这里,我把本来使用的sigmoid函数改为了relu函数,大家可以写的时候可以改回来。

​ 然后,定义全连接层(这里注意,我自己写的时候开始还有点懵,后来才理解):

self.classifier = nn.Sequential(
    # FC1层,输入为5*5*16=400,输出数为指定的参数值
    nn.Linear(in_features=400, out_features=120),
    nn.ReLU(),
    # FC2 层
    nn.Linear(in_features=120, out_features=84),
    nn.ReLU(),
    # FC3 层
    nn.Linear(in_features=84, out_features=10)
)

​ 最开始写的时候,没有想到还有400这个值,所以很纠结,后来才想起来:卷积层到全连接层的时候,需要把多维的数据拉平,所以显然此时的输入为卷积层输出的各个维度相乘,即6*6*25。

​ 最后,来定义前向算法,这个非常简单,只是注意要多一步把数据拉平(变为1维)的操作:

def forward(self,x):
    # 定义前向算法
    x = self.features(x)
    x = torch.flatten(x,1)
    result = self.classifier(x)
    return result

​ 这里,也许你会问:**为什么flatten参数要写一个1?这是因为,我们这里会采取批量训练,因此传入的数据是一个类似于[batch,32,32,5]的思维数据,其中batch指的是每批的个数,后面分别书图像大小(32-32)和卷积核个数。因此,我们拉平的时候,需要从第二位开始拉平,使之变为[batch,32*32*5]**的形式,因此需要指定为1。

3. 模型训练和评估:

3.1 数据加载:

​ 使用pytorch加载数据,很简单:

# 下载数据集或者加载数据集
train_dataset = MNIST(root='../data/',train=True,transform=transforms.ToTensor(),download=True)
test_dataset = MNIST(root='../data/',train=False,transform=transforms.ToTensor())
# 加载数据: 分批次,每批32个数据
batch_size = 32
train_loader = DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)
test_loader = DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False)

​ **基本上(也有需要自己定义Dataset的情况)**加载已有的数据都是这样的格式。

3.2 模型实例化和放入GPU中:

​ 这段很简单,另外调用GPU的方法也是固定的,就不多说:

# 创建模型
model = LeNet()
# 模型放入GPU中
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
3.3 定义损失函数、优化器:

​ 损失函数我们使用分类任务中常用的交叉熵损失函数,而优化器采用SGD优化器,学习率设置维常用的0.001,Momentum设置为0.9。

# 定义损失函数
loss_func = nn.CrossEntropyLoss()
loss_list = [] # 用来存储损失值
# 定义优化器:第一个参数是模型的参数
SGD = optim.SGD(params=model.parameters(),lr=0.001,momentum=0.9)
3.4 训练:

​ 首先,指定训练次数:

# 训练指定次数,这里写为了3
for i in range(3):
	xx

​ 然后,迭代读取数据:

for i in range(3):
    loss_temp = 0 # 定义一个损失值,用来打印查看
    # 其中j是迭代次数,data和label都是批量的,每批32个
    for j,(batch_data,batch_label) in enumerate(train_loader):
    	xxx

​ 接着将数据放入GPU中:

for i in range(3):
    loss_temp = 0
    for j,(batch_data,batch_label) in enumerate(train_loader):
        # 启用GPU
        batch_data,batch_label = batch_data.cuda(),batch_label.cuda()

​ 然后,就是常见的操作:清空梯度、计算模型、计算损失、反向传播、更新梯度

for i in range(3):
    loss_temp = 0 # 定义一个损失值,用来打印查看
    # 其中j是迭代次数,data和label都是批量的,每批32个
    for j,(batch_data,batch_label) in enumerate(train_loader):
        # 启用GPU
        batch_data,batch_label = batch_data.cuda(),batch_label.cuda()
        # 清空梯度
        SGD.zero_grad()
        # 模型训练
        prediction = model(batch_data)
        # 计算损失
        loss = loss_func(prediction,batch_label)
        loss_temp += loss
        # BP算法
        loss.backward()
        # 更新梯度
        SGD.step()

​ 最后,我们可以打印一下损失值来查看模型训练的怎么样了,这里我们每隔两百小批次就打印一次损失值:

# 训练指定次数
for i in range(3):
    loss_temp = 0 # 定义一个损失值,用来打印查看
    # 其中j是迭代次数,data和label都是批量的,每批32个
    for j,(batch_data,batch_label) in enumerate(train_loader):
        # 启用GPU
        batch_data,batch_label = batch_data.cuda(),batch_label.cuda()
        # 清空梯度
        SGD.zero_grad()
        # 模型训练
        prediction = model(batch_data)
        # 计算损失
        loss = loss_func(prediction,batch_label)
        loss_temp += loss
        # BP算法
        loss.backward()
        # 更新梯度
        SGD.step()
        if (j + 1) % 200 == 0:
            print('第%d次训练,第%d批次,损失值: %.3f' % (i + 1, j + 1, loss_temp / 200))
            loss_temp = 0

​ 运行上面的代码,显示的结果为:

1次训练,第200批次,损失值: 2.2981次训练,第400批次,损失值: 2.2851次训练,第600批次,损失值: 2.2411次训练,第800批次,损失值: 1.8121次训练,第1000批次,损失值: 0.7541次训练,第1200批次,损失值: 0.5211次训练,第1400批次,损失值: 0.4231次训练,第1600批次,损失值: 0.3651次训练,第1800批次,损失值: 0.3342次训练,第200批次,损失值: 0.2842次训练,第400批次,损失值: 0.2452次训练,第600批次,损失值: 0.2352次训练,第800批次,损失值: 0.2112次训练,第1000批次,损失值: 0.2022次训练,第1200批次,损失值: 0.1792次训练,第1400批次,损失值: 0.1802次训练,第1600批次,损失值: 0.1652次训练,第1800批次,损失值: 0.1483次训练,第200批次,损失值: 0.1473次训练,第400批次,损失值: 0.1433次训练,第600批次,损失值: 0.1363次训练,第800批次,损失值: 0.1303次训练,第1000批次,损失值: 0.1153次训练,第1200批次,损失值: 0.1143次训练,第1400批次,损失值: 0.1143次训练,第1600批次,损失值: 0.0933次训练,第1800批次,损失值: 0.122
3.5 评估:

​ 我们按照上面训练的思路,可以轻松写出测试的代码:

correct = 0
for batch_data,batch_label in test_loader:
    batch_data, batch_label = batch_data.cuda(), batch_label.cuda()
    prediction = model(batch_data)
    predicted = torch.max(prediction.data, 1)[1]
    correct += (predicted == batch_label).sum()
print('准确率: %.2f %%' % (100 * correct / 10000)) # 因为总共10000个测试数据

​ 当然上面的代码第一次接触还是有点疑问,主要的一点是predicted = torch.max(prediction.data, 1)[1]这段代码在干什么。

首先,我们一个图像传入模型,输出的是一个向量,这个向量10个值,代表数字0-9的概念值。而torch.max(x,1)表示按行(1:行,0:列)取出最大值,它返回的是一个特殊格式的数据,第一个元素是各个最大值,第二个元素是其索引(在这里就等价于0-9数字),因此使用[1]取出。

​ 运行结果:

准确率: 91.25 %
# 这个是只训练一次的结果
3.6 探究:

​ 下面将模型训练1、2、3、4次的准确率结果:

# 1次 : 准确率: 91.25 %
# 2次 : 准确率: 93.44 %
# 3次 : 准确率: 97.19 %
# 4次 : 准确率: 97.58 %

​ 另外,测试一下使用GPU与不使用GPU的时间差:

# 为了测试,我训练10次,并且仅仅记录训练花费时间
# 使用GPU: 训练花了: 124 s
# 不适用GPU:训练花了: 160 s

上面GPU测试不严谨,因为首次调用GPU是需要花费时间的,但是从两者差别看出,调用GPU确实效率很好。

4. 总结:

​ 这次算是把整个流程从头到尾跑了一遍,并简单探究了训练次数和调用GPU对训练的影响。会了本篇文章的代码,至少后面再实现CNN的图像分类架构,应该还是比较简单了。

完整代码

# author: baiCai
# 导包
import time
import torch
from torch import nn
from torch import optim
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
import torchvision.transforms as transforms
from torchvision.models import AlexNet

# 创建模型
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        # 定义模型
        self.features = nn.Sequential(
            nn.Conv2d(in_channels=1,out_channels=6,kernel_size=(5,5),stride=1,padding=2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2,stride=2),
            nn.Conv2d(in_channels=6,out_channels=16,kernel_size=(5,5),stride=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2,stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Linear(in_features=400, out_features=120),
            nn.ReLU(),
            nn.Linear(in_features=120, out_features=84),
            nn.ReLU(),
            nn.Linear(in_features=84, out_features=10)
        )

    def forward(self,x):
        # 定义前向算法
        x = self.features(x)
        # print(x.shape)
        x = torch.flatten(x,1)
        # print(x.shape)
        result = self.classifier(x)
        return result

# 下载数据集或者加载数据集
train_dataset = MNIST(root='../data/',train=True,transform=transforms.ToTensor(),download=True)
test_dataset = MNIST(root='../data/',train=False,transform=transforms.ToTensor())
# 加载数据: 分批次,每批256个数据
batch_size = 32
train_loader = DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)
test_loader = DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False)
# start time
start_time = time.time()
# 创建模型
model = LeNet()
# 模型放入GPU中
# device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# model = model.to(device)
# 定义损失函数
loss_func = nn.CrossEntropyLoss()
loss_list = [] # 用来存储损失值
# 定义优化器
SGD = optim.SGD(params=model.parameters(),lr=0.001,momentum=0.9)
# 训练指定次数
for i in range(10):
    loss_temp = 0 # 定义一个损失值,用来打印查看
    # 其中j是迭代次数,data和label都是批量的,每批32个
    for j,(batch_data,batch_label) in enumerate(train_loader):
        # 启用GPU
        # batch_data,batch_label = batch_data.cuda(),batch_label.cuda()
        # 清空梯度
        SGD.zero_grad()
        # 模型训练
        prediction = model(batch_data)
        # 计算损失
        loss = loss_func(prediction,batch_label)
        loss_temp += loss
        # BP算法
        loss.backward()
        # 更新梯度
        SGD.step()
        if (j + 1) % 200 == 0:
            print('第%d次训练,第%d批次,损失值: %.3f' % (i + 1, j + 1, loss_temp / 200))
            loss_temp = 0
# end_time
end_time = time.time()
print('训练花了: %d s' % int((end_time-start_time)))
# 使用GPU: 训练花了: 124 s
# 不适用GPU:训练花了: 160 s
# 测试
correct = 0
for batch_data,batch_label in test_loader:
    batch_data, batch_label = batch_data.cuda(), batch_label.cuda()
    prediction = model(batch_data)
    predicted = torch.max(prediction.data, 1)[1]
    correct += (predicted == batch_label).sum()
print('准确率: %.2f %%' % (100 * correct / 10000)) # 因为总共10000个测试数据
  • 10
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
PyTorch是一个用于深度学习的开源Python库,它提供了丰富的工具和函数,可以简化神经网络模型的实现。Transformers是一种用于自然语言处理的深度学习模型,其基本方法是通过自注意力机制来实现文本序列的编码和解码。下面将手把手你如何使用PyTorch实现Transformers。 1. 首先,安装PyTorch库。可以通过pip命令来安装:`pip install torch`。 2. 导入必要的库。在代码的开头,导入PyTorch和Transformers相关的库: ``` import torch from transformers import BertModel, BertTokenizer ``` 3. 加载预训练的Transformers模型和分词器。Transformers库提供了一些预训练的模型,可以从Hugging Face的模型库中下载。我们使用Bert模型作为例子: ``` model_name = 'bert-base-uncased' model = BertModel.from_pretrained(model_name) tokenizer = BertTokenizer.from_pretrained(model_name) ``` 4. 输入编码。将文本输入编码成模型所需的格式。使用分词器对文本进行分词,并将分词后的结果转化为模型所需的编码格式: ``` text = "I love PyTorch" tokens = tokenizer.tokenize(text) input_ids = tokenizer.convert_tokens_to_ids(tokens) input_tensor = torch.tensor([input_ids]) ``` 5. 模型前向计算。将输入数据传入模型进行前向计算: ``` model_output = model(input_tensor) ``` 6. 获取特征表示。从模型输出中获取特征表示。对于Bert模型,可以获取词嵌入和每个词的隐藏状态: ``` embeddings = model_output[0] # 词嵌入 hidden_states = model_output[2] # 每个词的隐藏状态 ``` 7. 解码输出。根据任务需求,对模型的输出进行解码。例如,可以使用BertPooler层获取整个句子的语义表示: ``` pooler_output = model.pooler(hidden_states[-1]) # BertPooler层 ``` 以上就是用PyTorch实现Transformers的基本步骤。根据具体任务的不同,可以对模型进行进一步的调整和优化。希望这个手把手程能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值