引用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给网络。
经过一系列的修改终于不出现nan了。接下来说明导致nan的原因。
逐步进行计算,因为交叉熵部分会对y_conv层进行求对数,对数输出不能为0,而有时因为计算机表示小数是有限的,若比此小数还小就会变成0,因此最好对y_conv先
进行clip操作,然后再进行计算,结果不再有nan出现。
——————————————————————————————————————————————
接下来对其求梯度,查看是否正常,因为我不知道clip后是否能正常求梯度。
求出梯度可以正常返回值。
然后接下来就是训练了
一开始不能训练,求梯度是0,我想了一宿也没想出来是为什么,后来第二天重启虚拟机再运行发现没问题了,所以tensorflow这个模块记得要把sess关闭,不一定重新运行
代码就会一切都初始化。
现在已经可以正常训练了,可以看到权重在每次迭代后都会发生变化。
——————————————————————————————————————————————————————————————————————————
接下来添加一个测试正确率代码模块
其中ttt4和ttt5里面各有500个bool类型数字,统计其中的trul数量即可以知道前500次训练和后500次训练的正确率,我计算两次的正确率分别是百分之11和百分之14.............
反正比百分之10高!
提高正确率部分请看下集继续