文档章节

Tensorflow内存分配与Keras模型参数

nextowang
 nextowang
发布于 2017/06/26 20:40
字数 2224
阅读 255
收藏 2
点赞 0
评论 0

最经典的LeNet模型

MNIST手写体识别的LeNet模型几乎已经成为深度学习玩家耳熟能详,妇孺皆知的模型。所以我们在这里简单的回顾一下,整个模型层次结构如下:

  • input输入层
  • 卷积核为5x5的conv2d卷积层
  • 2x2的Maxpooling池化层
  • 卷积核为5x5的conv2d卷积层
  • 2x2的Maxpooling池化层
  • 1024的full-connect全连接层
  • output输出层

MNIST数据集特征张量通常都是(X,28,28,1),那么由上述结构我们可以写出对应的Keras代码,如下代码所示:

from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D

model = Sequential()

model.add(Conv2D(32, (5,5), activation='relu', padding='same', input_shape=(28,28,1)))
model.add(MaxPooling2D((2,2)))

model.add(Conv2D(64, (5,5), activation='relu', padding='same'))
model.add(MaxPooling2D((2,2)))

model.add(Flatten())
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(11, activation='softmax'))

Keras框架是一个封装好的深度学习框架,但是Tensorflow框架相对于Keras框架更为底层一些,所以搭建过程相对复杂一些,笔者就直接用Tensorflow官方为我们提供的较为具有公信力的代码mnist_with_summaries.py

将权重设置,偏移值设置,卷积操作,池化操作,参数变化记录,添加层的操作都封装成函数,代码如下:

import tensorflow as tf
import numpy as np

def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

