个性化阅读
专注于IT技术分析

从求解方程式到深度学习:一个TensorFlow Python教程

点击下载

本文概述

人工智能领域最近有一些惊人的发展, 从无人驾驶汽车的广为人知的进步到现在构成肖邦模仿或真正擅长视频游戏的机器。

这些进步的核心是围绕着许多工具来帮助推导深度学习和其他机器学习模型, 其中包括Torch, Caffe和Theano。但是, 自从Google Brain在2015年11月通过其自己的框架TensorFlow开源以来, 我们已经看到该软件库的流行性飙升成为最受欢迎的深度学习框架。

TensorFlow

为什么会这样呢?原因包括大量可用的支持和文档, 其生产就绪状态, 易于在各种设备之间分布计算以及出色的可视化工具TensorBoard。

最终, TensorFlow设法将一套全面而灵活的技术功能结合在一起, 并且非常易于使用。

在本文中, 通过使用简单的神经网络实现将其用于深度学习之前, 你将通过使用它来解决一般的数值问题(这超出了机器学习通常涉及的范围), 从而对该工具的原理有所了解。

在你开始之前

假定机器学习方法的基本知识。如果你需要赶上进度, 请查看此非常有用的文章。

我们将在演示Python API时, 对Numpy的理解也是有益的。

要设置TensorFlow, 请按照此处的说明进行操作。

如果使用Windows, 则应注意, 在编写本文时, 必须使用Python 3.4+, 而不是2.7。

然后, 当你准备就绪时, 应该可以使用以下命令导入库:

import tensorflow as tf

TensorFlow解决方案的第2步(共2步):创建图形

TensorFlow程序的构建通常包含两个主要步骤, 第一步是构建一个计算图, 该图将描述你希望执行的计算, 但实际上并没有执行它们或保留任何值。

与任何图一样, 我们具有节点和边。边缘代表张量, 张量代表n维数组。例如, 维数(或TensorFlow中的等级)为0的张量是标量, 等级1是向量, 等级2是矩阵, 依此类推。

节点表示产生输出张量的操作, 如果需要, 将张量作为输入。这样的操作包括加法(tf.add), 矩阵乘法(tf.matmul)以及常量的创建(tf.constant)。

因此, 让我们将其中一些结合起来作为第一个图表。

a = tf.constant([2.5, 2])
b = tf.constant([3, 6], dtype=tf.float32)
total = tf.add(a, b)

在这里, 我们创建了三个操作, 其中两个创建了恒定的一维数组。

数据类型是从传入的values参数推断出来的, 或者可以使用dtype参数表示这些数据类型。如果我还没有为b完成此操作, 则将推断出一个int32并抛出了一个错误, 如tf.add, 它将试图在两种不同类型上定义一个加法。

TensorFlow解决方案的第2步(共2步):执行操作

定义了该图, 但是为了实际对其进行任何计算(或其任何部分), 我们必须设置一个TensorFlow会话。

sess = tf.Session()

另外, 如果我们在交互式外壳程序(例如IPython)中运行会话, 则可以使用:

sess = tf.InteractiveSession()

会话对象上的run方法是评估张量的一种方法。

因此, 为了评估上面定义的加法计算, 我们传递” total”, 即要检索的张量, 该张量表示tf.add op的输出。

print(sess.run(total)) # [ 5.5  8. ]

至此, 我们介绍了TensorFlow的Variable类。常量是图形定义的固定部分, 而变量可以更新。类的构造函数需要一个初始值, 但是即使这样, 变量也需要执行一个操作来显式初始化它们, 然后再对它们执行任何其他操作。

变量保存特定会话中图的状态, 因此我们应该观察使用同一图的多个会话会发生什么, 以更好地理解变量。

# Create a variable with an initial value of 1
some_var = tf.Variable(1)

# Create op to run variable initializers
init_op = tf.global_variables_initializer()
 
# Create an op to replace the value held by some_var to 3
assign_op = some_var.assign(3)
 
# Set up two instances of a session
sess1 = tf.Session()
sess2 = tf.Session()

# Initialize variables in both sessions
sess1.run(init_op)
sess2.run(init_op)
print(sess1.run(some_var)) # Outputs 1

# Change some_var in session1
sess1.run(assign_op)
print(sess1.run(some_var)) # Outputs 3
print(sess2.run(some_var)) # Outputs 1

# Close sessions
sess1.close()
sess2.close()

我们已经设置了图表和两个会话。

在两个会话上都执行了初始化之后(如果我们不运行它, 然后评估变量, 则我们遇到错误), 我们仅在一个会话上执行assign op。可以看到, 变量值会持续存在, 但不会跨会话持续。

输入图形来处理数值问题

