88bf必发娱乐 24

tensorflow分类-【老鱼学tensorflow】

前面我们学习过回归问题,比如对于房价的预测,因为其预测值是个连续的值,因此属于回归问题。

我们本节要用MNIST
数据集训练一个可以识别数据的深度学习模型来帮助识别手写数字。

但还有一类问题属于分类的问题,比如我们根据一张图片来辨别它是一只猫还是一只狗。某篇文章的内容是属于体育新闻还是经济新闻等,这个结果是有一个全集的离散值,这类问题就是分类问题。

MNIST

我有时会把回归问题看成是分类问题,比如对于房价值的预测,在实际的应用中,一般不需要把房价精确到元为单位的,比如对于均价,以上海房价为例,可以分为:5000-10万这样的一个范围段,并且以1000为单位就可以了,尽管这样分出了很多类,但至少也可以看成是分类问题了。

MNIST 是一个入门级计算机视觉数据集,包含了很多手写数字图片,如图所示:

因此分类算法应用范围非常广泛,我们来看下在tensorflow中如何解决这个分类问题的。
本文用经典的手写数字识别作为案例进行讲解。

88bf必发娱乐 1

准备数据

# 准备数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)

执行上述代码后,会从线上下载测试的手写数字的数据。
不过在我的机器上运行时好久都没有下载下相应的数据,最后我就直接到http://yann.lecun.com/exdb/mnist/
网站上下载其中的训练数据和测试数据文件到指定的目录下,然后再运行这个程序就能把数据给解压开来。
88bf必发娱乐 2

这里总共大概有6万个训练数据,1万个测试数据。

手写数字是一堆28X28像素的黑白图片,例如:
88bf必发娱乐 3

在本次案例中,我们把这个数组展开成一个向量,长度是 28×28 =
784,也就是一个图片就是一行784列的数据,每列中的值是一个像素的灰度值,0-255。
为何要把图像的二维数组转换成一维数组?
把图像的二维数组转换成一维数组一定是把图像中的某些信息给丢弃掉了,但目前本文的案例中也可以用一维数组来进行分类,这个就是深度神经网络的强大之处,它会尽力寻找一堆数据中隐藏的规律。
以后我们会用卷积神经网络来处理这个图像的分类,那时的精确度就能再次进行提高。
但是即便把此图像数据碾平成一维数据的方式也能有一个较好的分辨率。

另外这里有一个关于分类问题的重要概念就是one
hot数据,虽然我们对每个图片要打上的标签是0-9数字,但在分类中用一个总共有10个占位分类的数字来表示,如果属于哪个类就在那个位置设置为1,其它位置为0.
例如:
标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])
标签2将表示成([0,0,1,0,0,0,0,0,0,0,0])
这样结果集其实是一个10列的数据,每列的值为0或1。

数据集中包含了图片和对应的标注,在TensorFlow
中提供了这个数据集,我们可以用如下方法进行导入:

添加层

