1. 手写数字图片数据集
机器学习需要从数据中间学习,首先需要采集大量的真实样本数据。以手写的数字图片识别为例,如下图所示,需要收集大量的由真人书写的0~9的数字图片,为了便于存储和计算,一般把收集的原始图片缩放到某个固定的大小,比如224个像素的行和224个像素的列(224×224),这张图片将作为输入数据x。同时,需要给每一张图片标注一个标签,它将作为图片的真实值y,这个标签表明这张图片属于哪一个具体的类别,一般通过映射方式将类别名一一对应到从0开始编号的数字,对于手写数字图片识别问题,用数字的0~9来表示类别名字为0~9的图片。
然后来看一下图片的表示方法。一张图片包含了h行,w列,每个位置保存了像素值,像素值一般使用0~255的整形数值来表达颜色强度信息,例如0表示强度最低,255表示强度最高。如果是彩色图片,则每个像素点包含了R, G, B三个通道的强度信息,分别代表红色通道、绿色通道、蓝色通道的颜色强度,所以与灰度图片不同,它的每个像素点使用一个1维、长度为3的向量来表示,向量的3个元素依次代表了当前像素值上面的R, G, B颜色强值,因此彩色图片需要保存为形状是[h, w, 3]的张量。如果是灰度图片,则使用一个数值来表示灰度强度,因此它只需要一个形状为[h, w]的二维矩阵来表示一张图片信息。
这里利用TensorFlow自动在线下载MNIST数据集,并转换为Numpy数组格式:
load_data()函数返回两个元祖(tuple)对象,第一个是训练集,第二个是测试集,每个tuple的第一个元素是多个训练图片数据X,第二个元素是训练图片对应的类别数字Y。其中训练集X的大小为(60000,28,28),代表了60000个样本,每个样本由28行、28列构成,由于是灰度图片,故没有RGB通道;训练集Y的大小为(60000,),代表了这60000个样本的标签数字,每个样本标签用一个0~9的数字表示。测试集X的大小为(10000,28,28),代表了10000章测试图片,Y的大小为(10000,)。
从TensorFlow中加载MNIST数据图片,数值的范围在[0,255]之间。在机器学习中间,一般希望数据的范围在0周围小范围分布。通过预处理步骤,把[0,255]像素范围归一化到[0,1.]区间,再缩放到[-1,1]区间,从而有利于模型的训练。
每一张图片的计算流程是通用的,在计算的过程中可以一次进行多张图片的计算,充分利用CPU或GPU的并行计算能力。一张图片用shape为[h,w]的矩阵来表示,对于多张图片来说,在前面添加一个数量维度(Dimension),使用shape为[b,h,w]的张量来表示,其中的b代表了batch size(批量);多张彩色图片可以使用shape为[b,h,w,c]的张量来表示,其中的c表示通道数量(Channel),彩色图片c=3。通过TensorFlow的Dataset对象可以方便完成模型的批量训练,只需要调用batch()函数即可构建带batch功能的数据集对象。
2. 模型构建
在回归问题讨论的生物神经元结构。把一组长度为的输入向量
简化为单输入标量x,模型可以表达成y=x*w+b。如果是多输入、单输出的模型结构的话,需要借助于向量形式:
更一般地,通过组合多个多输入、单输出的神经元模型,可以拼成一个多输入、多输出的模型:
y=Wx+b
其中,
对于多输出节点、批量训练方式,将模型写成张量形式:
Y=X@W+b
其中,,
表示输入节点数,
表示输出节点数;@符号表示矩阵相乘。
考虑2个样本,输入特征长度,输出特征长度
的模型:
其中上标表示样本索引号,下标表示样本向量的元素。对应模型结构图为
可以看到,通过张量形式表达网络结构,更加简洁清晰,同时也可充分利用张量计算的并行加速能力。
对于输出标签,前面已经介绍了数字编码,它可以用一个数字来表示便签消息,例如数字1表示猫,数字3表示鱼等。但是数字编码一个最大的问题是,数字之间存在天然的大小关系。如果1,2,3分别对应的标签是猫、狗、鱼,它们之间并没有大小关系,所以采用数字编码的时候会迫使模型区学习这种不要的约束。
可以将输出设置为个输出节点的向量,
与类别数相同,让第
个输出值表示当前样本属于类别i的概率P。如果物体属于第i类的话,那么索引为i的位置上设置为1,其他位置设置为0,把这种编码方式叫做one-hot编码(独热编码)。
手写数字图片的总类别有10种,即输出节点数为10,那么对于某个样本,假设它属于类别i,即图片的中数字为i,只需要一个长度为10的向量y,向量y的索引号为i的元素设置为1,其他位为0。比如图片0的One-hot编码为[1,0,0,...,0]。One-hot编码是非常稀疏的,相对于数字编码来说,占用较多的存储空间,所以一般在存储时还是采用数字编码,在计算时,根据需要来把数字编码转换成One-hot编码:
现在回到手写数字图片识别任务,输入是一张打平后的图片向量,输出是一个长度为10的向量
,图片的真实标签y经过one-hot编码后变成长度为10的非0即1的稀疏向量。预测模型采用多输入,多输出的线性模型
,其中模型的输出记为输入的预测值o,希望o越接近真实标签y越好。把输入经过一次(线性)变换叫做一层网络。
3. 误差计算
对于分类问题来说,目标是最大化某个性能指标,比如准确率acc,但是把准确率当做损失函数去优化时,会发现是不可导的,无法利用梯度下降算法优化网络参数。一般的做法是,设立一个平滑可导的代理目标函数,比如优化模型的输出o与One-hot编码后的真实标签y之间的距离,通过优化代理目标函数得到的模型,一般在测试性能上也能有良好的表现。因此,相对回归问题而言,分类问题的优化目标函数和评价目标函数是不一致的。模型的训练目标是通过优化损失函数
来找到最优数值解W*, b*:
对于分类问题的误差计算来说,更常见的是采用交叉熵损失函数,而不是采用回归问题中介绍的均方差损失函数。
4. 真的解决了吗
- 线性模型
- 表达能力
目前所采用的多神经元模型仍是线性模型,表达能力偏弱。
5. 非线性模型
可以给线性模型嵌套一个非线性函数,即可将其转换为非线性函数。把这个非线性函数称为激活函数(Activation function),用σ表示:
o=σ(Wx+b)
这里的σ代表了某个具体的非线性激活函数,比如Sigmoid函数,ReLU函数。
6. 表达能力
针对于模型的表达能力偏弱的问题,可以通过重复堆叠多次变换来增加其表达能力:
把第一层神经元的输出值作为第二层神经元模型的输入,把第二层神经元的输出作为第三层神经元的输入,最后一层神经元的输出作为模型的输出o。
网络结构如下图所示。
7. 优化方法
对于仅一层的网络模型,如线性回归的模型,可以直接推导出和
的表达式,然后直接计算每一步的梯度,根据梯度更新法则循环更新w,b参数即可。但是,当网络层数增加、数据特征长度增大、添加复杂的非线性函数之后,模型的表达式将变得非常复杂,很难手动推导出梯度的计算公式;而且一旦网络结构发生变动,网络的函数模型也随之发生改变,依赖手工去计算梯度的方式显然不可行。
这个时候,就可以借助于自动求导(Autograd)技术,深度学习框架在计算函数的损失函数的过程中,会记录模型的计算图模型,并自动完成任意参数的偏导分
的计算,用户只需要构建出网络结构,梯度将自动完成计算和更新。
8. 手写数字图片识别体验
网络搭建
对于第一层模型来说,接受的输入,输出
设计为长度为256的向量。使用TensorFlow的Sequential容器可以非常方面地搭建多层的网络。对于3层网络:
快速完成3层网络的搭建,第1层的输出节点数设计为256,第2层设计为128,输出层节点数设计为10。直接调用这个模型对象model(x)就可以返回模型最后一层的输出o。
模型训练
得到模型输出o后,通过MSE损失函数计算当前的误差