春暖花开

使用 Tensorflow 实现神经网络

简介

如果你一直关注数据科学/机器学习,那么你肯定知道目前深度学习和神经网络非常流行。许多组织/公司都想要雇佣精通深度学习技能的人。从商业竞争到开源项目和支付高额薪水,人们在尝试一切可能的方法来挖掘这一有限的人才。自我驱动的工程师正被汽车工业中的利器所追逐,因为汽车工业正处于近几十年来最大的破坏边缘!

如果你对深度学习的前景感到兴奋,但还没有开始你的旅途,那么我将会开启你的旅程。从这篇文章开始,我将会写一系列关于深度学习的文章,包括最受欢迎的深度学习库和它们的实现。

在这篇文章中,我将会向你介绍 Tensorflow。读完这篇文章以后,你将会了解神经网络的应用,并且能够使用 TensorFlow 来解决实际生活中的问题。阅读本文前,你需要了解神经网络的基本知识并且对编程比较熟悉。虽然本文中使用的语言是 Python,但我关注得更多的是概念,并且尽可能保持语言的不可知性。

让我们开始吧!

TensorFlow

何时使用神经网络?

神经网络成为人们的关注点已经有很长的一段时间了。如果你想了解关于神经网络和深度学习的更详细的解释,请阅读这篇文章。它的 “depper” 版本在图像识别、语音和自然语言处理等许多领域都取得了巨大的突破。

最主要的问题是什么时候使用神经网络,什么时候不用?这个领域就像现在的金矿一样,每天都会有许多新的发现。为了成为一个“淘金热”分子,你需要记住下面这些事情:

  • 首先,神经网络需要清晰而详实的数据(主要是大数据)来进行训练。如果把神经网络想象成一个孩子,他首先需要观察他的父母如何走路,然后尝试独立行走,用他的每一步,学会如何来完成一个特定的任务。他可能会跌倒几次,但经过几次失败的尝试,他便学会了如何走路。如果你不让他自己尝试,那么他可能永远也不能学会走路。你给他接触的越多,那么他就会学得越好。
  • 使用神经网络来处理一些复杂的问题,比如图像处理是明智的。神经网络属于一类称为表示学习算法的算法类。这些算法将复杂的问题分解成简单的形式,从而使它们能够理解(或“表示“)。就像你在吃东西的时候,需要先把食物嚼碎,然后才咽下。这比传统(非表示学习)算法更加困难。
  • 什么时候你能够拥有合适的神经网络来解决问题?每个问题都有它的困难之处。所以,数据决定了解决问题的方式。比如,如果问题是序列生成,那么递归神经网络更加合适。然而,如果是图像相关的问题,那么改用卷积神经网络会更好。
  • 最后,但不是不重要,硬件需求对于运行一个深度神经网络模型是至关重要的。神经网络模型在很早之前就有了,但是在近些年才开始活跃起来,主要原因就是计算资源更好,更强大了。如果你想用神经网络来解决现实生活中的问题,请准备购买一些高端的硬件设备。

使用神经网络来解决问题的通常手段

神经网络是一种特殊的机器学习(ML)算法。就像每一个机器学习算法一样,你需要按照通常的机器学习工作流程来进行数据预处理,模型建立和模型评估。为了简单起见,我在下面列出了一个处理神经网络问题的清单:

  • 思考一下,使用神经网络算法来处理该问题是否比用传统算法更好(参考上面章节的清单)
  • 调查一下,待解决的问题使用哪种神经网络更适合
  • 使用你选择的语言/库来定义神经网络结构
  • 将数据转化为正确的格式并进行数据划分
  • 根据你的需要对数据进行预处理
  • 添加数据来增大数据量从而更好的训练模型
  • 将数据”喂给”神经网络进行训练
  • 训练并监测训练过程中的变化和验证数据集的变化
  • 测试你的模型,并将它保存下来以便将来使用

在这篇文章中,我将重点放在图像数据上。所以在开始探讨 TensorFlow 之前,让我们先理解一下图像数据。

图像数据以及常用于解决图像问题的库

图像数据通常以三维数据的形式排序,每一维分别代表高、宽和颜色通道。比如,此时你在你的电脑上截一张图,它将会先转化为一个三维数组,然后再压缩成 .jpeg.png 文件格式。

