Tensorflow 笔记: Tensorflow 为什么难用

栏目: 数据库 · 发布时间: 5年前

内容简介:常常会听到人说 Tensorflow 很难用,大家就开始用 Pytorch 什么的,我不得不承认Tensorflow 很难用,但也有人很喜欢用。每次读 Google 发布的模型的代码 比如Tensorflow 难用,而还是有很多人用,Google 也大力的推销。优势当然是有,我想搞清楚的是为什么难用,有没有什么最佳实践之类的东西。读过很多人写的 Tensorflow 的代码,除了Google 那些家伙写的代码基本是一个风格之外,其他的都各有自己的操作。比如官方维护的 tensor2tensor 是建立在 E

Tensorflow 笔记: Tensorflow 为什么难用

常常会听到人说 Tensorflow 很难用,大家就开始用 Pytorch 什么的,我不得不承认Tensorflow 很难用,但也有人很喜欢用。每次读 Google 发布的模型的代码 比如 berttensor2tensor 总发现作者把简单的事情写得很复杂。一个功能散在不同的文件里,不同的函数里。而 Python 又是动态语言,读起来就比较痛苦了。

Tensorflow 难用,而还是有很多人用,Google 也大力的推销。优势当然是有,我想搞清楚的是为什么难用,有没有什么最佳实践之类的东西。

读过很多人写的 Tensorflow 的代码,除了Google 那些家伙写的代码基本是一个风格之外,其他的都各有自己的操作。比如官方维护的 tensor2tensor 是建立在 Estimator 的 API 上的, 而且读起来是相当有难度的,因为首先得理解 Estimator 在实现抽象的时候定义的一些概念 比如 TrainSpec 是什么东西之类的。很遗憾的是Tensorflow 的文档也是相当的垃圾的,所以就导致我们需要去代码里看看到底在干啥。对于大部分人来说需要读源码才能用的 工具 自然就是难用的东西。

Tensorflow 的历史版本都有很大的差异,后面在升级的时候又会抛弃一些之前的API , 所以可能存在不兼容的情况。而且同一个功能会有不通 level 的实现。还有不同别名。举个例子吧。卷积是比较常用的了,我们可以用比较底层的 nn 模块中的方法 tf.nn.conv2d 而这个方法又会有不一样的名称,在1.14 的文档如下: 这个别称还算少的,多的时候有5到6个的。 Tensorflow 笔记: Tensorflow 为什么难用

把激活函数,和卷积放到一起实在 layers 的实现, 在 keras, slim 中又会有相应的实现,我用两种方式实现了下 LeNet5 一种是相对低级的 方式如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#author: wu.zheng midday.me

"""
low level implement LeNet5

"""

import tensorflow as tf
import os
import mnist
import random
import numpy as np
import time

def create_lenet5_graph(x):
    # input_image_size : 32, 32 所有参数按原始论文写死
    w1 = tf.Variable(initial_value=tf.truncated_normal(shape=[5, 5, 1, 6], stddev=0.1 ), name="w1")
    b1 = tf.Variable(initial_value=tf.zeros(6), name='b1' )
    ### X: (None, 32, 32, 1)
    ### out:(None, 28, 28, 6)
    conv_1 = tf.nn.conv2d(input=x, filter=w1, strides=[1, 1, 1, 1],  padding='VALID')
    conv_1 = conv_1 + b1
    conv_1 = tf.nn.sigmoid(conv_1)
    ### out: (None, 14, 14, 6)
    pool_1 = tf.nn.avg_pool(value=conv_1, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1 ], padding="VALID")

    w2 = tf.Variable(initial_value=tf.truncated_normal(shape=[5, 5, 6, 16], stddev=0.1), name='w2')
    b2 = tf.Variable(initial_value=tf.zeros(16),  name="b2")
    ### out: (None, 10, 10, 16)
    conv_2 = tf.nn.conv2d(input=pool_1, filter=w2, strides=[1, 1, 1, 1], padding='VALID')
    conv_2= conv_2 + b2
    conv_2= tf.nn.sigmoid(conv_2)
    ### out:(None, 5, 5, 16)
    pool_2= tf.nn.avg_pool(value=conv_2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="VALID")
    shape = pool_2.get_shape().as_list()
    flated_pool = tf.reshape(pool_2, [shape[0], shape[1]*shape[2]*shape[3]])


    w3 = tf.Variable(initial_value=tf.truncated_normal(shape=[400, 120], stddev=0.1), name="W3" )
    b3 = tf.Variable(initial_value=tf.zeros(120), name='b3')
    fc_3 =  tf.matmul(flated_pool, w3)
    fc_3 = fc_3+ b3
    fc_3 = tf.nn.sigmoid(fc_3)

    w4 = tf.Variable(initial_value=tf.truncated_normal(shape=[120, 84], stddev=0.1), name='W4')
    b4 = tf.Variable(initial_value=tf.zeros(84), name='b4')
    fc_4 = tf.matmul(fc_3, w4)
    fc_4= fc_4 + b4
    fc_4 = tf.nn.sigmoid(fc_4)

    w5 = tf.Variable(initial_value=tf.truncated_normal(shape=[84, 10], stddev=0.1), name='W5')
    b5 = tf.Variable(initial_value=tf.zeros(10), name='b5')
    fc_5 = tf.matmul(fc_4, w5)
    fc_5 = fc_5 + b5
    variables = [w1, b1, w2, b2, w3, b3, w4, b4, w5, b5]
    return fc_5, variables

