«

卷积神经网络: 打破图像识别的瓶颈

时间:2023-2-27 23:34     作者:wen     分类: AI


在机器学习, 神经网络领域, 对于不想过多了解底层原理的初学者而言, 其实有一个应用层面一般意义上的经典"hello world", 那就是手写体识别, 因为其场景和问题都很简单明确, 更有经典的数据集mnist, 成为众多入门着必备的实践项目, mnist数据集的图片采用的是28*28的灰度图, 一行有28个像素点, 一共有28行, 每个像素用一个字节的无符号数表示他的等级, 如果是0, 那就是最暗, 纯黑色. 如果是一个字节最大值255, 那么就最亮, 纯白色. 如果是中间的值, 那就是介于两个之间的灰色. 我们通过让不同像素点的灰度值不同而达到显示的效果.

如果用键盘输入这个字符, 那么判断起来并没有任何难度, 因为精确的计算机在显示同一个字符的时候, 每一次每个像素点的灰度值都是一样的, 但人毕竟不同于精度但是呆板的计算机, 比如我们拿一支手写笔在这个2828的屏幕上手写数值5, 每一次都不太一样, 这时候也就没有什么确定的规则, 根据这些像素的灰度值判断是什么数字了. 换句话说, 这不再是一个适用于计算机机械逻辑做判断的问题, 我们需要用有一定容错能力的系统来做这件事情, 很明显, 神经网络是一个很好的选择, 我们已经知道如何搭建一个神经网络, 那现在唯一的问题就是如何把这些图片送入其中, 神经网络的输入是一个多维向量, 或者说一个数组, 而图片是一个方形的像素灰度集合, 没错, 我们把这些像素从头到尾一行一行的依次拉出来就好, mnist数据集每张图片的尺寸是2828, 说以这将拉出784哥像素, 每个像素都是一个灰度值, 所以这将形成一个784维的向量. 或者说一个有784个元素的数组.

我们自需要把mnist数据集中每个手写体图片, 都变成这样784哥元素的数组, 依次送入神经元网络进行训练就好, 最开始, 人们利用深度全连接神经网络, 取得了不错的效果, 但并不十分的好, 在机器学习的工作流程中, 我们在训练时使用的数据集称之为"训练集", 当然我们希望在训练集上的准确率很高, 这意味着模型拟合的效果很棒, 但我们会在训练之后, 在训练集数据之外拿一些新的数据进行预测, 看看在这些新的数据在模型上的准确率如何. 这些用来测试的样本数据称之为"测试集", 我们希望在测试集上的准确度也很高, 实际上我们考量一个模型的好坏的时候, 更倾向于他在测试集上的表现, 因为在测试集上高准确性意味着, 模型在遇到训练中没有遇到过新问题时, 也能很准确的进行预测, 换句话说: 这个模型有足够的泛化能力. 而模型在训练集和测试集上的不同表现, 也就导致了机器学习中三种常见现象:

  1. 如果在训练集上的准确率都很低, 那这个模型多半是废了, 这种现象称之为欠拟合. 可能是模型太过简单, 比如我们之前说过的用一个神经元试图拟合弯曲分布的数据, 这是行不通的.
  2. 如果在训练集上的准确率很高, 而且在测试集上的准确率也很高, 并且相差不大, 那说明这个模型通过训练, 拥有了很好的泛化能力去解决新问题.
  3. 如果在训练集上的准确率很高, 但是在测试集准确率出现了明显的下滑, 那说明这个模型的泛化能力不行, 也就很难推而广之的进行实际应用. 这种现象称之为"过拟合".
  4. 训练集准确率很低, 但测试集准确率很高, 从概率上来说, 这种见鬼的情况几乎不存在.

导致过拟合的原因有很多, 比如我们用一个过分复杂的模型, 去拟合一些实则比较简单的问题. 正是在训练集上追求过分精准的拟合, 导致模型在新问题中的表现反倒没有那么好, 因为模型不够泛化, 或者说没有很好的把握事物的主要矛盾. 解决神经网络中的过拟合现象也有很多方式. 比如调整网络结构. L2正则化, 节点失活(Dropout)正则化等.

我们的课程就不展开讲解这些比较琐碎的问题了, 有兴趣的同学可以仔细查阅相关文档学习.

mnist数据集有60000个训练集样本和10000哥测试集样本, 而人们发现, 在用全连接神经网络做mnist数据集识别, 以及其他的图像识别的时候, 尽管我们把网络堆叠的越来越深, 神经元也添加的越来越多, 也用尽了各种防止过拟合的方法, 但网络的泛化能力任然越来越难有所突破.

