导言
[TensorFlow从入门到精通] 01 简单线性模型(上)介绍了TensorFlow如何加载MNIST、定义数据维度、TensorFlow图、占位符变量和One-Hot Encoding等知识点.
作者: Magnus Erik Hvass Pedersen
编辑: Amusi
校稿: Amusi
前言
前些天,Amusi整理了一份重磅 | TensorFlow学习资料最全集锦,里面包含TensorFlow相关的书籍、视频和在线学习网站等资料。
其中,Amusi很喜欢 Magnus Erik Hvass Pedersen大佬制作的TensorFlow Tutorials 课程。该课程提供视频教程(在YouTube上,需要访问外国网站观看),并在github上发布了源码。为此,Amusi决定推出【TensorFlow从入门到精通】系列文章,详情请看TensorFlow从入门到精通 | 预告篇
Amusi 曾经也学过一点 TensorFlow的知识,但觉得不够系统,所以觉得按部就班的将TensorFlow Tutorials学习一遍。日常整理的翻译和笔记都会同步发布到 TensorFlow-From-Zero-to-One 上。点击文末的“阅读全文”,即可查看。
正文
介绍
本教程介绍了使用TensorFlow实现简单线性模型的workflow。在加载MNISIT(手写字符图像数据集)后,我们使用TensorFlow定义并优化一个简单的数学模型。然后绘制并讨论结果。
你应该熟悉基本的线性代数,Python,Jupyter Notebook编辑器。如果你对机器学习和分类有基本的了解,这也会帮助到你。
导入必要库(Imports)
1%matplotlib inline
2import matplotlib.pyplot as plt
3import tensorflow as tf
4import numpy as np
5from sklearn.metrics import confusion_matrix
6}
大家可能会对上述代码中的第一句 %matplotlib inline有所好奇,该语句怎么是这样的?其作用是什么?
这里Amusi引出两种介绍,第一种是根本性解释,第二种是适用于此代码的解释。
第一种解释[1]:
%matplotlib inline 是一个魔法函数(Magic Functions)。官方给出的定义是:IPython有一组预先定义好的所谓的魔法函数(Magic Functions),你可以通过命令行的语法形式来访问它们。可见“%matplotlib inline”就是模仿命令行来访问magic函数的在IPython中独有的形式。
magic函数分两种:一种是面向行的,另一种是面向单元型的。
行magic函数是用前缀“%”标注的,很像我们在系统中使用命令行时的形式,例如在Mac中就是你的用户名后面跟着“$”。“%”后面就是magic函数的参数了,但是它的参数是没有被写在括号或者引号中来传值的。
单元型magic函数是由两个“%%”做前缀的,它的参数不仅是当前“%%”行后面的内容,也包括了在当前行以下的行。
注意:既然是IPython的内置magic函数,那么在Pycharm中是不会支持的。
总结:%matplotlib inline 可以在Ipython编译器里直接使用,功能是可以内嵌绘图,并且可以省略掉plt.show()这一步。
第二种解释[2]:
%matplotlib inline比较奇怪,而且无论你是用哪个python的IDE如spyder或者pycharm,这个地方都会报错,显示是invalid syntax(无效语法)。那为什么代码里面还是会有这一句呢?原来是这样的。 %matplotlib作用
是在使用jupyter notebook 或者 jupyter qtconsole的时候,才会经常用到%matplotlib,也就是说那一份代码可能就是别人使用jupyter notebook 或者 jupyter qtconsole进行编辑的。关于jupyter notebook是什么,可以参考这个链接:[Jupyter Notebook介绍、安装及使用教程][1] 而%matplotlib具体作用是当你调用matplotlib.pyplot的绘图函数plot()进行绘图的时候,或者生成一个figure画布的时候,可以直接在你的python console里面生成图像。 而我们在spyder或者pycharm实际运行代码的时候,可以直接注释掉这一句,也是可以运行成功的。如下示例:
Amusi 总结:
为了在Jupyter Notebook中使用matplotlib.pyplot的plot函数,即实现内嵌绘图,而需要加上%matplotlib inline
参考:
[1]:https://blog.csdn.net/liangzuojiayi/article/details/78183783?locationNum=8&fps=1
[2]:https://www.jianshu.com/p/2dda5bb8ce7d
加载数据(Load Data)
如果在给定路径下,没有MNIST数据集,那么程序会自动下载该数据集(大约11MB)
The MNIST data-set is about 11 MB and will be downloaded automatically if it is not located in the given path.
1from tensorflow.examples.tutorials.mnist import input_data
2data = input_data.read_data_sets("data/MNIST/", one_hot=True)
现在 MNIST数据集已经加载好,该数据集包含70,000幅图像和标签(即图像的类别)。数据集被分成3个互不交叉的子集(训练集、测试集和验证集),在本教程中,我们将只使用训练集和测试集。
其中:
1print("Size of:")
2print("- Training-set:\t\t{}".format(len(data.train.labels)))
3print("- Test-set:\t\t{}".format(len(data.test.labels)))
4print("- Validation-set:\t{}".format(len(data.validation.labels)))
Size of:
- Training-set: 55000
- Test-set: 10000
- Validation-set: 5000
MNIST数据集由One-hot encoding方式加载。这意味着标签由单个数字(类别)转换成一个向量,其长度等价于可能类别数量(如有10类,则长度为10)。向量的所有元素除了第i个元素为 1之外(因为该标签的类别是i),其它元素都为0。
1# 输出测试集前5个标签的向量数据
2data.test.labels[0:5, :]
输出:
array([[0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])
我们还需要将类(classes)作为单个数字进行各种比较和性能测量,因此我们通过获取最高元素(其值为1)的索引来将One-Hot编码向量转换为单个数字。请注意,'class'这个词是Python中使用的关键字,因此我们需要使用名称'cls'。
1# argmax(): Returns the indices of the maximum values along an axis# argma
2data.test.cls = np.array([label.argmax() for label in data.test.labels])
上面代码,我们使用了numpy的argmax()函数。该函数的作用是:Returns the indices of the maximum values along an axis。因为类别向量中只有1是最大值,而1所在的索引位置就是我们所要的值。argmax()正好完美适用!
现在我们可以知道测试集中前5幅图像的类别。你可以将其与上述One-Hot编码向量进行比较。例如,第一幅图像的类是7,其对应于One-Hot编码向量中索引为7的元素,该元素值为1。
1data.test.cls[0:5]
输出:
array([7, 2, 1, 0, 4], dtype=int64)
数据维度会在下面的代码中被使用。在计算机编程中,最好使用使用变量(variables)和常量(constants),而不是每次使用该编号时候都必须对特定数字进行硬编码(hard-code)。这意味着数字只需要在一个地方被修改。理想情况下,这些数据可以从已经读取的数据中推断(inferred)出来,但在这里我们只是写出数字。
1# MNIST图像数据的每个维度是28个像素(即28x28)
2img_size = 28
3
4# 图像存储在一维数组中
5img_size_flat = img_size * img_size
6
7# 将图像的高度和宽度变成不可变的元组(tuple),用于 reshape arrays。
8img_shape = (img_size, img_size)
9
10# 类别的数量:10,即每一类用一个数值来表示
11num_classes = 10
函数使用3x3网格绘制9幅图像,并在每幅图像下面显示真实正确的类别和预测的类别。
1def plot_images(images, cls_true, cls_pred=None):
2 # 判断输入参数是否符合要求
3 assert len(images) == len(cls_true) == 9
4
5 # 创建3x3的图和subplots的集合
6 fig, axes = plt.subplots(3, 3)
7 fig.subplots_adjust(hspace=0.3, wspace=0.3)
8
9 for i, ax in enumerate(axes.flat):
10 # 绘图
11 ax.imshow(images[i].reshape(img_shape), cmap='binary')
12
13 # 显示正确和预测的类别
14 if cls_pred is None:
15 xlabel = "True: {0}".format(cls_true[i]) # "{N}".format(n0,n1,...,nN)一定要掌握这种写法
16 else:
17 xlabel = "True: {0}, Pred: {1}".format(cls_true[i], cls_pred[i])
18
19 ax.set_xlabel(xlabel)
20
21 # 删除plot的刻度(ticks)
22 ax.set_xticks([])
23 ax.set_yticks([])
24
25 # 在单个Notebook 单元格中正确的显示plot图。
26 plt.show()
1# 从测试集中获得前9幅图像数据
2images = data.test.images[0:9]
3
4# 获得相应的正确类别
5cls_true = data.test.cls[0:9]
6
7# 使用上述函数绘制图像和标签(非预测)
8plot_images(images=images, cls_true=cls_true)
TensorFlow 的目的是实现一个计算图(computational graph),与直接在Python中执行的计算相比,其可以更有效的执行。TensorFlow可以比Numpy更有效,因为TensorFlow知道必须执行的整个计算图,而Numpy一次只知道单个数学运算的计算。
TensorFlow 还可以自动计算优化图中变量所需的梯度,以使得模型更好地运行。这是因为图(Graph)是简单数学表达式的组合,因此可以使用微分(derivatives)的链式法则(chain-rule)来计算整个图的梯度。
TensorFlow 还可以利用多核CPU和GPU的,而且Google甚至为TensorFlow研发了专用芯片,称为TPU(Tensor Processing Units),甚至比GPU更快。
TensorFlow图由以下部分组成,将在下面详述:
此外,TensorFlow图还可以包含各种调试语句,例如:可以由TensorBoard可视化的日志数据(本教程没有介绍)
占位符变量(Placeholder variables)作为图的输入,我们可以在每次执行图的时候进行更改。我们称之为 喂(feeding)占位符变量,并在下面进一步说明。
首先,我们定义输入图像的占位符变量‘x’。这允许我们改变输入到TensorFlow图的图像。这是一个所谓的张量(tensor),这意味着它是一个多维向量或矩阵。该占位符的数据类型设置成‘float32’,形状设置成‘[None, img_size_flat]’,其中‘None’表示张量可以存储(hold)任意数量的图像,每个图像是长度为‘img_size_flat’的向量。
1x = tf.placeholder(tf.float32, [None, img_size_flat])
接下来,我们定义占位符变量‘y_true’,其是存放与占位符‘x’中输入图像相关联的真实标签。该占位符变量的数据类型设置成‘float32’,形状是‘[None, num_classes]’,这意味着它可以包含任意数量的标签,每个标签是长度为‘num_classes’的向量,在这种情况下为10。
1y_true = tf.placeholder(tf.float32, [None, num_classes])
最后,我们定义占位符变量‘y_true_cls’,其实存放与占位符‘x’中输入图像相关的类别。该占位符的数据类型设置成‘int64’,形状设置为‘[None]’,这意味着该占位符变量是任意长度的一维向量。
1y_true_cls = tf.placeholder(tf.int64, [None])
除了上面定义用作将输入数据输入到模型中的占位符变量之外,还有一些模型变量必须由TensorFlow进行更改,以使模型在训练数据上表现更好。
必须优化的第一个变量称为“权重(weights)”,在这里定义为TensorFlow变量,必须用零初始化,形状为[img_size_flat,num_classes],因此它是具有img_size_flat
行和num_classes
列的二维张量(或矩阵) 。
1# tf.zeros: 返回全是由0元素组成的值
2weights = tf.Variable(tf.zeros([img_size_flat, num_classes]))
第二个必须要优化的变量称为“偏置(biases)”,其实长度为‘num_classes’的一维度张量(或向量)。这里也初始化为零。
1biasesbiases == tftf..VariableVariable(tf.zeros([num_classes]))
简单的数学模型是将占位符变量‘x’乘以权重‘weights’,并加上偏置‘biases’
根据矩阵性质,输出结果是形状为‘[num_images, num_classes]’的矩阵。因为‘x’的形状为‘[num_images, img_size_flat]’,‘weights’的形状为‘[img_size_flat, num_classes]’,所以这两个矩阵相乘的结果是形状为‘[num_images, num_classes]’的矩阵。然后将‘biases’向量加到矩阵的每一行上(利用广播的特性)。
注意:名称‘logits’是典型的TensorFlow术语(terminogy),但你也可以叫做其它变量。
1logits = tf.matmul(x, weights) + biases
现在logits
是一个带有num_images
行和num_classes
列的矩阵,其中第 i 行和第 j 列的元素是对第 i 幅输入图像估计为第 j 类的可能性(概率值)。
然而,这些估计是大概的(rough)值且难以解释,因为这些数字可能非常小或很大,所以我们想对它们进行归一化处理,以使logits
矩阵的每一行总和为1(因为概率值和为1),并且每个元素被限制在[0,1]。这是使用所谓的softmax函数(又称归一化指数函数)计算的,结果存储在y_pred
中。
注:附上Softmax相关资源(点击阅读全文即可查看)
[1] Softmax function wiki
[2] Softmax 函数的特点和作用是什么?
[3] Softmax回归
1y_pred = tf.nn.softmax(logits)
可以通过获取 y_pred
矩阵中每行中最大元素的索引计算预测的类别 y_pred_cls
。
1y_pred_cls = tf.argmax(y_pred, axis=1)
限于篇幅过大,便将【TensorFlow从入门到精通】01 简单线性模型内容分成上篇和下篇来介绍。
预告下篇将介绍代价函数、优化器、衡量指标、TensorFlow会话(Session)、变量初始化等知识点。