zoukankan      html  css  js  c++  java
  • 我所理解的tensorflow

    1. 开门见山

      其实只是刚入门,并不理解博大精深的tensorflow,只是想取个文艺点的名字。写这篇博客主要是记录下学习tensorflow会掌握哪些才够完成一些项目。首先想吐槽一下这个号称最火的深度学习框架,真的太不友好。我见过很多phd大佬吐槽过这个东西,莫名出现问题,而我,当然也是深受其害,作为一个用了挺久无脑Keras的人来说更是如此。因此个人觉得,tensorflow 应该要感受到 PyTorch 和 MXNet 的压力了。我也是跑过 PyTorch的例程,真是比较顺风顺水啊哈哈。但是不踩过tensorflow的坑人生怎么完整呢。好了,接下来我直接用 tf 代替 tensorflow。

    2. 正文

      接着还是继续吐槽,现在 tf 的网上很多教程,包括官方教程,包括各种博客都是问题百出,可能是版本更新太快了,很多已经不适用了,而且也没人及时更新,有些都被 deprecated 了。另外吐槽下 MNIST 这个数据集,感觉随便就能跑到95+,完全看不出代码哪里有问题。同样的网络模型放到人脸数据库上就完全不行,又比如之前实现过 RBM 在 MNIST上可以但是同样在别的数据集就不行(当然可能得调一下各种参数),我只是想说验证模型是否正确是不能光看 MNIST 上的效果啊。同样,验证你会不会 tf 同样不是只看你跑没跑过教程里面的无脑例子。举个栗子,教程里面说交叉熵损失函数写成:cross_entropy = -tf.reduce_sum(y*tf.log(outputs)),当你满心欢喜的觉得这样写十分简洁并且正确的时候,发现换了一个数据集竟然出问题了。原来是log里面出现0了,才会觉得这是什么鬼教程啊。其他可能存在的问题,就是数据一定要做好预处理(比如tensorflow中输入图像通道数放在了最后一维),权值初始化的方式可能是个要注意的地方,就这样。当然为了多次训练,需要知道怎么保存和加载模型,这就比较的完整了。

      说到这里,你可能可以勉强跑一个模型了,但可能会觉得管理起来非常麻烦,也不知道自己写的对不对。这是你可能需要对变量进行管理,和对代码进行封装,接下来 class 和 tensorboard 就要出场了。首先引用下知乎一篇文章(http://www.jianshu.com/p/e112012a4b2d),讲到写 tf 的时候要有良好的代码结构,比如分成操作,模型等几个部分。这个多看看 github 上别人是怎么写的会有一个初步的印象。其实写的跟别人 github 上的 tf 代码差不多也就很可以了,毕竟这就是我的最后最终目的。在这个过程中我发现,在代码结构中,还需要用到面向对象的思想,比如定义网络就写成一个类(class),封装好,调用也方便。这就是我之前说到的代码结构,封装,class。

      接下来,就是把可视化,一个看的清楚的可视化,可以看出你的代码是否有问题,这里就是使用我之前说到的 tensorboard。要用 tensorboard,就必须对变量进行管理,使用到的是name_scope和variable_scope,不管理的话你看到的 graph将是密密麻麻的东西。而使用scope,则可以显示出一个个结点,每个结点包含一些变量,如下代码使用name_scope则可以显示出一个简单的两个全连接层(fully-connected layer)的神经网络,代码部分引用自(http://www.jianshu.com/p/e112012a4b2d):

     1 #!/usr/bin/python
     2 # -*- coding:utf-8 -*-
     3 
     4 import tensorflow as tf
     5 from tensorflow.examples.tutorials.mnist import input_data
     6 
     7 def weight_variable(shape):
     8     initial = tf.truncated_normal(shape, stddev=0.1)
     9     return tf.Variable(initial)
    10 
    11 def bias_variable(shape):
    12     initial = tf.constant(0.1, shape=shape)
    13     return tf.Variable(initial)
    14 
    15 def conv2d(x, W):
    16     return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
    17 
    18 def max_pool_2x2(x):
    19     return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    20 
    21 def fc_layer(inputs, in_size, out_size, activation_func=None):
    22     with tf.name_scope('layer'): # 自动加下标
    23         with tf.name_scope('weights'):
    24             Weights = tf.Variable(tf.random_normal([in_size, out_size]), name='W') # 不写name就默认是'Variable'
    25         with tf.name_scope('biases'):
    26             biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, name='b')
    27         with tf.name_scope('Wx_plus_b'):
    28             Wx_plus_b = tf.matmul(inputs, Weights) + biases
    29         if activation_func is None:
    30             outputs = Wx_plus_b
    31         else:
    32             outputs = activation_func(Wx_plus_b)
    33         return outputs
    34 
    35 def main():
    36     mnist = input_data.read_data_sets('/tmp/data', one_hot=True,  fake_data=False)
    37 
    38     with tf.variable_scope('inputs'):
    39         x = tf.placeholder(tf.float32, [None, 784], name='x_input')
    40         y = tf.placeholder(tf.float32, [None, 10], name='y_input')
    41 
    42     fc1 = fc_layer(x, 784, 100, activation_func=tf.nn.relu)
    43     fc2 = fc_layer(fc1, 100, 100, activation_func=tf.nn.relu)
    44     logits = fc_layer(fc2, 100, 10)
    45     outputs = tf.nn.softmax(logits)
    46     
    47     with tf.name_scope('loss'):
    48         # cross_entropy1 = -tf.reduce_sum(y*tf.log(outputs)) # log里面可能为0
    49         cross_entropy2 = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))
    50 
    51     with tf.name_scope('train'):
    52         train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy2)
    53 
    54     sess = tf.Session()     
    55     sess.run(tf.global_variables_initializer())
    56     
    57     writer = tf.summary.FileWriter("logs/", sess.graph)
    58     
    59 if __name__ == '__main__':
    60     main()

      这个代码只有构建网络,没有训练的部分,因为我的小笔记本连训练 MNIST 都吃不消啊。首先说下一些无关紧要却又很紧要的地方:我的环境是win8下的anaconda3,配上tensorflow 1.0,现在sess.run(init)里面的init已经换成上面的tf.global_variables_initializer(),保存graph语句则变成了下面的writer = tf.summary.FileWriter("logs/", sess.graph)。首先python运行代码,然后tensorboard --logdir=logs运行(不用引号),就可以输入网址看可视化了。然后仔细看fc_layer函数会发现,name_scope中的"layer"两次调用重名了,会自动加下标变成layer_1,layer_2这样子,这样全部变量都不会重名了,很神奇。然后cross_entropy2就是我说的一种解决交叉熵log里面为0的问题的方法。

      现在你会发现,这完全没有分模块写啊。的确,但是先不急,先再接着看另外一个scope:variable_scope会有什么效果。好吧,其实差不多,也是定义一个scope,但是variable_scope 可以跟 get_variable一起使用,这样来说不同 variable_scope name 下我们使用相同的变量名字(如 'weights')也不会有冲突问题,因为前缀是不同的variable_scope。但是name_scope则没有这样的配合使用效果,因此我发现很多人常用的是 variable_scope 。

    ----------------------------手动分割----------------------

      OK,写到这里,你可以可视化看到漂亮的 graph 显示你的网络是长什么样子的。但是接下来还不急着训练网络,因为我们不希望向上面一样把整个流程(读数据,写网络,训练)写到同一份代码里,因此,这里分3块:网络写成一个类(class),读数据写一个文件,最后在main函数(文件)写完整个流程,这样分开写的重用性比较好,也体现了所谓的面向对象编程,感觉这样写是主流了。

      所以,接下来我就用一个fcn网络(Fully Convolutional Network)来举个栗子。所谓的fcn就是通过一堆卷积和转置卷积,把输入image输出二维的feature map,没有fc层,输出的可能是像素级别的分类,blablabla之类的,根据你的需求了。前面的卷积就不说了,后面的转置卷积是如何操作的,这个让我理解了很久。据说这个网站(https://github.com/vdumoulin/conv_arithmetic)的动画很权威,毕竟是来自 Montreal 的。这些操作对应的 stride 是在让我一开始很懵逼,但是后来我理解的是 这些转置卷积的 stride=2 描述的都是其对应卷积操作的 stride=2。这样转置卷积的时候,就相当于中间是间隔着插入0,再进行卷积,这样就把 feature map 增大了一倍,跟卷积的时候用stride=2 使得feature map缩小一倍刚好对应起来了。在 tf 里面实现的时候参数就是根据这样来设置 padding 和 stride 的。接着所谓的 bilinear upsampling 就是转置卷积的时候实现双线性插值一样的效果,即构造一个卷积核,通过 bilinear interpolation 的初始化方式,这样卷积后就是得到双线性插值的效果了。然后在这个基础上,这个卷积核还可以训练的,就设为get_variable,不可训练的就是constant。嗯,感觉没什么问题,直接上代码,一个网络类实现了构造网络,训练,模型保存载入等等功能,有需要的可以看下哈!基本符合我之前所讲的东西,改进后的代码看起来会高端一些了。

      1 #!/usr/bin/python
      2 # -*- coding:utf-8 -*-
      3 
      4 import inspect
      5 import os
      6 
      7 import numpy as np
      8 import tensorflow as tf
      9 import time
     10 from math import ceil
     11 import random
     12 
     13 VGG_MEAN = [103.939, 116.779, 123.68]
     14 
     15 
     16 class FullyConvNet:
     17     def __init__(self, batch_size=16, loss_type='mse', optimizer='momentum', vgg16_npy_path=None):
     18         # if vgg16_npy_path is None:
     19         #     path = inspect.getfile(Vgg16)
     20         #     path = os.path.abspath(os.path.join(path, os.pardir))
     21         #     path = os.path.join(path, "vgg16.npy")
     22         #     vgg16_npy_path = path
     23         #     print (path)
     24 
     25         # self.data_dict = np.load(vgg16_npy_path, encoding='latin1').item()
     26         # print("npy file loaded")
     27         self.batch_size = batch_size
     28         self.loss_type = loss_type
     29         self.optimizer = optimizer
     30         self.learning_rate = 0.01
     31         
     32         self.build()
     33         self.sess = tf.Session()
     34         
     35         self.sess.run(tf.global_variables_initializer())
     36         writer = tf.summary.FileWriter("logs/", self.sess.graph)
     37         self.saver = tf.train.Saver()
     38 
     39     def build(self):
     40 
     41         # start_time = time.time()
     42         # print("build model started")
     43         # rgb_scaled = rgb * 255.0
     44 
     45         # # Convert RGB to BGR
     46         # red, green, blue = tf.split(rgb_scaled, 3, 3)
     47         # assert red.get_shape().as_list()[1:] == [224, 224, 1]
     48         # assert green.get_shape().as_list()[1:] == [224, 224, 1]
     49         # assert blue.get_shape().as_list()[1:] == [224, 224, 1]
     50         # bgr = tf.concat([
     51         #     blue - VGG_MEAN[0],
     52         #     green - VGG_MEAN[1],
     53         #     red - VGG_MEAN[2],
     54         # ], 3)
     55         # assert bgr.get_shape().as_list()[1:] == [224, 224, 3]
     56         
     57 
     58         with tf.variable_scope('inputs'):
     59             self.x = tf.placeholder(tf.float32, [self.batch_size, 224, 224, 3], name='x_input')
     60             self.y = tf.placeholder(tf.float32, [self.batch_size, 224, 224, 5], name='y_input')
     61 
     62         self.conv1_1 = self.conv_layer(self.x, 16, [3, 3], 'conv1')
     63         self.pool1 = self.max_pool(self.conv1_1, 'pool1')
     64         # self.conv2_1 = self.conv_layer(self.pool1, 64, [3, 3], "conv2")
     65         # self.pool2 = self.max_pool(self.conv2_1, 'pool2')
     66         self.deconv1 = self.deconv_layer(self.pool1, 5, [3, 3], [self.batch_size, 224, 224, 5], 'deconv1')
     67         # self.pool2 = self.max_pool(self.deconv1, 'pool2')
     68 
     69         with tf.variable_scope('loss'):
     70             if self.loss_type == 'mse':
     71                 self.loss = tf.reduce_mean(tf.squared_difference(self.y, self.deconv1, name='mse'))
     72 
     73             else:
     74                 self.loss = tf.reduce_mean(tf.squared_difference(self.y, self.deconv1, name='mse'))
     75 
     76         with tf.variable_scope('train'):
     77             if self.optimizer == 'momentum':
     78                 self.train_step = tf.train.MomentumOptimizer(self.learning_rate, 0.9).minimize(self.loss)
     79 
     80             else:
     81                 self.train_step = tf.train.GradientDescentOptimizer(self.learning_rate).minimize(self.loss)
     82                 
     83 
     84     def fit(self, x_train, y_train, x_test, y_test, iterations=2):
     85         train_num = x_train.shape[0]
     86         # print (sample_index)
     87         # print (x_train[sample_index,:].shape)
     88         
     89         # tf.all_variables()
     90         old_cost = 100
     91         for i in range(iterations):
     92             sample_index = random.sample(range(0, train_num), self.batch_size)
     93             _, cost = self.sess.run([self.train_step, self.loss], feed_dict={self.x:x_train[sample_index,:], self.y:y_train[sample_index,:]})
     94             test_cost = self.sess.run([self.loss], feed_dict={self.x:x_test, self.y:y_test})
     95             if cost < old_cost:
     96                 print ('training_cost, testing_cost: ', i, cost, test_cost)
     97                 old_cost = cost
     98                 save_path = self.saver.save(self.sess, 'tmp/model.ckpt')
     99                 # self.saver.restore(self.sess, 'tmp/model.ckpt')
    100                 cost = self.sess.run([self.loss], feed_dict={self.x:x_train[sample_index,:], self.y:y_train[sample_index,:]})
    101                 print (cost)
    102 
    103     def forward(self, x, y):
    104         self.saver.restore(self.sess, 'tmp/model.ckpt')
    105         cost = self.sess.run([self.loss], feed_dict={self.x:x[0:10], self.y:y[0:10]})
    106         print (cost)
    107         output = self.sess.run([self.deconv1], feed_dict={self.x:x[0:10]})
    108         print (output[0].shape)
    109         return output[0]
    110         
    111     def max_pool(self, bottom, name):
    112         with tf.variable_scope(name):
    113             return tf.nn.max_pool(bottom, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    114 
    115     def conv_layer(self, bottom, out_size, kernel_shape, name):
    116         with tf.variable_scope(name):
    117             in_size = bottom.get_shape().as_list()[-1]
    118             # print (bottom.get_shape().as_list())
    119             weights = tf.get_variable('weights', shape=[kernel_shape[0], kernel_shape[1], in_size, out_size], initializer=tf.random_normal_initializer())
    120             # bias = tf.get_variable('bias', [1, n_l1], initializer=b_initializer, collections=c_names)
    121             conv = tf.nn.conv2d(bottom, weights, [1, 1, 1, 1], padding='SAME')
    122             # conv_biases = tf.nn.bias_add(conv, bias)
    123             relu = tf.nn.relu(conv)
    124             return relu
    125 
    126     def deconv_layer(self, bottom, out_size, kernel_shape, output_shape, name):
    127         with tf.variable_scope(name):
    128             in_size = bottom.get_shape().as_list()[-1]
    129             # print (in_size)
    130             f_shape = [kernel_shape[0], kernel_shape[1], out_size, in_size]
    131             weights = self.bilinear_interpolation_init(f_shape)
    132             # in_shape = bottom.get_shape().as_list()[0]
    133             deconv = tf.nn.conv2d_transpose(bottom, weights, output_shape=output_shape, strides=[1,2,2,1], padding="SAME")
    134             return deconv
    135 
    136 
    137     def bilinear_interpolation_init(self, f_shape):
    138         width = f_shape[0]
    139         heigh = f_shape[0]
    140         f = ceil(width/2.0)
    141         c = (2 * f - 1 - f % 2) / (2.0 * f)
    142         bilinear = np.zeros([f_shape[0], f_shape[1]])
    143         for x in range(width):
    144             for y in range(heigh):
    145                 value = (1 - abs(x / f - c)) * (1 - abs(y / f - c))
    146                 bilinear[x, y] = value
    147         weights = np.zeros(f_shape)
    148         for i in range(f_shape[2]):
    149             weights[:, :, i, i] = bilinear
    150 
    151         init = tf.constant_initializer(value=weights, dtype=tf.float32)
    152         var = tf.get_variable(name="up_filter", initializer=init, shape=weights.shape)
    153         return var

    这就是代码当中的模型部分,可以称为 neural net model,所有训练的步骤都写在那里了,main文件中直接调用这个类的函数就可以了,这样main函数就很简洁了。那么另外可以写一个utils文件进行图像预处理,画图等操作,这样三个文件基本就可以处理一个项目了。

    3. 未完待续

      先写这么多了,有漏的再补。写了这么多发现自己写的真是不怎么样,还不如会写诗的AI,有时候真的觉得AI做的比人好其实很正常,模型是很多高智商人士设计的,而AI学习过程却仍有黑箱并且总是能产生让我们意想不到的能力(比如AlphaGo),可以说是基于人类又胜于人类,非常可怕。匿了,Auf wiedersehen。

  • 相关阅读:
    GCDPlot已提交到Sourceforge,成为开源项目[GCDPlot become a open source software in sourceforge]
    GCDPlot 0.32
    谈谈“科技人员和白领是购买盗版的主力”
    c正则匹配小计
    C++ 模板惯用法
    C++模板常用使用方法介绍
    常用正则表达式
    C++ 容易忘的基本语法和特性
    C++ 对象资源管理惯用法
    Int? 代表什么意思, 可为空的值类型(Nullable<T>)需要注意的地方
  • 原文地址:https://www.cnblogs.com/dominjune/p/7157636.html
Copyright © 2011-2022 走看看