模块性

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

模块性 - 图1

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

  1. n_features = 3
  2. X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
  3. w1 = tf.Variable(tf.random_normal((n_features, 1)), name="weights1")
  4. w2 = tf.Variable(tf.random_normal((n_features, 1)), name="weights2")
  5. b1 = tf.Variable(0.0, name="bias1")
  6. b2 = tf.Variable(0.0, name="bias2")
  7. z1 = tf.add(tf.matmul(X, w1), b1, name="z1")
  8. z2 = tf.add(tf.matmul(X, w2), b2, name="z2")
  9. relu1 = tf.maximum(z1, 0., name="relu1")
  10. relu2 = tf.maximum(z1, 0., name="relu2")
  11. output = tf.add(relu1, relu2, name="output")

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

  1. def relu(X):
  2. w_shape = (int(X.get_shape()[1]), 1)
  3. w = tf.Variable(tf.random_normal(w_shape), name="weights")
  4. b = tf.Variable(0.0, name="bias")
  5. z = tf.add(tf.matmul(X, w), b, name="z")
  6. return tf.maximum(z, 0., name="relu")
  7. n_features = 3
  8. X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
  9. relus = [relu(X) for i in range(5)]
  10. output = tf.add_n(relus, name="output")

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

模块性 - 图2

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

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

模块性 - 图3