def variable_summaries(var):
    """Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""
    with tf.name_scope('summaries'):
        mean = tf.reduce_mean(var)
        tf.summary.scalar('mean', mean)
        with tf.name_scope('stddev'):
            stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
        tf.summary.scalar('stddev', stddev)
        tf.summary.scalar('max', tf.reduce_max(var))
        tf.summary.scalar('min', tf.reduce_min(var))
        tf.summary.histogram('histogram', var)

def add_layer(input_tensor, weights_shape, biases_shape, layer_name, act = tf.nn.relu, flag = 1):
    """Reusable code for making a simple neural net layer.

    It does a matrix multiply, bias add, and then uses relu to nonlinearize.
    It also sets up name scoping so that the resultant graph is easy to read,
    and adds a number of summary ops.
    """
    with tf.name_scope(layer_name):
        with tf.name_scope('weights'):
            weights = weight_variable(weights_shape)
            variable_summaries(weights)
        with tf.name_scope('biases'):
            biases = bias_variable(biases_shape)
            variable_summaries(biases)
        with tf.name_scope('Wx_plus_b'):
            if flag == 1:
                preactivate = tf.add(conv2d(input_tensor, weights), biases)
            else:
                preactivate = tf.add(tf.matmul(input_tensor, weights), biases)
            tf.summary.histogram('pre_activations', preactivate)
        if act == None:
            outputs = preactivate
        else:
            outputs = act(preactivate, name = 'activation')
            tf.summary.histogram('activation', outputs)
        return outputs

with tf.name_scope('Input'):
    x = tf.placeholder(tf.float32, [None, 28*28], name = 'input_x')
    y_ = tf.placeholder(tf.float32, [None, 10], name = 'target_y')

# First Convolutional Layer
x_image = tf.reshape(x, [-1, 28, 28 ,1])
conv_1 = add_layer(x_image, [5, 5, 1, 32], [32], 'First_Convolutional_Layer', flag = 1)

# First Pooling Layer
pool_1 = max_pool_2x2(conv_1)

# Second Convolutional Layer 
conv_2 = add_layer(pool_1, [5, 5, 32, 64], [64], 'Second_Convolutional_Layer', flag = 1)

# Second Pooling Layer 
pool_2 = max_pool_2x2(conv_2)

# Densely Connected Layer
pool_2_flat = tf.reshape(pool_2, [-1, 7*7*64])
dc_1 = add_layer(pool_2_flat, [7*7*64, 1024], [1024], 'Densely_Connected_Layer', flag = 0) 

# Dropout
keep_prob = tf.placeholder(tf.float32)
dc_1_drop = tf.nn.dropout(dc_1, keep_prob)

# Readout Layer
y = add_layer(dc_1_drop, [1024, 10], [10], 'Readout_Layer', flag = 0)

本文的重点并不是如何搭建MNIST深度学习模型,所以,对于损失函数和优化器的选择本文并未详细列出,这个问题笔者有研究过,详见之前篇博客深度学习的核心——分类器的选择,这篇博客中针对深度学习中激活函数的使用和优化器选择有详细解释。

Keras模型参数

很多用过Keras平台的深度学习玩家在搭建深度学习模型后会习惯性的使用一条语句model.summary(),这条语句的作用是将我们之前用代码搭建的模型用表格的形式展现出来。这个表格包括如下内容:模型层结构的名称、这层对应输出的张量Tensor以及这层运算产生的参数。下图所对应是上面Keras搭建MNIST模型后,调用model.summary()这条语句所打印的模型信息。很多玩家会用这条语句测试模型是否出现问题,笔者百度之后没有发现这个参数Param这些数字理论依据,所以,笔者在这里说明这些参数的由来。

我们不妨首先看全连接层的参数由来:

10250 = 1024×10+1×10

这个式子并不是在凑数,其本质的原因是,我们在最后一个全连接层,也就是我们的输出层的前一层设置10个神经元,上层设置的神经元1024个神经元要与本层的10个神经元全部连接,此时产生1024×10个参数。我们都知道,机器学习或深度学习中,一个核心的公式是:Y=WX+b,依据这个道理,我们每层都要有一个偏移值b,所以此时产生了1×10个参数,整个这一层产生的参数是1024×10+1×10=10250个。同理上一个全连接层参数3212288=3136×1024+1×1024 ,下图是笔者在模式识别课中,神经网络中多层感知器的神经元连接图,读者可以根据所学的知识计算一下总共的神经元数量,笔者算得如下结果,这是一个三个隐层的网络,总共有7个神经元。

读者会问,卷积层又是如何计算呢?卷积层存在参数共享的特性,什么是参数共享呢?参数共享(parameter sharing)是指在一个模型的多个函数中使用相同的参数。在传统的神经网络中,当计算一层的输出时,权重矩阵的每一个元素只使用一次,当它乘以输入的一个元素后就再也不会用到了。作为参数共享的同义词,我们可以说一个网络含有绑定的权重(tied weights),因为用于一个输入的权重也会被绑定在其他的权重上。在卷积神经网络中,核的每一个元素都作用在输入的每一位置上(是否考虑边界像素取决于对边界决策的设计)。卷积运算中的参数共享保证了我们只需要学习一个参数集合,而不是对于每一位置都需要学习一个单独的参数集合。根据这个原理我们可以的如下计算方法

832=(5×5×1+1)×32

其中5×5是我们定义的卷积核大小,第一个1是输入的通道数,第二个1是常数,意义为那个偏移值,32是输出通道数的大小。同理对于第二层卷积51264=(5×5×32+1)×64 。当我们们计算完卷积和全连接的所有参数后,将其相加就可以获得总的参数。

Tensorflow内存参数

tf.RunOptions(),tf.RunMetadata()是重要的记录Tensorflow运行计算过程中所用的计算时间和Memeroy,我们用MNIST数据集中一条数据来测试显示出每一层进行运算时所包括的参数信息,这里我们读取已经训练好的Tensorflow pb模型。其中pb模型将整个Tensorflow运算和训练的权值都存入到pb文件中,代码如下:

from tensorflow.python.platform import gfile
from tensorflow.examples.tutorials.mnist import input_data

import numpy as np
import tensorflow as tf
import mnist_inference

mnist_data_path = 'MNIST_data/'
mnist = input_data.read_data_sets(mnist_data_path, one_hot = True)
batch_xs, batch_ys = mnist.test.images[:1], mnist.test.labels[:1]
print batch_ys.shape

with tf.Session() as sess:
    with tf.device('/cpu:0'):

        with gfile.FastGFile('LeNet_mnist.pb','rb') as f:
            graph_def = tf.GraphDef()
            graph_def.ParseFromString(f.read())
        sess.run(tf.global_variables_initializer())
        tf.import_graph_def(graph_def, name='')

        input_tensor = sess.graph.get_tensor_by_name('input:0')
        output = sess.graph.get_tensor_by_name('layer6-fc2/output:0')
        
        y_ = tf.placeholder(tf.float32,[None,10], name = 'target')
        drop_rate = sess.graph.get_tensor_by_name('dropout:0')
        correct_prediction = tf.equal(tf.argmax(output,1),tf.argmax(y_,1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
        tf.summary.scalar('accuracy', accuracy)
        merged = tf.summary.merge_all()
        
        test_writer = tf.summary.FileWriter('pbtest', sess.graph)
        run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
        run_metadata = tf.RunMetadata()
        _, test_accuracy = sess.run([merged, accuracy], feed_dict={input_tensor: batch_xs, y_: batch_ys, drop_rate:1.0},
                                                     options=run_options,
                                                     run_metadata=run_metadata)
        test_writer.add_run_metadata(run_metadata, "test_process")
        test_writer.close()
        

如下图是Tensorboard中Graph对应点击模型每一层展现的对应信息,其中包括Inputs,Outputs,Node Stats(Memory和Compute Time)。内存分配是Tensorflow中一个重要的模块,但在这里的内存分配还是比较容易理解的,Tensorflow在这里的内存分配笔者认为分配给输出的内存,保证进行这一层操作后输出有足够的内存。所以,笔者认为其内存的大小是这样定义的

98KB=98×1024B=28×28×32×4B

如何理解?我们的输出Tensor是28×28×32,由于我们的Tensor定义的是float类型的值,float这种类型的变量分配4个字节的内存空间

同理我们的第二层卷积的内存分配

49KB=49×1024B=14×14×64×4B

那么我们的全连接层的内存分配:4KB=4×1024B=1024×4B

还想再聊聊

笔者并非刻意研究分享什么内容,而是在实际科研学习实践的过程中,发现一些比较小型,很多人未解决或大牛们懒的解决的点,并将其研究解释清楚。

如果有问题和对这个感兴趣的朋友留言给我,下面是我的邮箱

nextowang@stu.xidian.edu.cn

感谢阅读,期待您的反馈!

 

 

© 著作权归作者所有

共有 人打赏支持
nextowang
粉丝 0
博文 7
码字总数 12120
作品 0
西安
程序员
如何使用 TensorFlow mobile 将 PyTorch 和 Keras 部署到移动设备

雷锋网(公众号:雷锋网)按:本文为雷锋字幕组编译的技术博客,原标题 Deploying PyTorch and Keras Models to Android with TensorFlow Mobile ,作者为 John Olafenwa 。 翻译 | 于志鹏 整理...

雷锋字幕组
07/12
0
0
Keras 深度学习框架介绍----一起来慢慢走进deep learning

Introduce Keras是一个高级API,用Python编写,能够在TensorFlow、Theano或CNTK上运行。Keras提供了一个简单和模块化的API来创建和训练神经网络,隐藏了大部分复杂的细节。 How to install k...

qq_15642411
04/20
0
0
Java调用Keras、Tensorflow模型

实现python离线训练模型,Java在线预测部署。查看原文 目前深度学习主流使用python训练自己的模型,有非常多的框架提供了能快速搭建神经网络的功能,其中Keras提供了high-level的语法,底层可...

ioiogoo
04/03
0
0
Tensorflow快餐教程(12) - 用机器写莎士比亚的戏剧

高层框架:TFLearn和Keras 上一节我们学习了Tensorflow的高层API封装,可以通过简单的几步就生成一个DNN分类器来解决MNIST手写识别问题。 尽管Tensorflow也在不断推进Estimator API。但是,这...

lusing
05/29
0
0
TensorFlow应用实战-9-生成音乐

生成音乐的python文件 生成音符 训练时我们使用的是fit。我们现在是是有神经网络为我们生成新的输出预测值 以之训练所得最佳参数生成音乐 将训练时候最好loss的那个改名字,用来生成。 运行p...

天涯明月笙
04/22
0
0
开源的机器学习框架应当如何选择?

为何要选择机器学习框架呢?使用开源工具的好处不仅仅在于其可用性。通常来说,如此级别的项目均有大量的数据工程师和数据科学家愿意去分享数据集和前期训练模型。比如,你可以使用分类模型训...

小欣妹妹
04/20
0
0
Day 02:撰写第一个 Neural Network 程序 -- 阿拉伯数字辨识

入门 照理讲,我们应该先了解『神经网络』(Neural Network)概念,再谈如何写程式,但是,概念介绍内容有点硬,为了提高学习兴趣,避免一开始就搞一堆数学公式,造成读者跑光光,所以,还是柿...

readilen
05/21
0
0
机器学习者必知的5种深度学习框架

雷锋网按:本文为雷锋字幕组编译的技术博客,原标题The 5 Deep Learning Frameworks Every Serious Machine Learner Should Be Familiar With,作者为James Le。 翻译 | 杨恕权 张晓雪 陈明霏...

雷锋字幕组
05/03
0
0
Keras vs PyTorch:谁是「第一」深度学习框架?

  选自Deepsense.ai   作者:Rafa Jakubanis、Piotr Migdal   机器之心编译   参与:路、李泽南、李亚洲      「第一个深度学习框架该怎么选」对于初学者而言一直是个头疼的问题...

机器之心
06/30
0
0
16- 深度学习之神经网络核心原理与算法-caffe&keras框架图片分类

之前我们在使用cnn做图片分类的时候使用了CIFAR-10数据集 其他框架对于CIFAR-10的图片分类是怎么做的 来与TensorFlow做对比。 Caffe Keras 安装 官方安装文档: https://github.com/IraAI/ca...

天涯明月笙
06/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

rabbitmq学习记录(三)

工作队列:一个生产者,多个消费者,生产者直接将消息发送到rabbitmq的队列之中 默认采用的是轮询分配:即不管消费者处理信息的效率,队列给所有消费者轮流发送一条信息,直至消息发送完毕 ...

人觉非常君
21分钟前
0
0
Java 之 反射

反射,剖析 Java类 中的 各个组成部分,映射成 一个个 Java对象,多用于 框架和组件,写出复用性高的通用程序。 测试类代码如下: class Person { private String name; public St...

绝世武神
25分钟前
0
0
华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大

华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大!华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大! 在华为最新发布的nova 3手机上,抖音通过华为himedia SDK集成了60fps、超级...

华为终端开放实验室
30分钟前
0
0
多 SSH Key 实现同一台服务器部署多 Git 仓库

本文以以下需求为背景,介绍详细的做法: 需在同一台服务器同时部署两个不同的 Github 仓库(对 Bitbucket 等 git 服务同样适用) root 用户可在远程登录 SSH 后附上预期的 SSH Key 进行 gi...

yeahlife
33分钟前
0
0
003. es6数值的扩展

一、普通扩展 Number 方法,将字符串、数值转为十进制 : Number('0b111') Number.isFinite() 用来检查一个数值是否为有限的:Number.isFinite(15) Number.isNan() 用来检查一个值是否为NaN N...

秋季长青
47分钟前
0
0
C语言数组和指针的语法糖

对于C语言,我可以这样秀:比如当创建一个数组arr[n]之后,一般我们去遍历数组的时候是for (int i = 0; i < n; i++) { a[i]; }但是我知道下表访问符[]是个语法糖,也就是说a[i]在编译器看来是...

ustbgaofan
55分钟前
0
0
Call to undefined function bcmath()的解决方法

乐意黎的ECS主机环境,Centos7.2 + PHP7 由于使用了bcdiv()函数,运行时总在抛错。 Fatal error: Call to undefined function bcmath() in /usr/loca/apache/htdocs/... on line 4 一查得知:......

dragon_tech
今天
0
0
css优先级

..

architect刘源源
今天
0
0
【转】Twitter的分布式自增ID算法snowflake

结构 snowflake的结构如下(每部分用-分开): 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以...

talen
今天
0
0
hive支持行级修改

Hive从0.14版本开始支持事务和行级更新,但缺省是不支持的,需要一些附加的配置。要想支持行级insert、update、delete,需要配置Hive支持事务。 一、Hive具有ACID语义事务的使用场景 1. 流式...

hblt-j
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部