深度学习巨头人物之一的Lecun制作一个完整的表格, 罗列了机器学习各个领域在mnist数据集上的工作成果, 从线性分类器, KNN, 到Boosted Stumps, SVM, 直到神经网络和卷积神经网络, 判断标准就是测试集上的错误率, 就像我们说的模型只有在测试集上的正确率上去了, 才是有足够泛化能力好模型, 目前, 对于纯粹的(全连接)深度神经网络, 最好的效果是2010年一个6层的网络, 但是网络规模已经达到了每一层神经元数量分别是(784) 2500-2000-1500-1000-500-10 的巨大规模, 作者也毫不避讳的说到, 它们就是在用暴力, 它们有能加速训练的显卡, 这个网络规模和结果或许就是人们在mnist问题上, 用纯粹的全连接深度神经网络的极限了, 如果继续练死劲, 意义已然不大, 数量的堆积已经很难带来质量的提升, 此时神经网络的能力, 用时下流行的词汇就是: 内卷. 内卷现象在任何时候任何地方都是糟糕的, 打败神经网络能力陷入内卷的方式是另外一种"卷"法------ "卷积"

我们来看卷积神经网络在mnist数据集中的成果, 即使是早在1998年被提出的经典卷积神经网络LeNet-5, 也让测试集准确率达到了99.2%, 而2012更是把准确率提供到了99.77%, 所以为什么卷积神经网络会有这个好的效果?

首先, 你要知道卷积是怎么工作的, 对于人, 我们在识别一个图片的内容时候, 如果看到的是一个一个像素连接起来的数组, 那是很难做出判断的, 当然, 也不完全不可能, 比如死记硬背的记住这些数据的样子, 实际上我们人在识别一个图像的时候, 往往会不由自主的提取比如轮廓, 颜色, 花纹这样的元素, 也就是说图像作为一个二维物体, 在二维平面相邻像素之间存在关联的, 我们强行把它降到一维,也就破坏了这些关联, 失去了重要的特征. 这些特征对提高模型的泛化能力有很大的作用.

基于这种想法, 人们把卷积运算引入到神经网络, 卷积算法的过程是这样的, 一张2828的灰度图,当然2828还是有点大, 我们用一个88的灰度图举例, 我们知道对于灰度图, 这些像素点也就是0-255之间不同数字, 所以我们可以把它看作一个88的矩阵, 或者用计算机的话来说就是一个二维数组, 然后我们手动构建一个33的小矩阵, 然后从左上角开始, 把这个小的罩在这个大的上面, 把对应的元素相乘, 结果加到一起, 得到一个新值, 完成以后再把这个小的向右挪动一下, 同样和大的对应的元素相乘, 结果相加, 又得到一个值. 以此类推, 这就是图像的卷积操作, 而这些每次得到的新值, 按照位置排列后得到一个66的"新图片"当然一个像素的灰度值(的最大值)是一个字节最大值是255, 所以我们要把这个卷积结果显示成图片的话, 需要把超过255的像素点都处理成255, 这既是这个88的原始灰度图卷积之后的样子, 而这个33的小矩阵也就是"卷积核"(kernel). 有时候也被称之为"过滤器"(filter). 当然我个人喜欢叫卷积核, 因为听起来似乎格调高一点, 所以为什么这个卷积运算, 就可以提取出图像的诸如轮廓, 花纹颜色的特征呢?

我们可以用一个比较好理解的卷积核来简单说明这个问题, 利用不同卷积和可以提供图像的不同纹理, 实际上卷积是一种在图像处理领域很常见的操作, 现在很多图像处理软件中都会利用卷积运算给图片加上效果.

# mnist_recognizer.py
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import SGD
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical

(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 参数1 训练集的第一个样本数据
# cmap 绘图模式为:灰度图(gray)
plt.imshow(X_train[0], cmap='gray')
plt.show()

X_train = X_train.reshape(60000, 784) / 255.0
X_test = X_test.reshape(10000, 784) / 255.0

Y_train = to_categorical(Y_train, 10)
Y_test = to_categorical(Y_test, 10)

model = Sequential()
# units 当前层神经元数量
# activation 激活函数类型
# input_dim 输入数据特征维度
model.add(Dense(units=256, activation='relu', input_dim=784))
model.add(Dense(units=256, activation='relu'))
model.add(Dense(units=256, activation='relu'))
model.add(Dense(units=10, activation='softmax'))

# loss 代价函数 mean_squared_error (均方误差) categorical_crossentropy(多分类交叉熵代价函数)
# optimizer 优化器 sgd(随机梯度下降算法)
# metrics 评估标准 accuracy(准确度)
model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.05), metrics=['accuracy'])

# epochs 训练回合数
# batch_size 训练批数量
model.fit(X_train, Y_train, epochs=5000, batch_size=4096)

loss, accuracy = model.evaluate(X_test, Y_test)

print("loss" + str(loss))
print("accuracy" + str(accuracy))

标签: 人工智能