斯坦福课程“CS 20SI:TensorFlow for Deep Learning Research”中文讲义(2)

讲义2:TensorFlow操作

课程网站:http://web.stanford.edu/class/cs20si/index.html
翻译:弓小长

“CS 20SI:TensorFlowfor Deep Learning Research”(cs20si.stanford.edu)
由Chip Huyen编写(huyenn@stanford.edu)
审校:Danijar Hafner

1 玩转TensorBoard

在TensorFlow中,可以统称常量、变量、操作符为操作。TensorFlow不仅仅是一个软件库,而是一套包括TensorFlow、TensorBoard和TensorServing的软件。为了充分利用TensorFlow,我们应该知道如何将以上所有内容相互结合使用。在这个讲义中,我们将首先介绍TensorBoard。

TensorBoard是包含在任何标准化安装的TensorFlow中的图形可视化软件。用Google自己的话来说:“你将使用TensorFlow来进行计算,比如训练一个庞大的深度神经网络,可能会很复杂而且令人困惑。为了便于理解,调试和优化TensorFlow程序,我们已经包含了一套名为TensorBoard的可视化工具。”

完全配置的TensorBoard将看起来像这样。 图片来自TensorBoard的网站。
这里写图片描述

当用户在TensorFlow程序的TensorBoard中实现这些操作时,这些操作被导出到一个事件文件。TensorBoard可以将这些事件文件转换成能够洞察模型行为的图。早日学习TensorBoard早日轻松高效地进行TensorFlow的相关工作。

我们来写第一个TensorFlow程序并用TensorBoard将其可视化。

import tensorflow as tf
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a,b)
with tf.Session() as sess:
    print sess.run(x)

在程序中激活TensorBoard,在创建图形之后,开始train循环之前,添加这行代码
writer = tf.summary.FileWriter(logs_dir,sess.graph)
上面这行代码是创建一个写对象来把操作写到事件文件中,存在logs_dir文件夹下。比如你可以将logs_dir设为“./graphs”。

import tensorflow as tf
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a,b)

with tf.Session () as sess:
writer = tf.summary.FileWriter('./graphs',sess.graph)
print sess.run(x)

# 用完writer后关闭
writer.close()

然后,在终端运行这个程序。确保你的当前工作路径和你运行Python代码的路径一致。

$ python [ yourprogram.py ]
$ tensorboard -- logdir = "./graphs"

打开浏览器转到http://localhost:6006/(或者运行TensorBoard命令后返回的链接)。
回到图选项卡,你会看到:
这里写图片描述
回到图选项卡,我看到一个包含3个节点的图。
这里写图片描述

a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a,b)

“Const” 和 “Const_1”分别表示a和b,节点“Add”表示x。我们给他们的名字(a,b, and x)是我们在需要的时候访问它们的。 他们对于内部的TensorFlow没有任何意义。 为了使TensorBoard显示你的ops的名字,你必须明确地给它们命名。

a = tf.constant ([ 2,2 ], name = "a")
b = tf.constant ([ 3,6 ], name = "b")
x = tf.add(a,b,name = "add")

现在重新运行TensorBoard,你会看到下面的图。
这里写图片描述

图本身定义了操作和依赖,但是不显示值。它只在我们运行包含值的会话时关心值。如果你忘记了下面这行代码,这里提醒一下。
tf.Session.run(fetches,feed_dict = None, options = None, run_metadata = None)
注意:如果你运行代码很多次,那么在路径下就会有很多事件文件,TF只会显示最新的图并且显示多个事件的警告。想摆脱警告,你可以删除不再需要的事件文件。

2 常量类型

文档链接: https://www.tensorflow.org/api_docs/python/constant_op/

你可以创建标量或者张量值的常量

tf.constant(value,dtype = None,shape = None,name = 'Const',verify_shape = False)
# 1d张量常量 (向量)
a = tf.constant ([2,2], name = "vector")
# 2x2张量常量 (矩阵)
b = tf.constant ([[0,1],[2,3]], name = "b")

你可以创建特殊元素值的张量

与numpy.zeros, numpy.zeros_like, numpy.ones, numpy.ones_like相似。

tf.zeros(shape,dtype = tf.float32,name = None)
# 创建一个元素值全0的张量
tf.zeros ([2,3], tf.int32)==>[[0,0,0],[0,0,0]]