添加层的函数跟前面几个博文中一样,这里依然把它贴出来:

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加层
    :param inputs: 输入数据
    :param in_size: 输入数据的列数
    :param out_size: 输出数据的列数
    :param activation_function: 激励函数
    :return:
    """

    # 定义权重,初始时使用随机变量,可以简单理解为在进行梯度下降时的随机初始点,这个随机初始点要比0值好,因为如果是0值的话,反复计算就一直是固定在0中,导致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape为1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神经网络线性公式:inputs * Weights + biases,我们大脑中的神经元的传递基本上也是类似这样的线性公式,这里的权重就是每个神经元传递某信号的强弱系数,偏置值是指这个神经元的原先所拥有的电位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果没有设置激活函数,则直接就把当前信号原封不动地传递出去
        outputs = Wx_plus_b
    else:
        # 如果设置了激活函数,则会由此激活函数来对信号进行传递或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

88bf必发娱乐 4

定义输入数据

xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那个one hot结构的数据

输出结果如下:

定义层

# 定义层,输入为xs,其有28*28列,输出为10列one hot结构的数据,激励函数为softmax,对于one hot类型的数据,一般激励函数就使用softmax
prediction = add_layer(xs, 28*28, 10, activation_function=tf.nn.softmax)

88bf必发娱乐 5

定义损失函数

为了训练我们的模型,我们首先需要定义一个能够评估这个模型有多好程度的指标。其实,在机器学习,我们通常定义一个这个模型有多坏的指标,这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。这两种指标方式本质上是等价的。
在分类中,我们经常用“交叉熵”(cross-entropy)来定义其损失值,它的定义如下:
88bf必发娱乐 6

y 是我们预测的概率分布, y’ 是实际的分布(我们输入的one-hot
vector)。比较粗糙的理解是,交叉熵是用来衡量我们的预测用于描述真相的低效性。

# 定义loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

在这里程序会首先下载MNIST 数据集,然后解压并保存到刚刚制定好的
MNIST_data 文件夹中,然后输出数据集对象。

初始化变量

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

数据集中包含了55000 行的训练数据集(mnist.train)、5000
行验证集(mnist.validation)和 10000
行的测试数据集(mnist.test),文件如下所示:

计算准确度

首先让我们找出那些预测正确的标签。tf.argmax
是一个非常有用的函数,它能给出某个tensor对象在某一维上的其数据最大值所在的索引值。由于标签向量是由0,1组成,因此最大值1所在的索引位置就是类别标签,比如tf.argmax(y_pre,1)返回的是模型对于任一输入x预测到的标签值,而
tf.argmax(v_ys,1) 代表正确的标签,我们可以用 tf.equal
来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。

correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))

这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True,
False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75.

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

最后,我们计算所学习到的模型在测试数据集上面的正确率。

88bf必发娱乐 7

小批量方式进行训练

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys})
    if i % 50 == 0:
        # 每隔50条打印一下预测的准确率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

最终打印出:

Extracting D:/todel/python/MNIST_data/train-images-idx3-ubyte.gz
Extracting D:/todel/python/MNIST_data/train-labels-idx1-ubyte.gz
Extracting D:/todel/python/MNIST_data/t10k-images-idx3-ubyte.gz
Extracting D:/todel/python/MNIST_data/t10k-labels-idx1-ubyte.gz
2017-12-13 14:32:04.184392: I C:\tf_jenkins\home\workspace\rel-win\M\windows\PY\36\tensorflow\core\platform\cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
0.1125
0.6167
0.741
0.7766
0.7942
0.8151
0.8251
0.8349
0.8418
0.8471
0.8455
0.8554
0.8582
0.8596
0.8614
0.8651
0.8655
0.8676
0.8713
0.8746

正如前面提到的一样,每一个MNIST
数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为
xs,把这些标签设为 ys。训练数据集和测试数据集都包含 xs 和
ys,比如训练数据集的图片是 mnist.train.images ,训练数据集的标签是
mnist.train.labels,每张图片是 28 x 28 像素,即 784
个像素点,我们可以把它展开形成一个向量,即长度为 784 的向量。

完整代码

import tensorflow as tf

# 准备数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加层
    :param inputs: 输入数据
    :param in_size: 输入数据的列数
    :param out_size: 输出数据的列数
    :param activation_function: 激励函数
    :return:
    """

    # 定义权重,初始时使用随机变量,可以简单理解为在进行梯度下降时的随机初始点,这个随机初始点要比0值好,因为如果是0值的话,反复计算就一直是固定在0中,导致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape为1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神经网络线性公式:inputs * Weights + biases,我们大脑中的神经元的传递基本上也是类似这样的线性公式,这里的权重就是每个神经元传递某信号的强弱系数,偏置值是指这个神经元的原先所拥有的电位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果没有设置激活函数,则直接就把当前信号原封不动地传递出去
        outputs = Wx_plus_b
    else:
        # 如果设置了激活函数,则会由此激活函数来对信号进行传递或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

# 定义输入数据
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那个one hot结构的数据

# 定义层,输入为xs,其有28*28列,输出为10列one hot结构的数据,激励函数为softmax,对于one hot类型的数据,一般激励函数就使用softmax
prediction = add_layer(xs, 28*28, 10, activation_function=tf.nn.softmax)

# 定义loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)


def computer_accuracy(v_xs, v_ys):
    """
    计算准确度
    :param v_xs:
    :param v_ys:
    :return:
    """
    # predication是从外部获得的变量
    global prediction
    # 根据小批量输入的值计算预测值
    y_pre = sess.run(prediction, feed_dict={xs:v_xs})
    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys})
    return result

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys})
    if i % 50 == 0:
        # 每隔50条打印一下预测的准确率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

所以训练集我们可以转化为[55000, 784]
的向量,第一维就是训练集中包含的图片个数,第二维是图片的像素点表示的向量。

Softmax

Softmax
可以看成是一个激励(activation)函数或者链接(link)函数,把我们定义的线性函数的输出转换成我们想要的格式,也就是关于
10
个数字类的概率分布。因此,给定一张图片,它对于每一个数字的吻合度可以被
Softmax 函数转换成为一个概率值。Softmax 函数可以定义为:

88bf必发娱乐 8

展开等式右边的子式,可以得到:

88bf必发娱乐 9

比如判断一张图片中的动物是什么,可能的结果有三种,猫、狗、鸡,假如我们可以经过计算得出它们分别的得分为3.2、5.1、-1.7,Softmax
的过程首先会对各个值进行次幂计算,分别为
24.5、164.0、0.18,然后计算各个次幂结果占总次幂结果的比重,这样就可以得到
0.13、0.87、0.00
这三个数值,所以这样我们就可以实现差别的放缩,即好的更好、差的更差。

