【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第9章 (中)启动并运行TensorFlow

浏览: 1793

作者:ApacheCN【翻译】   Python机器学习爱好者

Python爱好者社区专栏作者

GitHub:https://github.com/apachecn/hands_on_Ml_with_Sklearn_and_TF



Linear Regression with TensorFlow

TensorFlow 操作(也简称为 ops)可以采用任意数量的输入并产生任意数量的输出。 例如,加法运算和乘法运算都需要两个输入并产生一个输出。 常量和变量不输入(它们被称为源操作)。 输入和输出是称为张量的多维数组(因此称为“tensor flow”)。 就像 NumPy 数组一样,张量具有类型和形状。 实际上,在 Python API 中,张量简单地由 NumPyndarray表示。 它们通常包含浮点数,但您也可以使用它们来传送字符串(任意字节数组)。

迄今为止的示例,张量只包含单个标量值,但是当然可以对任何形状的数组执行计算。例如,以下代码操作二维数组来对加利福尼亚房屋数据集进行线性回归(在第 2 章中介绍)。它从获取数据集开始;之后它会向所有训练实例添加一个额外的偏置输入特征(x0 = 1)(它使用 NumPy 进行,因此立即运行);之后它创建两个 TensorFlow 常量节点Xy来保存该数据和目标,并且它使用 TensorFlow 提供的一些矩阵运算来定义theta。这些矩阵函数transpose()matmul()matrix_inverse()是不言自明的,但是像往常一样,它们不会立即执行任何计算;相反,它们会在图形中创建在运行图形时执行它们的节点。您可以认识到θ的定义对应于方程image.png 。

最后,代码创建一个session并使用它来求出theta

import numpy as np  
from sklearn.datasets import fetch_california_housing  
housing = fetch_california_housing()  
m, n = housing.data.shape  
#np.c_按colunm来组合array  
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]  

X = tf.constant(housing_data_plus_bias, dtype=tf.float32, name="X")  
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")  
XT = tf.transpose(X)  
theta = tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(XT, X)), XT), y)  
with tf.Session() as sess:  
   theta_value = theta.eval()  
print(theta_value)  

如果您有一个 GPU 的话,上述代码相较于直接使用 NumPy 计算正态方程式的主要优点是 TensorFlow 会自动运行在您的 GPU 上(如果您安装了支持 GPU 的 TensorFlow,则 TensorFlow 将自动运行在 GPU 上,请参阅第 12 章了解更多详细信息)。

其实这里就是用最小二乘法算θ

http://blog.csdn.net/akon_wang_hkbu/article/details/77503725

实现梯度下降

让我们尝试使用批量梯度下降(在第 4 章中介绍),而不是普通方程。 首先,我们将通过手动计算梯度来实现,然后我们将使用 TensorFlow 的自动扩展功能来使 TensorFlow 自动计算梯度,最后我们将使用几个 TensorFlow 的优化器。

当使用梯度下降时,请记住,首先要对输入特征向量进行归一化,否则训练可能要慢得多。 您可以使用 TensorFlow,NumPy,Scikit-Learn 的StandardScaler或您喜欢的任何其他解决方案。 以下代码假定此规范化已经完成。

手动计算渐变

以下代码应该是相当不言自明的,除了几个新元素:

  • random_uniform()函数在图形中创建一个节点,它将生成包含随机值的张量,给定其形状和值作用域,就像 NumPy 的rand()函数一样。

  • assign()函数创建一个为变量分配新值的节点。 在这种情况下,它实现了批次梯度下降步骤image.png

  • 主循环一次又一次(共n_epochs次)执行训练步骤,每 100 次迭代都打印出当前均方误差(MSE)。 你应该看到 MSE 在每次迭代中都会下降。

housing = fetch_california_housing()  
m, n = housing.data.shape  
m, n = housing.data.shape  
#np.c_按colunm来组合array  
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]  
scaled_housing_data_plus_bias = scale(housing_data_plus_bias)  
n_epochs = 1000  
learning_rate = 0.01  
X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X")  
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")  
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name="theta")  
y_pred = tf.matmul(X, theta, name="predictions")  
error = y_pred - y  
mse = tf.reduce_mean(tf.square(error), name="mse")  
gradients = 2/m * tf.matmul(tf.transpose(X), error)  
training_op = tf.assign(theta, theta - learning_rate * gradients)  
init = tf.global_variables_initializer()  
with tf.Session() as sess:  
   sess.run(init)  
   for epoch in range(n_epochs):  
       if epoch % 100 == 0:  
           print("Epoch", epoch, "MSE =", mse.eval())  
       sess.run(training_op)  
   best_theta = theta.eval()  
