9.启动并运行_TensorFlow.md 45.2 KB
Newer Older
W
style  
wizardforcel 已提交
1
# 九、启动并运行 TensorFlow
2

W
wizardforcel 已提交
3 4
> 译者:[@akonwang](https://github.com/wangxupeng)、[@WilsonQu](https://github.com/WilsonQu)
> 
W
wizardforcel 已提交
5
> 校对者:[@Lisanaaa](https://github.com/Lisanaaa)、[@飞龙](https://github.com/wizardforcel)、[@YuWang](https://github.com/bigeyex)
W
wizardforcel 已提交
6

W
wizardforcel 已提交
7
TensorFlow 是一款用于数值计算的强大的开源软件库,特别适用于大规模机器学习的微调。 它的基本原理很简单:首先在 Python 中定义要执行的计算图(例如图 9-1),然后 TensorFlow 使用该图并使用优化的 C++ 代码高效运行该图。
W
WilsonQu 已提交
8

W
wizardforcel 已提交
9
![](../images/chapter_9/9-1.png)
W
WilsonQu 已提交
10

W
wizardforcel 已提交
11
最重要的是,Tensorflow 可以将图分解为多个块并在多个 CPU 或 GPU 上并行运行(如图 9-2 所示)。 TensorFlow 还支持分布式计算,因此您可以在数百台服务器上分割计算,从而在合理的时间内在庞大的训练集上训练庞大的神经网络(请参阅第 12 章)。 TensorFlow 可以训练一个拥有数百万个参数的网络,训练集由数十亿个具有数百万个特征的实例组成。 这应该不会让您吃惊,因为 TensorFlow 是 由Google 大脑团队开发的,它支持谷歌的大量服务,例如 Google Cloud Speech,Google Photos 和 Google Search。
W
WilsonQu 已提交
12

W
wizardforcel 已提交
13
![](../images/chapter_9/9-2.png)
W
WilsonQu 已提交
14

W
wizardforcel 已提交
15
当 TensorFlow 于 2015 年 11 月开放源代码时,已有许多深度学习的流行开源库(表 9-1 列出了一些),公平地说,大部分 TensorFlow 的功能已经存在于一个库或另一个库中。 尽管如此,TensorFlow 的整洁设计,可扩展性,灵活性和出色的文档(更不用说谷歌的名字)迅速将其推向了榜首。 简而言之,TensorFlow 的设计灵活性,可扩展性和生产就绪性,现有框架可以说只有其中三种可用。 这里有一些 TensorFlow 的亮点:
W
WilsonQu 已提交
16

W
wizardforcel 已提交
17
-   它不仅在 Windows,Linux 和 MacOS 上运行,而且在移动设备上运行,包括 iOS 和 Android。
W
WilsonQu 已提交
18

W
wizardforcel 已提交
19
    它提供了一个非常简单的 Python API,名为 TF.Learn2(`tensorflow.con trib.learn`),与 Scikit-Learn 兼容。正如你将会看到的,你可以用几行代码来训练不同类型的神经网络。之前是一个名为 Scikit Flow(或 Skow)的独立项目。
W
WilsonQu 已提交
20

W
wizardforcel 已提交
21
-   它还提供了另一个简单的称为 TF-slim(`tensorflow.contrib.slim`)的 API 来简化构建,训练和求出神经网络。
W
WilsonQu 已提交
22

W
wizardforcel 已提交
23
-   其他几个高级 API 已经在 TensorFlow 之上独立构建,如 **Keras****Pretty Tensor**
W
WilsonQu 已提交
24

W
wizardforcel 已提交
25
-   它的主要 Python API 提供了更多的灵活性(以更高复杂度为代价)来创建各种计算,包括任何你能想到的神经网络结构。
W
WilsonQu 已提交
26

W
wizardforcel 已提交
27
-   它包括许多 ML 操作的高效 C ++ 实现,特别是构建神经网络所需的 C++ 实现。还有一个 C++ API 来定义您自己的高性能操作。
W
WilsonQu 已提交
28

W
wizardforcel 已提交
29
-   它提供了几个高级优化节点来搜索最小化损失函数的参数。由于 TensorFlow 自动处理计算您定义的函数的梯度,因此这些非常易于使用。这称为自动分解(或`autodi`)。
W
WilsonQu 已提交
30

W
wizardforcel 已提交
31
-   它还附带一个名为 TensorBoard 的强大可视化工具,可让您浏览计算图表,查看学习曲线等。
W
WilsonQu 已提交
32

W
wizardforcel 已提交
33
-   Google 还推出了云服务来运行 TensorFlow 表。
W
WilsonQu 已提交
34

W
wizardforcel 已提交
35
-   最后,它拥有一支充满热情和乐于助人的开发团队,以及一个不断成长的社区,致力于改善它。它是 GitHub 上最受欢迎的开源项目之一,并且越来越多的优秀项目正在构建之上(例如,查看 <https://www.tensorflow.org/><https://github.com/jtoy/awesome-tensorflow>)。  要问技术问题,您应该使用 <http://stackoverflow.com/> 并用`tensorflow`标记您的问题。您可以通过 GitHub 提交错误和功能请求。有关一般讨论,请加入 **Google 小组**
W
WilsonQu 已提交
36

W
wizardforcel 已提交
37
![](../images/chapter_9/Tbale%209-1.png)
W
WilsonQu 已提交
38

W
wizardforcel 已提交
39
在本章中,我们将介绍 TensorFlow 的基础知识,从安装到创建,运行,保存和可视化简单的计算图。 在构建第一个神经网络之前掌握这些基础知识很重要(我们将在下一章中介绍)。
W
WilsonQu 已提交
40 41 42



W
wizardforcel 已提交
43 44 45
## 安装

让我们开始吧!假设您按照第 2 章中的安装说明安装了 Jupyter 和 Scikit-Learn,您可以简单地使用`pip`来安装 TensorFlow。 如果你使用`virtualenv`创建了一个独立的环境,你首先需要激活它:
W
WilsonQu 已提交
46

W
wizardforcel 已提交
47
<pre><code>
W
fix  
wizardforcel 已提交
48 49
&#x24; cd &#x24;ML_PATH                            #Your ML working directory(e.g., &#x24;HOME/ml)
&#x24; source env/bin/activate
W
wizardforcel 已提交
50
</code></pre>
W
WilsonQu 已提交
51

W
wizardforcel 已提交
52
下一步,安装 Tensorflow。
W
WilsonQu 已提交
53 54 55 56 57

```
$ pip3 install --upgrade tensorflow
```

W
wizardforcel 已提交
58
对于 GPU 支持,你需要安装`tensorflow-gpu`而不是`tensorflow`。具体请参见 12 章内容。
W
WilsonQu 已提交
59

W
wizardforcel 已提交
60
为了测试您的安装,请输入一下命令。其输出应该是您安装的 Tensorflow 的版本号。
W
WilsonQu 已提交
61 62 63 64 65 66 67 68 69

```
$ python -c 'import tensorflow; print(tensorflow.__version__)'
1.0.0
```




W
wizardforcel 已提交
70
## 创造第一个图谱,然后运行它
71 72 73 74 75 76 77 78

```python
import tensorflow as tf  
x = tf.Variable(3, name="x")  
y = tf.Variable(4, name="y")  
f = x*x*y + y + 2  
```

W
wizardforcel 已提交
79
这就是它的一切! 最重要的是要知道这个代码实际上并不执行任何计算,即使它看起来像(尤其是最后一行)。 它只是创建一个计算图谱。 事实上,变量都没有初始化.要求出此图,您需要打开一个 TensorFlow 会话并使用它初始化变量并求出`f`。TensorFlow 会话负责处理在诸如 CPU 和 GPU 之类的设备上的操作并运行它们,并且它保留所有变量值。以下代码创建一个会话,初始化变量,并求出`f`,然后关闭会话(释放资源):
80 81 82 83 84 85 86 87 88 89 90 91

```python
# way1  
sess = tf.Session()  
sess.run(x.initializer)  
sess.run(y.initializer)  
result = sess.run(f)  
  
print(result)  
sess.close()  
```

92
不得不每次重复sess.run() 有点麻烦,但幸运的是有一个更好的方法:
93 94 95 96 97 98 99 100


```python
# way2  
with tf.Session() as sess:  
    x.initializer.run()  
    y.initializer.run()  
    result = f.eval()  
W
wizardforcel 已提交
101
print(result) 
W
WilsonQu 已提交
102
```
103

W
wizardforcel 已提交
104
`with`块中,会话被设置为默认会话。 调用`x.initializer.run()`等效于调用`tf.get_default_session().run(x.initial)``f.eval()`等效于调用`tf.get_default_session().run(f)`。 这使得代码更容易阅读。 此外,会话在块的末尾自动关闭。
105

W
wizardforcel 已提交
106
你可以使用`global_variables_initializer()` 函数,而不是手动初始化每个变量。 请注意,它实际上没有立即执行初始化,而是在图谱中创建一个当程序运行时所有变量都会初始化的节点:
107 108 109

```python
# way3  
110 111 112 113 114
init = tf.global_variables_initializer()  
with tf.Session() as sess:  
    init.run()  
    result = f.eval()  
    print(result)  
115 116
```

W
wizardforcel 已提交
117
在 Jupyter 内部或在 Python shell 中,您可能更喜欢创建一个`InteractiveSession`。 与常规会话的唯一区别是,当创建`InteractiveSession`时,它将自动将其自身设置为默认会话,因此您不需要使用模块(但是您需要在完成后手动关闭会话):
118 119 120 121 122 123 124 125 126 127 128 129

```python
# way4  
init = tf.global_variables_initializer()  
sess = tf.InteractiveSession()  
init.run()  
result = f.eval()  
print(result)  
sess.close()  
```


W
wizardforcel 已提交
130
TensorFlow 程序通常分为两部分:第一部分构建计算图谱(这称为构造阶段),第二部分运行它(这是执行阶段)。 建设阶段通常构建一个表示 ML 模型的计算图谱,然后对其进行训练,计算。 执行阶段通常运行循环,重复地求出训练步骤(例如,每个小批次),逐渐改进模型参数。 
131 132


W
wizardforcel 已提交
133
## 管理图谱
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

您创建的任何节点都会自动添加到默认图形中:

```python
>>> x1 = tf.Variable(1)  
>>> x1.graph is tf.get_default_graph()  
True  
```

在大多数情况下,这是很好的,但有时您可能需要管理多个独立图形。 您可以通过创建一个新的图形并暂时将其设置为一个块中的默认图形,如下所示:


```python
>>> graph = tf.Graph()  
>>> with graph.as_default():  
... x2 = tf.Variable(2)  
...  
>>> x2.graph is graph  
True  
>>> x2.graph is tf.get_default_graph()  
False  
```

W
wizardforcel 已提交
157
在 Jupyter(或 Python shell)中,通常在实验时多次运行相同的命令。 因此,您可能会收到包含许多重复节点的默认图形。 一个解决方案是重新启动 Jupyter 内核(或 Python shell),但是一个更方便的解决方案是通过运行`tf.reset_default_graph()`来重置默认图。
158

W
wizardforcel 已提交
159
## 节点值的生命周期
160

W
wizardforcel 已提交
161
求出节点时,TensorFlow 会自动确定所依赖的节点集,并首先求出这些节点。 例如,考虑以下代码:
162 163 164 165 166 167 168 169 170 171 172 173

```python
# w = tf.constant(3)  
# x = w + 2  
# y = x + 5  
# z = x * 3  
  
# with tf.Session() as sess:  
#     print(y.eval())  
#     print(z.eval())  
```

W
wizardforcel 已提交
174
首先,这个代码定义了一个非常简单的图。然后,它启动一个会话并运行图来求出`y`:TensorFlow 自动检测到`y`取决于`x`,它取决于`w`,所以它首先求出`w`,然后`x`,然后`y`,并返回`y`的值。最后,代码运行图来求出`z`。同样,TensorFlow 检测到它必须首先求出`w``x`。重要的是要注意,它不会复用以前的w和x的求出结果。简而言之,前面的代码求出`w``x`两次。
175
所有节点值都在图运行之间删除,除了变量值,由会话跨图形运行维护(队列和读者也保持一些状态)。变量在其初始化程序运行时启动其生命周期,并且在会话关闭时结束。
W
wizardforcel 已提交
176
如果要有效地求出`y``z`,而不像之前的代码那样求出`w``x`两次,那么您必须要求 TensorFlow 在一个图形运行中求出`y``z`,如下面的代码所示:
177 178 179 180 181 182 183 184

```python
# with tf.Session() as sess:  
#     y_val, z_val = sess.run([y, z])  
#     print(y_val) # 10  
#     print(z_val) # 15  
```

W
wizardforcel 已提交
185
在单进程 TensorFlow 中,多个会话不共享任何状态,即使它们复用同一个图(每个会话都有自己的每个变量的副本)。 在分布式 TensorFlow 中,变量状态存储在服务器上,而不是在会话中,因此多个会话可以共享相同的变量。
186

W
wizardforcel 已提交
187
## Linear Regression with TensorFlow
188

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

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

W
wizardforcel 已提交
193
最后,代码创建一个`session`并使用它来求出`theta`
194 195 196 197 198 199 200 201 202 203 204

```python
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")  
205
XT = tf.transpose(X)  
206 207 208 209 210 211
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)  
```

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


W
wizardforcel 已提交
215
其实这里就是用最小二乘法算`θ`
216 217 218 219

[http://blog.csdn.net/akon_wang_hkbu/article/details/77503725](http://blog.csdn.net/akon_wang_hkbu/article/details/77503725)


W
wizardforcel 已提交
220
## 实现梯度下降
221

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

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

W
wizardforcel 已提交
226
## 手动计算渐变
227 228 229

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

W
wizardforcel 已提交
230
* `random_uniform()`函数在图形中创建一个节点,它将生成包含随机值的张量,给定其形状和值作用域,就像 NumPy 的`rand()`函数一样。
W
wizardforcel 已提交
231
* `assign()`函数创建一个为变量分配新值的节点。 在这种情况下,它实现了批次梯度下降步骤 ![\theta(next step)= \theta - \eta  \nabla_{\theta}MSE(\theta)](../images/tex-5c180c9b9130b5dacda464ca73ee8f1e.gif)
W
wizardforcel 已提交
232
* 主循环一次又一次(共`n_epochs`次)执行训练步骤,每 100 次迭代都打印出当前均方误差(MSE)。 你应该看到 MSE 在每次迭代中都会下降。
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260

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

W
wizardforcel 已提交
261
## Using autodiff
262

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

W
wizardforcel 已提交
265
为了理解为什么,考虑函数`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)`。 使用这种方法,你只需要调用它三次。
266 267


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

提示:不要尝试。

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

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

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

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

W
wizardforcel 已提交
288
自动计算梯度有四种主要方法。 它们总结在表 9-2 中。 TensorFlow 使用反向模式,这是完美的(高效和准确),当有很多输入和少量的输出,如通常在神经网络的情况。 它只需要通过 ![n_{outputs} + 1](../images/tex-378b3eaa9c01f52bc8987807984a5a88.gif) 次图遍历即可计算所有输出的偏导数。
289 290 291

![](/images/chapter_9/20171030144740842.jpg)

W
wizardforcel 已提交
292
## 使用优化器
293

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


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

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

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

W
wizardforcel 已提交
308
## 将数据提供给训练算法
309

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

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


```python
>>> 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.]]  
```

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

W
wizardforcel 已提交
331
要实现小批量渐变下降,我们只需稍微调整现有的代码。 首先更改`X``Y`的定义,使其定义为占位符节点:
332 333 334 335 336 337 338 339 340 341 342 343 344

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

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

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

W
wizardforcel 已提交
345
最后,在执行阶段,逐个获取小批量,然后在求出依赖于`X``y`的值的任何一个节点时,通过`feed_dict`提供`X``y`的值。
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

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

W
wizardforcel 已提交
362
在求出theta时,我们不需要传递X和y的值,因为它不依赖于它们。
363

W
wizardforcel 已提交
364
## MINI-BATCH 完整代码
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395

```python
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  
W
wizardforcel 已提交
396
n_batches = int(np.ceil(m / batch_size)) # ceil() 方法返回 x 的值上限 - 不小于 x 的最小整数。  
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
  
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)  
```

W
wizardforcel 已提交
419
## 保存和恢复模型
W
Wizard Zhang 已提交
420

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

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

425
```python
W
Wizard Zhang 已提交
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
[...]
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")
```

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

445
```python
W
Wizard Zhang 已提交
446 447 448 449 450
with tf.Session() as sess:
    saver.restore(sess, "/tmp/my_model_final.ckpt")
    [...]
```

W
wizardforcel 已提交
451
默认情况下,保存器将以自己的名称保存并还原所有变量,但如果需要更多控制,则可以指定要保存或还原的变量以及要使用的名称。 例如,以下保存器将仅保存或恢复`theta`变量,它的键名称是`weights`
W
Wizard Zhang 已提交
452 453 454 455 456 457 458

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

完整代码

459
```python
460
import numpy as np
W
Wizard Zhang 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
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文件夹就找到文件了
```

W
wizardforcel 已提交
501
使用 TensorBoard 展现图形和训练曲线
W
Wizard Zhang 已提交
502

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

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

507
```python
W
Wizard Zhang 已提交
508 509 510 511 512 513
from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = "tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
```

W
wizardforcel 已提交
514
接下来,在构建阶段结束时添加以下代码:
W
Wizard Zhang 已提交
515

516
```python
W
Wizard Zhang 已提交
517 518 519 520
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
```

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

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

525
```python
W
Wizard Zhang 已提交
526 527 528 529 530 531 532 533 534 535 536
[...]
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})
[...]
```

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

W
wizardforcel 已提交
539
最后,要在程序结束时关闭`FileWriter`
W
Wizard Zhang 已提交
540

541
```pytho
W
Wizard Zhang 已提交
542 543 544 545 546
file_writer.close()
```

完整代码

547
```python
W
Wizard Zhang 已提交
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
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)
```

W
ch9 pic  
wizardforcel 已提交
610
![](../images/chapter_9/o-9-2.png)
W
Wizard Zhang 已提交
611

W
wizardforcel 已提交
612
## 名称作用域
W
Wizard Zhang 已提交
613

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

616
```python
W
Wizard Zhang 已提交
617 618 619 620 621
with tf.name_scope("loss") as scope:
    error = y_pred - y
    mse = tf.reduce_mean(tf.square(error), name="mse")
```

W
wizardforcel 已提交
622
在作用域内定义的每个`op`的名称现在以`loss/`为前缀:
W
Wizard Zhang 已提交
623

624
```python
W
Wizard Zhang 已提交
625 626 627 628 629 630
>>> print(error.op.name)
loss/sub
>>> print(mse.op.name)
loss/mse
```

W
wizardforcel 已提交
631
在 TensorBoard 中,`mse``error`节点现在出现在`loss`命名空间中,默认情况下会出现崩溃(图 9-5)。
W
Wizard Zhang 已提交
632

W
ch9 pic  
wizardforcel 已提交
633
![](../images/chapter_9/9-5.png)
W
Wizard Zhang 已提交
634 635 636

完整代码

637
```python
W
Wizard Zhang 已提交
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
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)
```

W
ch9 pic  
wizardforcel 已提交
708
![](../images/chapter_9/o-9-3.png)
W
Wizard Zhang 已提交
709

W
wizardforcel 已提交
710
## 模块性
W
Wizard Zhang 已提交
711

W
wizardforcel 已提交
712
假设您要创建一个图,它的作用是将两个整流线性单元(ReLU)的输出值相加。 ReLU 计算一个输入值的对应线性函数输出值,如果为正,则输出该结值,否则为 0,如等式 9-1 所示。
W
Wizard Zhang 已提交
713

W
ch9 pic  
wizardforcel 已提交
714
![](../images/chapter_9/e-9-1.png)
W
Wizard Zhang 已提交
715 716 717

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

718
```python
W
Wizard Zhang 已提交
719 720 721 722 723 724 725 726 727 728 729 730 731
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")
```

W
wizardforcel 已提交
732
这样的重复代码很难维护,容易出错(实际上,这个代码包含了一个剪贴错误,你发现了吗?) 如果你想添加更多的 ReLU,会变得更糟。 幸运的是,TensorFlow 可以让您保持 DRY(不要重复自己):只需创建一个功能来构建 ReLU。 以下代码创建五个 ReLU 并输出其总和(注意,`add_n()`创建一个计算张量列表之和的操作):
W
Wizard Zhang 已提交
733

734
```python
W
Wizard Zhang 已提交
735 736 737 738 739 740 741 742 743 744 745 746 747
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")
```

W
wizardforcel 已提交
748
请注意,创建节点时,TensorFlow 将检查其名称是否已存在,如果它已经存在,则会附加一个下划线,后跟一个索引,以使该名称是唯一的。 因此,第一个 ReLU 包含名为`weights``bias``z``relu`的节点(加上其他默认名称的更多节点,如`MatMul`); 第二个 ReLU 包含名为`weights_1``bias_1`等节点的节点; 第三个 ReLU 包含名为 `weights_2``bias_2`的节点,依此类推。 TensorBoard 识别这样的系列并将它们折叠在一起以减少混乱(如图 9-6 所示)
W
Wizard Zhang 已提交
749

W
ch9 pic  
wizardforcel 已提交
750
![](../images/chapter_9/9-6.png)
W
Wizard Zhang 已提交
751

W
wizardforcel 已提交
752
使用名称作用域,您可以使图形更清晰。 简单地将`relu()`函数的所有内容移动到名称作用域内。 图 9-7 显示了结果图。 请注意,TensorFlow 还通过附加`_1``_2`等来提供名称作用域的唯一名称。
W
Wizard Zhang 已提交
753

754
```python
W
Wizard Zhang 已提交
755 756 757 758 759 760 761 762 763
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
```

W
ch9 pic  
wizardforcel 已提交
764
![](../images/chapter_9/9-7.png)
W
Wizard Zhang 已提交
765

W
wizardforcel 已提交
766
## 共享变量
W
Wizard Zhang 已提交
767

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

770
```python
W
Wizard Zhang 已提交
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
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")
```

W
wizardforcel 已提交
787
这很好:现在您可以使用阈值变量来控制所有 ReLU 的阈值。但是,如果有许多共享参数,比如这一项,那么必须一直将它们作为参数传递,这将是非常痛苦的。许多人创建了一个包含模型中所有变量的 Python 字典,并将其传递给每个函数。另一些则为每个模块创建一个类(例如:一个使用类变量来处理共享参数的 ReLU 类)。另一种选择是在第一次调用时将共享变量设置为`relu()`函数的属性,如下所示:
W
Wizard Zhang 已提交
788

789
```python
W
Wizard Zhang 已提交
790 791 792 793 794 795 796 797 798 799 800
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")
```

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

803
```python
W
Wizard Zhang 已提交
804 805 806 807 808
with tf.variable_scope("relu"):
    threshold = tf.get_variable("threshold", shape=(),
                                initializer=tf.constant_initializer(0.0))
```

W
wizardforcel 已提交
809
请注意,如果变量已经通过较早的`get_variable()`调用创建,则此代码将引发异常。 这种行为可以防止错误地复用变量。如果要复用变量,则需要通过将变量`scope`的复用属性设置为`True`来明确说明(在这种情况下,您不必指定形状或初始值):
W
Wizard Zhang 已提交
810

811
```python
W
Wizard Zhang 已提交
812 813 814 815
with tf.variable_scope("relu", reuse=True):
    threshold = tf.get_variable("threshold")
```

W
wizardforcel 已提交
816
该代码将获取现有的`relu/threshold`变量,如果不存在会引发异常(如果没有使用`get_variable()`创建)。 或者,您可以通过调用`scope``reuse_variables()`方法将复用属性设置为`true`
W
Wizard Zhang 已提交
817

818
```python
W
Wizard Zhang 已提交
819 820 821 822 823
with tf.variable_scope("relu") as scope:
    scope.reuse_variables()
    threshold = tf.get_variable("threshold")
```

W
wizardforcel 已提交
824
一旦重新使用设置为`True`,它将不能在块内设置为`False`。 而且,如果在其中定义其他变量作用域,它们将自动继承`reuse = True`。 最后,只有通过`get_variable()`创建的变量才可以这样复用.
W
Wizard Zhang 已提交
825

W
wizardforcel 已提交
826
现在,您拥有所有需要的部分,使`relu()`函数访问阈值变量,而不必将其作为参数传递:
W
Wizard Zhang 已提交
827

828
```python
W
Wizard Zhang 已提交
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
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")
```

W
wizardforcel 已提交
846
该代码首先定义`relu()`函数,然后创建`relu/threshold`变量(作为标量,稍后将被初始化为 0.0),并通过调用`relu()`函数构建五个ReLU。`relu()`函数复用`relu/threshold`变量,并创建其他 ReLU 节点。
W
Wizard Zhang 已提交
847

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

W
ch9 pic  
wizardforcel 已提交
850
![](../images/chapter_9/9-8.png)
W
Wizard Zhang 已提交
851

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

854
```python
W
Wizard Zhang 已提交
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
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")
```

W
wizardforcel 已提交
872
生成的图形与之前略有不同,因为共享变量存在于第一个 ReLU 中(见图 9-9)。
W
Wizard Zhang 已提交
873

W
ch9 pic  
wizardforcel 已提交
874
![](../images/chapter_9/9-9.png)
W
Wizard Zhang 已提交
875

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