1. 前言
MNIST是一个手写数字识别的数据集,包含0-9的数字图片共计60000张,其中训练集有55000张,验证集5000张,测试集10000张。TensorFlow是Google官方推出的机器学习框架,可用于开发各种人工智能应用。本文将介绍如何使用TensorFlow实现对MNIST数据集的手写数字识别。
2. 数据集下载
首先,我们需要从Yann LeCun的网站上下载MNIST数据集。可以使用以下Python代码自动下载MNIST:
import urllib.request
import os
url = 'http://yann.lecun.com/exdb/mnist/'
filenames = ['train-images-idx3-ubyte.gz', 'train-labels-idx1-ubyte.gz',
't10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz']
if not os.path.exists('data'):
os.mkdir('data')
for filename in filenames:
urllib.request.urlretrieve(url+filename, 'data/'+filename)
这将在当前目录下创建一个名为"data"的文件夹,并下载MNIST数据集文件到该文件夹。
3. 数据集预处理
数据集下载完成后,我们需要对数据进行预处理。在TensorFlow中,MNIST数据集可以使用input_data
模块进行加载。我们可以使用以下代码从数据集文件中读取MNIST数据:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('data', one_hot=True)
其中的one_hot
参数表示是否将数字标签转换成One-hot向量。数据集加载完成后,我们可以使用以下代码查看数据集的基本信息:
print('Training data shape:', mnist.train.images.shape)
print('Training labels shape:', mnist.train.labels.shape)
print('Validation data shape:', mnist.validation.images.shape)
print('Validation labels shape:', mnist.validation.labels.shape)
print('Test data shape:', mnist.test.images.shape)
print('Test labels shape:', mnist.test.labels.shape)
这将输出训练集、验证集和测试集的图像和标签的形状。
4. 构建神经网络模型
我们将使用TensorFlow构建一个简单的神经网络模型。我们的模型将包含一个带有128个隐藏单元的全连接层和一个输出大小为10的全连接层。我们将使用softmax激活函数将输出映射到0-1的概率分布。
我们首先需要定义模型的输入占位符。我们的输入图像大小为28x28,所以我们需要一个形状为(None, 784)的2D张量,其中None表示该张量的行数可以是任意值。我们还需要一个形状为(None, 10)的占位符,用于存储数字标签:
import tensorflow as tf
x = tf.placeholder(tf.float32, shape=[None, 784])
y_true = tf.placeholder(tf.float32, shape=[None, 10])
接下来,我们定义模型的权重和偏差变量。随机产生的权重和偏差将在模型的训练过程中不断更新,以最小化损失函数。我们可以使用以下代码定义权重和偏差变量:
weights = tf.Variable(tf.truncated_normal([784, 128], stddev=0.1))
biases = tf.Variable(tf.constant(0.1, shape=[128]))
output_weights = tf.Variable(tf.truncated_normal([128, 10], stddev=0.1))
output_biases = tf.Variable(tf.constant(0.1, shape=[10]))
接下来,我们定义模型的中间计算过程。我们将输入x与权重矩阵相乘,并加上偏差向量,然后应用ReLU激活函数。我们将这个中间结果传递给输出层的权重矩阵,再加上输出层的偏差向量。最后,我们使用softmax激活函数将输出映射到0-1的概率分布:
hidden_layer = tf.nn.relu(tf.matmul(x, weights) + biases)
logits = tf.matmul(hidden_layer, output_weights) + output_biases
y_pred = tf.nn.softmax(logits)
我们使用交叉熵损失函数来衡量模型的性能。交叉熵损失函数测量了模型输出的概率分布与真实标签之间的差距:
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=logits))
我们使用梯度下降算法来最小化交叉熵损失函数。梯度下降算法越接近损失函数的最小值,它调整的步长就越小,因此它可以在最小值处停止:
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
我们将使用准确率作为模型的性能指标。我们将计算模型输出中的最大值,该最大值对应于预测的标签,然后将其与真实标签进行比较以计算准确率。我们可以使用以下代码计算准确率并输出结果:
correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_true, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_true: batch_ys})
if i % 100 == 0:
print('Step %d, training accuracy %g' % (i, sess.run(accuracy, feed_dict={x: mnist.train.images,
y_true: mnist.train.labels})))
这将输出训练过程中每100步的准确率。
5. 结果分析
接下来,我们使用测试集评估模型的性能:
test_accuracy = sess.run(accuracy, feed_dict={x:mnist.test.images, y_true:mnist.test.labels})
print('Test accuracy:', test_accuracy)
这将输出测试集上的准确率。temperature=0.6时,我们的模型在测试集上的准确率约为98.3%。这是一个相当不错的结果,但我们可以通过改进模型来提高准确率。
5.1. 调整超参数
我们可以通过调整模型超参数来改进模型。我们将尝试在隐藏层中增加神经元数量和增加训练迭代次数。我们可以使用以下代码对模型进行修改:
weights = tf.Variable(tf.truncated_normal([784, 512], stddev=0.1))
biases = tf.Variable(tf.constant(0.1, shape=[512]))
output_weights = tf.Variable(tf.truncated_normal([512, 10], stddev=0.1))
output_biases = tf.Variable(tf.constant(0.1, shape=[10]))
hidden_layer = tf.nn.relu(tf.matmul(x, weights) + biases)
logits = tf.matmul(hidden_layer, output_weights) + output_biases
y_pred = tf.nn.softmax(logits)
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=logits))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_true, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(2000):
batch_xs, batch_ys = mnist.train.next_batch(256)
sess.run(train_step, feed_dict={x: batch_xs, y_true: batch_ys})
if i % 100 == 0:
print('Step %d, training accuracy %g' % (i, sess.run(accuracy, feed_dict={x: mnist.train.images,
y_true: mnist.train.labels})))
test_accuracy = sess.run(accuracy, feed_dict={x:mnist.test.images, y_true:mnist.test.labels})
print('Test accuracy:', test_accuracy)
我们将隐藏层的神经元数量增加到512,每个批次中使用的样本量也增加到256。这样做会增加模型的计算成本,但也有可能提高模型的准确率。当temperature=0.6时,我们的模型在测试集上的准确率约为98.4%。
5.2. 使用卷积神经网络
我们可以使用卷积神经网络来处理MNIST数据集。卷积神经网络可以利用空间特征,以比全连接神经网络更少的参数学习更复杂的特征。我们将使用以下Python代码定义一个包括2个卷积层和2个全连接层的卷积神经网络:
x = tf.placeholder(tf.float32, shape=[None, 784])
y_true = tf.placeholder(tf.float32, shape=[None, 10])
conv1_weights = tf.Variable(tf.truncated_normal([5, 5, 1, 32], stddev=0.1))
conv1_biases = tf.Variable(tf.constant(0.1, shape=[32]))
x_image = tf.reshape(x, [-1,28,28,1])
conv1 = tf.nn.conv2d(x_image, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
conv1_relu = tf.nn.relu(conv1 + conv1_biases)
pool1 = tf.nn.max_pool(conv1_relu, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
conv2_weights = tf.Variable(tf.truncated_normal([5, 5, 32, 64], stddev=0.1))
conv2_biases = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
conv2_relu = tf.nn.relu(conv2 + conv2_biases)
pool2 = tf.nn.max_pool(conv2_relu, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
fc1_weights = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024], stddev=0.1))
fc1_biases = tf.Variable(tf.constant(0.1, shape=[1024]))
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
fc1 = tf.nn.relu(tf.matmul(pool2_flat, fc1_weights) + fc1_biases)
fc2_weights = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
fc2_biases = tf.Variable(tf.constant(0.1, shape=[10]))
logits = tf.matmul(fc1, fc2_weights) + fc2_biases
y_pred = tf.nn.softmax(logits)
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=logits))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_true, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(20000):
batch_xs, batch_ys = mnist.train.next_batch(50)
sess.run(train_step, feed_dict={x: batch_xs, y_true: batch_ys})
if i % 100 == 0:
print('Step %d, training accuracy %g' % (i, sess.run(accuracy, feed_dict={x: mnist.train.images,
y_true: mnist.train.labels})))
test_accuracy = sess.run(accuracy, feed_dict={x:mnist.test.images, y_true:mnist.test.labels})
print('Test accuracy:', test_accuracy)
这个卷积神经网络包含2个卷积层和2个全连接层。卷积层在数字图像中提取特征并通过ReLU激活函数进行处理,池化层则将图像分辨率减小,减轻了模型的计算负担。我们使用Adam优化算法,该算法可以动态调整学习率以加速收敛。使用上述模型训练恐怖难以完成,笔者将训练次数减少至20000,最终测试准确度约99.2%。
6. 总结
本文介绍了如何使用TensorFlow实现对MNIST数据集的手写数字识别。我们展示了如何使用全连接神经网络和卷积神经网络来训练模型。我们还讨论了如何调整超参数以优化模型性能。最终,我们得出了一个约99.2%的测试准确度。相信通过本文的学习,读者可以了解到使用TensorFlow解决实际问题的步骤,同时对TensorFlow的基础有进一步的了解。