从0到1实现基于Tornado和Tensorflow的人脸年龄性别识别2.docx
《从0到1实现基于Tornado和Tensorflow的人脸年龄性别识别2.docx》由会员分享,可在线阅读,更多相关《从0到1实现基于Tornado和Tensorflow的人脸年龄性别识别2.docx(11页珍藏版)》请在冰豆网上搜索。
![从0到1实现基于Tornado和Tensorflow的人脸年龄性别识别2.docx](https://file1.bdocx.com/fileroot1/2022-12/29/a34ba2e5-b97b-46f1-8dc3-a3333ea60548/a34ba2e5-b97b-46f1-8dc3-a3333ea605481.gif)
从0到1实现基于Tornado和Tensorflow的人脸年龄性别识别2
从0到1实现基于Tornado和Tensorflow的人脸、年龄、性别识别
(2)
网址:
年龄识别模型的训练过程
defmain(argv=None):
###一个图中包含有一个名称范围的堆栈,在使用name_scope(...)之后,将压(push)新名称进栈中,
#并在下文中使用该名称
withtf.Graph().as_default():
##这句比较重要选择模型
model_fn=select_model(FLAGS.model_type)
##假设这里选的是默认的,这里我们发现其实作者是实现了inceptionv3、levi_hassner_bn两种模型
##Openthemetadatafileandfigureoutnlabels,andsizeofepoch
##年龄模型的年龄段labes分别是['(0,2)','(4,6)','(8,12)','(15,20)','(25,32)','(38,43)','(48,53)','(60,100)']应该是8个标签12345678910
可以看到,select_model选择了模型,我们以deflevi_hassner(nlabels,images,pkeep,is_training):
为例子,
这个模型的命名是因为GilLeviandTalHassner,AgeandGenderClassificationusing
ConvolutionalNeuralNetworks,IEEEWorkshoponAnalysisandModelingof
FacesandGestures(AMFG),attheIEEEConf.onComputerVisionand
PatternRecognition(CVPR),Boston,June2015这篇文章。
模型的实现源代码
weight_decay=0.0005
weights_regularizer=tf.contrib.layers.l2_regularizer(weight_decay)
withtf.variable_scope("LeviHassner","LeviHassner",[images])asscope:
withtf.contrib.slim.arg_scope(
[convolution2d,fully_connected],
weights_regularizer=weights_regularizer,
biases_initializer=tf.constant_initializer(1.),
weights_initializer=tf.random_normal_initializer(stddev=0.005),
trainable=True):
withtf.contrib.slim.arg_scope(
[convolution2d],
weights_initializer=tf.random_normal_initializer(stddev=0.01)):
conv1=convolution2d(images,96,[7,7],[4,4],padding='VALID',biases_initializer=tf.constant_initializer(0.),scope='conv1')
pool1=max_pool2d(conv1,3,2,padding='VALID',scope='pool1')
norm1=tf.nn.local_response_normalization(pool1,5,alpha=0.0001,beta=0.75,name='norm1')
conv2=convolution2d(norm1,256,[5,5],[1,1],padding='SAME',scope='conv2')
pool2=max_pool2d(conv2,3,2,padding='VALID',scope='pool2')
norm2=tf.nn.local_response_normalization(pool2,5,alpha=0.0001,beta=0.75,name='norm2')
conv3=convolution2d(norm2,384,[3,3],[1,1],biases_initializer=tf.constant_initializer(0.),padding='SAME',scope='conv3')
pool3=max_pool2d(conv3,3,2,padding='VALID',scope='pool3')
flat=tf.reshape(pool3,[-1,384*6*6],name='reshape')
full1=fully_connected(flat,512,scope='full1')
drop1=tf.nn.dropout(full1,pkeep,name='drop1')
full2=fully_connected(drop1,512,scope='full2')
drop2=tf.nn.dropout(full2,pkeep,name='drop2')12345678910111213141516171819202122232425261234567891011121314151617181920212223242526
这是典型的卷积神经网络用于分类的模型结构
L2范数
其中weights_regularizer=tf.contrib.layers.l2_regularizer(weight_decay)
l2_regularizer是L2范数的意思,在《线性代数》《矩阵论中》,我们还了解到有L1范数,那么更受宠幸的规则化范数是L2范数:
||W||2。
它也不逊于L1范数,它有两个美称,在回归里面,有人把有它的回归叫“岭回归”(Ridge
Regression),有人也叫它“权值衰减weightdecay”。
它的强大功效是改善机器学习里面一个非常重要的问题:
过拟合。
至于过拟合是什么,上面也解释了,就是模型训练时候的误差很小,但在测试的时候误差很大,也就是我们的模型复杂到可以拟合到我们的所有训练样本了,但在实际预测新的样本的时候,糟糕的一塌糊涂。
通俗的讲就是应试能力很强,实际应用能力很差。
下面这幅图可以看到过拟合大概是什么状况,绿色线
在上面的图像中有两个不同的类,分别由蓝色和红色圆圈表示。
绿线是过度拟合的分类器。
它完全遵循训练数据,同时也严重依赖于训练数据,并且可能在处理未知数据时比代表正则化模型的黑线表现更差。
因此,我们的正则化目标是得到一个简单的模型,不附带任何不必要的复杂。
我们选择L2-正则化来实现这一点,L2正则化将网络中所有权重的平方和加到损失函数。
如果模型使用大权重,则对应重罚分,并且如果模型使用小权重,则小罚分。
这就是为什么我们在定义权重时使用了regularizer参数,并为它分配了一个l2_regularizer。
这告诉了TensorFlow要跟踪l2_regularizer这个变量的L2正则化项(并通过参数reg_constant对它们进行加权)。
所有正则化项被添加到一个损失函数可以访问的集合——tf.GraphKeys.REGULARIZATION_LOSSES。
将所有正则化损失的总和与先前计算的交叉熵相加,以得到我们的模型的总损失。
理解是:
限制了参数很小,实际上就限制了多项式某些分量的影响很小,这样就相当于减少参数个数,过拟合状况就会被降低。
下面走到withtf.variable_scope(“LeviHassner”,“LeviHassner”,[images])asscope:
可以看到有不少scope,那么它是什么呢?
Tensorflow为了更好的管理变量,提供了variablescope机制,具体参考TensorFlow官方这篇文章
https:
//www.tensorflow.org/programmers_guide/variable_scope
后面又来了个比较复杂的东西tf.contrib.slim,这是什么鬼,看看官方的解释:
TF-Slimisalightweightlibraryfordefining,trainingandevaluating
complexmodelsinTensorFlow.Componentsoftf-slimcanbefreelymixed
withnativetensorflow,aswellasotherframeworks,suchas
tf.contrib.learn.
我们用到的是arg_scope
arg_scope:
providesanewscopenamedarg_scopethatallowsauserto
definedefaultargumentsforspecificoperationswithinthatscope.
卷积神经网络
卷积神经网络(CNN)由输入层、卷积层、激活函数、池化层、全连接层组成,即INPUT-CONV-RELU-POOL-FC
下面终于来到网络层了啊!
来看代码
conv1=convolution2d(images,96,[7,7],[4,4],padding='VALID',biases_initializer=tf.constant_initializer(0.),scope='conv1')
pool1=max_pool2d(conv1,3,2,padding='VALID',scope='pool1')
conv2=convolution2d(pool1,256,[5,5],[1,1],padding='SAME',scope='conv2')
pool2=max_pool2d(conv2,3,2,padding='VALID',scope='pool2')
conv3=convolution2d(pool2,384,[3,3],[1,1],padding='SAME',biases_initializer=tf.constant_initializer(0.),scope='conv3')
pool3=max_pool2d(conv3,3,2,padding='VALID',scope='pool3')
#canusetf.contrib.layer.flatten
flat=tf.reshape(pool3,[-1,384*6*6],name='reshape')
full1=fully_connected(flat,512,scope='full1')
drop1=tf.nn.dropout(full1,pkeep,name='drop1')
full2=fully_connected(drop1,512,scope='full2')
drop2=tf.nn.dropout(full2,pkeep,name='drop2')123456789101112123456789101112
卷积层
卷积层:
用它来进行特征提取,如下:
关于卷积的图解如下:
输入图像和filter的对应位置元素相乘再求和,最后再加上b,得到特征图。
如图中所示,filter
w0的第一层深度和输入图像的蓝色方框中对应元素相乘再求和得到0,其他两个深度得到2,0,则有0+2+0+1=3即图中右边特征图的第一个元素3.,卷积过后输入图像的蓝色方框再滑动,stride=2,如下:
如上图,完成卷积,得到一个3*3*1的特征图;在这里还要注意一点,即zeropad项,即为图像加上一个边界,边界元素均为0.(对原输入无影响)一般有
F=3=>zeropadwith1
F=5=>zeropadwith2
F=7=>zeropadwith3,边界宽度是一个经验值,加上zeropad这一项是为了使输入图像和卷积后的特征图具有相同的维度,如:
输入为5*5*3,filter为3*3*3,在zeropad为1,则加上zeropad后的输入图像为7*7*3,则卷积后的特征图大小为5*5*1((7-3)/1+1),与输入图像一样;
卷积层还有一个特性就是“权值共享”原则。
如下图:
如没有这个原则,则特征图由10个32*32*1的特征图组成,即每个特征图上有1024个神经元,每个神经元对应输入图像上一块5*5*3的区域,即一个神经元和输入图像的这块区域有75个连接,即75个权值参数,则共有75*1024*10=768000个权值参数,这是非常复杂的,因此卷积神经网络引入“权值”共享原则,即一个特征图上每个神经元对应的75个权值参数被每个神经元共享,这样则只需75*10=750个权值参数,而每个特征图的阈值也共享,即需要10个阈值,则总共需要750+10=760个参数。
而关于特征图的大小计算方法具体如下:
第一层是个卷积层convolution2d(images,96,[7,7],[4,4],padding=’VALID’,
biases_initializer=tf.constant_initializer(0.),scope=’conv1’),
tf.contrib.layers.convolution2d(*args,**kwargs)
通过pycharm跟到tensorflow源码,实际是调用了下面这个函数
defconvolution(inputs,
num_outputs,
kernel_size,
stride=1,
padding=’SAME’,
data_format=None,
rate=1,
activation_fn=nn.relu,
normalizer_fn=None,
normalizer_params=None,
weights_initializer=initializers.xavier_initializer(),
weights_regularizer=None,
biases_initializer=init_ops.zeros_initializer(),
biases_regularizer=None,
reuse=None,
variables_collections=None,
outputs_collections=None,
trainable=True,
scope=None):
Returns:
atensorrepresentingtheoutputoftheoperation.
有点难以理解是吧,还是稍微再回顾一下卷积吧!
这个函数很强大,1到3维的卷积都支持。
(这里暂时只用2维)
·inputs:
输入变量,是一个N+2维的Tensor
类型要求是一个Tensor,而我们一般训练的数据都是常量(比如mnist,load以后得到是Python的数据类型,不是tf的),所以需要把用tf的方法做一下转换,比如tf.reshape
为什么是N+2维呢,比如图像,除了宽度和高度,实际上还有样本数量和通道数量(如RGB3通道),所以多了2维。
inputs的格式,由date_format这个参数来觉得,比如2维,有NHWC和NCHW两种。
N是样本数量,H高度,W宽度,C通道数。
·num_outputs:
卷积filter的数量,或者说提取的特征数量,比如5,10
·kernel_size:
卷积核的大小,是N个参数的list,比如二维图像,可以时候[10,10],如果参数值相同,用一个整数来·表示也可以;
·stride:
卷积步长,同样是N个参数的序列,或者都相等的话,用一个整数来表示,默认是1.
·padding:
字符串格式,默认SAME,可选’VALID’。
(我也不知道效果差异怎么样,具体的滤波过程,看下面的图)
·data_format:
字符串,指定inputs的格式
一维数据:
”NWC”(default)and“NCW”
二维数据:
”NHWC”(default)and“NCHW”
三维数据:
”NDHWC”
也就是,不指定的话,通道数都是最后一个参数。
·rate:
asequenceofNpositiveintegersspecifyingthedilationrate
tousefora’trousconvolution.Canbeasingleintegertospecifythe
samevalueforallspatialdimensions.(暂时没看到其作用)
·activation_fn:
激活函数,默认relu
·normalizer_fn:
normalizationfunctiontouseinsteadofbiases.(没用过,不知道起作用)
·normalizer_params:
normalizationfunctionparameters.
·weights_initializer:
这不用说了,有默认值,估计用默认的就可以了。
·weights_regularizer:
Optionalregularizerfortheweights.(防止过拟合)
·biases_initializer:
有默认值,一般也就不用指定。
·biases_regularizer:
…
·reuse:
whetherornotthelayeranditsvariablesshouldbereused.To
beabletoreusethelayerscope·mustbegiven.
应该都需要reuse吧,所以这个参数默认为True更好,现在是None。
·variables_collections:
怎么用暂时不太明白,但应该不用指定也可以;
·outputs_collections:
同上;
·trainable:
IfTruealsoaddvariablestothegraphcollection
GraphKeys.TRAINABLE_VARIABLES,默认是True。
(这个是不是说在fit的时候需要设为True,evaluate和predict的时候为false?
)
·scope:
也即是variable_scope,如果用多个卷积层的话,需要设置这个参数,以便把每一次的weight和bias区别出来。
了解更多关于layer的函数说明,访问tensorflow官方网站
https:
//www.tensorflow.org/api_guides/python/contrib.layers
池化层
池化层:
对输入的特征图进行压缩,一方面使特征图变小,简化网络计算复杂度;一方面进行特征压缩,提取主要特征
池化操作一般有两种,一种是AvyPooling,一种是maxPooling,如下:
同样地采用一个2*2的filter,maxpooling是在每一个区域中寻找最大值,这里的stride=2,最终在原特征图中提取主要特征得到右图。
(Avypooling现在不怎么用了,方法是对每一个2*2的区域元素求和,再除以4,得到主要特征),而一般的filter取2*2,最大取3*3,stride取2,压缩为原来的1/4.
注意:
这里的pooling操作是特征图缩小,有可能影响网络的准确度,因此可以通过增加特征图的深度来弥补(这里的深度变为原来的2倍)。
可以用tf.contrib.layers.max_pool2d或者tf.contrib.layers.avg_pool2d
max_pool2d(inputs,kernel_size,stride=2,padding=’VALID’,data_format=DATA_FORMAT_NHWC,outputs_collections=None,scope=None)
inputs:
就是卷积的输出了;
kernel_size:
是不是叫pool_size更贴切。
[kernel_height,kernel_width]或者是一个整数;
stride:
[stride_height,stride_width],不过文档上说目前这两个值必须一样
padding:
这里默认是VALID,和卷积默认不一样
data_format:
注意和卷积用的一样哦;
outputs_collections:
…
scope:
pooling的时候没有参数,需要scope吗?
reshape
flat=tf.reshape(pool3,[-1,384*6*6],name=’reshape’)
-1,就是缺省值,就是先以你们合适,到时总数除以你们几个的乘积,我该是几就是几。
Reshapeoutputtofitfullyconnectedlayerinput
全连接层
连接所有的特征,将输出值送给分类器(如softmax分类器)。
其实是神经网络中的隐含层。
dropout
drop2=tf.nn.dropout(full2,pkeep,name=’drop2’)
这里的keep_prob是保留概率,即我们要保留的RELU的结果所占比例,tensorflow建议的语法是,让它作为一个placeholder,在run时传入。
大概的意思如下图所示:
dropout一般用在全连接的部分,卷积部分不会用到dropout,输出曾也不会使用dropout,适用范围[输入,输出)
上面只是说了深度学习的网络层,下面讲一下训练方法:
训练方法
logits=model_fn(md['nlabels'],images,1-FLAGS.pdrop,True)
total_loss=loss(logits