[译]TensorFlow Tutorial #01 Simple Linear Model

原文地址git

本教程演示了使用TensorFlow和简单线性模型的基本工做流程。 在使用手写数字图像加载所谓的MNIST数据集以后,咱们在TensorFlow中定义并优化了一个简单的数学模型。 而后绘制并讨论结果。github

您应该熟悉基本的线性代数,Python和Jupyter Notebook编辑器。 若是您对机器学习和分类有基本的了解,它也会有所帮助。数组

#Importsbash

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

#Load Datasession

MNIST数据集大约为12 MB,若是它不在给定路径中,将自动下载。dom

from mnist import MNIST
data = MNIST(data_dir="data/MNIST/")
复制代码

MNIST数据集现已加载,由70,000个图像和图像的类号组成。 数据集被分红3个互斥的子集。 咱们将仅使用本教程中的培训和测试集。机器学习

print("Size of:")
print("- Training-set:\t\t{}".format(data.num_train))
print("- Validation-set:\t{}".format(data.num_val))
print("- Test-set:\t\t{}".format(data.num_test))

Size of:
- Training-set:		55000
- Validation-set:	5000
- Test-set:		10000
复制代码

为方便起见,复制一些数据维度。编辑器

# The images are stored in one-dimensional arrays of this length.
img_size_flat = data.img_size_flat

# Tuple with height and width of images used to reshape arrays.
img_shape = data.img_shape

# Number of classes, one class for each of 10 digits.
num_classes = data.num_classes
复制代码

#One-Hot Encoding函数

输出数据做为整数类数和所谓的One-Hot编码数组加载。 这意味着类号已从单个整数转换为长度等于可能类数的向量。 向量的全部元素都是零,除了i'元素是1而且意味着类是i。 例如,测试集中前5个图像的One-Hot编码标签是:性能

data.y_test[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.]])
复制代码

咱们还须要将类做为整数进行各类比较和性能测量。 经过使用np.argmax()函数获取最高元素的索引,能够从One-Hot编码数组中找到这些数据。 可是在加载数据集时咱们已经完成了这项工做,所以咱们能够看到测试集中前五个图像的类编号。 将它们与上面的One-Hot编码数组进行比较。

data.y_test_cls[0:5]

array([7, 2, 1, 0, 4])
复制代码

接下来作一个小例子: 在3x3网格中绘制9个图像,并在每一个图像下面写入真实和预测类的函数。

def plot_images(images, cls_true, cls_pred=None):
    assert len(images) == len(cls_true) == 9
    
    # Create figure with 3x3 sub-plots.
    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 true 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([])
        
    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()

复制代码

输出图像看看

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

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

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


#TensorFlow Graph

TensorFlow的所有目的是拥有一个所谓的计算图,与直接在Python中执行相同的计算相比,它能够更有效地执行。 TensorFlow能够比NumPy更有效,由于TensorFlow知道必须执行的整个计算图,而NumPy一次只知道一个数学运算的计算 TensorFlow还能够自动计算优化图形变量所需的梯度,以使模型更好地运行。 这是由于图是简单数学表达式的组合,所以可使用衍生的链规则计算整个图的梯度。 TensorFlow还能够利用多核CPU和GPU - 而Google甚至为TensorFlow构建了专用芯片,称为TPU(Tensor Processing Units)甚至比GPU更快。

TensorFlow图由如下部分组成,将在下面详述:

占位符变量用于将输入提供给图表。
模型变量将进行优化,以使模型更好地运行。
该模型本质上只是一个数学函数,它根据占位符变量和模型变量中的输入计算一些输出。
可用于指导变量优化的成本度量。
一种更新模型变量的优化方法。
复制代码

此外,TensorFlow图还能够包含各类调试语句,例如: 用于记录使用TensorBoard显示的数据,本教程未介绍。


#Placeholder variables

占位符变量用做图形的输入,咱们能够在每次执行图形时更改这些输入。 咱们称之为占位符变量,并在下面进一步说明。

首先,咱们为输入图像定义占位符变量。 这容许咱们更改输入到TensorFlow图形的图像。 这是一个所谓的张量,这意味着它是一个多维向量或矩阵。 数据类型设置为float32,形状设置为[None,img_size_flat],其中None表示张量能够保持任意数量的图像,每一个图像是长度为img_size_flat的向量。

x = tf.placeholder(tf.float32, [None, img_size_flat])
复制代码

接下来,咱们有占位符变量,用于与占位符变量x中输入的图像相关联的真实标签。 此占位符变量的形状为[None,num_classes],这意味着它能够包含任意数量的标签,每一个标签是长度为num_classes的向量,在这种状况下为10。

y_true = tf.placeholder(tf.float32, [None, num_classes])
复制代码

最后,咱们在占位符变量x中为每一个图像的真实类提供占位符变量。 这些是整数,此占位符变量的维度设置为[None],这意味着占位符变量是任意长度的一维向量。

