tensorflow构造DNN识别cifar10记录1

引用tf的自带模块,直接读下载好的二进制数据,二进制数据自己去网上下。

import cifar10_inpute

引用tensorflow

import tensorflow as tf


创造一个tensorflow的内置queue,其长度为1.

q=tf.FIFOQueue(1,'string')


创造一个向q中加入元素的运算

init=q.enqueue_many((['数据文件的路径','数据文件的路径','数据文件的路径'],))


创造一个执行器

sess=tf.Session()


执行加入元素的操作,执行后q里就有文件路径了

sess.run(init)


建立算子,作用是调用cifar10_input里的一个读取信息的操作,返回一个对象result,此对象有几个参数,result.uint8image(高32,宽32,三个通道RGB);label 10

result=cifar10_input.read_cifar10(q)


建立一个用来建立权重变量的函数

def weight_variable(shape):

initial=tf.truncated_normal(shape,stddev=0.1)

return tf.Variable(initial)


同上,建立偏执的

def bias_variable(shape):

initial=tf.constant(0.2,shape=shape)

return tf.Variable(initial)


同上,建卷积方式的算子,注意这里padding参数的意思是:当5x5卷积时,原来图像是32x32会变成28x28,padding参数是same时将在卷积前将图像扩大为36x36,补充的元素是0,这样卷积后图像大小仍旧是32x32.

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


同上,建pool方式的算子

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


【】弄简单一点,先弄成一层卷积层,一层pool,然后直接连全连接层,然后直接输出结果。【】



创建第一层卷积核的权重tensor,5x5是大小,3是通道,1是一个特征,你可以根据你电脑的性能设置很多个特征,也即是说卷积后乘以权重加到一起变成relu的输。

所谓的特征就是类似于图像处理中的filter办法,比如gabor、sober滤波器等,可以将图像中的边提取出来。

W_conv1 = weight_variable([5, 5, 3, 1])
b_conv1 = bias_variable([1])

把图像转换成目标大小,并且转换格式为float

x_image=tf.to_float(tf.reshape(result.uint8image,[1,32,32,3]))


卷积与pool运算,最后输出[1,14,14,3]tensor。其中1是batch的大小,表示每次一张图片。

h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)

h_pool1=max_pool_2x2(h_conv1)


接下来连接到全连接层,输入有14x14x3个元素,隐藏层神经元设置为100,我用的虚拟机,你可以多设置一点,那么此时权重为:

W_fc1 = weight_variable([14 * 14 * 3, 100])
b_fc1 = bias_variable([100])
 
  

把h_pool1的输出变成1维的,再连接到relu上,输出形状为100个输出,由于隐藏层神经元是100个。
h_pool2_flat = tf.reshape(h_pool1, [-1, 14x14x3])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)


最后将隐藏层连接到输出层,输出层用softmax:

W_fc2 = weight_variable([100, 10])
b_fc2 = bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1, W_fc2) + b_fc2)

到这里网络的前向连接构造完毕,接下来是训练部分,采用cross_entropy代价函数。优化参数采样Adam模型。