Using autodiff

前面的代码工作正常,但它需要从代价函数(MSE)中利用数学公式推导梯度。 在线性回归的情况下,这是相当容易的,但是如果你必须用深层神经网络来做这个事情,你会感到头痛:这将是乏味和容易出错的。 您可以使用符号求导来为您自动找到偏导数的方程式,但结果代码不一定非常有效。

为了理解为什么,考虑函数f(x) = exp(exp(exp(x)))。如果你知道微积分,你可以计算出它的导数f'(x) = exp(x) * exp(exp(x)) * exp(exp(exp(x)))。如果您按照普通的计算方式分别去写f(x)f'(x),您的代码将不会如此有效。 一个更有效的解决方案是写一个首先计算exp(x),然后exp(exp(x)),然后exp(exp(exp(x)))的函数,并返回所有三个。这直接给你(第三项)f(x),如果你需要求导,你可以把这三个子式相乘,你就完成了。 通过传统的方法,您不得不将exp函数调用 9 次来计算f(x)f'(x)。 使用这种方法,你只需要调用它三次。

当您的功能由某些任意代码定义时,它会变得更糟。 你能找到方程(或代码)来计算以下函数的偏导数吗?

提示:不要尝试。

def my_func(a, b):  
   z = 0  
   for i in range(100):  
       z = a * np.cos(z + i) + z * np.sin(b - i)  
   return z  

幸运的是,TensorFlow 的自动计算梯度功能可以计算这个公式:它可以自动高效地为您计算梯度。 只需用以下面这行代码替换上一节中代码的gradients = ...行,代码将继续工作正常:

gradients = tf.gradients(mse, [theta])[0]  

gradients()函数使用一个op(在这种情况下是MSE)和一个变量列表(在这种情况下只是theta),它创建一个ops列表(每个变量一个)来计算op的梯度变量。 因此,梯度节点将计算 MSE 相对于theta的梯度向量。

自动计算梯度有四种主要方法。 它们总结在表 9-2 中。 TensorFlow 使用反向模式,这是完美的(高效和准确),当有很多输入和少量的输出,如通常在神经网络的情况。 它只需要通过image.png次图遍历即可计算所有输出的偏导数。

image.png


使用优化器

所以 TensorFlow 为您计算梯度。 但它还有更好的方法:它还提供了一些可以直接使用的优化器,包括梯度下降优化器。您可以使用以下代码简单地替换以前的gradients = ...training_op = ...行,并且一切都将正常工作:

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)  
training_op = optimizer.minimize(mse)  

如果要使用其他类型的优化器,则只需要更改一行。 例如,您可以通过定义优化器来使用动量优化器(通常会比渐变收敛的收敛速度快得多;参见第 11 章)

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9) 

将数据提供给训练算法

我们尝试修改以前的代码来实现小批量梯度下降(Mini-batch Gradient Descent)。 为此,我们需要一种在每次迭代时用下一个小批量替换XY的方法。 最简单的方法是使用占位符(placeholder)节点。 这些节点是特别的,因为它们实际上并不执行任何计算,只是输出您在运行时输出的数据。 它们通常用于在训练期间将训练数据传递给 TensorFlow。 如果在运行时没有为占位符指定值,则会收到异常。

要创建占位符节点,您必须调用placeholder()函数并指定输出张量的数据类型。 或者,您还可以指定其形状,如果要强制执行。 如果指定维度为None,则表示“任何大小”。例如,以下代码创建一个占位符节点A,还有一个节点B = A + 5。当我们求出B时,我们将一个feed_dict传递给eval()方法并指定A的值。注意,A必须具有 2 级(即它必须是二维的),并且必须有三列(否则引发异常),但它可以有任意数量的行。

>>> A = tf.placeholder(tf.float32, shape=(None, 3))  
>>> B = A + 5  
>>> with tf.Session() as sess:  
... B_val_1 = B.eval(feed_dict={A: [[1, 2, 3]]})  
... B_val_2 = B.eval(feed_dict={A: [[4, 5, 6], [7, 8, 9]]})  
...  
>>> print(B_val_1)  
[[ 6. 7. 8.]]  
>>> print(B_val_2)  
[[ 9. 10. 11.]  
[ 12. 13. 14.]]  

您实际上可以提供任何操作的输出,而不仅仅是占位符。 在这种情况下,TensorFlow 不会尝试求出这些操作;它使用您提供的值。

要实现小批量渐变下降,我们只需稍微调整现有的代码。 首先更改XY的定义,使其定义为占位符节点:

X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")  
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")  

然后定义批量大小并计算总批次数:

batch_size = 100  
n_batches = int(np.ceil(m / batch_size))  

最后,在执行阶段,逐个获取小批量,然后在求出依赖于Xy的值的任何一个节点时,通过feed_dict提供Xy的值。

def fetch_batch(epoch, batch_index, batch_size):
   [...] # load the data from disk
   return X_batch, y_batch

with tf.Session() as sess:
   sess.run(init)

   for epoch in range(n_epochs):
       for batch_index in range(n_batches):
           X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
           sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
   best_theta = theta.eval()

在求出theta时,我们不需要传递X和y的值,因为它不依赖于它们。

MINI-BATCH 完整代码

import numpy as np  
from sklearn.datasets import fetch_california_housing  
import tensorflow as tf  
from sklearn.preprocessing import StandardScaler  

housing = fetch_california_housing()  
m, n = housing.data.shape  
print("数据集:{}行,{}列".format(m,n))  
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]  
scaler = StandardScaler()  
scaled_housing_data = scaler.fit_transform(housing.data)  
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]  

n_epochs = 1000  
learning_rate = 0.01  

X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")  
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")  
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")  
y_pred = tf.matmul(X, theta, name="predictions")  
error = y_pred - y  
mse = tf.reduce_mean(tf.square(error), name="mse")  
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)  
training_op = optimizer.minimize(mse)  

init = tf.global_variables_initializer()  

n_epochs = 10  
batch_size = 100  
n_batches = int(np.ceil(m / batch_size)) # ceil() 方法返回 x 的值上限 - 不小于 x 的最小整数。  

def fetch_batch(epoch, batch_index, batch_size):  
   know = np.random.seed(epoch * n_batches + batch_index)  # not shown in the book  
   print("我是know:",know)  
   indices = np.random.randint(m, size=batch_size)  # not shown  
   X_batch = scaled_housing_data_plus_bias[indices] # not shown  
   y_batch = housing.target.reshape(-1, 1)[indices] # not shown  
   return X_batch, y_batch  

with tf.Session() as sess:  
   sess.run(init)  

   for epoch in range(n_epochs):  
       for batch_index in range(n_batches):  
           X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)  
           sess.run(training_op, feed_dict={X: X_batch, y: y_batch})  

   best_theta = theta.eval()  

print(best_theta)  

保存和恢复模型

一旦你训练了你的模型,你应该把它的参数保存到磁盘,所以你可以随时随地回到它,在另一个程序中使用它,与其他模型比较,等等。 此外,您可能希望在训练期间定期保存检查点,以便如果您的计算机在训练过程中崩溃,您可以从上次检查点继续进行,而不是从头开始。

TensorFlow 可以轻松保存和恢复模型。 只需在构造阶段结束(创建所有变量节点之后)创建一个保存节点; 那么在执行阶段,只要你想保存模型,只要调用它的save()方法:

[...]
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name="theta")
[...]
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
   sess.run(init)

   for epoch in range(n_epochs):
       if epoch % 100 == 0: # checkpoint every 100 epochs
           save_path = saver.save(sess, "/tmp/my_model.ckpt")

       sess.run(training_op)
   best_theta = theta.eval()
   save_path = saver.save(sess, "/tmp/my_model_final.ckpt")

恢复模型同样容易:在构建阶段结束时创建一个保存器,就像之前一样,但是在执行阶段的开始,而不是使用init节点初始化变量,你可以调用restore()方法 的保存器对象:

with tf.Session() as sess:
   saver.restore(sess, "/tmp/my_model_final.ckpt")
   [...]

默认情况下,保存器将以自己的名称保存并还原所有变量,但如果需要更多控制,则可以指定要保存或还原的变量以及要使用的名称。 例如,以下保存器将仅保存或恢复theta变量,它的键名称是weights

saver = tf.train.Saver({"weights": theta})

完整代码

import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler

housing = fetch_california_housing()
m, n = housing.data.shape
print("数据集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]

n_epochs = 1000  # not shown in the book
learning_rate = 0.01  # not shown

X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X")  # not shown
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")  # not shown
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")  # not shown
error = y_pred - y  # not shown
mse = tf.reduce_mean(tf.square(error), name="mse")  # not shown
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)  # not shown
training_op = optimizer.minimize(mse)  # not shown

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
   sess.run(init)

   for epoch in range(n_epochs):
       if epoch % 100 == 0:
           print("Epoch", epoch, "MSE =", mse.eval())  # not shown
           save_path = saver.save(sess, "/tmp/my_model.ckpt")
       sess.run(training_op)

   best_theta = theta.eval()
   save_path = saver.save(sess, "/tmp/my_model_final.ckpt") #找到tmp文件夹就找到文件了