class Lenet5(object):
    def __init__(self, batch_size, mode='train', learning_rate=0.1):
        self.images = tf.placeholder(shape=[batch_size, 32, 32, 1], name='image', dtype=tf.float32)
        self.labels = tf.placeholder(shape=[batch_size, 10 ], name='labels', dtype=tf.float32)
        self.logits, self.variables = create_lenet5_graph(self.images)
        self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=self.logits, labels=self.labels))
        self.optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss=self.loss)
        self.predict_prob = tf.nn.softmax(self.logits)
        self.prediction = tf.argmax(self.predict_prob, axis=1)
        if mode == 'train' or mode=='eval':
            t_index = tf.argmax(self.labels, axis=1)
            equal = tf.equal(t_index, self.prediction)
            self.accuracy = tf.reduce_mean(tf.cast(equal, tf.float32))

这种方式看起来耦合了各种参数,而且很多代码重复,这就像是在用numpy 了。几乎有很多优化的方法,看看下面这个版本

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#author: wu.zheng midday.me

import tensorflow as tf

def create_lenet5_graph(x):
    # x: (32,32, 1)
    conv1 = tf.layers.conv2d(
            inputs=x,
            filters=6,
            kernel_size=(5,5),
            padding='VALID',
            activation=tf.nn.relu,
            name='conv1',
            use_bias=True,
            kernel_initializer=tf.initializers.truncated_normal(mean=0.0, stddev=0.1))
    pool1 = tf.layers.average_pooling2d(inputs=conv1, pool_size=[2,2], strides=[1,1], name='pool1')

    conv2 = tf.layers.conv2d(
            inputs=pool1,
            filters=16,
            kernel_size=(5,5),
            padding='VALID',
            activation=tf.nn.relu,
            name='conv2',
            use_bias=True,
            kernel_initializer=tf.initializers.truncated_normal(mean=0.0, stddev=0.1))

    pool2 = tf.layers.average_pooling2d(inputs=conv2, pool_size=[2,2], strides=[1,1], name='pool2')
    pooled_shape = pool2.get_shape().as_list()
    flated_pool = tf.reshape(pool2, [pooled_shape[0], pooled_shape[1]*pooled_shape[2]*pooled_shape[3]])
    fc3 = tf.layers.dense(
            inputs=flated_pool,
            units=120,
            activation=tf.nn.sigmoid,
            kernel_initializer=tf.initializers.truncated_normal(mean=0.0, stddev=0.1),
            name='f3')
    fc4 = tf.layers.dense(
            inputs=fc3,
            units=84,
            activation=tf.nn.sigmoid,
            kernel_initializer=tf.initializers.truncated_normal(mean=0.0, stddev=0.1),
            name='f4')
    fc5 = tf.layers.dense(
            inputs=fc4,
            units=10,
            activation=None,
            kernel_initializer=tf.initializers.truncated_normal(mean=0.0, stddev=0.1),
            name='f5')
    return fc5

class Lenet5(object):
    def __init__(self, batch_size, mode='train', learning_rate=0.1):
        self.images = tf.placeholder(shape=[batch_size, 32, 32, 1], name='image', dtype=tf.float32)
        self.labels = tf.placeholder(shape=[batch_size, 10 ], name='labels', dtype=tf.float32)
        self.logits = create_lenet5_graph(self.images)
        self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=self.logits, labels=self.labels))
        self.optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss=self.loss)
        self.predict_prob = tf.nn.softmax(self.logits)
        self.prediction = tf.argmax(self.predict_prob, axis=1)
        if mode == 'train' or mode=='eval':
            t_index = tf.argmax(self.labels, axis=1)
            equal = tf.equal(t_index, self.prediction)
            self.accuracy = tf.reduce_mean(tf.cast(equal, tf.float32))

现在简单了些,但还不是最优,这里用了 layers 的 api ,layers 根据平常比较多的使用模式封装到了同一个API 里面,这种封装可能是一个 类可能是一个方法。这种方式一直优化下去,或许 Keras 的使用方式就是最简单的方式了。但是也可以看到越高层的API 就会更重,自然就失去很多灵活,增加很多复杂度,这时候就需要很强大的文档配合才能比较好的使用。

Tensorflow 的复杂度是最开始的架构设计上的一些缺陷,或者说是随着发展一个正常的状态。我们不需要去抱怨好用性,对于只是调包的来说清晰明了的文档很重要。但是没有文档的时候也要能读懂代码,这世界上不是任何东西都是按照你的想法转的。 Tensorflow 的优势自然在你逐渐的使用过程中体会到。那有没有最佳实践这种东西,我想是有的,只是没有标准就自然说什么是最好的。但是很多场景下会有一些规律的使用方法而已。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创新者

创新者

[美] 沃尔特 · 艾萨克森 / 关嘉伟、牛小婧 / 中信出版社 / 2016-6 / 88.00

讲述了计算机和互联网从无到有的发展历程,并为我们生动地刻画出数字时代的创新者群像。 在近200年的数字化进程中群星闪耀,艾萨克森从一个计算机程序的创造者、诗人拜伦之女埃达说起,细数了这一群站在科学与人文交叉路口的创新者,他们包括通用型电子计算机的创造者奠奇利、科学家冯·诺依曼、仙童半导体公司的“八叛逆”、天才图灵、英特尔的格鲁夫、微软的比尔·盖茨、苹果公司的乔布斯、谷歌的拉里·佩奇等。《创新......一起来看看 《创新者》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具