虽然这些图像对于人类来说很容易理解,但是计算机却很难理解它们。这种现象被称为“语义鸿沟”。我们的大脑能够在几秒钟的时间内看到图像并理解完整的图像,然而,计算机却只是把图像看成一组数字。所以问题是如何向计算机解释图片?

在早期时候,人们试图将图像分解成机器“可理解”的形式,就像一个模板一样。比如,人脸总是有一种特定的结构,这种结构在每个人中都有一定的保留,例如眼睛、鼻子或脸的形状。但是这种方法很乏味,因为当要识别的对象数量增加时,“模板”就不能保存了。

快速前进到 2012 年,一个深度神经网络赢得了 ImageNet 挑战,这是一个根据自然场景来识别对象的著名挑战。在所有随即到来的 ImageNet 挑战中,它持续占领统治地位,从而证明了用它来解决图像问题的有用性。

所以,人们通常使用哪种语言(库)来解决图像识别问题呢?在一个最近的调查中,我发现绝大多数受欢迎的深度学习库都有 Python 接口,紧跟着的是 Lua、Java 和 Matlab 。下面是一些最受欢迎的库:

现在,你已经知道了一张图像是如何存储的,并且知道了常用的库。下面,让我们来看一下 TensorFlow 能够为我们提供什么。

TensorFlow 是什么?

我们先看一下官方的定义:

“TensorFlow 是一个使用数据流图来进行数值计算的开源软件库。图中的节点表示数学运算,而图中的边表示在节点之间进行传递的多维数据数组(又叫做张量)。灵活的架构允许你在桌面、服务器或移动设备上通过一个单一的 API 使用一个或多个 CPU 或 GPU 部署进行计算。”

tensors_flowing tensors_flowing

如果这听起来很让人迷糊,别担心,下面是我的简单定义:你可以简单的把 TensorFlow 看成一个扭曲的 numpy。如果你过去接触过 numpy,那么理解 TensorFlow 就非常简单了。numpy 和 TensorFlow 之间最主要的区别就是 TensorFLow 使用一种惰性的编程范式。它首先搭建一个把所有操作都定义好了的图,然后,当一个“会话“被调用时,它就会开始”运行“这个图。这个图是可扩展的,只需要改变内部张量(又叫做多维数组)的数据表示即可。构建计算图可以看作是使用 TensorFlow 的主要工作。如果想了解更多关于计算图的数学组成,请阅读这篇文章

我们很容易把 TensorFlow 归为一个神经网络库,但它不仅仅是这样。的确,它是一个非常强大的神经网络库。但是,它还能够做更多的事情。你可以使用它来构建其他机器学习算法,比如决策树或 K-近邻算法。你可以用它来完成任何你通常用 numpy 来完成的事情。因此它又被形象地称为 “NumPy 类固醇”。

使用 TensorFlow 的优点有:

  • 它具有一个直观的结构。顾名思义它具有”张量流“。你可以轻松的可视化图的每一个部分。
  • 对于分布式计算,很容易在 CPU/GPU 上面进行训练。
  • 平台灵活性。你可以在任何地方运行你的模型,移动设备、服务器或 PC 。

一个典型的 TensorFlow ”流“

每个库都有自己的”实现细节“,即一种遵循其编码范式的编写方式。例如,在实现 scikit-learn 时,你首先创建一个需要的算法对象,然后构建一个训练模型,进而对测试集进行预测,看起来就像下面这样:

1
2
3
4
5
6
# 定义机器学习算法的 hyperparamters
clf = svm.SVC(gamma=0.001, C=100.)
# 训练
clf.fit(X, y)
# 测试
clf.predict(X_test)

正如前面说过的,TensorFlow 遵循一种惰性的编程范式。在 TensorFlow 中运行一个程序的通常工作流如下:

  • 构建一个计算图,它可以是 TensorFlow 支持的任意一种数学操作。
  • 初始化变量,编译之前定义的变量
  • 创建会话,这是魔法开始的地方
  • 在会话中运行图,编译后的图传到会话中,然后会话开始执行
  • 关闭会话