TensorFlow的另一个重要概念是占位符。变量保持状态, 而占位符则用于定义图形可以期望的输入以及它们的数据类型(以及形状)。然后, 当我们运行计算时, 可以通过这些占位符将数据输入到图中。

TensorFlow图开始类似于我们最终要训练的神经网络, 但是在此之前, 让我们使用这些概念来解决金融界的常见数值问题。

假设我们想在这样的方程式中找到y:

v = Ce-0.5y + Ce-y + Ce-1.5y +(C + P)e-2y

对于给定的v(具有C和P常数)。

这是一个公式, 用于计算市场价为v, 本金P和息票C每半年支付一次但现金流量通过连续复利折现的债券的到期收益率(y)。

基本上, 我们必须通过反复试验来求解这样的方程式, 然后将对分方法选择为y的最终值归零。

首先, 我们将这个问题建模为TensorFlow图。

C和P是固定常数, 并构成图的定义的一部分。我们希望有一个过程可以细化y的上下边界。因此, 这些界限(分别表示为a和b)是变量的很好的候选者, 这些变量需要在每次猜测y(取为a和b的中点)之后进行更改。

# Specify the values our constant ops will output
C = tf.constant(5.0)
P = tf.constant(100.0)

# We specify the initial values that our lower and upper bounds will be when initialised.
# Obviously the ultimate success of this algorithm depends on decent start points
a = tf.Variable(-10.0)
b = tf.Variable(10.0)
 
# We expect a floating number to be inserted into the graph
v_target = tf.placeholder("float")
 
# Remember the following operations are definitions, # none are carried out until an operation is evaluated in a session!
y = (a+b)/2
v_guess = C*tf.exp(-0.5*y) + C*tf.exp(-y) + C*tf.exp(-1.5*y) + (C + P)*tf.exp(-2*y)
 
# Operations to set temporary values (a_ and b_) intended to be the next values of a and b.
# e.g. if the guess results in a v greater than the target v, # we will set a_ to be the current value of y
a_ = tf.where(v_guess > v_target, y, a)
b_ = tf.where(v_guess < v_target, y, b)
 
# The last stage of our graph is to assign the two temporary vals to our variables
step = tf.group( a.assign(a_), b.assign(b_) )

因此, 我们现在有了一个操作和变量的列表, 其中任何一个都可以针对特定的会话进行评估。其中一些操作依赖于要运行的其他操作, 因此运行(例如v_guess)会引发连锁反应, 以首先评估其他张量(例如C和P)。

其中一些操作依赖于需要为其指定值的占位符, 那么我们如何实际输入该值?

这是通过run函数本身中的feed_dict参数完成的。

如果要评估a_, 则插入占位符v_target的值, 如下所示:

sess.run(a_, feed_dict={v_target: 100})

给我们0.0

插入v_target 130, 我们得到-10.0。

这是我们的”逐步”操作, 实际上需要将所有其他操作都作为前提条件执行, 并实际上执行了整个图形。这实际上是在整个会话中更改实际状态的一项操作。因此, 执行该步骤的次数越多, 我们向a的实际值y逐渐递增变量a和b的次数就越多。

因此, 假设我们方程中的v值等于95。我们建立一个会话并对其执行100次图形。

# Set up a session and initialize variables
sess = tf.Session()
tf.global_variables_initializer().run()
# Run the step operation (and therefore whole graph) 100 times
for i in range (100):
	sess.run(step, feed_dict={v_target:95})

如果我们现在评估y张量, 我们会得到类似理想答案的结果

print(sess.run(y)) # 0.125163

神经网络

现在我们已经了解了TensorFlow的机制, 我们可以将其与TensorFlow内置的一些其他机器学习操作结合起来, 以训练一个简单的神经网络。

在这里, 我们要根据2d坐标系上的数据点是否落入特定区域(以原点为中心的半径为0.5的圆)内进行分类。

当然, 只要a ^ 2 + b ^ 2 <0.5, 只要检查给定点(a, b), 就可以具体验证这一点, 但是出于本机器学习实验的目的, 我们希望传入一个训练集:一系列随机点以及它们是否落入我们的预期区域。这是一种创建方法:

import numpy as np
NO_OF_RANDOM_POINTS = 100
CIRCLE_RADIUS = 0.5
random_spots = np.random.rand(NO_OF_RANDOM_POINTS, 2) * 2 - 1
is_inside_circle = (np.power(random_spots[:, 0], 2) + np.power(random_spots[:, 1], 2) < CIRCLE_RADIUS).astype(int)