使用 TensorBoard 展现图形和训练曲线

所以现在我们有一个使用小批量梯度下降训练线性回归模型的计算图谱,我们正在定期保存检查点。 听起来很复杂,不是吗? 然而,我们仍然依靠print()函数可视化训练过程中的进度。 有一个更好的方法:进入 TensorBoard。如果您提供一些训练统计信息,它将在您的网络浏览器中显示这些统计信息的良好交互式可视化(例如学习曲线)。 您还可以提供图形的定义,它将为您提供一个很好的界面来浏览它。 这对于识别图中的错误,找到瓶颈等是非常有用的。

第一步是调整程序,以便将图形定义和一些训练统计信息(例如,training_error(MSE))写入 TensorBoard 将读取的日志目录。 您每次运行程序时都需要使用不同的日志目录,否则 TensorBoard 将会合并来自不同运行的统计信息,这将会混乱可视化。 最简单的解决方案是在日志目录名称中包含时间戳。 在程序开头添加以下代码:

from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = "tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)

接下来,在构建阶段结束时添加以下代码:

mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())

第一行创建一个节点,这个节点将求出 MSE 值并将其写入 TensorBoard 兼容的二进制日志字符串(称为摘要)中。 第二行创建一个FileWriter,您将用它来将摘要写入日志目录中的日志文件中。 第一个参数指示日志目录的路径(在本例中为tf_logs/run-20160906091959/,相对于当前目录)。 第二个(可选)参数是您想要可视化的图形。 创建时,文件写入器创建日志目录(如果需要),并将其定义在二进制日志文件(称为事件文件)中。

接下来,您需要更新执行阶段,以便在训练期间定期求出mse_summary节点(例如,每 10 个小批量)。 这将输出一个摘要,然后可以使用file_writer写入事件文件。 以下是更新的代码:

[...]
for batch_index in range(n_batches):
   X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
   if batch_index % 10 == 0:
       summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})
       step = epoch * n_batches + batch_index
       file_writer.add_summary(summary_str, step)
   sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
[...]

避免在每一个训练阶段记录训练数据,因为这会大大减慢训练速度(以上代码每 10 个小批量记录一次).

最后,要在程序结束时关闭FileWriter

file_writer.close()

完整代码

import numpy as np
from sklearn.datasets import fetch_california_housing
import tensorflow as tf
from sklearn.preprocessing import StandardScaler

housing = fetch_california_housing()
m, n = housing.data.shape
print("数据集:{}行,{}列".format(m,n))
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
scaler = StandardScaler()
scaled_housing_data = scaler.fit_transform(housing.data)
scaled_housing_data_plus_bias = np.c_[np.ones((m, 1)), scaled_housing_data]

from datetime import datetime

now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = r"D://tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
n_epochs = 1000
learning_rate = 0.01

X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)

init = tf.global_variables_initializer()
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())

n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))

def fetch_batch(epoch, batch_index, batch_size):
   np.random.seed(epoch * n_batches + batch_index)  # not shown in the book
   indices = np.random.randint(m, size=batch_size)  # not shown
   X_batch = scaled_housing_data_plus_bias[indices] # not shown
   y_batch = housing.target.reshape(-1, 1)[indices] # not shown
   return X_batch, y_batch

with tf.Session() as sess:                                                        # not shown in the book
   sess.run(init)                                                                # not shown

   for epoch in range(n_epochs):                                                 # not shown
       for batch_index in range(n_batches):
           X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
           if batch_index % 10 == 0:
               summary_str = mse_summary.eval(feed_dict={X: X_batch, y: y_batch})
               step = epoch * n_batches + batch_index
               file_writer.add_summary(summary_str, step)
           sess.run(training_op, feed_dict={X: X_batch, y: y_batch})

   best_theta = theta.eval()
file_writer.close()
print(best_theta)


image.png



Python爱好者社区历史文章大合集

Python爱好者社区历史文章列表(每周append更新一次)

福利:文末扫码立刻关注公众号,“Python爱好者社区”,开始学习Python课程:

关注后在公众号内回复课程即可获取

小编的Python入门免费视频课程!!!

【最新免费微课】小编的Python快速上手matplotlib可视化库!!!

崔老师爬虫实战案例免费学习视频。

陈老师数据分析报告制作免费学习视频。

玩转大数据分析!Spark2.X+Python 精华实战课程免费学习视频。

image.png

推荐 0
本文由 Python爱好者社区 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册