如果要进一步求损失值可以进一步求对数然后取负值,这样Softmax
后的值如果值越接近 1,那么得到的值越小,即损失越小,如果越远离
1,那么得到的值越大。

实现回归模型

88bf必发娱乐 ,首先导入TensorFlow,命令如下:

88bf必发娱乐 10

接下来我们指定一个输入,在这里输入即为样本数据,如果是训练集那么则是55000
x 784 的矩阵,如果是验证集则为 5000 x 784 的矩阵,如果是测试集则是 10000
x 784 的矩阵,所以它的行数是不确定的,但是列数是确定的。

所以可以先声明一个placeholder 对象:

88bf必发娱乐 11

这里第一个参数指定了矩阵中每个数据的类型,第二个参数指定了数据的维度。

接下来我们需要构建第一层网络,表达式如下:

88bf必发娱乐 12

这里实际上是对输入的x 乘以 w
权重,然后加上一个偏置项作为输出,而这两个变量实际是在训练的过程中动态调优的,所以我们需要指定它们的类型为
Variable,代码如下:

88bf必发娱乐 13

接下来需要实现的就是上图所述的公式了,我们再进一步调用Softmax
进行计算,得到 y:

88bf必发娱乐 14

通过上面几行代码我们就已经把模型构建完毕了,结构非常简单。

损失函数

为了训练我们的模型,我们首先需要定义一个指标来评估这个模型是好的。其实,在机器学习,我们通常定义指标来表示一个模型是坏的,这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。但是这两种方式是相同的。

一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)。交叉熵产生于信息论里面的信息压缩编码技术,但是它后来演变成为从博弈论到机器学习等其他领域里的重要技术手段。它的定义如下:

88bf必发娱乐 15

y 是我们预测的概率分布, y_label
是实际的分布,比较粗糙的理解是,交叉熵是用来衡量我们的预测用于描述真相的低效性。

我们可以首先定义y_label,它的表达式是:

88bf必发娱乐 16

接下来我们需要计算它们的交叉熵,代码如下:

88bf必发娱乐 17

首先用reduce_sum() 方法针对每一个维度进行求和,reduction_indices
是指定沿哪些维度进行求和。

然后调用reduce_mean() 则求平均值,将一个向量中的所有元素求算平均值。

这样我们最后只需要优化这个交叉熵就好了。

所以这样我们再定义一个优化方法:

88bf必发娱乐 18

这里使用了GradientDescentOptimizer,在这里,我们要求 TensorFlow
用梯度下降算法(gradient descent algorithm)以 0.5
的学习速率最小化交叉熵。梯度下降算法(gradient descent
algorithm)是一个简单的学习过程,TensorFlow
只需将每个变量一点点地往使成本不断降低的方向移动即可。

运行模型

定义好了以上内容之后,相当于我们已经构建好了一个计算图,即设置好了模型,我们把它放到Session
里面运行即可:

88bf必发娱乐 19

该循环的每个步骤中,我们都会随机抓取训练数据中的batch_size
个批处理数据点,然后我们用这些数据点作为参数替换之前的占位符来运行
train。

这里需要一些变量的定义:

88bf必发娱乐 20

测试模型

那么我们的模型性能如何呢?

首先让我们找出那些预测正确的标签。tf.argmax()
是一个非常有用的函数,它能给出某个 Tensor
对象在某一维上的其数据最大值所在的索引值。由于标签向量是由 0,1
组成,因此最大值 1 所在的索引位置就是类别标签,比如 tf.argmax(y, 1)
返回的是模型对于任一输入 x 预测到的标签值,而 tf.argmax(y_label, 1)
代表正确的标签,我们可以用 tf.equal()
方法来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。

88bf必发娱乐 21

这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True,
False, True, True] 会变成 [1, 0, 1, 1] ,取平均值后得到 0.75。

88bf必发娱乐 22

最后,我们计算所学习到的模型在测试数据集上面的正确率,定义如下:

88bf必发娱乐 23

这个最终结果值应该大约是92%。

这样我们就通过完成了训练和测试阶段,实现了一个基本的训练模型,后面我们会继续优化模型来达到更好的效果。

运行结果如下:

88bf必发娱乐 24

结语

本节通过一个MNIST
数据集来简单体验了一下真实数据的训练和预测过程,但是准确率还不够高,后面我们会学习用卷积的方式来进行模型训练,准确率会更高。

本节代码

本节代码地址为:https://github.com/AIDeepLearning/MNIST

转载自注明:静觅 » TensorFlow
MNIST初级学习