在TensorFlow上定义你的模型可以很容易建成一个巨大的代码墙。如何用一个具有可读性和可重复使用的方法来构建你的代码?对于你的inpacient,这里是一个到工作示例依据的链接。
为每个模型划分类别是合理的。类别的接口是什么?通常情况下,你的模型连接到一些输入数据和目标的占位符,并提供培训和考核操作。
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基本代码如何定义模型是十分基础的。但是,它也有一些问题。最值得注意的是,整个图形定义功能单一,即构造者。这既不是特别易读取也不可重复使用。
单纯的代码分成功能并不能起作用,因为每个功能被调用时,该图形将被新的代码来扩展。因此,我们必须确保仅当功能第一次被调用时,该操作被添加到函数的曲线图。这基本上是延迟加载。
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 = '_' + 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
使用这个构建后,我们的例子简化成下面的代码。
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