社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

Scikit-Learn与TensorFlow机器学习实用指南 中文精要 九、Up and Running with TensorFlow(2)

龙哥盟飞龙 • 6 年前 • 494 次点击  

本篇文章是个人翻译的,如有商业用途,请通知本人谢谢.

存储和回复模型

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

TensorFlow可以轻松保存和恢复模型。 只需在构造阶段结束(创建所有变量节点之后)创建一个Save节点; 那么在执行阶段,只要你想保存模型,只要调用它的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")
恢复模型同样容易:在构建阶段结束时创建一个Saver,就像之前一样,但是在执行阶段的开始,而不是使用init节点初始化变量,你可以调用restore()方法 的Saver对象: 
with tf.Session() as sess:
    saver.restore(sess, "/tmp/my_model_final.ckpt")
    [...]

默认情况下,Saver将以自己的名称保存并还原所有变量,但如果需要更多控制,则可以指定要保存或还原的变量以及要使用的名称。 例如,以下Saver将仅保存或恢复名称权重下的theta变量: 
saver = tf.train.Saver({"weights": theta})

完整代码

 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展现图形和训练曲线
所以现在我们有一个使用Mini_batch 梯度下降训练线性回归模型的计算图谱,我们正在定期保存检查点。 听起来很复杂,不是吗? 然而,我们仍然依靠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})
[...]
避免在每一个培训阶段记录培训数据,因为这会大大减慢培训速度.

最后,要在程序结束时关闭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)

名称范围

当处理更复杂的模型(如神经网络)时,该图可以很容易地与数千个节点混淆。 为了避免这种情况,您可以创建名称范围来对相关节点进行分组。 例如,我们修改以前的代码来定义名为“loss”的名称范围内的错误和mse操作:


with tf.name_scope("loss") as scope:
    error = y_pred - y
    mse = tf.reduce_mean(tf.square(error), name="mse")
在范围内定义的每个op的名称现在以“loss /”为前缀: 


>>> print(error.op.name)
loss/sub
>>> print(mse.op.name)
loss/mse

在TensorBoard中,mse和error节点现在出现在Loss命名空间中,默认情况下会出现崩溃(图9-5)。

完整代码




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


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.name_scope("loss") as scope:
    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))

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)
            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.flush()
file_writer.close()
print("Best theta:")
print(best_theta)



模块性

假设您要创建一个添加两个整流线性单元(ReLU)的输出的图形。 ReLU计算输入的线性函数,如果为正,则输出结果,否则为0,如等式9-1所示。


下面的代码做这个工作,但是它是相当重复的:

n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
w1 = tf.Variable(tf.random_normal((n_features, 1)), name="weights1")
w2 = tf.Variable(tf.random_normal((n_features, 1)), name="weights2")
b1 = tf.Variable(0.0, name="bias1")
b2 = tf.Variable(0.0, name="bias2")
z1 = tf.add(tf.matmul(X, w1), b1, name="z1")
z2 = tf.add(tf.matmul(X, w2), b2, name="z2")
relu1 = tf.maximum(z1, 0., name="relu1")
relu2 = tf.maximum(z1, 0., name="relu2")
output = tf.add(relu1, relu2, name="output")
这样的重复代码很难维护,容易出错(实际上,这个代码包含了一个剪贴错误,你发现了吗?) 如果你想添加更多的ReLU,会变得更糟。 幸运的是,TensorFlow可以让您保持DRY(不要重复自己):只需创建一个功能来构建ReLU。 以下代码创建五个ReLU并输出其总和(注意,add_n()创建一个将计算张量列表的和的操作): 
def relu(X):
    w_shape = (int(X.get_shape()[1]), 1)
    w = tf.Variable(tf.random_normal(w_shape), name="weights")
    b = tf.Variable(0.0, name="bias")
    z = tf.add(tf.matmul(X, w), b, name="z")
    return tf.maximum(z, 0., name="relu")