cross_entropy = -tf.reduce_sum(result.label*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

——————————————————————————————————————————————————————————————————————————


运行以上代码,记得先要初始化变量!我就直接运行了,我执行一次就是训练一次。

sess.run(train_step)

结果我运行一下权重就都消失了,都变成0了,再运行就出错了。总结可能原因有两点:

(1)relu可能导致权重变成0

(2)不知道pool层是如何微分的


首先针对第二条,我测试一下pool层是如何微分的,经过思考和查阅资料,最后进行实验,得到以下结论:

在上面代码情况下,对h_conv1求梯度,利用tf.grandient函数求梯度,得到的梯度值基本上都是好几万。而对h_pool1层求梯度得到梯度是上一层的四分之一。

为什么是好几万?因为32x32x100=90000,基本上平均像素值是100,一共有32x32个像素值,所以梯度就是这些。而pool后是原来的1/4,这样一切就解释通了。

因为梯度很大,所以一次更新就把所有权重弄成负数了,然后就导致最后的结果,我们需要让梯度乘以比例的值小于当前权重值的百分之25,这样步长就比较小,

而不会一次性的就将权重弄成负数了。这也就是relu和sigmoid不同的地方。


————————————————————————————————————————————————————————————————————————

查找上文代码的问题,我认为权重消失是因为步长没有控制好,所以应该调整步长,于是先用函数求出各部分的梯度,观察梯度大小是否合适:

grad=tf.gradients(crossentropy,*****)

得到结果发现梯度都是0。接下来观察神经网络各层的输出情况,从第一层开始依次观察:

h_conv1层输出平均值是100多。

h_pool1层输出平均值是上一层的1/4

h_fc1输出平均值是20左右。

最后一层被归一化。

cross_entropy输出有时是0,有时是无穷大,有时是400左右的值。ylna+(1-y)ln(1-a)是cross_entropy的算法,a是神经网络输出值,若a是0或1都会导致其是无穷大。


求梯度情况记录:

h_conv1对W_conv1求导值大约为8W到10W

h_pool1对W_conv1求导值大约为上面的四分之一。这样求没意义,因为dy/dx=(dy/da)(da/dx),所以要一部一部求导。

所以这里求导接下来应该是这样

h_fc1对W_conv1求导值大约为几十到几千


crossentropy对h_fc1求导值大约为-1到1,由此推出crossentropy对W_conv1求导值大约为两者相乘。

验证结果:

sess.run(tf.gradients(cross_entropy,W_conv1))

结果显示推理正确,其值也是几十到几千,那么其值最后将要乘以一个步长值然后与原W_conv1相减,这样的话很有可能直接就把

全部权重给减成负值,由此会导致以后无法训练。


因为权重的值大约是-1到1之间,那么我们需要让更新的值小于0.025,假设梯度最大是3000的话,0.025/3000即是步长。预计将

步长调整为此值以后将不会出现权重消失情况。


于是我将权重设置为0.000008,权重消失问题解。总结为:梯度的大小是一个波动的数值,尤其是前期可能会很大,所以现今训练多层神经网络并配有relu核对步长的选择一定要非常小心,在训练前一定要首先分别单独计算各层的可能最大梯度,最大梯度乘以步长不要超过权重的十分之一甚至更小。

--------------------——————————————————————————————————————————————————————————————

经过刚刚修改后,运行仍旧可能会导致权重全部消失情况,但tensorflow有一个函数功能是控制权重的更新量的,权重一次性的更新量不能超过或小于一定数值,接下来对网络添加此功能进行尝试。

cippp=tf.clip_by_value(weit,0.2,0.7)

weit是tensor,weit里的任意元素的值小于0.2则取值为0.2,大于0.7取值为0.69999999。


接下来,首先计算梯度,再对梯度进行操作,最后应用梯度到权重上。

创建更新权重算子

opt=tf.train.GradientDescentOptimizer(0.00008)


对weit进行计算其梯度

grads=opt.compute_gradients(cross_entropy,[W_conv1,b_conv1])

其中grads是一个列表,list的元素是元组,元组有两个元素分别是梯度与对应权重


用for循环对里面grad元素进行clip,然后放到新的grad1里


不要像我似得将最小值设置为正数,因为这样的话梯度永远是正的,那么权重就会一直减小,所以要设置成负


最后更新权重

applyy=opt.apply_gradients(grads1)


——————————————————————————————————————————————————————————————————————————

仍旧是不对,但是我发现几个问题,首先会产生nan的结果,而nan不是none,我以为它们是相同的,nan是计算不出来的意思。

而回顾网络的计算只有corss_entropy那块我不懂,首先看格式就有问题,图像的result.label是一个数字而不是一个[0,0,1,0.0.0.0...]这种格式的。

问了大神和查阅资料tf没有对单个元素进行操作的机制。所以解决思路是:

先用占位符号替代输入,然后在训练时同时将一个result的图和标签转化成array,再转换成想要的格式。再用feed给网络。

tensorflow构造DNN识别cifar10记录1_第1张图片


经过一系列的修改终于不出现nan了。接下来说明导致nan的原因。

逐步进行计算,因为交叉熵部分会对y_conv层进行求对数,对数输出不能为0,而有时因为计算机表示小数是有限的,若比此小数还小就会变成0,因此最好对y_conv先

进行clip操作,然后再进行计算,结果不再有nan出现。


——————————————————————————————————————————————

接下来对其求梯度,查看是否正常,因为我不知道clip后是否能正常求梯度。


求出梯度可以正常返回值。


然后接下来就是训练了

一开始不能训练,求梯度是0,我想了一宿也没想出来是为什么,后来第二天重启虚拟机再运行发现没问题了,所以tensorflow这个模块记得要把sess关闭,不一定重新运行

代码就会一切都初始化。


现在已经可以正常训练了,可以看到权重在每次迭代后都会发生变化。

——————————————————————————————————————————————————————————————————————————

接下来添加一个测试正确率代码模块

tensorflow构造DNN识别cifar10记录1_第2张图片


其中ttt4和ttt5里面各有500个bool类型数字,统计其中的trul数量即可以知道前500次训练和后500次训练的正确率,我计算两次的正确率分别是百分之11和百分之14.............

反正比百分之10高!

提高正确率部分请看下集继续








































你可能感兴趣的:(tensorflow构造DNN识别cifar10记录1)