摘要:最值得注意的一点是,整个图都是在一个函数中定义和构造的,那么这即不可读也不可重复使用。
在 TensorFlow 中定义你的模型,可能会导致一个巨大的代码量。那么,如何去组织代码,使得它是一个高可读性和高可重用的呢?如果你刚刚开始学习代码架构,那么这里有一个例子,不妨学习一下。
定义计算图
当你设计一个模型的时候,从类出发是一个非常好的开始。那么如何来设计一个类的接口呢?通常,我们会为模型设计一些输入接口和输出目标值,并且提供训练接口,验证接口,测试接口等等。
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
self
.
_prediction
=
tf
.
nn
.
softmax
(
incoming
)
cross_entropy
=
-
tf
.
reduce_sum
(
target
,
tf
.
log
(
self
.
_prediction
))
self
.
_optimize
=
tf
.
train
.
RMSPropOptimizer
(
0.03
).
minimize
(
cross_entropy
)
mistakes
=
tf
.
not_equal
(
tf
.
argmax
(
target
,
1
),
tf
.
argmax
(
self
.
_prediction
,
1
))
self
.
_error
=
tf
.
reduce_mean
(
tf
.
cast
(
mistakes
,
tf
.
float32
))
@property
def
prediction
(
self
):
return
self
.
_prediction
@property
def
optimize
(
self
):
return
self
.
_optimize
@property
def
error
(
self
):
return
self
.
_error
基本上,我们都会使用 TensorFlow 提供的代码块来构建我们的模型。但是,它也存在一些问题。最值得注意的一点是,整个图都是在一个函数中定义和构造的,那么这即不可读也不可重复使用。
使用 @property 装饰器
如果你不了解装饰器,那么可以先学习这篇文章。
如果我们只是把代码分割成函数,这肯定是行不通的,因为每次调用函数时,图都会被新代码进行扩展。因此,我们要确保只有当函数第一次被调用时,这个操作才会被添加到图中。这个方式就是懒加载(lazy-loading,使用时才创建)。
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 一样的装饰器,但是只评估一次函数。它将结果存储在一个以装饰函数命名的成员中,并且在随后的调用中返回该值。如果你不是很了解这个装饰器是什么东西,你可以看看这个学习指南。
import
functools
def
lazy_property
(
function
):
attribute
=
"_cache_"
+
function
.
__name__
@property
@functools
.
wraps
(
function
)
def
decorator
(
self
):
if
not
hasattr
(
self
,
attribute
):
setattr
(
self
,
attribute
,
function
(
self
))
return
getattr
(
self
,
attribute
)
return
decorator
使用这个装饰器,我们可以将上面的代码简化如下:
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() 来定义的。
使用 scopes 来组织图
我们现在有一个简单的方法可以来定义我们的模型,但是由此产生的计算图仍然是非常拥挤的。如果你想要将图进行可视化,那么它将会包含很多互相链接的小节点。解决方案是我们对每一个函数(每一个节点)都起一个名字,利用 tf.namescope("name") 或者 tf.variablescope("name") 就可以实现。然后节点会在图中自由组合在一起,我们只需要调整我们的装饰器就可以了。
import
functools
def
define_scope
(
function
):
attribute
=
"_cache_"
+
function
.
__name__
@property
@functools
.
wraps
(
function
)
def
decorator
(
self
):
if
not
hasattr
(
self
,
attribute
):
with
tf
.
variable_scope
(
function
.
__name
):
setattr
(
self
,
attribute
,
function
(
self
))
return
getattr
(
self
,
attribute
)
return
decorator
我给了装饰器一个新的名字,因为除了我们加的懒惰缓存,它还具有特定的 TensorFlow 功能。除此之外,模型看起来和原来的是一样的。
商业智能与数据分析群
兴趣范围包括各种让数据产生价值的办法,实际应用案例分享与讨论,分析工具,ETL工具,数据仓库,数据挖掘工具,报表系统等全方位知识
QQ群:81035754
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/4685.html
摘要:机器学习模型内部的组成部分,可以使用进行打包和共享。为机器学习开发者提供库产生了库。库是一个在中进行发布和重用中机器学习模块的平台。 摘要: 本文对TensorFlow Hub库的介绍,并举例说明其用法。 在软件开发中,最常见的失误就是容易忽视共享代码库,而库则能够使软件开发具有更高的效率。从某种意义上来说,它改变了编程的过程。我们常常使用库构建块或模块,并将其连接在一起进行编程。 开...
摘要:第一个深度学习框架该怎么选对于初学者而言一直是个头疼的问题。简介和是颇受数据科学家欢迎的深度学习开源框架。就训练速度而言,胜过对比总结和都是深度学习框架初学者非常棒的选择。 「第一个深度学习框架该怎么选」对于初学者而言一直是个头疼的问题。本文中,来自 deepsense.ai 的研究员给出了他们在高级框架上的答案。在 Keras 与 PyTorch 的对比中,作者还给出了相同神经网络在不同框...
摘要:共字,读完需分钟。下面提出一种可以帮你写出高可读的实践方法,这个方法并非原创,最早的实践来自于这篇文章。本文作者王仕军,商业转载请联系作者获得授权,非商业转载请注明出处。 showImg(https://segmentfault.com/img/remote/1460000009341335?w=1240&h=403); 共 1926 字,读完需 4 分钟。所有工程师都知道,代码是编写...
摘要:是为了大规模分布式训练和推理而设计的,不过它在支持新机器学习模型和系统级优化的实验中的表现也足够灵活。本文对能够同时兼具规模性和灵活性的系统架构进行了阐述。尽管大多数训练库仍然只支持,但确实能够支持有效的推理。 TensorFlow 是为了大规模分布式训练和推理而设计的,不过它在支持新机器学习模型和系统级优化的实验中的表现也足够灵活。 本文对能够同时兼具规模性和灵活性的系统架构进行了阐述。设...
阅读 779·2023-04-25 22:57
阅读 3020·2021-11-23 10:03
阅读 593·2021-11-22 15:24
阅读 3136·2021-11-02 14:47
阅读 2878·2021-09-10 11:23
阅读 3096·2021-09-06 15:00
阅读 3895·2019-08-30 15:56
阅读 3305·2019-08-30 15:52