结构化TensorFlow模型代码

译自http://danijar.com/structuring-your-tensorflow-models/

使用TensorFlow构建神经网络模型很容易导致较大的代码量,那么如何以可读和可复用的方式构建代码?(没耐心的可直接参考可直接参考源代码https://gist.github.com/danijar/8663d3bbfd586bffecf6a0094cd116f2)

定义计算图

  在每一个模型里面定义一个类是一个较好的选择。 那么,如何定义该类的接口呢? 通常,每个模型会连接到一些输入数据和占位符,并提供training,evaluation和inference的操作。

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中定义模型的基本示例。但是,它有一些问题。 其中最值得注意的是,整个graph是在单个函数(即构造函数__init__)中定义的, 这既不是可读也不可复用。

使用 Properties

  只需将代码拆分为函数就不起作用,因为每次调用函数时,图形中就会添加其他代码。 因此,我们必须确保只有当第一次调用函数时,操作才会添加到图形中,即使用 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

比第一个例子要好很多, 代码现在被组织为多个函数。 然而,由于lazy-loading逻辑,代码仍然有点膨胀。 让我们看看可以如何进一步改进。

Lazy Property Decorator

Python是一种非常灵活的语言, 如何从最后一个例子中删除冗余代码呢? 可以使用一个类似于@property的装饰器,但只能对该函数进行一次评估。 它将结果存储在以装饰器函数命名的成员中(加一个前缀),并在后续的任意调用中返回此值。 如果您尚未使用自定义装饰器,可以参考下这个教程:http://blog.apcelent.com/python-decorator-tutorial-with-example.html

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))

请注意,我们在构造函数中的添加了properties。 这样,可以保证在我们运行tf.initialize_variables()时,整个graph会被创建。

使用Scope组织计算图

  现在,我们有一个简洁明了的方法定义了代码中的模型,但是所得的计算图仍然十分繁缛。 如果你可视化计算图,可以发现大量互连的小型节点。 解决方案是用tf.name_scope(‘name‘)或tf.variable_scope(‘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的功能。 除此之外,该模型看起来与前一个模型相同。

我们可以更进一步地,使用@define_scope装饰器将参数转发给tf.variable_scope(),例如为作用域定义默认的initializer。 有兴趣的话可查看完整示例:https://gist.github.com/danijar/8663d3bbfd586bffecf6a0094cd116f2

现在我们可以以结构化和紧凑的方式定义模型,从而构造结构清晰的计算图。

时间: 2024-10-05 10:04:03

结构化TensorFlow模型代码的相关文章

【ACM Chp3】结构化风险模型的部分具体细节

原文地址:https://www.cnblogs.com/amosding/p/12275319.html

CMM模型,结构化开发方法和面向对象开发方法的比较,UML(统一建模语言),jackson开发方法

CMM模型 一.CMM简介 CMM,英文全称为Capability Maturity Model for Software,即:软件成熟度模型. CMM的核心是把软件开发视为一个过程.它是对于软件在定义.实施.度量.控制和改善其软件过程的实践中各个发展阶段的描述. 根据这一原则对软件开发和维护进行过程监控和研究,以使其更加科学化.标准化,使企业能够更好地实现商业目标. 分级:一级为初始级,二级为可重复级,三级为已定义级,四级为已管理级,五级为优化级. 优点: 1.提高软件开发的管理能力,因为CM

结构化与面向对象化之应用比较

结构化与面向对象化之应用比较 引言 软件工程中构建工程经常使用两种方法:结构化方法和面对对象方法.结构化方法由艾兹格.迪杰斯特拉在1967年发表<goto陈述有害论>时提出.面向对象方法在80年代起逐步形成.两种方法各有优点,相伴存在至今.下面我们就来分析.探讨结构化程序设计方法与面向对象的方法的区别,以及在现实应用中如何在两种方法中做出选择. 一.结构化方法 1. 基本思想 结构化方法程序设计的基本思想是: a.自顶向下 b.采用模块化技术 c.分而治之 d.逐步求精地将信息系统按功能分解为

深入研究 Win32 结构化异常处理(好多相关文章)

摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的最基本概念讲起. Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理 在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了.一提到结构化异常处理,可能就会令人想起

非结构化数据的存储与查询

当今信息化时代充斥着大量的数据.海量数据存储是一个必然的趋势.然而数据如何的存储和查询,尤其是当今非结构化数据的快速增长,对其数据的存储,处理,查询.使得如今的 关系数据库存储带来了巨大的挑战.分布存储技术是云计算的基础,主要研究如何存储.组织和管理数据中心上的大规模海量数据.由于面临的数据规模和用户规模更加庞大,在可扩展性.容错性以及成本控制方面面临着更加严峻的挑战[1]. 对于大量的半结构化数据(semi-structure data)和非结构化数据,对其存储和并发计算以及扩展能力而设计出了

结构化和面向对象之应用比较

---恢复内容开始--- 结构化和面向对象之应用比较 在无数程序设计人员的不断实践和理论改进中,软件工程程序设计中极其重要的指导性思路一直在发生着变革.在相对较长的时间里,不断有新的软件工程中的程序设计思路涌现,其中在生产实践中得到了十分广泛的应用的,当属结构化和面向对象的方法. 结构化程序设计在结构上将软件系统划分为若干功能模块或实体,分别采用模块化程序设计语言编程实现,再由各模块联结,组合成相应结构的软件系统. 而在面向对象的程序设计中,所谓对象是指具有一定结构.属性和功能的实体,采用对象和

【文智背后的奥秘】系列篇——结构化抽取平台

版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/91 来源:腾云阁 https://www.qcloud.com/community 随着大数据时代的到来,一个大规模生成.分享.处理以及应用数据的时代正在开启.如果能将互联网上异源异构的非结构化或半结构化数据转换为更易处理的结构化数据,可以极大的降低获取数据的门槛,为信息检索和数据挖掘提供基础,更好的挖掘数据中蕴藏的价值. 单纯考虑网页这种半结构化数据

深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的最基本概念讲起. Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理 在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了.一提到结构化异常处理,可能就会令人想起

视频结构化相关调研

视频结构化是一种视频内容信息提取的技术,它对视频内容按照语义关系,采用时空分割.特征提取.对象识别等处理手段,组织成可供计算机和人理解的文本信息的技术. 深度学习为视觉和语言之间搭建了一座桥梁https://weibo.com/ttarticle/p/show?id=2309404128390117477519 NLP注意力模型https://mp.weixin.qq.com/s/5miocWSsDyOtUUwiTaUZdw人脑的注意力模型,说到底是一种资源分配模型,注意力总是集中在画面中的某个