本文为数盟原创译文,转载时请注明出处为“数盟社区”。
在TensorFlow上定义你的模型可以很容易建成一个巨大的代码墙。如何用一个具有可读性和可重复使用的方法来构建你的代码?对于你的inpacient,这里是一个到工作示例依据的链接。
定义计算图
为每个模型划分类别是合理的。类别的接口是什么?通常情况下,你的模型连接到一些输入数据和目标的占位符,并提供培训和考核操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class
Model
:
def
__init__
(
self
,
data
,
target
)
:
data_size
=
int
(
data
.
get_shape
(
)
[
1
]
)
target_size
=
int
(
target
.
get_shape
(
)
[
1
]
)
weight
=
tf
.
Variable
(
tf
.
truncated_normal
(
[
data_size
,
target_size
]
)
)
bias
=
tf
.
Variable
(
tf
.
constant
(
0.1
,
shape
=
[
target_size
]
)
)
incoming
=
tf
.
matmul
(
data
,
weight
)
+
bias
prediction
=
tf
.
nn
.
softmax
(
incoming
)
cross_entropy
=
-
tf
.
reduce_sum
(
target
,
tf
.
log
(
prediction
)
)
self
.
_optimize
=
tf
.
train
.
RMSPropOptimizer
(
0.03
)
.
minimize
(
cross_entropy
)
mistakes
=
tf
.
not_equal
(
tf
.
argmax
(
target
,
1
)
,
tf
.
argmax
(
prediction
,
1
)
)
self
.
_error
=
tf
.
reduce_mean
(
tf
.
cast
(
mistakes
,
tf
.
float32
)
)
@
property
def
optimize
(
self
)
:
return
self
.
_optimize
@
property
def
error
(
self
)
:
return
self
.
_error
|
TensorFlow基本代码如何定义模型是十分基础的。但是,它也有一些问题。最值得注意的是,整个图形定义功能单一,即构造者。这既不是特别易读取也不可重复使用。
使用属性
单纯的代码分成功能并不能起作用,因为每个功能被调用时,该图形将被新的代码来扩展。因此,我们必须确保仅当功能第一次被调用时,该操作被添加到函数的曲线图。这基本上是延迟加载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
class
Model
:
def
__init__
(
self
,
data
,
target
)
:
self
.
data
=
data
self
.
target
=
target
self
.
_prediction
=
None
self
.
_optimize
=
None
self
.
_error
=
None
@
property
def
prediction
(
self
)
:
if
not
self
.
_prediction
:
data_size
=
int
(
self
.
data
.
get_shape
(
)
[
1
]
)
target_size
=
int
(
self
.
target
.
get_shape
(
)
[
1
]
)
weight
=
tf
.
Variable
(
tf
.
truncated_normal
(
[
data_size
,
target_size
]
)
)
bias
=
tf
.
Variable
(
tf
.
constant
(
0.1
,
shape
=
[
target_size
]
)
)
incoming
=
tf
.
matmul
(
self
.
data
,
weight
)
+
bias
self
.
_prediction
=
tf
.
nn
.
softmax
(
incoming
)
return
self
.
_prediction
@
property
def
optimize
(
self
)
:
if
not
self
.
_optimize
:
cross_entropy
=
-
tf
.
reduce_sum
(
self
.
target
,
tf
.
log
(
self
.
prediction
)
)
optimizer
=
tf
.
train
.
RMSPropOptimizer
(
0.03
)
self
.
_optimize
=
optimizer
.
minimize
(
cross_entropy
)
return
self
.
_optimize
@
property
def
error
(
self
)
:
if
not
self
.
_error
:
mistakes
=
tf
.
not_equal
(
tf
.
argmax
(
self
.
target
,
1
)
,
tf
.
argmax
(
self
.
prediction
,
1
)
)
self
.
_error
=
tf
.
reduce_mean
(
tf
.
cast
(
mistakes
,
tf
.
float32
)
)
return
self
.
_error
|
这肯定比第一个例子更好。现在你的代码结构为,你可以专注于各自的功能。但是,由于延迟加载逻辑,代码还是有点迟缓。让我们看看如何改善这一点。
延迟属性构建
Python是一种非常灵活的语言。让我来告诉你如何从刚才的例子中剔除冗余代码。我们将使用一个像@property这样只有一次评估函数的构建者。它将结果存储在一个用成员名字命名的构建功能下(用下划线前缀),并在后续任何调用时返回该值。如果你还没有使用自定义构建,你可能也想看看这个指南。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
functools
def
lazy_property
(
function
)
:
attribute
=
'_'
+
function
.
__name_
_
@
property
@
functools
.
wraps
(
function
)
def
wrapper
(
self
)
:
if
not
hasattr
(
self
,
attribute
)
:
setattr
(
self
,
attribute
,
function
(
self
)
)
return
getattr
(
self
,
attribute
)
return
wrapper
|
使用这个构建后,我们的例子简化成下面的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
class
Model
:
def
__init__
(
self
,
data
,
target
)
:
self
.
data
=
data
self
.
target
=
target
self
.
prediction
self
.
optimize
self
.
error
@
lazy_property
def
prediction
(
self
)
:
data_size
=
int
(
self
.
data
.
get_shape
(
)
[
1
]
)
target_size
=
int
(
self
.
target
.
get_shape
(
)
[
1
]
)
weight
=
tf
.
Variable
(
tf
.
truncated_normal
(
[
data_size
,
target_size
]
)
)
bias
=
tf
.
Variable
(
tf
.
constant
(
0.1
,
shape
=
[
target_size
]
)
)
incoming
=
tf
.
matmul
(
self
.
data
,
weight
)
+
bias
return
tf
.
nn
.
softmax
(
incoming
)
@
lazy_property
def
optimize
(
self
)
:
cross_entropy
=
-
tf
.
reduce_sum
(
self
.
target
,
tf
.
log
(
self
.
prediction
)
)
optimizer
=
tf
.
train
.
RMSPropOptimizer
(
0.03
)
return
optimizer
.
minimize
(
cross_entropy
)
@
lazy_property
def
error
(
self
)
:
mistakes
=
tf
.
not_equal
(
tf
.
argmax
(
self
.
target
,
1
)
,
tf
.
argmax
(
self
.
prediction
,
1
)
)
return
tf
.
reduce_mean
(
tf
.
cast
(
mistakes
,
tf
.
float32
)
)
|
请注意,我们提到在构造函数中的属性。这种方式要确保是当我们运行tf.initialize_variables()的时候完整图形被定义。
现在,我们可以用结构化的和紧凑的方式定义模型。这很适合我。如果您有任何建议或疑问,可以随意使用注释部分。
原文链接:https://danijar.github.io/structuring-your-tensorflow-models