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

已发表: 2022-03-11

人工智能领域最近出现了一些显着的发展,从广为人知的自动驾驶汽车的进步到现在组成肖邦模仿作品的机器,或者只是非常擅长电子游戏。

这些进步的核心是许多工具来帮助推导出深度学习和其他机器学习模型,其中 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 解决方案的第 1 步(共 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 Session。

 sess = tf.Session()

或者,如果我们在交互式 shell 中运行会话,例如 IPython,那么我们使用:

 sess = tf.InteractiveSession()

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

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

 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()

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

在两个会话上执行初始化后(如果我们不运行它然后评估我们遇到错误的变量)我们只在一个会话上执行分配操作。 可以看到,变量值仍然存在,但不会跨会话。

提供图形以解决数值问题

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

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

假设我们想在这样的等式中找到y

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

对于给定的v (具有CP常数)。

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

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

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

CP是固定常数,构成我们图形定义的一部分。 我们希望有一个过程来细化y的下限和上限。 因此,这些界限(表示为ab )是在每次猜测y后需要更改的变量的良好候选者(取ab的中点)。

 # 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. # eg if the guess results in av 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将引发连锁反应,让其他张量,例如CP ,首先被评估。

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

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

如果我们想评估a_ ,我们插入占位符v_target的值,如下所示:

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

给我们0.0。

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

这是我们的“步骤”操作,它实际上需要执行所有其他操作作为先决条件,并且实际上执行了整个图。 这也是一个实际改变会话中实际状态的操作。 因此,我们运行该步骤的次数越多,我们就越会将变量ab逐步推向y的实际值。

所以,假设我们等式中的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 中内置的一些额外机器学习操作结合起来,以训练一个简单的神经网络。

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

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

 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,因此,在我们希望运行的 epoch 数量上循环。 我们可以运行图的其他部分,例如用于信息目的的“损失”张量。

 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

包起来

这是一个很好的教训,增加的训练集或 epoch 数量并不能保证一个好的学习者——应该适当地调整学习率。

希望这些演示能够让您深入了解 TensorFlow 的核心原理,并为实施更复杂的技术提供坚实的基础。

我们没有涵盖诸如 Tensorboard 之类的概念或跨 GPU 训练我们的模型,但这些在 TensorFlow 文档中得到了很好的介绍。 可以在文档中找到许多秘诀,这些秘诀可以帮助您使用这个强大的框架快速处理令人兴奋的深度学习任务!

相关: TensorFlow 中梯度下降的许多应用