tf.zeros_like(input_tensor,dtype = None,name = None,optimize = True)
# 创建一个与input_tensor大小一致,但是值全0的张量
# input_tensor 是[0, 1],[2, 3],[4, 5]]
tf.zeros_like(input_tensor)==>[[0,0],[0,0],[0,0]]

tf.ones(shape,dtype = tf.float32,name = None)
# 创建一个元素值全1的张量
tf.ones ([2,3], tf.int32)==>[[1,1,1],[1,1,1]]

tf.ones_like(input_tensor,dtype = None,name = None,optimize = True)
# 创建一个与input_tensor大小一致,但是值全1的张量
# input_tensor 是[0, 1],[2, 3],[4, 5]]
tf.ones_like(input_tensor)==>[[1,1],[1,1],[1,1]]

tf.fill(dims,value,name = None )
# 创建一个张量,其全部元素值为给定的标量值
tf.ones([2,3],8)==>[[8,8,8],[8,8,8]]

你可以创建顺序常量

tf.linspace(start,stop,num,name=None)
#创建一个序列num,从start开始.如果num>1,序列中的值以步长stop-start/num–1增加,最后一个值正好是stop.
#start,stop,num必须是标量
#可以与numpy.linspace相比,但是略有不同
#numpy.linspace(start,stop,num=50,endpoint=True,retstep=False,dtype=None)
tf.linspace(10.0,13.0,4,name="linspace")==>[10.0 11.0 12.0 13.0]

tf.range(start,limit=None,delta=1,dtype=None,name='range')
#创建一个数值序列从start开始,以delta步长增加,但是不包含limit
# 与Python中的range略有不同
# 'start' is 3, 'limit' is 18, 'delta' is 3
tf.range(start,limit,delta)==>[3,6,9,12,15]
# 'start' 是 3, 'limit' 是 1, 'delta' 是 -0.5
tf.range(start,limit,delta)==>[3,2.5,2,1.5]
# 'limit' 是 5
tf.range(limit)==>[0,1,2,3,4]

与 NumPy 或 Python 序列不同,TensorFlow的序列不可迭代

for _ in np.linspace(0,10,4 ): # OK
for _ in tf.linspace(0,10,4 ): # TypeError("'Tensor' object is not iterable.")
for _ in range(4 ): # OK
for _ in tf.range(4 ): # TypeError("'Tensor' object is not iterable.")

你还可以从特定分布中生成随机常量

tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None,name=None)
tf.random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None,name=None)
tf.random_shuffle(value, seed=None, name=None)
tf.random_crop(value, size, seed=None, name=None)
tf.multinomial(logits, num_samples, seed=None, name=None)
tf.random_gamma(shape, alpha, beta=None, dtype=tf.float32, seed=None, name=None)

3 数学操作

TensorFlow数学操作很标准,与NumPy类似,了解更多请访问
https://www.tensorflow.org/api_docs/python/math_ops/arithmetic_operators

a = tf.constant([3, 6])
b = tf.constant([2, 2])
tf.add(a, b) # >>[5 8]
tf.add_n([a, b, b]) # >>[7 10]. Equivalent to a + b + b
tf.mul(a, b) # >>[6 12] because mul is element wise
tf.matmul(a, b) # >> ValueError
tf.matmul(tf.reshape(a, shape=[1, 2]), tf.reshape(b, shape=[2, 1])) # >>[[18]]
tf.div(a, b) # >>[1 3]
tf.mod(a, b) # >>[1 0]

下面的表中列出了Python中的操作, 感谢“Fundamentals of Deep Learning”这本书的作者。
这里写图片描述

4 数据类型

Python 原生类型

TensorFlow接受Python原生类型,如Python布尔值,数值(整数,浮点数)和字符串。 单值将被转换为0-d张量(或标量),值列表将被转换为1-d张量(矢量),值列表的列表将被转换为2-d张量(矩阵)等等。 下面的例子是从“TensorFlow for Machine Intelligence”一书中改编和修改的。

