1- Simple Linear Model

简单线性模型

本教程主要介绍基于TensorFlow简单线性模型处理MNIST 数据集,这里我们使用jupyter notebook。MNIST数据集是基本的手写数字集合,主要分为以下4个部分:

                                train-images-idx3-ubyte: training set images
                                train-labels-idx1-ubyte: training set labels
                                t10k-images-idx3-ubyte: test set images
                                t10k-labels-idx1-ubyte: test set labels

MNIST数据集一共包含三个部分:训练数据集(55,000份,mnist.train)、测试数据集(10,000份,mnist.test)和验证数据集(5,000份,mnist.validation)。一般来说,训练数据集是用来训练模型,验证数据集可以检验所训练出来的模型的正确性和是否过拟合,测试集是不可见的(相当于一个黑盒),但我们最终的目的是使得所训练出来的模型在测试集上的效果(这里是准确性)达到最佳。每一张都是抗锯齿(Anti-aliasing)的灰度图,图片大小28*28像素,数字部分被归一化为20*20大小,位于图片的中间位置,保持了原来形状的比例。

通过Tensorflow定义基本的线性模型,帮助我们熟悉基本的线性代数矩阵处理以及分类方法。

导入使用框架

%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
from sklearn.metrics import confusion_matrix

导入数据

将Mnist数据集下载到指定路径中,如果路径中已经存在,那就直接读取。

from tensorflow.examples.tutorials.mnist import input_data
data = input_data.read_data_sets("MNIST_data",one_hot=True)
Extracting MNIST_data\train-images-idx3-ubyte.gz
Extracting MNIST_data\train-labels-idx1-ubyte.gz
Extracting MNIST_data\t10k-images-idx3-ubyte.gz
Extracting MNIST_data\t10k-labels-idx1-ubyte.gz
print("Size of:")
print("- Training-set:\t\t{}".format(len(data.train.labels)))
print("- Test-set:\t\t{}".format(len(data.test.labels)))
print("- Validation-set:\t{}".format(len(data.validation.labels)))
Size of:
- Training-set:     55000
- Test-set:     10000
- Validation-set:   5000

One-Hot Encoding

### One-Hot编码,又称为一位有效编码,主要是采用位状态寄存器来对个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。在实际的机器学习的应用任务中,特征有时候并不总是连续值,有可能是一些分类值,如性别可分为“male”和“female”。在机器学习任务中,对于这样的特征,通常我们需要对其进行特征数字化,如下面的例子:有如下三个特征属性:性别:[“male”,”female”] 地区:[“Europe”,”US”,”Asia”] 浏览器:[“Firefox”,”Chrome”,”Safari”,”Internet Explorer”] 对于某一个样本,如[“male”,”US”,”Internet Explorer”],我们需要将这个分类值的特征数字化,最直接的方法,我们可以采用序列化的方式:[0,1,3]。但是这样的特征处理并不能直接放入机器学习算法中。

One-Hot Encoding的处理方法

### 对于上述的问题,性别的属性是二维的,同理,地区是三维的,浏览器则是思维的,这样,我们可以采用One-Hot编码的方式对上述的样本“[“male”,”US”,”Internet Explorer”]”编码,“male”则对应着[1,0],同理“US”对应着[0,1,0],“Internet Explorer”对应着[0,0,0,1]。则完整的特征数字化的结果为:[1,0,0,1,0,0,0,0,1]。这样导致的一个结果就是数据会变得非常的稀疏。

data.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.]])

MNIST中的一个数据样本包含两块:手写体图片和对于的label。这里我们用xs和ys分别代表图片和对应的label,训练数据集和测试数据集都有xs和ys,我们使用 mnist.train.images 和 mnist.train.labels 表示训练数据集中图片数据和对于的label数据。

这里写图片描述

但是,这里我们可以先简单地使用一个长度为28 * 28 = 784的一维数组来表示图像,因为下面仅仅使用softmax regression来对图片进行识别分类(尽管这样做会损失图片的二维空间信息,所以实际上最好的计算机视觉算法是会利用图片的二维信息的)

MNIST的训练数据集可以是一个形状为55000 * 784位的tensor,也就是一个多维数组,第一维表示图片的索引,第二维表示图片中像素的索引(”tensor”中的像素值在0到1之间)。如下图:

这里写图片描述

MNIST中的数字手写体图片的label值在1到9之间,是图片所表示的真实数字。这里用One-hot vector来表述label值,vector的长度为label值的数目,vector中有且只有一位为1,其他为0.为了方便,我们表示某个数字时在vector中所对应的索引位置设置1,其他位置元素为0. 例如用[0,0,0,1,0,0,0,0,0,0]来表示3。所以,mnist.train.labels是一个55000 * 10的二维数组。如下:

这里写图片描述

data.test.cls = np.array([label.argmax() for label in data.test.labels])

argmax()

def argmax(self, axis=None, fill_value=None, out=None):

返回沿着某个维度最大值的位置

Returns array of indices of the maximum values along the given axis.
Masked values are treated as if they had the value fill_value.

Parameters
----------
axis : {None, integer}
    If None, the index is into the flattened array, otherwise along
    the specified axis
fill_value : {var}, optional
    Value used to fill in the masked values.  If None, the output of
    maximum_fill_value(self._data) is used instead.
out : {None, array}, optional
    Array into which the result can be placed. Its type is preserved
    and it must be of the right shape to hold the output.

Returns
-------
index_array : {integer_array}

Examples
--------
>>> a = np.arange(6).reshape(2,3)
>>> a.argmax()
5
>>> a.argmax(0)
array([1, 1, 1])
>>> a.argmax(1)
array([2, 2])

"""
if fill_value is None:
    fill_value = maximum_fill_value(self._data)
d = self.filled(fill_value).view(ndarray)
return d.argmax(axis, out=out)
data.test.cls[0:5]
array([7, 2, 1, 0, 4], dtype=int64)

数据维度

# MNIST images在每一个维度上有28个像素点
img_size = 28

# images 数据转换成一维数据
img_size_flat = img_size * img_size

# 包含(高度,宽度)的元组,原来reshape 数组
img_shape = (img_size,img_size)

# 分类数目,每个类包含10个数字
num_classes = 10

显示图片函数

函数用 3x3 图像窗口 显示9张图片,同时显示图像代表的数字

def plot_images(images, cls_true,cls_pred=None):
    assert len(images) == len(cls_true) == 9

    #建立3x3 sub-plot
    fig, axes = plt.subplots(3,3)
    fig.subplots_adjust(hspace=0.3, wspace=0.3)

    for i, ax in enumerate(axes.flat):
        # plot image.
        ax.imshow(images[i].reshape(img_shape),cmap='binary')

        # show ture and predicted classes.
        if cls_pred is None:
            xlabel = "True: {0}".format(cls_true[i])
        else:
            xlabel = "True: {0}, Pred:{1}".format(cls_true[i],cls_pred[i])
        ax.set_xlabel(xlabel)

        # Remove ticks from the plot
        ax.set_xticks([])
        ax.set_yticks([])

显示图片检查数据是否正确

# Get the first 10 images from the test-set.
images = data.test.images[0:9]

# Get the true classes for those images.
cls_true = data.test.cls[0:9]

# Plot the images and labels using our helper-function above.
plot_images(images=images,cls_true=cls_true)

这里写图片描述

图计算

TensorFlow程序中图的创建类似于一个 [施工阶段],而在 [执行阶段] 则利用一个session来执行图中的节点。很常见的情况是,在 [施工阶段] 创建一个图来表示和训练神经网络,而在 [执行阶段] 在图中重复执行一系列的训练操作。

Placeholder variables

通过创建 placeholder 来具体指定一个可变的批次维度(variable batch dimention)。shape 的 None 元素与可变大小的维度(a variable-sized dimension)相对应。要给节点输入数据时用 placeholder,在 TensorFlow 中用placeholder 来描述等待输入的节点,只需要指定类型即可,然后在执行节点的时候用一个字典来“喂”这些节点。相当于先把变量 hold 住,然后每次从外部传入data,注意 placeholder 和 feed_dict 是绑定用的。这里简单提一下 feed 机制, 给 feed 提供数据,作为 run()调用的参数, feed 只在调用它的方法内有效, 方法结束, feed 就会消失。

import tensorflow as tf

input1 = tf.placeholder(tf.float32)

input2 = tf.placeholder(tf.float32)

ouput = tf.mul(input1, input2)

with tf.Session() as sess:

### print(sess.run(ouput, feed_dict={input1: [7.], input2: [2.]}))

1.我们首先为输入图片定义相应的placeholder。这样我们可以更改传入到图模型中。这里所谓的张量,其实就是多维向量或者矩阵。数据类型为float32,shape设置成[None, img_size_float],None表示这个向量可能保持一个可变数目的图片,每张图片是一个img_size_flat长度的向量。

x = tf.placeholder(tf.float32, [None,img_size_flat])

2.随后我们把包含图片以及和它关联的真实标签赋予到x中。新建保持变量y的shape是[None,num_classes],表示变量包含了可变数目的长度为num_classes(实际上就是10)的标签。

shape就是几行几列。

y_true = tf.placeholder(tf.float32,[None,num_classes])

3.最后我们建立保持变量用于存放x中表示每一张图片的真实分类(数字)。数据类型为整数,列设置为[None],表示这个变量是可变长度的一维向量。

y_true_cls = tf.placeholder(tf.int64,[None])

优化变量

区别于保持变量,还有一些需要被优化的变量,通过这些变量,可以在训练数据的时候有更好的表现。

其中第一个优化变量为weights。weights变量的shape是[img_size_flat,num_classes],所以它是一个2阶张量,并且初始化为0.

weights = tf.Variable(tf.zeros([img_size_flat,num_classes]))

第二个优化变量为biases。这是一个长度为num_classes的一阶张量。

biases = tf.Variable(tf.zeros([num_classes]))

模型

模型运算结果为[num_images,num_classes]矩阵,因为x是[num_images,img_size_flat]矩阵,weights是[img_size_flat,num_classes]矩阵,最后加上biases向量。

with tf.device('/gpu:0'):
    logits = tf.matmul(x,weights) + biases

Very Important:现在logits 是一个num_images 行,num_classes列的矩阵,第i行j列的元素表示第i张图片,是分类j的相似程度。具体的就是代表第i张图片是数字j的相似程度。

但是,这个元素有可能很大或者很小,所以我们需要将这些元素正则化到0~1之间。这里我们采用softmax函数,把正则化之后的数据保存到y_pred中。

y_pred = tf.nn.softmax(logits)

提取每一行中最大的数值的位置,这个值就代表着张图片的数字

y_pred_cls = tf.argmax(y_pred,dimension=1)

优化cost

为了模型能够更好的分类我们的输入图片,我们必须调整weighs和biases变量。因此,我们需要知道目前模型预测的怎么样,这是通过比较模型的预测值y_pred,以及真实值y_ture.

cross-entropy 是分类中性能衡量的一个函数。它是一个非负的连续函数,如果这个模型的预测值和真实值完全匹配,那么这个函数值为0,因此我们优化目标就是最小化cross-entropy函数的值,这是通过调整模型的weights和biases来实现的。

cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=y_true)

虽然我们计算了每一张图片的分类结果与真实结果之间的误差,但是我们需要一个用于计算全局误差的变量,这里我们采用最简单的取评价值的一个方式。

cost = tf.reduce_mean(cross_entropy)

优化方法

我们采用梯度下降法来优化这个cost值,步长设置为0.5。

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.5).minimize(cost)

性能

我们需要为用户显示性能衡量以及进度方式。下面这个数组是预测值是否与实际值一致的boolean向量。

correct_predition = tf.equal(y_pred_cls, y_true_cls)

最后把这个boolean向量转换成float数组,False = 0, True = 1,accuracy即为这个float数组的平局值。

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

运行模型

建立Session

sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))

初始化变量

sess.run(tf.initialize_all_variables())
WARNING:tensorflow:From <ipython-input-24-9f5df2c7784a>:1: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed after 2017-03-02.
Instructions for updating:
Use `tf.global_variables_initializer` instead.

迭代优化函数

模型训练集总共有50000张图片,计算所有的图片的梯度需要很长的时间,因此我们采用SGD优化方法,这种优化方法仅仅在每一步优化过程中采用图片的一小部分来进行。

batch_size = 100
def optimize(num_iterations):
    for i in range(num_iterations):
        # Get a batch of training examples.
        # x_batch now holds a batch of images and 
        # y_true_batch are the true labels for those images.
        x_batch, y_true_batch = data.train.next_batch(batch_size)

        # Put the batch into a dict with the proper names
        # for palcehodler variables int the tensorflow graph.
        # Note that the placeholder for y_true_cls is not set
        # because it is not used during training.
        feed_dict_train = {x:x_batch,
                          y_true: y_true_batch}

        # Run the optimizer using this batch of training data.
        # TensorFlow assigns the variables in feed_dict_train
        # to the placeholder variables and then runs the optimizer.
        sess.run(optimizer,feed_dict=feed_dict_train)

性能显示函数

feed_dict_test = {x: data.test.images,
                 y_true:data.test.labels,
                 y_true_cls:data.test.cls}
def print_accuracy():
    acc = sess.run(accuracy, feed_dict=feed_dict_test)
    print("Accuracy on test-set:{0:.1%}".format(acc))

confusion matrix 显示函数

def print_confusion_matrix():
    # Get the true classifications for the test-set.
    cls_true = data.test.cls

    # Get the predicted classifications for the test-set.
    cls_pred = sess.run(y_pred_cls, feed_dict=feed_dict_test)

    # Get the confusion matrix using sklearn.
    cm = confusion_matrix(y_true=cls_true,
                         y_pred=cls_pred)

    #print the confusion matrix as text.
    print(cm)

    # Plot the confusion matrix as an image
    plt.imshow(cm,interpolation='nearest',cmap=plt.cm.Blues)

    # Make various adjustments to the plot.
    plt.tight_layout()
    plt.colorbar()
    tick_marks = np.arange(num_classes)
    plt.xticks(tick_marks,range(num_classes))
    plt.yticks(tick_marks,range(num_classes))
    plt.xlabel('Predicted')
    plt.ylabel('True')
def plot_example_errors():
    # Use tensorflow to get a list of boolean values
    # whether each test-image has been correctly classified,
    # and a list for the predicted class of each imge.
    correct, cls_pred = sess.run([correct_predition,y_pred_cls],
                                feed_dict=feed_dict_test)
    # Negate the boolean array.
    incorrect = (correct == False)

    # Get the images from the test-set taht have been
    # incorrectly classified.
    images = data.test.images[incorrect]

    # Get the predicted classes for those images.
    cls_pred = cls_pred[incorrect]

    # Get the true classes for those images.
    cls_true = data.test.cls[incorrect]

    # Plot the first 9 images.
    plot_images(images=images[0:9],cls_true=cls_true[0:9],cls_pred=cls_pred[0:9])

weights显示函数

def plot_weights():
    # Get the values for the weights from the tensorflow variable.
    w = sess.run(weights)

    # Get the lowest and highest values for the weights.
    # This is used to correct the color intensity across
    # the images so they can be compared with each other.
    W_min = np.min(w)
    W_max = np.max(w)


    # Create figure with 3x4 sub-plots
    fig, axes = plt.subplots(3, 4)
    fig.subplots_adjust(hspace=0.3, wspace=0.3)

    for i, ax in enumerate(axes.flat):
        # Only use the weights for the first 10 sub-plots
        if i < 10:
            # Get the weights for the i'th digit and reshape it.
            # Note that w.shape == (img_size_flat,10)
            image = w[:,i].reshape(img_shape)

            # Set the label for the sub-plot.
            ax.set_xlabel("Weights:{0}".format(i))

            # Plot the image
            ax.imshow(image,vmin=W_min,vmax=W_max,cmap='seismic')

        # Remove ticks from each sub-plot.
        ax.set_xticks([])
        ax.set_yticks([])
print_accuracy()
Accuracy on test-set:9.8%
optimize(num_iterations=1)

通过一次优化迭代,识别率有明显的上升,达到40.7%。

print_accuracy()
Accuracy on test-set:40.7%
plot_example_errors()

这里写图片描述

weights如下显示:正值为红色,负值为蓝色。这些weights可以直观的认为是图像过滤器。

例如:如果图像中心显示有红色的圆圈,而圆圈的中心为蓝色,这个就是数字0.

同样的,如果图像中心一条红色的线,而周围都是蓝色的区域,那么这个数字就是1.

通过1次迭代,实际上只训练了所有图片中的100张。如果训练几千张图片之后,weights将会变得非常难识别,因为这样会有更多手写数字体被融合在一起。

plot_weights()

这里写图片描述

优化10次之后

optimize(num_iterations=9)
print_accuracy()
Accuracy on test-set:78.2%
plot_example_errors()

这里写图片描述

plot_weights()

这里写图片描述

1000次迭代之后

optimize(num_iterations=990)
print_accuracy()
Accuracy on test-set:91.8%
plot_example_errors()

这里写图片描述

模型已经迭代了1000优化,每一次迭代使用训练集中的100张图片。由于图片种类太多,weights现在已经非常难区分了,因此我们怀疑模型是否真的理解了数字怎么样从基本的线条组成,或者模型只是记住了不同的像素变化。

plot_weights()

这里写图片描述

我们可以通过所谓的confusion matrix来看误分类的细节。例如,数组5的图像有时会被分类成其他可能的数字,但不会是3,6,8。

print_confusion_matrix()

[[ 957 0 3 2 0 5 11 1 1 0]
[ 0 1108 2 2 1 2 4 2 14 0]
[ 4 9 914 19 15 5 13 14 35 4]
[ 1 0 16 928 0 28 2 14 13 8]
[ 1 1 3 2 939 0 10 2 6 18]
[ 10 3 3 33 10 784 17 6 19 7]
[ 8 3 3 2 11 14 915 1 1 0]
[ 3 9 21 9 7 1 0 959 2 17]
[ 8 8 8 38 11 40 14 18 825 4]
[ 11 7 1 13 75 13 1 39 4 845]]

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值