n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = [relu(X) for i in range(5)]
output = tf.add_n(relus, name="output")
请注意,创建节点时,TensorFlow将检查其名称是否已存在,如果它已经存在,则会附加一个下划线,后跟一个索引,以使该名称是唯一的。 因此,第一个ReLU包含名为“权重”,“偏差”,“z”和“relu”的节点(加上其他默认名称的更多节点,如“MatMul”); 第二个ReLU包含名为“weights_1”,“bias_1”等节点的节点; 第三个ReLU包含名为 “weights_2”,“bias_2”的节点,依此类推。 TensorBoard识别这样的系列并将它们折叠在一起以减少混乱(如图9-6所示)

使用名称范围,您可以使图形更清晰。 简单地将relu()函数的所有内容移动到名称范围内。 图9-7显示了结果图。 请注意,TensorFlow还通过附加_1,_2等来给出名称范围的唯一名称。

def relu(X):
    with tf.name_scope("relu"):
        w_shape = (int(X.get_shape()[1]), 1)                          # not shown in the book
        w = tf.Variable(tf.random_normal(w_shape), name="weights")    # not shown
        b = tf.Variable(0.0, name="bias")                             # not shown
        z = tf.add(tf.matmul(X, w), b, name="z")                      # not shown
        return tf.maximum(z, 0., name="max")                          # not shown


共享变量

如果要在图形的各个组件之间共享一个变量,一个简单的选项是首先创建它,然后将其作为参数传递给需要它的函数。 例如,假设要使用所有ReLU的共享阈值变量来控制ReLU阈值(当前硬编码为0)。 您可以先创建该变量,然后将其传递给relu()函数:

reset_graph()

def relu(X, threshold):
    with tf.name_scope("relu"):
        w_shape = (int(X.get_shape()[1]), 1)                        # not shown in the book
        w = tf.Variable(tf.random_normal(w_shape), name="weights")  # not shown
        b = tf.Variable(0.0, name="bias")                           # not shown
        z = tf.add(tf.matmul(X, w), b, name="z")                    # not shown
        return tf.maximum(z, threshold, name="max")

threshold = tf.Variable(0.0, name="threshold")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = [relu(X, threshold) for i in range(5)]
output = tf.add_n(relus, name="output")