TensorFlow 中使用的几个术语:

  • 1
    占位符:一种将数据输入图表的方法
  • 1
    feed_dict: 一个将数组传递给计算图的字典

让我们写一个小程序并添加两个数字!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 导入 TensorFlow
import tensorflow as tf
# 搭建计算图
a = tf.placeholder(tf.int16)
b = tf.placeholder(tf.int16)
addition = tf.add(a, b)
# 初始化变量
init = tf.initialize_all_variables()
# 创建会话并运行图
with tf.Session() as sess:
sess.run(init)
print "Addition: %i" % sess.run(addition, feed_dict={a: 2, b: 3})
# 关闭会话
sess.close()

在 TensorFlow 中实现神经网络

注意:我们可以使用不同的神经网络结构来解决问题,但为了简单起见,在这儿,我们致力于一个前馈多层感知机的深层实现。

让我们首先记住我们学到的关于神经网络的知识。

一个典型的神经网络实现如下:

  • 定义需要编译的神经网络结构
  • 将数据传输到模型
  • 在后台,数据首先被分成批,以便进行摄取,首先对批数据进行预处理,扩充,然后送入神经网络进行训练
  • 神经网络模型进行增量训练
  • 在特定时间显示准确率
  • 训练过后,保存模型,以便日后再次使用
  • 用新数据测试模型并检查它是如何执行的

在这儿,我们解决深度学习实践问题 - 识别数字。让我们先看一下问题陈述。

这是一个图像识别问题,从一个给定的 28x28 的图像中识别数字。我们将所有图像的一个子集用于训练,剩余部分用于测试模型。首先,下载训练和测试文件。数据集包含一个所有图像的压缩文件,以及名字和训练/测试图像相对应的 train.csvtest.csv 文件。在数据集中没有提供任何额外的特征,只提供了 .png 格式的原始图像。

正如你所知道的,我们将使用 TensorFlow 来搭建一个神经网络模型。所以你首先需要在你的系统上安装 TensorFlow。请参考官方安装指南,根据你的系统进行安装。

我们将按照上面描述的步骤来搭建神经网络。首先,使用 Python 2.7 核来创建一个 Jupyter notebook,后续步骤如下:

  • 首先导入所有需要的模块:
1
2
3
4
5
6
7
# pylab inline
import os
import numpy as np
import pandas as pd
from scipy.misc import imread
from sklearn.metrics import accuracy_score
import tensorflow as tf
  • 让我们设置一个种子值,从而我们能够控制模型的随机性
1
2
3
# To stop potential randomness
seed = 128
rng = np.random.RandomState(seed)
  • 第一步是设置目录路径,以便安全保存!
1
2
3
4
5
6
7
8
root_dir = os.path.abspath('../..')
data_dir = os.path.join(root_dir, 'data')
sub_dir = os.path.join(root_dir, 'sub')
# check for existence
os.path.exists(root_dir)
os.path.exists(data_dir)
os.path.exists(sub_dir)
  • 现在,让我们读入数据集。数据集是 .csv 文件格式的,并且有一个伴有合适标签的名字。
1
2
3
4
5
6
train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))
test = pd.read_csv(os.path.join(data_dir, 'Test.csv'))
sample_submission = pd.read_csv(os.path.join(data_dir, 'Sample_Submission.csv'))
train.head()
文件名 标签
0 0.png 4
1 1.png 9
2 2.png 1
3 3.png 7
4 4.png 3
  • 让我们看一下我们的数据长什么样!读取图片并显示:
1
2
3
4
5
6
7
8
img_name = rng.choice(train.filename)
filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
img = imread(filepath, flatten=True)
pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()

3

上面的图片表示为 numpy 数组,就是下面这样:

one

  • 为了使数据操作更简单,我们把所有图像存为 numpy 数组:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
temp = []
for img_name in train.filename:
image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
img = imread(image_path, flatten=True)
img = img.astype('float32')
temp.append(img)
train_x = np.stack(temp)
temp = []
for img_name in test.filename:
image_path = os.path.join(data_dir, 'Train', 'Images', 'test', img_name)
img = imread(image_path, flatten=True)
img = img.astype('float32')
temp.append(img)
test_x = np.stack(temp)
  • 因为这是一个典型的机器学习问题,为了测试模型的正常功能,我们需要创建一个验证集。我们将数据按 7:3 分为训练集和验证集。
