CNN发展史:
1.经典卷积神经网络
以下仅列出关于CNN的深层次理解:
卷积层
tensorflow中卷积层的建立函数:_conv1 = tf.nn.conv2d(_input_r, tf.Variable(tf.random_normal([3, 3, 1, 64], stddev=0.1)), strides=[1, 1, 1, 1], padding='SAME') 参数说明:
- 输入图像
- tensor变量建立(正态分布初始化(filter_height,fileter_width,in_channels,out_channels],方差))
- stride=[1,stride,stride,1]
- padding='SAME'or'VALID'(same是圈圈取零包裹)
- 卷积核的通道数必须与输入的一致,但可以有多个卷积核,如此输出通道数便增多了
- 经过卷积核之后的输出图像大小:以高为例:
- 卷积核的意义,其实就跟图像处理当中的“图像分割”里线检测、边缘检测用的算子一般:线检测算子--水平、+45°、-45°、垂直;边缘检测算子:sobel、prewitt、Laplacian等,这些是基于图像的突变性和连续性,从“一阶导数”、“二阶导数”等数学原理所推导出来的几何特征的提取算子。而我们利用神经网络的BP反馈对卷积核进行训练,则这个算子则能够帮助我们提取我们所需要的特征。
- 卷积核的分类--扩张卷积、转置卷积、可分离卷积:http://www.sohu.com/a/159591827_390227
- padding边缘填充是为了不让一些边界消失,分为full、same、valid。
- “局部连接”--每个神经元(卷积层的一个像素)仅与输入神经元的一块区域(输入图像的一个局部区域)连接,这块局部区域称作感受野。如此保证了学习后的过滤器能够对于局部的输入特征有最强的响应。局部连接的思想,也是受启发于生物学里面的视觉系统结构,视觉皮层的神经元就是局部接受信息的;而且局部连接使得参数大量减少。
- “权值共享”--计算同一个深度切片的神经元时采用的滤波器是共享的。共享权重在一定程度上讲是有意义的,例如图片的底层边缘特征与特征在图中的具体位置无关。但是在一些场景中是无意的,比如输入的图片是人脸,眼睛和头发位于不同的位置,希望在不同的位置学到不同的特征 (参考斯坦福大学公开课)。请注意权重只是对于同一深度切片的神经元是共享的,在卷积层,通常采用多组卷积核提取不同特征,即对应不同深度切片的特征,不同深度切片的神经元权重是不共享,如此组成的特征即为Feature map。另外,偏重对同一深度切片的所有神经元都是共享的。
- “分布式表征”--神经网络的重要性质,即如“编码”一般,可将样本从原始空间投影到一个更好的特征空间中。
池化层
tensorflow中池化层的建立函数:_pool1 = tf.nn.max_pool(_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
- 池化层的作用:通过池化来降低卷积层输出的特征向量,同时改善结果(不易出现过拟合)。
- 池化层的操作:max、mean。
- “区域不变形”--pooling 这步综合了局部特征,失去了每个特征的位置信息。这很适合基于图像的任务,比如要判断一幅图里有没有猫这种生物,你可能不会去关心这只猫出现在图像的哪个区域。但是在 NLP 里,词语在句子或是段落里出现的位置,顺序,都是很重要的信息。
一个经典卷积神经网络的实现:input-conv1-relu-pooling1-conv2-relu-pooling2-dc1-output
n_input = 784
n_output = 10
weights = {
'wc1': tf.Variable(tf.random_normal([3, 3, 1, 64], stddev=0.1)),
'wc2': tf.Variable(tf.random_normal([3, 3, 64, 128], stddev=0.1)),
'wd1': tf.Variable(tf.random_normal([7*7*128, 1024], stddev=0.1)),
'wd2': tf.Variable(tf.random_normal([1024, n_output], stddev=0.1))
}
biases = {
'bc1': tf.Variable(tf.random_normal([64], stddev=0.1)),
'bc2': tf.Variable(tf.random_normal([128], stddev=0.1)),
'bd1': tf.Variable(tf.random_normal([1024], stddev=0.1)),
'bd2': tf.Variable(tf.random_normal([n_output], stddev=0.1))
}
def conv_basic(_input, _w, _b, _keepratio):
# INPUT
_input_r = tf.reshape(_input, shape=[-1, 28, 28, 1])
# CONV LAYER 1
_conv1 = tf.nn.conv2d(_input_r, _w['wc1'], strides=[1, 1, 1, 1], padding='SAME')
#_mean, _var = tf.nn.moments(_conv1, [0, 1, 2])
#_conv1 = tf.nn.batch_normalization(_conv1, _mean, _var, 0, 1, 0.0001)
_conv1 = tf.nn.relu(tf.nn.bias_add(_conv1, _b['bc1']))
_pool1 = tf.nn.max_pool(_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
_pool_dr1 = tf.nn.dropout(_pool1, _keepratio)
# CONV LAYER 2
_conv2 = tf.nn.conv2d(_pool_dr1, _w['wc2'], strides=[1, 1, 1, 1], padding='SAME')
#_mean, _var = tf.nn.moments(_conv2, [0, 1, 2])
#_conv2 = tf.nn.batch_normalization(_conv2, _mean, _var, 0, 1, 0.0001)
_conv2 = tf.nn.relu(tf.nn.bias_add(_conv2, _b['bc2']))
_pool2 = tf.nn.max_pool(_conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
_pool_dr2 = tf.nn.dropout(_pool2, _keepratio)
# VECTORIZE
_dense1 = tf.reshape(_pool_dr2, [-1, _w['wd1'].get_shape().as_list()[0]])
# FULLY CONNECTED LAYER 1
_fc1 = tf.nn.relu(tf.add(tf.matmul(_dense1, _w['wd1']), _b['bd1']))
_fc_dr1 = tf.nn.dropout(_fc1, _keepratio)
# FULLY CONNECTED LAYER 2
_out = tf.add(tf.matmul(_fc_dr1, _w['wd2']), _b['bd2'])
# RETURN
out = { 'input_r': _input_r, 'conv1': _conv1, 'pool1': _pool1, 'pool1_dr1': _pool_dr1,
'conv2': _conv2, 'pool2': _pool2, 'pool_dr2': _pool_dr2, 'dense1': _dense1,
'fc1': _fc1, 'fc_dr1': _fc_dr1, 'out': _out
}
return out
print ("CNN READY")
x = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_output])
keepratio = tf.placeholder(tf.float32)
# FUNCTIONS
_pred = conv_basic(x, weights, biases, keepratio)['out']
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=_pred))
#使用了最先进的adam优化算法,其中包含了正则化的思想,但不像传统的正则化那样。
optm = tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost)
_corr = tf.equal(tf.argmax(_pred,1), tf.argmax(y,1))
accr = tf.reduce_mean(tf.cast(_corr, tf.float32))
init = tf.global_variables_initializer()
# SAVER
print ("GRAPH READY")
sess = tf.Session()
sess.run(init)
training_epochs = 30
batch_size = 10
display_step = 5
time1=time.time()
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(mnist.train.num_examples/batch_size)
#total_batch = 10
# Loop over all batches
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
# Fit training using batch data
sess.run(optm, feed_dict={x: batch_xs, y: batch_ys, keepratio:0.7})
# Compute average loss
avg_cost += sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keepratio:1.})/total_batch
# Display logs per epoch step
if epoch % display_step == 0:
print ("Epoch: %03d/%03d cost: %.9f" % (epoch, training_epochs, avg_cost))
train_acc = sess.run(accr, feed_dict={x: batch_xs, y: batch_ys, keepratio:1.})
print (" Training accuracy: %.3f" % (train_acc))
test_acc = sess.run(accr, feed_dict={x: testimg, y: testlabel, keepratio:1.})
print (" Test accuracy: %.3f" % (test_acc))
print ("OPTIMIZATION FINISHED")
time_waste=time.time()-time1
print("The time wasting is:%dh %dm %ds"%(time_waste//(60*24),time_waste%(60*24)//60,time_waste%(60*24)%60))
Epoch: 000/030 cost: 0.122792258
Training accuracy: 0.900
Test accuracy: 0.986
Epoch: 005/030 cost: 0.012638518
Training accuracy: 1.000
Test accuracy: 0.993
Epoch: 010/030 cost: 0.006675720
Training accuracy: 1.000
Test accuracy: 0.992
Epoch: 015/030 cost: 0.005300980
Training accuracy: 1.000
Test accuracy: 0.992
Epoch: 020/030 cost: 0.003683299
Training accuracy: 1.000
Test accuracy: 0.990
Epoch: 025/030 cost: 0.003249645
Training accuracy: 1.000
Test accuracy: 0.993
OPTIMIZATION FINISHED
The time wasting is:0h 13m 54s
2.Alexnet
以上程序很多地方其实已经用到了Alexnet的特点了:像是RELU、dropout。
Alexnet的特点实际上就是:
- 采用了三个链接层:2048、2048、1000
- 激活函数采用relu
- 全链接层采用dropout
- 在relu和pool之间采用局部相应归一化LRN,但是当net的层数到达11层时,这个lrn没有作用,反而起了副作用;而且因为lrn在池化层前的计算会不经济,所以后面的alexnet改进有把lrn放在了pool后面
- conv-pool数更多了,达到5个
- GPUS分布式计算,如图所示
- 扩展数据:随机裁剪、旋转......
LRN的介绍:
参数可以百度LRN看看,这里讲讲它的含义--来源于《深度学习与计算机视觉》
局部相应归一化模拟的是动物神经中的横向抑制效应(将相似的记忆分开),从公式可以看出,如果在该位置,该通道和临近通道的绝对值都比较大的话,归一化之后值会有变得更小的趋势。
3.模型的保存和读取
#保存模型
saver = tf.train.Saver(max_to_keep=3)#最多保存三个模型,再存入则会按照first delete
saver.save(sess, "save/nets/cnn_mnist_basic.ckpt-" + str(epoch))#将训练好的计算图存入该文件
#读取模型
epoch = training_epochs-1
saver.restore(sess, "save/nets/cnn_mnist_basic.ckpt-" + str(epoch))
test_acc = sess.run(accr, feed_dict={x: testimg, y: testlabel, keepratio:1.})
print (" TEST ACCURACY: %.3f" % (test_acc))
参考资料:https://github.com/scutan90/DeepLearning-500-questions