这很好:现在您可以使用阈值变量来控制所有ReLUs的阈值。但是,如果有许多共享参数,比如这一项,那么必须一直将它们作为参数传递,这将是非常痛苦的。许多人创建了一个包含模型中所有变量的Python字典,并将其传递给每个函数。另一些则为每个模块创建一个类(例如:一个使用类变量来处理共享参数的ReLU类。另一种选择是在第一次调用时将共享变量设置为relu()函数的属性,如下所列:
def relu(X):
    with tf.name_scope("relu"):
        if not hasattr(relu, "threshold"):
            relu.threshold = tf.Variable(0.0, name="threshold")
        w_shape = int(X.get_shape()[1]), 1                          # not shown in the book
        w = tf.Variable(tf.random_normal(w_shape), name="weights")  # not shown
        b = tf.Variable(0.0, name="bias")                           # not shown
        z = tf.add(tf.matmul(X, w), b, name="z")                    # not shown
        return tf.maximum(z, relu.threshold, name="max")



TensorFlow提供了另一个选项,这可能会导致比以前的解决方案稍微更清洁和更模块化的代码.5这个解决方案首先要明白一点,但是由于它在TensorFlow中使用了很多,值得深入细节。 这个想法是使用get_variable()函数来创建共享变量,如果它还不存在,或者如果已经存在,则重用它。 所需的行为(创建或重用)由当前variable_scope()的属性控制。 例如,以下代码将创建一个名为“relu / threshold”的变量(作为标量,因为shape =(),并使用0.0作为初始值):

with tf.variable_scope("relu"):
    threshold = tf.get_variable("threshold", shape=(),
                                initializer=tf.constant_initializer(0.0))
请注意,如果变量已经通过较早的get_variable()调用创建,则此代码将引发异常。 这种行为可以防止错误地重用变量。 如果要重用变量,则需要通过将变量scope的重用属性设置为True来明确说明(在这种情况下,您不必指定形状或初始值):
with tf.variable_scope("relu", reuse=True):
    threshold = tf.get_variable("threshold")
该代码将获取现有的“relu / threshold”变量,如果不存在或引发异常(如果没有使用get_variable()创建)。 或者,您可以通过调用scope的reuse_variables()方法将重用属性设置为true:
with tf.variable_scope("relu") as scope:
    scope.reuse_variables()
    threshold = tf.get_variable("threshold")
一旦重新使用设置为True,它将不能在块内设置为False。 而且,如果在其中定义其他变量作用域,它们将自动继承reuse = True。 最后,只有通过get_variable()创建的变量才可以这样重用.


现在,您拥有所有需要的部分,使relu()函数访问阈值变量,而不必将其作为参数传递:

def relu(X):
    with tf.variable_scope("relu", reuse=True):
        threshold = tf.get_variable("threshold")
        w_shape = int(X.get_shape()[1]), 1                          # not shown
        w = tf.Variable(tf.random_normal(w_shape), name="weights")  # not shown
        b = tf.Variable(0.0, name="bias")                           # not shown
        z = tf.add(tf.matmul(X, w), b, name="z")                    # not shown
        return tf.maximum(z, threshold, name="max")

X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
with tf.variable_scope("relu"):
    threshold = tf.get_variable("threshold", shape=(),
                                initializer=tf.constant_initializer(0.0))
relus = [relu(X) for relu_index in range(5)]
output = tf.add_n(relus, name="output")

该代码首先定义relu()函数,然后创建relu / threshold变量(作为标量,稍后将被初始化为0.0),并通过调用relu()函数构建五个ReLU。 relu()函数重用relu / threshold变量,并创建其他ReLU节点。

使用get_variable()创建的变量始终以其variable_scope的名称作为前缀命名(例如,“relu / threshold”),但对于所有其他节点(包括使用tf.Variable()创建的变量),变量范围的行为就像一个新名称的范围。 特别是,如果已经创建了具有相同名称的名称范围,则添加后缀以使该名称是唯一的。 例如,在前面的代码中创建的所有节点(阈值变量除外)的名称前缀为“relu_1 /”到“relu_5 /”,如图9-8所示。


不幸的是,必须在relu()函数之外定义阈值变量,其中ReLU代码的其余部分都驻留在其中。 要解决此问题,以下代码在第一次调用时在relu()函数中创建阈值变量,然后在后续调用中重新使用。 现在,relu()函数不必担心名称范围或变量共享:它只是调用get_variable(),它将创建或重用阈值变量(它不需要知道是哪种情况)。 其余的代码调用relu()五次,确保在第一次调用时设置reuse = False,而对于其他调用来说,reuse = True。


def relu(X):
    threshold = tf.get_variable("threshold", shape=(),
                                initializer=tf.constant_initializer(0.0))
    w_shape = (int(X.get_shape()[1]), 1)                        # not shown in the book
    w = tf.Variable(tf.random_normal(w_shape), name="weights")  # not shown
    b = tf.Variable(0.0, name="bias")                           # not shown
    z = tf.add(tf.matmul(X, w), b, name="z")                    # not shown
    return tf.maximum(z, threshold, name="max")

X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = []
for relu_index in range(5):
    with tf.variable_scope("relu", reuse=(relu_index >= 1)) as scope:
        relus.append(relu(X))
output = tf.add_n(relus, name="output")

生成的图形与之前略有不同,因为共享变量存在于第一个ReLU中(见图9-9)。



TensorFlow的这个介绍到此结束。 我们将在以下章节中讨论更多高级课题,特别是与深层神经网络,卷积神经网络和复发神经网络相关的许多操作,以及如何使用多线程,队列,多个GPU以及TensorFlow进行扩展多台服务器。







今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/sNQaYCtWya
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/10551
 
494 次点击