y_true_cls = tf.placeholder(tf.int64, [None])
复制代码

#Variables to be optimized

除了上面定义的占位符变量以及用做将输入数据输入到模型中以外,还有一些模型变量必须由TensorFlow更改,以使模型在训练数据上表现更好。

必须优化的第一个变量称为权重,在此定义为TensorFlow变量,必须用零初始化而且其形状为[img_size_flat,num_classes],所以它是具备img_size_flat行的二维张量(或矩阵)和 num_classes列。

weights = tf.Variable(tf.zeros([img_size_flat, num_classes]))
复制代码

必须优化的第二个变量称为误差,并定义为长度为num_classes的1维张量(或向量)。

biases = tf.Variable(tf.zeros([num_classes]))
复制代码

#Model

这个简单的数学模型将占位符变量x中的图像与权重相乘,而后添加误差。

结果是形状[num_images,num_classes]的矩阵,由于x具备形状[num_images,img_size_flat]而且权重具备形状[img_size_flat,num_classes],所以这两个矩阵的乘法是具备形状[num_images,num_classes]的矩阵而后 误差向量被添加到该矩阵的每一行。

请注意,名称logits是典型的TensorFlow术语,但其余人可能会将变量称为其余内容。

logits = tf.matmul(x, weights) + biases
复制代码

如今logits是一个包含num_images行和num_classes列的矩阵,其中i'行和j'列的元素估计i'输入图像是多少多是j'。

然而,这些估计有点粗糙且难以解释,由于数字可能很是小或很大,所以咱们但愿对它们进行归一化,使得logits矩阵的每一行总和为1,而且每一个元素限制在0和1之间。 这是使用所谓的softmax函数计算的,结果存储在y_pred中。

y_pred = tf.nn.softmax(logits)
复制代码

能够经过获取每行中最大元素的索引从y_pred矩阵计算预测类。

y_pred_cls = tf.argmax(y_pred, axis=1)
复制代码

#Cost-function to be optimized

为了使模型更好地对输入图像进行分类,咱们必须以某种方式更改权重和误差的变量。 为此,咱们首先须要经过将模型y_pred的预测输出与指望输出y_true进行比较来了解模型当前的执行状况。

交叉熵是用于分类的性能度量。 交叉熵是一个始终为正的连续函数,若是模型的预测输出与指望输出彻底匹配,则交叉熵等于零。 所以,优化的目标是最小化交叉熵,使其经过改变模型的权重和误差尽量接近零。

TensorFlow具备用于计算交叉熵的内置函数。 请注意,它使用logits的值,由于它还在内部计算softmax。

cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits,
                                                           labels=y_true)
复制代码

咱们如今已经计算了每一个图像分类的交叉熵,所以咱们能够测量模型对每一个图像的单独执行状况。 可是为了使用交叉熵来指导模型变量的优化,咱们须要一个标量值,所以咱们只须要对全部图像分类采用交叉熵的平均值。

cost = tf.reduce_mean(cross_entropy)
复制代码

#Optimization method

如今咱们有一个必须最小化的成本度量,而后咱们能够建立一个优化器。 在这种状况下,它是渐变降低的基本形式,其中步长设置为0.5。

请注意,此时不执行优化。 事实上,根本没有计算任何东西,咱们只需将optimizer-object添加到TensorFlow图中以便之后执行。

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

复制代码

#Performance measures

咱们还须要一些性能指标来向用户显示进度。 这是布尔的向量,不管预测的等级是否等于每一个图像的真实等级。

correct_prediction = tf.equal(y_pred_cls, y_true_cls)
复制代码

这经过首先将布尔值的矢量类型转换为浮点数来计算分类精度,使得False变为0而且True变为1,而后计算这些数字的平均值。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
复制代码

#TensorFlow Run 一旦建立了TensorFlow图,咱们就必须建立一个用于执行图的TensorFlow会话。

session = tf.Session()
复制代码

在咱们开始优化以前,必须初始化权重和误差的变量。

session.run(tf.global_variables_initializer())
复制代码

训练集中有55,000个图像。 使用全部这些图像计算模型的梯度须要很长时间。 所以,咱们使用Stochastic Gradient Descent,它只在优化器的每次迭代中使用一小批图像。

batch_size = 100
复制代码

用于执行多个优化迭代的功能,以便逐渐改善模型的权重和误差。 在每次迭代中,从训练集中选择一批新数据,而后TensorFlow使用这些训练样本执行优化程序。

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.random_batch(batch_size=batch_size)
        
        # Put the batch into a dict with the proper names
        # for placeholder variables in 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.
        session.run(optimizer, feed_dict=feed_dict_train)
复制代码

#Helper-functions to show performance 使用测试集数据进行Dict,以用做TensorFlow图的输入。 请注意,咱们必须在TensorFlow图中使用占位符变量的正确名称。

feed_dict_test = {x: data.x_test,
                  y_true: data.y_test,
                  y_true_cls: data.y_test_cls}
复制代码

