flyEn'blog

通过MNIST场景实现理解机器学习

本文为自己整理的技术分享。

概念

机器学习是在不直接针对问题进行编程的情况下,赋予计算机学习能力的一个研究领域。

在维基百科,定义如下

1
2
3
4
5
机器学习有下面几种定义: 
机器学习是一门人工智能的科学,该领域的主要研究对象是人工智能,特别是如何在经验学习中改善具体算法的性能。
机器学习是对能通过经验自动改进的计算机算法的研究。
机器学习是用数据或以往的经验,以此优化计算机程序的性能标准。
一种经常引用的英文定义是:A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.

西瓜书中:

1
机器学习正是这样一门学科,它致力于研究如何通过计算的手段,利用经验来改善系统自身的性能。在计算机系统中,“经验”通常以“数据”形式存在,因此,机器学习所研究的主要内容,是关于在计算机上从数据中产生“模型”(model)的算法,即“学习算法”(learning algorithm)

数据科学入门:

1
创建并使用那些由学习数据而得出的模型。在其他语境中,也可以被叫作预测建模或者数据挖掘。

归功于机器学习,今天才会有强大的垃圾邮件过滤、方便的文本和语音识别、可靠的网络搜索引擎、具有挑战性的下棋程序,并有希望在不久的将来可以享受安全和高效的自动驾驶。

分类

  • 有监督学习。它从有标记的训练数据中推导出预测函数。有标记的训练数据是指每个训练实例都包括输入和期望的输出。给定数据,预测标签
  • 无监督学习。它从无标记的训练数据中推断结论。最典型的无监督学习就是聚类分析,它可以在探索性数据分析阶段用于发现隐藏的模式或者对数据进行分组。给定数据,寻找隐藏的结构
  • 强化学习是机器学习的另一个领域。它关注的是软件代理如何在一个环境中采取行动以便最大化某种累积的回报。给定数据,学习如何选择一系列行动,以最大化长期收益

image-20201103103745211

image-20201103103811167

经典算法

  1. 线性回归
  2. 逻辑(Logistic)回归
  3. Bagging 和随机森林
  4. K近邻算法
  5. 朴素贝叶斯
  6. 支持向量机(SVM)
  7. ……

场景

MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片:

image-20201103103837584

它也包含每一张图片对应的标签,告诉我们这个是数字几。比如,上面这四张图片的标签分别是5,0,4,1。 接下来我们将训练一个机器学习模型用于预测图片里面的数字。

MNIST数据集的官网是Yann LeCun’s website。在这里,我们提供了一份python源代码用于自动下载和安装这个数据集。

1
2
import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

下载下来的数据集被分成两部分:60000行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test)。

这样的切分很重要,在机器学习模型设计时必须有一个单独的测试数据集不用于训练而是用来评估这个模型的性能,从而更加容易把设计的模型推广到其他数据集上(泛化)。

正如前面提到的一样,每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。 我们把这些图片设为“xs”,把这些标签设为“ys”。训练数据集和测试数据集都包含xs和ys。

每一张图片包含28X28个像素点。我们可以用一个数字数组来表示这张图片:

image-20201103103910372

我们把这个数组展开成一个向量,长度是 28x28 = 784。这样可以看做MNIST数据集的图片就是在784维向量空间里面的点。 在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。

image-20201103103946925

相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。 我们使标签数据是”one-hot vectors”。 一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。 数字n将表示成一个只有在第n维度(从0开始)数字为1的10维向量。比如,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。因此, mnist.train.labels 是一个 [60000, 10] 的数字矩阵。

image-20201103104009904

实现原理

softmax回归

Softmax Regression是解决多分类问题的算法(Softmax是Logistic的推广,Logistic算法一般用于二分法)。

我们知道MNIST的每一张图片都表示一个数字,从0到9。我们希望得到给定图片代表每个数字的概率。比如说,我们的模型可能推测一张包含9的图片代表数字9的概率是80%但是判断它是8的概率是5%(因为8和9都有上半部分的小圆),然后给予它代表其他数字的概率更小的值。

这是一个使用softmax回归(softmax regression)模型的经典案例。softmax模型可以用来给不同的对象分配概率。即使在之后,我们训练更加精细的模型时,最后一步也需要用softmax来分配概率。

