Pytorch学习(四)—— Lenet模型训练

本文使用pytorch训练了一个对CIFAR-10数据集进行分类的Lenet模型,并使用Visdom可视化整个训练过程。

数据集介绍

CIFAR-10数据集共有60000张彩色图像,图像像素为32x32,分10类,每类6000张图片。其中50000张用于训练,另外10000张用于测试。测试数据集对每一类图像随机抽取1000张。
在这里插入图片描述

模型训练

本次训练过程遵循以下几个步骤:

  1. 数据集加载、预处理
  2. 定义模型网络结构
  3. 定义损失函数和优化器
  4. 训练模型并更新权重
  5. 测试模型准确率

数据集加载、预处理

使用torchvision下载并处理数据集

import torch
import torch.nn as nn
import torch.nn.functional as func
from torch import optim
import torchvision
import torchvision.transforms as transforms     # 图像预处理包
from torchvision.transforms import ToPILImage   # Tensor -> image,便于可视化
from visdom import Visdom                       # 训练过程可视化

classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# 数据集预处理
# Compose组合多种预处理 [ToTensor, Normalize]
compose = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 下载训练数据集
train_set = torchvision.datasets.CIFAR10(root='./', train=True, download=True,transform=compose)
    
# 下载测试数据集
test_set = torchvision.datasets.CIFAR10(root='./', train=False, download=True, transform=compose)

# 加载数据集
# batch_size 为每次加载的样本个数
train_loader = torch.utils.data.DataLoader(train_set, batch_size=5, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=5, shuffle=True)

定义模型网络结构

定义网络时,需要继承nn.Moudle,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数__init__中,Lenet的网络结构这里不做过多分析,只进行网络实现:

# 定义网络结构
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()

        # 卷积层参数分别为:输入图片的通道数、输出通道数、卷积核size
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)

        # 全连接层: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = func.max_pool2d(func.relu(self.conv1(x)), 2)
        x = func.max_pool2d(func.relu(self.conv2(x)), 2)
        x = x.view(x.shape[0], -1)
        x = func.relu(self.fc1(x))
        x = func.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

# 查看模型结构
print(net)

#  Net权重信息
params = net.parameters()
for name, parameters in net.named_parameters():
    print(name, ":", parameters.size())
    

定义损失函数和优化器

nn实现了神经网络中的大多数损失函数,本文采用的损失函数为交叉熵损失函数nn.CrossEntropyLoss(),并设置学习率为0.001,
反向传播计算完所有参数的梯度后,还需要使用优化方法更新网络的权重和参数,本文采用随机梯度下降法(SGD)的更新策略。

loss = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

模型训练

模型训练流程基本类似,不断地执行:

  • 输入数据
  • 前向传播+反向传播
  • 更新参数

本文引入Visdom将训练过程可视化,Visdom的安装这里不赘述:

# visdom初始化
vis = Visdom()
vis.line([3.], [0.], win="Loss", opts=dict(title='train_loss'))

# 训练3个epoch,遍历完一遍数据集称为一个epoch
epoch = 3

# 模型训练
for each_epoch in range(epoch):
    train_loss = 0.0
    for i, data in enumerate(train_loader):
        # 输入数据
        inputs, labels = data

        # 梯度清零
        optimizer.zero_grad()

        # forward + backward
        out = net(inputs)
        l = loss(out, labels)
        l.backward()

        # 更新参数
        optimizer.step()

        train_loss += l.data.item()
        if i % 200 == 199:
            print('[%d, %5d] loss: %.3f' % (each_epoch+1, i+1, train_loss/i))
            vis.line([train_loss / i], [i + each_epoch * len(train_loader)], win='Loss', update='append')
            # train_loss = 0

从图中大致能看到损失值的变化情况:
在这里插入图片描述

测试模型准确性

使用验证集在得到的模型上进行验证,准确率为57.48%

# 统计准确率
correct = 0
total = 0
for inputs, labels in test_loader:
    outputs = net(inputs)
    correct += (outputs.argmax(dim=1) == labels).float().sum().item()
    total += labels.size(0)
    
print("accuracy: %f" % (correct / total))

结果:

... ...
[3,  9000] loss: 1.241
[3,  9200] loss: 1.240
[3,  9400] loss: 1.239
[3,  9600] loss: 1.238
[3,  9800] loss: 1.236
[3, 10000] loss: 1.235
Finished Training
accuracy: 0.574800
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个简单的 LeNet 的 C 代码实现: ``` #include <stdio.h> #include <stdlib.h> #include <math.h> #define WIDTH 28 #define HEIGHT 28 #define NUM_CLASSES 10 #define NUM_TRAIN 60000 #define NUM_TEST 10000 typedef struct conv_layer { int stride; int num_filters; int filter_size; int input_size; int output_size; double ***filters; double **biases; double **output; } conv_layer; typedef struct pool_layer { int stride; int pool_size; int input_size; int output_size; double **output; } pool_layer; typedef struct dense_layer { int num_neurons; double *weights; double *biases; double *output; } dense_layer; int reverse_int(int i) { unsigned char ch1, ch2, ch3, ch4; ch1 = i & 255; ch2 = (i >> 8) & 255; ch3 = (i >> 16) & 255; ch4 = (i >> 24) & 255; return ((int) ch1 << 24) + ((int) ch2 << 16) + ((int) ch3 << 8) + ch4; } void read_images(double **images, char *filename, int num_images) { FILE *file = fopen(filename, "rb"); int magic_number = 0; int num_images_read = 0; fread(&magic_number, sizeof(magic_number), 1, file); magic_number = reverse_int(magic_number); if (magic_number != 2051) { printf("Error: Invalid image file format\n"); exit(1); } fread(&num_images_read, sizeof(num_images_read), 1, file); num_images_read = reverse_int(num_images_read); if (num_images_read != num_images) { printf("Error: Invalid number of images\n"); exit(1); } int num_rows = 0, num_cols = 0; fread(&num_rows, sizeof(num_rows), 1, file); fread(&num_cols, sizeof(num_cols), 1, file); num_rows = reverse_int(num_rows); num_cols = reverse_int(num_cols); int i, j, k; for (i = 0; i < num_images; i++) { for (j = 0; j < num_rows; j++) { for (k = 0; k < num_cols; k++) { unsigned char pixel = 0; fread(&pixel, sizeof(pixel), 1, file); images[i][(j * num_cols) + k] = (double) pixel; } } } fclose(file); } void read_labels(double *labels, char *filename, int num_labels) { FILE *file = fopen(filename, "rb"); int magic_number = 0; int num_labels_read = 0; fread(&magic_number, sizeof(magic_number), 1, file); magic_number = reverse_int(magic_number); if (magic_number != 2049) { printf("Error: Invalid label file format\n"); exit(1); } fread(&num_labels_read, sizeof(num_labels_read), 1, file); num_labels_read = reverse_int(num_labels_read); if (num_labels_read != num_labels) { printf("Error: Invalid number of labels\n"); exit(1); } int i; ### 回答2: LeNet是一种经典的卷积神经网络模型,由Yann LeCun等人于1998年提出。它被广泛应用于手写数字识别任务,并为现代深度学习模型的发展打下了基础。 LeNet的C代码实现主要分为个部分:前向传播、反向传播、网络参数初始化和训练过程。 在前向传播部分,我们首先定义输入层的大小,将输入图像的数据在二维数组中表示。接着定义卷积层和池化层的参数,包括卷积核大小、池化核大小和步长等。依次进行卷积操作、ReLU激活函数和平均池化操作,直到得到最后一个卷积层的输出结果。最后将输出结果展平为一维向量,并定义全连接层的参数。通过全连接层将特征图与标签进行线性拟合得到预测结果。 在反向传播部分,我们首先定义损失函数,可以使用交叉熵损失函数来度量预测值和真实标签之间的差异。通过梯度下降算法,根据损失函数对各层参数求导更新参数值,从而不断优化网络的预测结果。 在网络参数初始化部分,我们通过随机初始化来为卷积核和全连接层的权重和偏置项赋初值。 在训练过程中,我们使用训练集的数据作为输入,通过前向传播计算出预测结果,再通过反向传播更新网络参数。不断重复这个过程,直到网络收敛或达到预定的训练轮数。 通过以上的C代码实现,我们可以构建一个能够识别手写数字的LeNet神经网络模型。该模型在计算机视觉领域有广泛的应用,如字符识别、人脸识别等。 ### 回答3: LeNet是由Yann LeCun在1998年提出的一种经典的卷积神经网络模型。它被广泛应用于手写数字识别任务,并为后期的深度学习研究奠定了基础。 LeNet的C代码实现是一个基于C语言的卷积神经网络模型,主要包括卷积层、平均池化层、全连接层和激活函数等组成。 首先,定义了一些宏定义,如图像尺寸、卷积核大小、滤波器个数等参数。 接着,声明了一些用于存储模型参数的矩阵,如卷积核参数、全连接层的权重和偏置等。 然后,实现了卷积层的操作。通过嵌套的循环实现了卷积操作,并使用了sigmoid激活函数对卷积结果进行非线性处理。 接着是平均池化层的操作。通过对每个2x2的池化窗口内的元素求平均值,减少了特征图的维度。 在卷积层和平均池化层之后,将特征图展开成一维向量,作为全连接层的输入。 然后,实现了全连接层的操作。通过矩阵相乘和加法操作,得到了全连接层的输出。 最后,使用了softmax函数对全连接层的输出进行处理,得到了模型的最终输出。 总的来说,LeNet的C代码实现了一个卷积神经网络模型,通过卷积操作、池化操作和全连接操作,对输入的图像进行特征提取和分类等任务。这段代码是LeNet模型的基本实现,对于理解卷积神经网络的原理和应用具有重要的意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值