我们在这篇博文中,将向大家介绍采用TensorFlow Eager Execution API,来实现一个最简单的多层感知器(MLP)模型,并用于MNIST手写数字识别。需要注意的是,我们在这里并不是讲解的TenforFlow Eager Execution官方的例子,因为该例子封装得太多了,使我们无从了解其中的细节。在大家学习深度学习技术时,一个特别典型的痛点就是看到数学理论,似乎也看明白了,再来看TensorFlow的例程,也可以看明白,但是如何从数学公式到TensorFlow程序,例如如何求偏导、如何更新参数等等,就还是不太明白。在这里我们将完全从头开始,编写一个完整的多层感知器(MLP)模型,并以前两篇博文的数学理论为指导,让大家真正理解深度学习理论与实践技术,真正打通数学理论与程序实现之间的鸿沟。
我们首先来研究一下MNIST数据集,原来我们在读取MNIST数据集时,都是用input_data.read_data_sets(‘MNIST_data’, one_hot=True),但是在新版本中,会提示这个API已经过期,将在未来移除。所以我们需要使用models中的dataset类,为了使用这个类,我们需要先安装tensorflow models。这是一个三方库,有很多安装方式,这里向大家介绍最简单的一种,源码方式安装。
我们在项目根目录下,创建libs/tf目录,用这个目录放置与tensorflow相关的第三方库。进入该目录,通过Git下载源码:
git clone https://github.com/tensorflow/models.git
我们首先应该将下载了数据集按照例如20%的比例,将80%定为训练样本集,将剩余的20%作为验证样本集,我们用训练样本集训练我们的模型,然后每隔一定时间,在验证样本集上计算一下正确率,如果正确率有所提高,则继续拿训练样本集训练模型,反之如果发现在验证样本集上的精度出现下降趋势,则停止训练过程,这就是著名的Early Stopping算法。对于新的dataset类,网上还没有比较详细的讲解这一过程的内容,所以这一部分会讲得比较详细。
我们将TensorFlow的models下载到libs/tf/models目录下,我们首先需要将这个目录添加到Python的搜索目录中:
import sys
sys.path.append('./libs/tf/')
......
from models.official.mnist import dataset
接下来我们来看MNIST数据集具体读入过程:
class Mlpee(object):
batch_size = 128
shuffle_size = 60000
validation_percent = 0.2
raw_size = 60000
@staticmethod
def load_dataset():
tfe = tf.contrib.eager
# 将训练样本集划分为训练样本集和验证样本集(20%)
raw_ds = dataset.train('datas').shuffle(Mlpee.shuffle_size).batch(Mlpee.batch_size)
train_image = []
train_label = []
validation_image = []
validation_label = []
for batch in tfe.Iterator(raw_ds):
b_size = batch[0].shape[0]
batch_x = []
batch_y = []
for idx in range(b_size):
batch_x.append(np.array(batch[0][idx]))
batch_y = Mlpee.make_one_hot_Y(batch[1]).numpy().reshape(b_size, 10)
if random.random() < 0.2:
validation_image.append(np.array(batch_x))
validation_label.append(np.array(batch_y))
else:
train_image.append(np.array(batch_x))
train_label.append(batch_y)
train_x = np.array(train_image)
train_y = np.array(train_label)
validation_x = np.array(validation_image)
validation_y = np.array(validation_label)
return train_x, train_y, validation_x, validation_y
第10行:我们首先读出MNIST所有数据,然后将样本顺序打乱进行随机重排(增加随机性可以增加模型的准确性),最后按照每个迷你批次128条记录,将样本分为迷你批次
第15行~27行:对每个迷你批次进行循环处理:
先求出该批次包含的样本数,虽然我们规定迷你批次大小为128,但是最后一个迷你批次,个数可能小于128,例如本例中最后一个迷你批次的大小就是96。
对于每个批次,由128个样本组成,每个样本下标为0的元素为784(28*28个像素值)数组,下标为1的元素为一个整数,代表是0到9之间的哪个数字。因此我们直接将下标为0的元素添加到batch_x中,对于下标为1的元素,我们以整个批次为单位,调用Mlpee.make_one_hot_Y函数将其转化为对应的one-hot矩阵,例如原来下标为1的元素值为2,代表结果应该为数字2,调用该函数后将变为:
[0,0,1,0,0,0,0,0,0,0]
[
0
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
,将结果赋给batch_y。
我们以20%的概率将样本分配到验证样本集,这里实现的方式为产生一个随机数,当值小于0.2时就将其作为验证样本批次,否则作为训练样本批次。
下面我们来看怎样将某批次中下标为1的元素,由整数转变为one-hot向量形式:
@staticmethod
def make_one_hot_Y(Y_raw):
y_size = Mlpee.batch_size
if y_size > Y_raw.shape[0]:
y_size = Y_raw.shape[0]
Y_raw = tf.expand_dims(Y_raw, 1)
indices = tf.expand_dims(tf.range(0, y_size, 1), 1)
concated = tf.concat([indices, Y_raw],1)
return tf.sparse_to_dense(concated, tf.stack([y_size, 10]), 1.0, 0.0)
我们举一个简单的例子,让大家可以明白上面代码的意义。假设我们迷你批次的大小为3,即包括3个样本,下标为1的元素形式为:[2, 3, 5],代表正确答案为2、3、5。这个函数就是将这些数字转换为one-hot表示方式,[[0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]]形式。
程序开始部分的判断是为了处理最后一个批次样本数小于规定样本数的情况。
有了这些准备工作之后,我们就可以读入训练样本集、验证样本集,并绘制出其图形和相应的识别结果:
@staticmethod
def startup(args={}):
random.seed(10)
print('基于Eager Execution的多层感知器模型')
tf.enable_eager_execution()
tfe = tf.contrib.eager
# 获取训练及验证样本集
train_x, train_y, validation_x, validation_y = Mlpee.load_dataset()
print('train_x: {0} item: {1}'.format(train_x.shape, train_x[0].shape))
print('train_y: {0} item: {1}'.format(train_y.shape, train_y[0].shape))
# 显示训练样本集某记录
batch_idx = 1
rec_idx = 5
train_image = (train_x[batch_idx][rec_idx] * 255).reshape([28, 28])
rst = np.nonzero(train_y[batch_idx][rec_idx])[0]
plt.title('Training Result:{0}'.format(rst))
plt.imshow(train_image)
plt.show()
# 显示验证样本集某记录
validation_image = (validation_x[batch_idx][rec_idx]*255).reshape([28, 28])
rst = np.nonzero(validation_y[batch_idx][rec_idx])[0]
plt.title('Validation Result:{0}'.format(rst))
plt.imshow(validation_image)
plt.show()
我们首先读入训练样本集图像集train_x、训练样本集标签集train_y、验证样本集图像集validation_x、验证样本集标签集validation_y。然后分别绘制指定批次序号和批次内样本序号的样本的图像和对应的正确结果,对训练样本集和验证样本集分别进行绘制,运行上面的程序会显示类似下面的结果:
注意:由于我们加入了随机排序,读者绘制的图片与我这里的可能会有区别。
我们在本篇博文中,采用TensorFlow新版dataset API,并采用Eager模式,直接读取MNIST手写数字识别数据集,同时将其划分为训练样本集和验证样本集,在下一节中,我们将正式开始创建多层感知器(MLP)模型。