t_0 = 19 # Treated as a 0-d tensor, or "scalar"
tf.zeros_like(t_0)# ==> 0
tf.ones_like(t_0)# ==> 1
t_1 =[b "apple",b "peach",b "grape"] # treated as a 1-d tensor, or "vector"
tf.zeros_like(t_1)# ==>['' '' '']
tf.ones_like(t_1)# ==> TypeError: Expected string, got 1 of type 'int' instead.
t_2 =[[True,False,False],
      [False,False,True],
      [False,True,False]] # treated as a 2-d tensor, or "matrix"
tf.zeros_like(t_2)# ==> 2x2 tensor, all elements are False
tf.ones_like(t_2)# ==> 2x2 tensor, all elements are True

TensorFlow原生类型

类似NumPy, TensorFlow也有自己的数据类型,就比如你看到的 tf.int32, tf.float32.下面是目前TensorFlow的数据类型,摘自TensorFlow’s official documentation.
这里写图片描述

NumPy数据类型

现在,你肯意识到了NumPy和TensorFlow之间的相似性。 TensorFlow被设计成与Numpy无缝整合,因为Numpy已经成为数据科学的通用语言。

TensorFlow的数据类型基于NumPy的数据类型; 实际上,np.int32 == tf.int32返回True。您可以将NumPy类型传递给TensorFlow操作。
例如:
tf.ones ([2,2], np.float32)==>[[1.0 1.0],[1.0 1.0]]
还记得我们的好朋友tf.Session.run(fetches)吗?如果请求对象是一个张量,那么输出将是一个NumPy数组。

TL; DR:大多数情况下,可以互换使用TensorFlow类型和NumPy类型。

  1. 字符串数据类型有一个问题。对于数值和布尔类型,TensorFlow和NumPy dtype匹配该行。但是,由于NumPy处理字符串的方式,tf.string在NumPy中没有完全匹配。 TensorFlow仍然可以从NumPy导入字符串数组,完全没问题 - 只是不要在NumPy中指定一个dtype!
  2. TensorFlow和NumPy都是n-d数组库。 NumPy支持nd数组,但不提供创建张量函数和自动计算导数的方法,也不提供GPU支持。所以TensorFlow仍然获胜!
  3. 使用Python类型来指定TensorFlow对象既快速又简单,而且对原创也很有用。但是,这样做有一个重要的陷阱。 Python类型缺乏显式声明数据类型的能力,但TensorFlow的数据类型更具体。例如,所有整数都是相同的类型,但TensorFlow有8位,16位,32位和64位整数可用。因此,如果你要使用Python类型,则TensorFlow必须推断你指的是哪个数据类型。

将数据传递到TensorFlow时,可以将数据转换为适当的类型,但某些数据类型仍然可能难以正确声明,例如复数。正因为如此,通常将手工定义的张量对象创建为NumPy数组。但是,如果可能的话,尽量使用TensorFlow类型,因为TensorFlow和NumPy都可以发展到不再存在这种兼容性的程度。

5 变量

常量很有趣,但我认为现在你们已经足以了解变量了。常量和变量之间的差别:

  1. 常量是常数。一个变量可以被赋值,其值可以被改变。
  2. 一个常量的值被存储在图中,并且图在任何地方被加载常量的值都被复制。 一个变量是单独存储的,可能存在于参数服务器上。

第2点基本上意味着常量被存储在图形定义中。 当常量非常消耗内存的时候,每次你必须加载图时会很慢。要查看图形的定义以及图形定义中存储的内容,只需打印图形的protobuf即可。 Protobuf代表协议缓冲区,“Google的语言中立、平台中立、可扩展的序列化结构数据机制 – 想象XML,但更小,更快,更简单。

import tensorflow as tf
my_const = tf.constant([1.0, 2.0], name="my_const")
print tf.get_default_graph().as_graph_def()

输出:

node {
    name: "my_const"
    op: "Const"
    attr {
        key: "dtype"
        value {
        type: DT_FLOAT
        }
    }
    attr {
        key: "value"
            value {
                tensor {
                dtype: DT_FLOAT
                tensor_shape {
                    dim {
                    size: 2
                    }
                }
                tensor_content: "\000\000\200?\000\000\000@"
            }
        }
    }
}
versions {
    producer: 17
}

声明变量

声明一个变量,你需要创建一个关于tf.Variable类的实例。tf.constant中c小写,但tf.Variable中的V大写。因为tf.constant是一个操作,而tf.Variable是一个类。

#create variable a with scalar value
a = tf.Variable(2,name = "scalar" )