1
2
3
4
split_size = int(train_x.shape[0]*0.7)
train_x, val_x = train_x[:split_size], train_x[split_size:]
train_y, val_y = train.label.values[:split_size], train.label.values[split_size:]
  • 现在,在程序中定义一些我们在后面将会用到的有用函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def dense_to_one_hot(labels_dense, num_classes=10):
"""Convert class labels from scalars to one-hot vectors"""
num_labels = labels_dense.shape[0]
index_offset = np.arange(num_labels) * num_classes
labels_one_hot = np.zeros((num_labels, num_classes))
labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
return labels_one_hot
def preproc(unclean_batch_x):
"""Convert values to range 0-1"""
temp_batch = unclean_batch_x / unclean_batch_x.max()
return temp_batch
def batch_creator(batch_size, dataset_length, dataset_name):
"""Create batch with random samples and return appropriate format"""
batch_mask = rng.choice(dataset_length, batch_size)
batch_x = eval(dataset_name + '_x')[[batch_mask]].reshape(-1, input_num_units)
batch_x = preproc(batch_x)
if dataset_name == 'train':
batch_y = eval(dataset_name).ix[batch_mask, 'label'].values
batch_y = dense_to_one_hot(batch_y)
return batch_x, batch_y
  • 现在,到了主要部分。让我们首先来定义神经网络的结构。我们定义一个三层神经网络:输入层、隐含层和输出层。输入层和输出层的神经元数目是固定的,因为输入就是一个 28x28 的图像,输出就是一个 10x1 的表示类别的向量。在隐含层中,我们设置 500 个神经元,这个数目可以根据你的需要来设置。同时,我们也需要对其余变量赋值。阅读神经网络基础这篇文章来深入了解它是如何工作的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
### set all variables
# number of neurons in each layer
input_num_units = 28*28
hidden_num_units = 500
output_num_units = 10
# define placeholders
x = tf.placeholder(tf.float32, [None, input_num_units])
y = tf.placeholder(tf.float32, [None, output_num_units])
# set remaining variables
epochs = 5
batch_size = 128
learning_rate = 0.01
### define weights and biases of the neural network (refer this article if you don't understand the terminologies)
weights = {
'hidden': tf.Variable(tf.random_normal([input_num_units, hidden_num_units], seed=seed)),
'output': tf.Variable(tf.random_normal([hidden_num_units, output_num_units], seed=seed))
}
biases = {
'hidden': tf.Variable(tf.random_normal([hidden_num_units], seed=seed)),
'output': tf.Variable(tf.random_normal([output_num_units], seed=seed))
}
  • 现在,开始创建神经网络计算图:
1
2
3
4
hidden_layer = tf.add(tf.matmul(x, weights['hidden']), biases['hidden'])
hidden_layer = tf.nn.relu(hidden_layer)
output_layer = tf.matmul(hidden_layer, weights['output']) + biases['output']
  • 我们也需要定义神经网络的损失(cost):