用于在测试集上打印分类精度的功能。

def print_accuracy():
    # Use TensorFlow to compute the accuracy.
    acc = session.run(accuracy, feed_dict=feed_dict_test)
    
    # Print the accuracy.
    print("Accuracy on test-set: {0:.1%}".format(acc))
复制代码

使用scikit-learn打印和绘制混淆矩阵的功能。

def print_confusion_matrix():
    # Get the true classifications for the test-set.
    cls_true = data.y_test_cls
    
    # Get the predicted classifications for the test-set.
    cls_pred = session.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')
    
    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()
复制代码

用于绘制来自测试集的图像的错误分类的示例的功能。

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 image.
    correct, cls_pred = session.run([correct_prediction, y_pred_cls],
                                    feed_dict=feed_dict_test)

    # Negate the boolean array.
    incorrect = (correct == False)
    
    # Get the images from the test-set that have been
    # incorrectly classified.
    images = data.x_test[incorrect]
    
    # Get the predicted classes for those images.
    cls_pred = cls_pred[incorrect]

    # Get the true classes for those images.
    cls_true = data.y_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])
复制代码

#Helper-function to plot the model weights 用于绘制模型权重的函数。 绘制10个图像,每一个数字一个,模型被训练识别。

def plot_weights():
    # Get the values for the weights from the TensorFlow variable.
    w = session.run(weights)
    
    # Get the lowest and highest values for the weights.
    # This is used to correct the colour 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,
    # where the last 2 sub-plots are unused.
    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([])
        
    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()
复制代码

#Performance before any optimization 测试集的准确度为9.8%。 这是由于模型只是初始化而根本没有优化,所以它老是预测图像显示零位数,以下图所示,结果发现测试集中9.8%的图像发生了 为零位数。

print_accuracy()
Accuracy on test-set: 9.8%
plot_example_errors()
复制代码

Performance after 1 optimization iteration(1次优化迭代后的性能)

optimize(num_iterations=1)
print_accuracy()
Accuracy on test-set: 15.9%
plot_example_errors()
复制代码

权重也能够以下所示绘制。 正权重为红色,负权重为蓝色。 这些权重能够直观地理解为图像过滤器。

例如,用于肯定图像是否显示零位的权重对圆的图像具备正反应(红色),而且对具备在圆的中心的内容的图像具备负反应(蓝色)。

相似地,用于肯定图像是否显示一位数的权重对图像中心的垂直线呈正(红色)反应,而且对具备围绕该线的内容的图像做出负反应(蓝色)。

请注意,权重大多看起来像是他们应该识别的数字。 这是由于仅执行了一次优化迭代,所以权重仅在100个图像上进行训练。 在对数千个图像进行训练以后,权重变得更难以解释,由于它们必须识别数字如何被写入的许多变化。

plot_weights()
复制代码

Performance after 10 optimization iterations(10次优化迭代后的性能)

optimize(num_iterations=9)
print_accuracy()
Accuracy on test-set: 66.2%
plot_example_errors()
复制代码

plot_weights()
复制代码

Performance after 1000 optimization iterations(1000次优化迭代后的性能) 在1000次优化迭代以后,模型仅对大约十分之一的图像进行错误分类。 以下所示,一些误分类是合理的,由于即便对于人类来讲,图像也很难肯定,而其余图像很是明显,应该经过一个好的模型正确分类。 可是这种简单的模型没法达到更好的性能,所以须要更复杂的模型。

optimize(num_iterations=990)
print_accuracy()
Accuracy on test-set: 91.5%
plot_example_errors()
复制代码

该模型现已通过1000次优化迭代的训练,每次迭代使用来自训练集的100个图像。 因为图像种类繁多,权重如今变得难以解释,咱们可能会怀疑模型是否真正理解数字是如何由线条组成的,或者模型是否只记忆了许多不一样的像素变化。

plot_weights()
复制代码

咱们还能够打印和绘制所谓的混淆矩阵,让咱们能够看到有关错误分类的更多细节。 例如,它显示实际描绘5的图像有时被错误分类为全部其余可能的数字,但大多数为6或8。

print_confusion_matrix()
复制代码

[[ 956 0 3 1 1 4 11 3 1 0] [ 0 1114 2 2 1 2 4 2 8 0] [ 6 8 925 23 11 3 13 12 26 5] [ 3 1 19 928 0 34 2 10 5 8] [ 1 3 4 2 918 2 11 2 6 33] [ 8 3 7 36 8 781 15 6 20 8] [ 9 3 5 1 14 12 912 1 1 0] [ 2 11 24 10 6 1 0 941 1 32] [ 8 13 11 44 11 52 13 14 797 11] [ 11 7 2 14 50 10 0 30 4 881]]

咱们如今使用TensorFlow完成,所以咱们关闭会话以释放其资源。

# This has been commented out in case you want to modify and experiment
# with the Notebook without having to restart it.
# session.close()
复制代码
相关文章
相关标签/搜索