我们将制作具有以下特征的神经网络:

  1. 它由一个带有两个节点的输入层组成, 我们在其中输入” random_spots”中包含的一系列二维矢量。这将由等待训练数据的占位符表示。
  2. 输出层还将有两个节点, 因此我们需要将一系列训练标签(” is_inside_circle”)输入到标量的占位符中, 然后将这些值转换为一维二维矢量。
  3. 我们将拥有一个由三个节点组成的隐藏层, 因此我们将需要使用变量作为权重矩阵和偏差向量, 因为这些值是进行训练时需要完善的值。
INPUT_LAYER_SIZE = 2
HIDDEN_LAYER_SIZE = 3
OUTPUT_LAYER_SIZE = 2
 
# Starting values for weights and biases are drawn randomly and uniformly from  [-1, 1]
# For example W1 is a matrix of shape 2x3
W1 = tf.Variable(tf.random_uniform([INPUT_LAYER_SIZE, HIDDEN_LAYER_SIZE], -1, 1))
b1 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE], -1, 1))
W2 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE, OUTPUT_LAYER_SIZE], -1, 1))
b2 = tf.Variable(tf.random_uniform([OUTPUT_LAYER_SIZE], -1, 1))
# Specifying that the placeholder X can expect a matrix of 2 columns (but any number of rows)
# representing random spots
X = tf.placeholder(tf.float32, [None, INPUT_LAYER_SIZE])
# Placeholder Y can expect integers representing whether corresponding point is in the circle
# or not (no shape specified)
Y = tf.placeholder(tf.uint8)
# An op to convert to a one hot vector
onehot_output = tf.one_hot(Y, OUTPUT_LAYER_SIZE)

为了完成图的定义, 我们定义了一些操作, 这些操作将帮助我们训练变量以达到更好的分类器。这些包括矩阵计算, 激活函数和优化器。

LEARNING_RATE = 0.01
# Op to perform matrix calculation X*W1 + b1
hidden_layer = tf.add(tf.matmul(X, W1), b1)
# Use sigmoid activation function on the outcome
activated_hidden_layer = tf.sigmoid(hidden_layer)
# Apply next weights and bias (W2, b2) to hidden layer and then apply softmax function
# to get our output layer (each vector adding up to 1)
output_layer = tf.nn.softmax(tf.add(tf.matmul(activated_hidden_layer, W2), b2))
# Calculate cross entropy for our loss function
loss = -tf.reduce_sum(onehot_output * tf.log(output_layer))
# Use gradient descent optimizer at specified learning rate to minimize value given by loss tensor
train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)

设置好图表后, 就该设置会话并运行” train_step”(它也运行任何先决条件操作)。其中一些操作使用占位符, 因此需要提供这些值。此训练步骤代表了我们学习算法中的一个纪元, 因此, 在我们希望运行的纪元数上循环。我们可以运行图表的其他部分, 例如”损失”张量, 以供参考。

EPOCH_COUNT = 1000
sess = tf.Session()
tf.global_variables_initializer().run()
for i in range(EPOCH_COUNT):
    if i%100 == 0:
    	print('Loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle})))
	sess.run(train_step, feed_dict={X: random_spots, Y: is_inside_circle})
print('Final loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle})))

训练完算法后, 我们可以输入一个点并获得神经网络的输出, 如下所示:

sess.run(output_layer, feed_dict={X: [[1, 1]]}) # Hopefully something close to [1, 0]
sess.run(output_layer, feed_dict={X: [[0, 0]]}) # Hopefully something close to [0, 1]

如果输出向量的第一个成员大于0.5, 则可以将点分类为圆外, 否则分类为圆内。

通过对多个点运行output_layer张量, 我们可以了解学习者如何设想包含正分类点的区域。值得一试的是训练集的大小, 学习率和其他参数, 以了解我们可以距离目标圆有多近。

几乎三角形

训练集:100分

学习率:0.01

时期:1000

小三角形

训练套:1000分

学习率:0.01

时期:1000

大三角形

训练套:1000分

学习率:0.01

纪元:10000

差不多圈

训练套:1000分

学习率:0.001

纪元:10000

包起来

这是一个很好的教训, 即增加培训集或时期数并不能保证一个好的学习者—学习率应适当调整。

希望这些演示使你对TensorFlow的核心原理有很好的了解, 并为实施更复杂的技术奠定坚实的基础。

我们没有涵盖过Tensorboard之类的概念, 也没有涵盖在GPU上训练模型的概念, 但是TensorFlow文档中已经很好地介绍了这些概念。可以在文档中找到许多食谱, 这些食谱可以帮助你使用此强大的框架快速完成令人兴奋的深度学习任务!

赞(0)
未经允许不得转载:srcmini » 从求解方程式到深度学习:一个TensorFlow Python教程

评论 抢沙发

评论前必须登录!