![a4d8172361331e7a3dafe73fcf909d9c.png](https://i-blog.csdnimg.cn/blog_migrate/83e0cef5653bcb75fc7f203a28ef6216.jpeg)
机器学习问题可以分为回归问题和分类问题,回归问题已经在线性回归讲过,本文学习分类问题。分类问题跟回归问题有明显的区别,回归问题是连续的数值,而分类问题是离散的类别,比如将性别分为[男,女],将图片分为[猫,狗,兔]等。
学习分类问题用的最多的示例是LeCun的MNIST手写数字识别。
MNIST手写数字数据集介绍
MNIST手写数字数据集包含60000张训练图片和10000张测试图片,这些图片是从0~9的手写数字,分辨率为28*28,大致是下面这个样子:
![afe861d5350c14e027a0b1bc75319f8d.png](https://i-blog.csdnimg.cn/blog_migrate/67a2982269ae7ab2e6f63707789f9262.jpeg)
该数据集广泛适用于各大机器学习平台入门程序,有非常成熟的处理方案。不过为了深入理解数据格式,我们先来手动下载并读取这些数据,数据格式详见。
手动读取MINST数据集:
import
输出:
![79a45d927ef4c245bc4817781db78085.png](https://i-blog.csdnimg.cn/blog_migrate/0517fe7f3ffdc9e738639be43ac34b78.jpeg)
分类任务的误差函数
前面说过回归型任务可以用MSE(Mean squared error)均方误差作为loss函数,分类任务的误差如何表示呢?比如手写数字识别任务需要将图片分成10类,既0~9每个数字一类,很容易想到的一个误差衡量方式是:如果分类正确误差是0,如果分类不正确误差是1。这样做人很容易理解,但对计算机来说作用不大,因为并没法表示出分类不正确的程度。
通常使用交叉熵(Cross Entropy)表示分类问题的loss函数,其定义如下:
-
表示类别的个数,比如数字识别的类别是个数是10个
-
表示真实的类别,如果属于当前类别
,则为值1,否则为值0
-
表示预测输出属于类别
的概率,如果有10个类别,则根据总概率为1的约束一定有:
特别的如果只有两个类别,比如性别[男,女]这种二分类问题,上述公式可以简写成:
TensorFlow实现手写数字识别(一)
可以用神经网络来做手写数字识别,一个只有一层全连接层的网络拓扑如下:
![9307266ef54dfdb72b1441576d30d7d7.png](https://i-blog.csdnimg.cn/blog_migrate/39300ccc88c2f0137622a8a820b0b584.jpeg)
注:图上全连接的线条没有全部画出,两层之间任意两点之间都有一条连线。
网络非常的简单,784个像素输入通过一个全连接层跟10个输出相连。网络所需参数个数为权重 784 * 10,外加10个bias。用公式表达就是:
- 其中
表示训练数据向量,对于MNIST就是(60000, 784)维的向量
-
是权重矩阵,维度是(784, 10)
-
是bias向量,维度是(10)
- activation是激活函数,他是一个将线性空间向非线性空间映射的函数,用以增强网络的表达能力。常见的激活函数有relu, sigmoid, tanh等。
-
表示预测属于各个类别的概率,最终将采用概率最大的那个类别作为最终类别。
为什么需要激活函数?
多个线性函数的组合仍然是线性函数,激活函数将线性空间映射到非线性空间,增强网络的表达能力。比如任意多个一元线性回归的加和是多元线性回归,无法表示抛物线、正弦曲线等非线性数据。而多个线性单元经过激活函数之后再做组合,理论上可以拟合任意复杂的公式。详见 https:// zhuanlan.zhihu.com/p/16 5849993
![69d3e7831f42feda1fe09947250b8cba.png](https://i-blog.csdnimg.cn/blog_migrate/7f726ca530b902c4185b7f2d0a639a23.png)
上述神经网络拓扑对应的代码实现如下,该版本的代码尽可能的展现细节,除了微分计算采用了tf自动求导其余的操作均手工完成,对理解原理很有帮助。
import
输出结果:
![8d78f28d71c5d2648c843ebd6d1de5dd.png](https://i-blog.csdnimg.cn/blog_migrate/146cba64eb3742b15bba14e7691d253f.jpeg)
![8bf2a647bbdedbeda320a0f1b4fcec0c.png](https://i-blog.csdnimg.cn/blog_migrate/18373e470c9906fbb3b88e31cacd8e60.jpeg)
这么简单的网络经过1000轮的训练,在测试集上取得了77.42%的识别正确率,且到后期随着训练轮数增加预测效果也不再增加,算是差强人意吧。
上图中看到训练误差在逐渐降低,预测效果在逐渐升高但有些波动,这根learning_rate的设置有关系,比如学习率设置过大导致训练效果来回抖动。学习率如何设置是一个单独的话题,感兴趣的话可以了解一下“优化器”。
TensorFlow实现手写数字识别(二)
上述网络只有一层表现力较弱,识别正确率只有77.42%,通过增加网络层数可以提高网络的表达能力,两层网络结构如下:
![70c99bd231d2395f5f8308639be95bf2.png](https://i-blog.csdnimg.cn/blog_migrate/f977b8df9a627d2347762b7934898e5c.jpeg)
注:图上全连接的线条没有全部画出,两层之间任意两点之间都有一条连线。
代码如下:
import
执行结果:
![581660a06d22c69f8200340f1a5ce52f.png](https://i-blog.csdnimg.cn/blog_migrate/5dc7a2f195583e18b85d57162eff302c.jpeg)
识别效果达到了82.16%,比一层网络要好一些。
keras实现手写数字识别
深层网络比浅层有更好的表现力,不过手写深层网络显得很麻烦,keras大大简化网络构造的复杂度。
keras是一套针对模型的的极简API,可以用几行代码写出复杂的网络结构,完全屏蔽实现细节(那是具体后端的工作,比如tensorflow)
使用keras编写两层网络的手写数字识别:
import
网络构建和训练超乎想象的简洁!最终训练效果如下:
注意:keras.datasets.mnist会从 https:// storage.googleapis.com/ tensorflow/tf-keras-datasets/mnist.npz 下载数据。可以手动下载这个数据并放在~/.keras/datasets,全路径名为 ~/.keras/datasets/mnist.npz
![a4a6abf3396acf2c861e89e0ed5875d5.png](https://i-blog.csdnimg.cn/blog_migrate/9e0bac64bfa02395ab3b9e2d0c49ea73.jpeg)
![20c505aaf0555e277a90f911e784adab.png](https://i-blog.csdnimg.cn/blog_migrate/d53e52f79b43da2a1be6d40362941326.jpeg)
最终识别效果95%,效果比手写的示例有大幅提升。
keras实现手写数字识别——卷积神经网络
针对图像处理有专门的神经网络结构——卷积神经网络。使用keras可以很容易的搭建卷积神经网络:
卷积神经网络是一种专门用于处理图像的特殊的神经网络,他在传统神经网络中加入“卷积”和“池化”层模拟人的大脑对视觉的处理方式,大幅提升图片的识别准确度。
import
输出:
![b740b315287d4ed4d64b47b7c84814f6.png](https://i-blog.csdnimg.cn/blog_migrate/69b70be21c237f8b7345b065959437dc.jpeg)
![c95c19dfe114b40f0c3f40e9f595ec8b.png](https://i-blog.csdnimg.cn/blog_migrate/b096cba32973012340bd2fb8e293726f.jpeg)
识别精度高达99%,卷积神经网络处理图像分类果然有奇效!
总结
本文首先对MNIST数据集进行了介绍,之后用多种方式实现了手写数字识别的神经网络,对理解神经网络原理很有帮助。Keras是编写神经网络的利器,用几行代码就能构建出复杂的网络模型,极大简化编码复杂度。另外简单介绍了激活函数、keras、卷积神经网络等概念。
如有收获请直接点赞~