使用tiny-cnn实现MNIST手写数字识别教程
tiny-dnn 项目地址: https://gitcode.com/gh_mirrors/tin/tiny-dnn
前言
MNIST手写数字数据集是深度学习领域的"Hello World"项目,它包含了0-9的手写数字图片,每张图片都是28x28像素的灰度图。本文将介绍如何使用tiny-cnn这个轻量级深度学习框架来实现MNIST手写数字识别。
模型架构
我们采用经典的LeNet-5网络结构,这是Yann LeCun在1998年提出的卷积神经网络,专为手写数字识别设计。原始LeNet-5包含7层网络,包括卷积层、池化层和全连接层。
在tiny-cnn中实现的LeNet-5变体包含以下层次结构:
- 输入层(32x32,原始MNIST图片被填充到32x32)
- 卷积层C1(5x5卷积核,输出6个特征图)
- Tanh激活层
- 平均池化层S2(2x2)
- Tanh激活层
- 卷积层C3(5x5卷积核,输出16个特征图)
- Tanh激活层
- 平均池化层S4(2x2)
- Tanh激活层
- 卷积层C5(5x5卷积核,输出120个特征图)
- Tanh激活层
- 全连接层F6(120→10)
- Tanh激活层
代码实现详解
1. 网络构建
network<sequential> nn;
adagrad optimizer;
// 定义连接表(实现稀疏连接)
static const bool tbl [] = {
O, X, X, X, O, O, O, X, X, O, O, O, O, X, O, O,
// ...省略部分连接表数据
};
// 构建网络
nn << convolutional_layer(32, 32, 5, 1, 6, padding::valid, true, 1, 1)
<< tanh_layer(28, 28, 6)
<< average_pooling_layer(28, 28, 6, 2)
// ...其他网络层
<< fully_connected_layer(120, 10, true)
<< tanh_layer(10);
这里有几个关键点需要注意:
- 使用Adagrad优化器
- 连接表(tbl)实现了S2到C3层的稀疏连接
- 每层卷积后都使用Tanh激活函数
- 使用平均池化而非最大池化
2. 数据加载与预处理
parse_mnist_labels("train-labels.idx1-ubyte", &train_labels);
parse_mnist_images("train-images.idx3-ubyte", &train_images, -1.0, 1.0, 2, 2);
数据预处理包括:
- 将像素值从[0,255]归一化到[-1.0,1.0]
- 在图像四周添加2像素的边框(28x28→32x32)
- 自动解析MNIST的IDX格式文件
3. 训练过程
int minibatch_size = 10;
int num_epochs = 30;
// 定义回调函数
auto on_enumerate_epoch = [&](){
// 每个epoch结束时测试准确率
tiny_dnn::result res = nn.test(test_images, test_labels);
std::cout << res.num_success << "/" << res.num_total << std::endl;
};
// 开始训练
nn.train<mse>(optimizer, train_images, train_labels,
minibatch_size, num_epochs,
on_enumerate_minibatch, on_enumerate_epoch);
训练参数说明:
- 使用均方误差(MSE)作为损失函数
- 小批量大小为10
- 训练30个epoch
- 每个epoch结束后在测试集上评估性能
4. 模型保存与加载
// 保存模型
nn.save("LeNet-model");
// 加载模型
nn.load("LeNet-model");
tiny-cnn提供了简单的模型序列化功能,可以将整个网络结构和训练好的权重保存到单个文件中。
模型应用示例
训练完成后,我们可以使用模型进行手写数字识别:
// 加载图像并预处理
vec_t data;
convert_image("4.bmp", -1.0, 1.0, 32, 32, data);
// 预测
auto res = nn.predict(data);
// 输出top-3预测结果
for (int i = 0; i < 3; i++)
cout << scores[i].second << "," << scores[i].first << endl;
输出示例:
4,78.1403
7,33.5718
8,14.0017
这表示模型以78.14%的置信度认为输入图片是数字"4"。
可视化分析
tiny-cnn还提供了可视化工具,可以查看各层的输出和卷积核:
// 保存各层输出
for (size_t i = 0; i < nn.depth(); i++) {
auto out_img = nn[i]->output_to_image();
out_img.save("layer_" + std::to_string(i) + ".png");
}
// 保存第一层卷积核
auto weight = nn.at<convolutional_layer>(0).weight_to_image();
weight.save("weights.png");
这些可视化可以帮助我们理解网络是如何处理输入图像的,以及卷积核学习到了什么样的特征。
性能优化建议
- 学习率调整:Adagrad优化器的初始学习率可以根据批量大小进行调整
- 批量大小:适当增大批量大小可以提高训练速度,但可能影响模型性能
- 激活函数:可以尝试使用ReLU替代Tanh,可能会获得更好的效果
- 数据增强:对训练数据进行旋转、平移等变换可以提高模型泛化能力
总结
通过tiny-cnn实现MNIST手写数字识别,我们展示了如何:
- 构建卷积神经网络
- 加载和预处理数据
- 训练和评估模型
- 保存和加载模型
- 使用模型进行预测
- 可视化网络内部状态
tiny-cnn作为一个轻量级深度学习框架,非常适合学习和快速原型开发。虽然功能不如大型框架全面,但其简洁的API和易于理解的实现使其成为入门深度学习的优秀工具。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考