为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片像素值进行加权求和。如果这个像素具有很强的证据说明这张图片不属于该类,那么相应的权值为负数,相反如果这个像素拥有有利的证据支持这张图片属于这个类,那么权值是正数。

下面的图片显示了一个模型学习到的图片上每个像素对于特定数字类的权值。红色代表负数权值,蓝色代表正数权值。

image-20201103104138202

我们也需要加入一个额外的偏置量(bias),因为输入往往会带有一些无关的干扰量。因此对于给定的输入图片 x 它代表的是数字 i 的证据可以表示为 image-20201103104159931

其中W代表权重,b代表数字i类的偏置量,j 代表给定图片x的像素索引用于像素求和。然后用softmax函数可以把这些证据转换成概率y:

image-20201103104217534

这里的softmax可以看成是一个激励(activation)函数或者链接(link)函数,把我们定义的线性函数的输出转换成我们想要的格式,也就是关于10个数字类的概率分布。

因此,给定一张图片,它对于每一个数字的吻合度可以被softmax函数转换成为一个概率值。softmax函数可以定义为:

image-20201103104238687

对于softmax回归模型可以用下面的图解释:

image-20201103104302229

如果把它写成一个等式,我们可以得到:

image-20201103104317526

我们也可以用向量表示这个计算过程:用矩阵乘法和向量相加。这有助于提高计算效率。(也是一种更有效的思考方式)

image-20201103104332103

更进一步,可以写成更加紧凑的方式:

image-20201103104344661

具体例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 读入MNIST数据
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 创建x,x是一个占位符,代表待识别的图片
x = tf.placeholder(tf.float32, [None, 784])

# W是Softmax模型的参数,将一个784维的输入转换为一个10维的输出
# 在TensorFlow中,变量的参数用tf.Variable表示
W = tf.Variable(tf.zeros([784, 10]))
# b是又一个Softmax模型的参数,我们一般叫做“偏置项”(bias)。
b = tf.Variable(tf.zeros([10]))

# y=softmax(Wx + b),y表示模型的输出
y = tf.nn.softmax(tf.matmul(x, W) + b)

# y_是实际的图像标签,同样以占位符表示。
y_ = tf.placeholder(tf.float32, [None, 10])

# 至此,我们得到了两个重要的Tensor:y和y_。
# y是模型的输出,y_是实际的图像标签,不要忘了y_是独热表示的
# 下面我们就会根据y和y_构造损失

# 根据y, y_构造交叉熵损失
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y)))

# 有了损失,我们就可以用随机梯度下降针对模型的参数(W和b)进行优化
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

# 创建一个Session。只有在Session中才能运行优化步骤train_step。
sess = tf.InteractiveSession()
# 运行之前必须要初始化所有变量,分配内存。
tf.global_variables_initializer().run()
print('start training...')

# 进行1000步梯度下降
for _ in range(1000):
# 在mnist.train中取100个训练数据
# batch_xs是形状为(100, 784)的图像数据,batch_ys是形如(100, 10)的实际标签
# batch_xs, batch_ys对应着两个占位符x和y_
batch_xs, batch_ys = mnist.train.next_batch(100)
# 在Session中运行train_step,运行时要传入占位符的值
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

# 正确的预测结果
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# 计算预测准确率,它们都是Tensor
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 在Session中运行Tensor可以得到Tensor的值
# 这里是获取最终模型的正确率
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) # 0.9185

训练模型

代价函数:

  1. 交叉熵损失函数(例子中:平均交叉熵)

image-20201103104426182

y 是我们预测的概率分布, y’ 是实际的分布(我们输入的one-hot vector)。

熵:被用于描述一个系统中的不确定性。

  1. 最小二乘法

image-20201103104441003

优化算法:

梯度下降算法:最小化损失函数。

image-20201103104459498

总结

实现的整个流程:

  • 定义算法公式,建模。
  • 定义损失 loss ,选定优化器,并指定优化器优化 loss。 迭代地对数据进行训练。
  • 在测试集或验证集上对准确率进行评测。

参考资料:

  1. scikit-learn:是基于Python语言的机器学习免费工具库。(中文)
  2. TensorFlow中文社区
Fork me on GitHub