1
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(output_layer, y))
  • 设置优化算法,比如后向传播算法(BP 算法)。在这儿,我们使用 Adam,这是一个高效的梯度下降算法的变种。在 TensorFlow 中还有许多可用的优化算法(参考这儿
1
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
  • 定义好神经网络结构之后,初始化所有变量:
1
init = tf.initialize_all_variables()
  • 现在,创建一个会话,在会话中运行我们的神经网络。同时,使用我们已经创建好的验证集来验证我们的模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
with tf.Session() as sess:
# create initialized variables
sess.run(init)
### for each epoch, do:
### for each batch, do:
### create pre-processed batch
### run optimizer by feeding batch
### find cost and reiterate to minimize
for epoch in range(epochs):
avg_cost = 0
total_batch = int(train.shape[0]/batch_size)
for i in range(total_batch):
batch_x, batch_y = batch_creator(batch_size, train_x.shape[0], 'train')
_, c = sess.run([optimizer, cost], feed_dict = {x: batch_x, y: batch_y})
avg_cost += c / total_batch
print "Epoch:", (epoch+1), "cost =", "{:.5f}".format(avg_cost)
print "\nTraining complete!"
# find predictions on val set
pred_temp = tf.equal(tf.argmax(output_layer, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(pred_temp, "float"))
print "Validation Accuracy:", accuracy.eval({x: val_x.reshape(-1, input_num_units), y: dense_to_one_hot(val_y)})
predict = tf.argmax(output_layer, 1)
pred = predict.eval({x: test_x.reshape(-1, input_num_units)})

上面代码的输出如下:

1
2
3
4
5
6
7
8
Epoch: 1 cost = 8.93566
Epoch: 2 cost = 1.82103
Epoch: 3 cost = 0.98648
Epoch: 4 cost = 0.57141
Epoch: 5 cost = 0.44550
Training complete!
Validation Accuracy: 0.952823
  • 为了使用我们的眼睛来测试模型,让我们来看一些它的预测结果:
1
2
3
4
5
6
7
8
9
10
11
12
img_name = rng.choice(test.filename)
filepath = os.path.join(data_dir, 'Train', 'Images', 'test', img_name)
img = imread(filepath, flatten=True)
test_index = int(img_name.split('.')[0]) - 49000
print "Prediction is: ", pred[test_index]
pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()

Prediction is: 8

  • 我们看到模型的性能非常好。现在我们来创建一个子会话:
1
2
3
4
5
sample_submission.filename = test.filename
sample_submission.label = pred
sample_submission.to_csv(os.path.join(sub_dir, 'sub01.csv'), index=False)

到现在,我们就把已经训练好的神经网络保存下来了。

TensorFlow 的局限性

  • 尽管 TensorFlow 很强大,但它依旧是一个低级库,打个比方,你可以把它看出一个机器级语言。但是大多数情况下,你需要模块化和高层次的接口,比如 keras 这样的库便能够提供。
  • TensorFlow 目前仍然在开发中,因此未来还会有很多令人激动的东西到来。
  • TensorFlow 依赖于你的硬件配置,硬件配置越高越好。
  • 对于许多语言,TensorFlow 还没有 API.
  • TensorFlow 中还有许多东西需要实现,比如 OpenCL 的支持。

上面提到的内容大多数是站在 TensorFlow 开发者的角度的。他们已经制定了一个路线图来说明将来这个库应该如何发展。

TensorFlow vs. 其他库

TensorFlow 是以和 Theano 和 Torch 相似的原则构建的,都是使用数学计算图。但是由于具有分布式计算的支持,TensorFlow 对于解决复杂的问题表现得更好。同时,由于已经支持 TensorFlow 模型的部署(即开源免费),这使得在工业上使用更加容易,对 Deeplearning4j、H2O 和 Turi 这些商业库造成了竞争威胁。TensorFlow 有 Python、C++ 和 Matlab 的 API,接下来可能将要有对其他语言的支持,比如 Ruby 和 R。所以,TensorFlow 正在试图成为具有普遍语言的机器学习库。

接下来往哪走

你已经看到了如何使用 TensorFlow 来建立一个简单的神经网路。这个代码对于来理解如何开始实现 TensorFlow 是有意义的,所以不要小瞧它。但是请记住现实生活需要解决的问题将会更加复杂,你需要稍微修改一下代码。

上面的许多函数能够被抽象出来,以提供无缝的端到端工作流。如果你使用过 scikit-learn,那么你可能知道一个高级库是如何抽象出底层实现,然后给终端用户提供一个更加简单的接口的。虽然 TensorFlow 已经抽象出了大多数的实现,但高级库正在出现,比如 TF-slim 和 TFlearn 。

有用的资源

结束语

我希望你觉得这篇文章很有帮助,现在,是时候来进行更多的练习和阅读了。Good luck!如果你使用一个不同的方法/包/库来实现神经网络,那么我很愿意在评论中跟你互动。如果你有什么建议,也请在评论中告知。为了让自己对神经网络的使用更加熟练,请不要忘记尝试解决我们的深度学习练习问题 - 数字识别

你可以测试你的技能和知识。查看 Live Competitions,来和世界上最优秀的数据科学家进行比赛。

原文链接:https://www.analyticsvidhya.com/blog/2016/10/an-introduction-to-implementing-neural-networks-using-tensorflow/

翻译:Flynn

0%