#create variable b as a vector
b = tf.Variable ([2,3], name = "vector" )

#create variable c as a 2x2 matrix
c = tf.Variable ([[0,1],[2,3]], name = "matrix" )

# create variable W as 784 x 10 tensor, filled with zeros
W = tf.Variable(tf.zeros ([784,10]))

tf.Variable有一些操作:

x = tf.Variable(...)

x.initializer # init
x.value() # read op
x.assign(...) # write op
x.assign_add(...)
# and more

你必须在使用变量前先初始化变量

如果您在初始化变量之前尝试评估变量,则会遇到FailedPreconditionError:试图使用未初始化的值张量。

最简单的方法是使用以下命令一次初始化所有变量:tf.global_variables_initializer()

init = tf.global_variables_initializer ()

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

请注意,您使用tf.run()来运行初始化程序,而不是获取任何值。
要只初始化变量的一个子集,可以在变量列表中使用tf.variables_initializer()
你想初始化:

init_ab = tf.variables_initializer ([a,b], name = "init_ab")
with tf.Session () as sess:
    tf.run(init_ab)

你也可以使用tf.Variable.initializer分别初始化每个变量

# create variable W as 784 x 10 tensor, filled with zeros
W = tf.Variable(tf.zeros ([784,10]))
with tf.Session () as sess:
    tf.run(W.initializer)

另一种初始化变量的方法是从保存文件中恢复它。 我们将在几个星期内谈谈。

评估变量值

如果我们输出初始化的变量,我们只会看到张量对象。

# W is a random 700 x 100 variable object
W = tf.Variable(tf.truncated_normal ([700,10]))
with tf.Session () as sess:
    sess.run(W.initializer)
    print W
>> Tensor("Variable/read:0",shape =( 700,10 ), dtype = float32)

想要获取变量值,我们需要使用eval()来评估它。

# W is a random 700 x 100 variable object
W = tf.Variable(tf.truncated_normal ([700,10]))
with tf.Session () as sess:
    sess.run(W.initializer)
    print W.eval ()
>>[[- 0.76781619 - 0.67020458 1.15333688 ..., - 0.98434633 - 1.25692499
- 0.90904623]
[- 0.36763489 - 0.65037876 - 1.52936983 ..., 0.19320194 - 0.38379928
0.44387451]
[0.12510735 - 0.82649058 0.4321366 ..., - 0.3816964 0.70466036
1.33211911]
...,
[0.9203397 - 0.99590844 0.76853162 ..., - 0.74290705 0.37568584
0.64072722]
[- 0.12753558 0.52571583 1.03265858 ..., 0.59978199 - 0.91293705
- 0.02646019]
[0.19076447 - 0.62968266 - 1.97970271 ..., - 1.48389161 0.68170643
1.46369624]]

给变量赋值

我们可以使用tf.Variable.assign()给变量赋值。

W = tf.Variable(10 )
W.assign(100 )
with tf.Session () as sess:
    sess.run(W.initializer)
    print W.eval () # >> 10

为什么是10而不是100?W.assign(100)并没有把100赋值给W,而是赋了一个操作,为了使这个操作生效,你必须在会话中运行这个操作。

W = tf.Variable(10 )
assign_op = W.assign(100 )
with tf.Session () as sess:
    sess.run(assign_op)
    print W.eval () # >> 100

请注意,在这种情况下,我们没有初始化W,因为assign()为我们做的。实际上,初始化操作是将变量的初始值赋给变量本身的赋值操作。

# in the source code
self._initializer_op = state_ops.assign(self._variable, self._initial_value,
                                        validate_shape=validate_shape).op

有趣的例子:

# create a variable whose original value is 2
a = tf.Variable(2,name = "scalar" )

# assign a * 2 to a and call that op a_times_two
a_times_two = a.assign(a * 2)

init = tf.global_variables_initializer ()
with tf.Session () as sess:
    sess.run(init)
    # have to initialize a, because a_times_two op depends on the value of a
    sess.run(a_times_two)# >> 4
    sess.run(a_times_two)# >> 8
    sess.run(a_times_two)# >> 16

每次获取a_times_two时,TensorFlow都会给a赋一个* 2。

