通常在写关于卷积神经网络的文章中都会说一句:其实卷积神经网络并不神秘而且它也不是万能的。就目前我也认为确实这样子的。今天才把卷积神经网络从理论到实践(基于 tensorflow)整个流程初步搞清,想基于我的理解,希望尽量通俗易懂的介绍我理解的卷积神经网络。
1 神经网络
神经网络一般指包含一个输入层一个输出层多个隐层的网络结构,如下图所示,神经网络其实可以理解为一个输入序列,经过一个函数f(x),得到一个输出序列,这个函数f(x)包含很多的参数,所以可以通过训练(求最优解)得到一组最优的参数。
图 1
2 卷积神经网络
在图像处理中,往往把图像表示为像素的向量,比如一个1000×1000的图像,可以表示为一个1000000的向量。在上一节中提到的神经网络中,如果隐含层数目与输入层一样,即也是1000000时,那么输入层到隐含层的参数数据为1000000×1000000=10^12,这样就太多了,基本没法训练。所以图像处理要想练成神经网络大法,必先减少参数加快速度。就跟辟邪剑谱似的,普通人练得很挫,一旦自宫后内力变强剑法变快,就变的很牛了。
2.1 局部感知
卷积神经网络有两种神器可以降低参数数目,第一种神器叫做局部感知野。一般认为人对外界的认知是从局部到全局的,而图像的空间联系也是局部的像素联系较为紧密,而距离较远的像素相关性则较弱。因而,每个神经元其实没有必要对全局图像进行感知,只需要对局部进行感知,然后在更高层将局部的信息综合起来就得到了全局的信息。网络部分连通的思想,也是受启发于生物学里面的视觉系统结构。视觉皮层的神经元就是局部接受信息的(即这些神经元只响应某些特定区域的刺激)。如下图所示:左图为全连接,右图为局部连接。
图 2
在上右图中,假如每个神经元只和10×10个像素值相连,那么权值数据为1000000×100个参数,减少为原来的千分之一。而那10×10个像素值对应的10×10个参数,其实就相当于卷积操作。
2.2 参数共享
但其实这样的话参数仍然过多,那么就启动第二级神器,即权值共享。在上面的局部连接中,每个神经元都对应100个参数,一共1000000个神经元,如果这1000000个神经元的100个参数都是相等的,那么参数数目就变为100了。
怎么理解权值共享呢?我们可以这100个参数(也就是卷积操作)看成是提取特征的方式,该方式与位置无关。这其中隐含的原理则是:图像的一部分的统计特性与其他部分是一样的。这也意味着我们在这一部分学习的特征也能用在另一部分上,所以对于这个图像上的所有位置,我们都能使用同样的学习特征。
更直观一些,当从一个大尺寸图像中随机选取一小块,比如说 8×8 作为样本,并且从这个小块样本中学习到了一些特征,这时我们可以把从这个 8×8 样本中学习到的特征作为探测器,应用到这个图像的任意地方中去。特别是,我们可以用从 8×8 样本中所学习到的特征跟原本的大尺寸图像作卷积,从而对这个大尺寸图像上的任一位置获得一个不同特征的激活值。
如下图所示,展示了一个3x3的卷积核在5x5的图像上做卷积的过程。每个卷积都是一种特征提取方式,就像一个筛子,将图像中符合条件(激活值越大越符合条件)的部分筛选出来。
图 3
2.3池化层
图 4
2.4 多核
一个卷积核只表示一个特征,一个卷积核肯定是不够的,所以,一般会有多个卷积核,而且每个卷积核都会生成一个新的“图片”。如下图,第一步是做卷积,共3个卷积核,所以经过第一步会得到3个结果(3张图片),我们把每个结果称为一个通道,经过池化后进行第二次卷积,这次卷积的源通道就是上次的3个,卷积核为6,那么经过第二次卷积后的结果数为6个结果(6张图片),卷积过程中通道数据的合并是线性相加的,一个地方卷积后的值等于各个通道与对应卷积核做卷积后相加再加上偏移两。
图 5
2.5多通道
很多时候,我们输入的是多通道图像。如RGB三通道图像,下图就是。也有可能我们出于特定目的,将几张图组成一组一次性输入处理。
对于这种情况,这时一个过滤器或卷积核其实对应的是x个卷积核(对应x通道)和一个偏置量,如下图中的W0和W1。一个通道中的某个区域(蓝框)和它对应的卷积核(红框)做卷积,多个通道的卷积结果线性相加,再加上偏置量(最下的单个红框),得到卷积结果(最右列中心的绿色框)。下图的卷积层包含2个多通道卷积核W0和W1,所以结果为2个绿色矩阵。而该卷积层的输出对于下一卷积层一定程度上也就可以说是2通道,处理方式也是类似。
图 6
2.6 全连接构建分类器
经过多次的卷积和池化,我们得到了n个通道的卷积结果,也就是特征值,把这些特征值作为输入,搭建一个全连接的分类器,然后就可以通过像普通的神经网络一样训练这些分类器了,如图5。
2.7基于tensorFlow的代码实现
# load MNIST data
import input_data
mnist = input_data.read_data_sets("Mnist_data/", one_hot=True)
# start tensorflow interactiveSession
import tensorflow as tf
sess = tf.InteractiveSession()
# weight initialization
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)
# convolution
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# pooling
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# Create the model
# placeholder
x = tf.placeholder("float", [None, 784])
y_ = tf.placeholder("float", [None, 10])
# variables
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x,W) + b)
# first convolutinal layer
w_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1, 28, 28, 1])
h_conv1 = tf.nn.relu(conv2d(x_image, w_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# second convolutional layer
w_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, w_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# densely connected layer
w_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, w_fc1) + b_fc1)
# dropout
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# readout layer
w_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2)
# train and evaluate the model
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.GradientDescentOptimizer(1e-3).minimize(cross_entropy)
#train_step = tf.train.AdagradOptimizer(1e-5).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={x:batch[0], y_:batch[1], keep_prob:1.0})
print "step %d, train accuracy %g" %(i, train_accuracy)
train_step.run(feed_dict={x:batch[0], y_:batch[1], keep_prob:0.5})
print "test accuracy %g" % accuracy.eval(feed_dict={x:mnist.test.images, y_:mnist.test.labels, keep_prob:1.0})
参考文章:
http://www.36dsj.com/archives/24006
http://www.jianshu.com/p/606a33ba04ff
http://www.omegaxy.com/blog/2016-01-14/cnn/