Jammy@Jetson Orin - Tensorflow & Keras Get Started: 002 Implementing an MLP in TensorFlow & Keras
1. 源由
直到现在为止,还没有接触使用神经网络解决图像问题。接下来,随着时代的步伐(早期神经网络算法),来研读以下第一个使用神经网络算法解决手写阿拉伯数字转换为计算机量化数据的问题。
这个有限图像分类需求,将运用前馈多层感知器(MLP)网络对MNIST数据集中的手写数字进行分类。
MLP并不是处理图像数据的首选方法,但这是一个很好的例子,可以介绍一些新概念。MNIST手写数字数据集已经包含在Tensorflow中,可以轻松导入和加载,正如我们将在下面看到的。使用这个数据集和一个简单的前馈网络,我们将演示如何处理图像数据并构建一个分类数字[0,9]的网络的一种方法。
2. 举例 - 手写数字
2.1 获取MNIST手写图像数据
MNIST数据集包含70,000张图像,其中60,000张用于训练,10,000张用于测试。
- 50000张 训练
- 10000张 验证
- 10000张 测试
- 单张图像是28x28像素
2.2 展示手写图像
2.3 数据预处理
将图像数据转换为可以处理的特征集的一种方法是将2D数组展平为1D数组。因此,28x28的输入图像变成了包含784个特征的1D数组。还将像素强度标准化为范围[0, 1]内。这在处理图像数据时非常常见,有助于模型更有效地训练。
Fashion MNIST数据集本身包含整数标签,我们可以通过加载数据集并打印一些标签来验证这一点,如下面代码单元格的输出所示。这种类型的标签编码称为整数编码,因为使用唯一的整数来编码类别(字符串)标签。
One-hot encoding 是一种将分类标签表示为独热编码向量的技术。因此,我们可以使用Keras中的to_categorical()函数将类别标签表示为二进制向量,而不是将类别标签表示为唯一的整数,这作为预处理步骤。在这种情况下,每个标签被转换为一个二进制向量,其中向量的长度等于类别的数量。所有条目都设置为零,除了对应于整数标签的元素。
2.4 分析业务逻辑
关于这个架构有几点需要注意:
- 一个输入层、两个隐藏层和一个输出层。
- 输入数据:图像输入数据被预处理(展平)成一个二维数组[28x28]到一个长度为[784x1]的一维向量,其中该输入向量的元素是标准化的像素强度。网络的输入有时被称为输入“层”,但它在技术上不是网络中的一层,因为它没有与之相关联的可训练参数。
- 隐藏层:我们有两个隐藏层,包含一些神经元(需要我们指定)。这些层中的每个神经元都有一个非线性激活函数(例如ReLU、Sigmoid等)。
- 输出层:现在我们有十个神经元在输出层,代表十个不同的类别(数字:0到9)。
- 全连接层:网络中的所有层都是全连接的,意味着给定层中的每个神经元与前一层中的每个神经元都是完全连接的(或密集的)。与每一层相关联的权重用粗体表示,以表明这些是包含网络中相邻层之间所有连接的权重的矩阵。
- Softmax函数:输出层中的每个神经元的值通过softmax函数传递,以生成数据集中每个十个数字的概率分数。
- 网络输出:网络输出(𝑦’)是一个长度为十的向量,包含每个输出神经元的概率。预测类标签只需通过argmax函数将(𝑦’)传递来确定预测标签的索引。
- 损失函数:所使用的损失函数是交叉熵损失,通常是分类问题的首选损失函数。它是从地面实况标签(𝑦)和网络输出概率(𝑦’)计算得到的。注意,𝑦和𝑦’都是长度等于类别数的向量。
虽然这个图看起来与我们之前学习的单层感知器网络有很大不同,但在训练和预测过程中发生的处理方面,它在基本上是非常相似的。我们仍然根据网络的预测输出和输入的地面实况标签计算损失。反向传播用于计算损失相对于网络中权重的梯度。一个优化器(实现梯度下降)被用来更新神经网络中的权重。
2.5 Keras建模
使用Keras来定义模型架构,
- 其中包含两个密集层(每个层有128个神经元)和一个具有10个神经元的单个输出层。
- 输出层中的每个神经元对应于数据集中的一个类别标签(0到9),
- 其中每个神经元的输出表示输入图像对应于与该神经元相关联的类别的概率。
- 第一个隐藏层的输入形状为[784,1],因为28×28的图像被展平为长度为784的向量。
- 每个隐藏层中的神经元具有称为“ReLU”的激活函数,它代表修正线性单元。
- 输出层中的神经元通过“softmax”函数,该函数转换(归一化)原始输出,将其解释为概率。
:1:如果来自第5个神经元的输出是0.87,那么这意味着输入图像是4的概率为87%(因为第一个类是0,第5个神经元代表数字4)。
注2:softmax函数将网络的输出归一化并将其转换为概率。交叉熵损失函数计算预测输出概率与地面实况标签之间的损失。预测输出概率与目标标签的差距越大,损失就越高。
2.5.1 定义模型
2.5.2 编译模型
- 优化器:在Keras中使用RMSProp优化器。
- 损失函数:分类问题的首选损失函数是交叉熵。但是,根据标签的编码方式,需要指定适当形式的交叉熵损失函数。
- 如果标签是One-hot encoding,则应将损失函数指定为categorical_crossentropy;
- 如果标签是整数编码的,则应使用sparse_categorical_crossentropy。
- 在执行二元分类时,应将binary_crossentropy作为损失函数。
注:由于本示例中使用了独热编码,因此我们将指定损失函数为categorical_crossentropy。
2.5.3 训练模型
- batch_size批量的数据集
- epochs迭代的次数
- validation_split训练集分割,训练集/验证集
2.5.4 检查迭代损失渐进曲线
在下面的第一个图中,我们可以看到训练和验证的损失最初都开始下降,但大约在五个时期之后,验证损失开始稳步增加,而训练损失继续下降,并且在20个时期之后几乎达到零。这种行为表明模型在训练数据集上表现出色,但在从未见过的验证数据集中呈现图像时,效果不佳。我们在第二个图中看到了类似的趋势,训练准确率几乎达到了100%,而验证准确率接近98%。即使98%已经相当不错了,模型还是过度拟合了训练数据。
这是机器学习中一个非常常见的问题,有几种技术可以用来减轻这个问题。
2.6 使用模型进行预测
预测结果:digit7的概率为1.0,其他都远小于1,因此预测结果为手写7。
3. 准确性评估 - 混淆矩阵
从这张表格可以看出,准确率并不是100%,与前面损失图和准确图一致,存在预测月真实值不一致的情况,也就是所谓的98%准确率。
4. 总结
在本文中,使用 Keras 实现多层感知器前馈神经网络进行图像分类的简单方法。虽然,这个方法并不好,但是是我们接触多因素图像进行神经网络算法学习、预测的第一个例子。
总的来说,学会这个工具能够从概率的角度来使用这种方法来解决问题。深入研究专业问题的时候,需要更多的了解:
- 专业知识与建模的关系
- 损失函数的选择
- 激活函数的选择
- 特征值之间关系以及正则化函数的选择
- etc. //也许还有很多,总的来说感触颇深,但是这个算法看似并不那么神奇,也许并没有所谓的magic
测试代码:002_Keras-MLP-MNIST-Classification
5. 参考资料
【1】Jammy@Jetson Orin - Tensorflow & Keras Get Started
6. 补充 - seaborn
有些朋友在运行的时候,可能会遇到以下错误:
解决方法:安装seaborn
$ pip install seaborn