对于简单的递增和递减变量,TensorFlow包含了tf.Variable.assign_add()和tf.Variable.assign_sub()方法。 与tf.Variable.assign()不同,tf.Variable.assign_add()和tf.Variable.assign_sub()不会为你初始化变量,因为这些操作依赖于变量的初始值。

W = tf.Variable(10)
with tf.Session() as sess:
    sess.run(W.initializer)
    print sess.run(W.assign_add(10)) # >> 20
    print sess.run(W.assign_sub(2)) # >> 18

由于TensorFlow会话分别维护值,因此对于图中定义的变量每个会话可以有其自己的当前值。

W = tf.Variable(10)
sess1 = tf.Session()
sess2 = tf.Session()
sess1.run(W.initializer)
sess2.run(W.initializer)
print sess1.run(W.assign_add(10)) # >> 20
print sess2.run(W.assign_sub(2)) # >> 8
print sess1.run(W.assign_add(100)) # >> 120
print sess2.run(W.assign_sub(50)) # >> -42
sess1.close()
sess2.close()

你可以依靠其他变量来声明一个变量
假如你想声明U= W * 2

# W is a random 700 x 100 tensor
W = tf.Variable(tf.truncated_normal ([700,10]))
U = tf.Variable(W * 2)

在这种情况下,应该使用initialized_value()来确保W在初始化W之前被初始化。
U = tf.Variable(W.intialized_value () * 2)
类文档 tf.Variable()

6 交互会话

有时会看到InteractiveSession而不是Session。 唯一的区别是InteractiveSession使自己成为默认会话,因此你可以在不显式调用会话的情况下调用run()或eval()。 这在交互式shell和IPython notebooks中很方便,因为它避免了必须传递一个显式的会话对象来运行ops。但是,如果有多个会话运行,则会很复杂。

sess = tf.InteractiveSession ()
a = tf.constant(5.0)
b = tf.constant(6.0)
c = a * b
# We can just use 'c.eval()' without passing 'sess'
print(c.eval ())
sess.close ()

tf.InteractiveSession.close() 关闭一个 InteractiveSession.
tf.get_default_session()返回当前线程的默认会话。 返回的会话将是一个Session或Session.as_default()已经进入的最内层会话。

7 控制相关性

有时我们有两个独立的操作,但是你想指定哪个操作先运行,你可以用tf.Graph.control_dependencies(control_inputs)
例如:

# 你的图 g 有5个操作: a, b, c, d, e
with g.control_dependencies ([a,b,c]):
    # `d` and `e` will only run after `a`, `b`, and `c` have executed.
    d = ...
    e = …

8 占位符和feed_dict

记得从第一讲提到TensorFlow程序通常有两个阶段:
阶段1:组装一个图形
阶段2:使用会话来执行图中的操作。

因此,我们可以在不知道计算所需的值的情况下先组装图。 这相当于在不知道x,y的值的情况下先定义x,y的函数。
例如,f(x,y)= x * 2 + y。x,y是实际值的占位符。

通过组装图,我们或我们的客户可以稍后在需要执行计算时提供自己的数据。

要定义一个占位符,我们使用:
tf.placeholder(dtype,shape = None,name = None)
Dtype是指定占位符值的数据类型的必需参数。

Shape指定可以被接受为占位符的实际值的张量的大小。 shape = None意味着任何大小的张量都可以被接受。 使用shape = None很容易构建图形,但是对于调试来说是噩梦。你应该始终尽可能详细地定义占位符的大小。

你也可以为你的占位符指定一个名称,就像在TensorFlow中可以使用其他操作一样。

有关占位符的更多信息见官方文档。

# 创建一个占位符,32-bit浮点类型, 3个元素的向量
a = tf.placeholder(tf.float32,shape =[3])
# 创建一个常量,32-bit浮点类型, 3个元素的向量
b = tf.constant ([5,5,5], tf.float32)
# 像使用常量或变量一样使用占位符
c = a + b # Short for tf.add(a, b)
# 如果我们试图捕获c,将会报错.
with tf.Session () as sess:
    print(sess.run(c ))
>> NameError

这会产生一个错误,因为要计算c,我们需要a的值,但a只是一个没有实际值的占位符。 我们必须先给a一个实际值。

with tf.Session () as sess:
    # feed[1, 2, 3] to placeholder a via the dict {a:[1, 2, 3]}
    # fetch value of c
    print(sess.run(c,{ a :[1,2,3]}))
>>[6. 7. 8.]

我们要想看它在TensorBoard中什么样,添加
writer = tf.summary.FileWriter('./my_graph',sess.graph)
并且在终端输入:
$ tensorboard -- logdir = 'my_graph'
正如你所看到的,占位符与其他任何操作一样。 3是占位符的形状。
这里写图片描述
在前面的例子中,我们给占位符提供一个单一的值。 如果我们想给占位符提供多个数据点呢? 这是一个合理的假设,因为我们经常需要通过训练或测试集中的多个数据点来运行计算。

通过遍历数据集,我们可以按照我们的需要提供任意数据点给占位符,也可以一次提供一个值。

with tf.Session () as sess:
    for a_value in list_of_a_values:
    print(sess.run(c,{ a : a_value }))

也可以给非占位符的张量赋值,任何张量都可以被赋值。要检验一个张量是否可以被赋值,可以用:
tf.Graph.is_feedable(tensor)

# 创建操作,张量,等(使用默认图)
a = tf.add(2, 5)
b = tf.mul(a, 3)

# 使用默认图开始一个会话
sess = tf.Session()

# 定义一个字典把15赋给 `a`
replace_dict = {a: 15}

#运行会话,传入`replace_dict`作为`feed_dict`的值
sess.run(b, feed_dict=replace_dict) # returns 45

feed_dict对于测试模型非常有用。 当你有一个大图,只是想测试某些部分,你可以提供虚拟值,所以TensorFlow不会浪费时间做不必要的计算。

9 懒加载的陷阱*

*我可能已经提出了这个条款

被我和我的朋友Danijar称之为“懒加载”的错误是我看到的(和我曾经提交的)最常见的TensorFlow非bug错误之一。 懒加载是一个术语,指的是当你延迟声明/初始化一个对象直到它被加载的编程模式。 在TensorFlow的情况下,它意味着你推迟创建一个操作,直到你需要计算它。 例如,在组装图形时创建操作z,这是正常的加载。

x = tf.Variable(10, name='x')
y = tf.Variable(20, name='y')
z = tf.add(x, y)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for _ in range(10):
        sess.run(z)
    writer.close()

这是当有人决定使用懒加载保存一行代码时会发生的事情:

x = tf.Variable(10, name='x')
y = tf.Variable(20, name='y')

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for _ in range(10):
        sess.run(tf.add(x, y)) # create the op add only when you need to compute it
    writer.close()

让我们在TensorBoard里看看它们的图形。
正常加载的图看起来就像我们期望的那样。
懒加载(lazy loading)

这里写图片描述

那么,节点“Add”缺失,这是可以理解的,因为我们已经把图形写入FileWriter之后添加了“Add”注释。这使得阅读图变得困难,但这不是一个错误。
那么,最重要的是什么?…

我们来看看图的定义。 请记住,要打印图形定义,我们使用:
print tf.get_default_graph().as_graph_def()
正常加载的图形的protobuf只有1个节点“添加”:

node {
    name: "Add"
    op: "Add"
    input: "x/read"
    input: "y/read"
    attr {
        key: "T"
        value {
            type: DT_INT32
        }
    }
}

另一方面,懒加载图的protobuf有“Add”节点的10个副本。 每当你想计算z时,它会添加一个新的节点“Add”。

node {
    name: "Add"
    op: "Add"
    ...
}
node {
    name: "Add_9"
    op: "Add"
    ...
}

你可能会想:“这很愚蠢。为什么我要多次计算相同的值?”并认为这是一个没人会犯的错误。它发生得比你想象的更频繁。例如,你可能想要计算相同的损失函数,或者训练一定数量的样本之后做一些预测。在你知道它之前,你已经计算了好几千次,并且在你的图中增加了数千个不必要的节点。 你的图定义变得臃肿,加载速度慢,而且传递的代价很高。

有两种方法可以避免这个错误。首先,尽可能将操作的定义和执行分开。但是,当这样行不通的时候,因为你想将相关的操作归类,你可以使用Python属性来确保函数在首次调用时仅加载一次。这不是Python课程,所以我不会深究如何去做。但如果你想知道,请查看Danijar Hafner的这篇精彩的博客文章

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值