卷积神经网络
学习资料
为什么用CNN
CNN架构
Convolution
假设现在我们的network的input是一张的Image,如果是黑白的,一个pixel就只需要用一个value去描述它,1就代表有涂墨水,0就代表没有涂到墨水。那在convolution layer里面,它由一组的filter,(其中每一个filter其实就等同于是fully connect layer里面的一个neuron),每一个filter其实就是一个matrix(),这每个filter里面的参数(matrix里面每一个element值)就是network的parameter(这些parameter是要学习出来的,并不是需要人去设计的)。
每个filter如果是的detects意味着它就是再侦测一个 的pattern(看的一个范围)。在侦测pattern的时候不看整张image,只看一个的范围内就可以决定有没有某一个pattern的出现。这个就是我们考虑的第一个Property。
这个filter咋样跟这个image运作呢?首先第一个filter是一个的matrix,把这个filter放在image的左上角,把filter的9个值和image的9个值做内积,两边都是1,1,1(斜对角),内积的结果就得到3。(移动多少是事先决定的),移动的距离叫做stride(stride等于多少,自己来设计),内积等于-1。stride等于2,内积等于-3。我们先设stride等于1。
你把filter往右移动一格得到-1,再往右移一格得到-3,再往右移动一格得到-1。接下里往下移动一格,得到-3。以此类推(每次都移动一格),直到你把filter移到右下角的时候,得到-1(得到的值如图所示)
经过这件事情以后,本来是6 *6的matrix,经过convolution process就得到4 *4的matrix。如果你看filter的值,斜对角的值是1,1,1。所以它的工作就是detection有没有1,1,1(连续左上到右下的出现在这个image里面)。比如说:出现在这里(如图所示蓝色的直线),所以这个filter就会告诉你:左上跟左下出现最大的值
就代表说这个filter要侦测的pattern,出现在这张image的左上角和左下角,这件事情就考虑了propetry2。同一个pattern出现在了左上角的位置跟左下角的位置,我们就可以用filter 1侦测出来,并不需要不同的filter来做这件事。
在一个convolution layer 里面会有很多的filter(刚才只是一个filter的结果),那另外的filter会有不同的参数(图中显示的filter2),它也做跟filter1一模一样的事情,在filter放到左上角再内积得到结果-1,依次类推。你把filter2跟 input image做完convolution之后,你就得到了另一个4*4的matrix,红色4 *4的matrix跟蓝色的matrix合起来就叫做feature map,看你有几个filter,你就得到多少个image(你有100个filter,你就得到100个4 *4的image)
刚才举的例子是一张黑白的image,所以input是一个matrix。若今天换成彩色的image,彩色的image是由RGB组成的,所以,一个彩色的image就是好几个matrix叠在一起,就是一个立方体。如果要处理彩色image,这时候filter不是一个matrix,filter而是一个立方体。如果今天是RGB表示一个pixel的话,那input就是3*6 *6,那filter就是3 *3 *3。
在做convolution的话,就是将filter的9个值和image的9个值做内积(不是把每一个channel分开来算,而是合在一起来算,一个filter就考虑了不同颜色所代表的channel)
convolution和fully connected之间的关系
convolution就是fully connected layer把一些weight拿掉了。经过convolution的output其实就是一个hidden layer的neural的output。如果把这两个link在一起的话,convolution就是fully connected拿掉一些weight的结果。
我们在做convolution的时候,我们filter1放到左上角(先考虑filter1),然后做inner product,得到内积为3,这件事情就等同于把6* 6的image拉直(变成如图所示)。然后你有一个neural的output是3,这个neural的output考虑了9个pixel,这9个pixel分别就是编号(1,2,3,7,8,9,13,14,15)的pixel。这个filter做inner product以后的output 3就是某个neuron output 3时,就代表这个neuron的weight只连接到(1,2,3,7,8,9,13,14,15)。这9个weight就是filter matrix里面的9个weight(同样的颜色)。
在fully connected中,一个neural应该是连接在所有的input(有36个pixel当做input,这个neuron应连接在36个input上),但是现在只连接了9个input(detain一个pattern,不需要看整张image,看9个input就好),这样做就是用了比较少的参数了。
将stride=1(移动一格)做内积得到另外一个值-1,假设这个-1是另外一个neural的output,这个neural连接到input的(2,3,4,8,9,10,14,15,16),同样的weight代表同样的颜色。在9个matrix
当我们做这件事情就意味说:这两个neuron本来就在fully connect里面这两个neural本来是有自己的weight,当我们在做convolution时,首先把每一个neural连接的wight减少,强迫这两个neural共用一个weight。这件事就叫做shared weight,当我们做这件事情的时候,我们用的这个参数就比原来的更少。
Max pooling
相对于convolution来说,Max Pooling是比较简单的。我们根据filter 1得到4*4的maxtrix,根据filter2得到另一个4 *4的matrix,接下来把output ,4个一组。每一组里面可以选择它们的平均或者选最大的都可以,就是把四个value合成一个value。这个可以让你的image缩小。
假设我们选择四个里面的max vlaue保留下来,这样可能会有个问题,把这个放到neuron里面,这样就不能够微分了,但是可以用微分的办法来处理的。
做完一个convolution和一次max pooling,就将原来6 * 6的image变成了一个2 *2的image。这个2 *2的pixel的深度depend你有几个filter(你有50个filter你就有50维),得到结果就是一个new image but smaller,一个filter就代表了一个channel。
这件事可以repeat很多次,通过一个convolution + max pooling就得到新的 image。它是一个比较小的image,可以把这个小的image,做同样的事情,再次通过convolution + max pooling,将得到一个更小的image。
这边有一个问题:第一次有25个filter,得到25个feature map,第二个也是由25个filter,那将其做完是不是要得到的feature map。其实不是这样的!
假设第一层filter有2个,第二层的filter在考虑这个imput时是会考虑深度的,并不是每个channel分开考虑,而是一次考虑所有的channel。所以convolution有多少个filter,output就有多少个filter(convolution有25个filter,output就有25个filter。只不过,这25个filter都是一个立方体)
Flatten
flatten就是feature map拉直,拉直之后就可以丢到fully connected feedforward netwwork,然后就结束了。
CNN in Keras
唯一要改的是:network structure和input format,本来在DNN中input是一个vector,现在是CNN的话,会考虑 input image的几何空间的,所以不能给它一个vector。应该input一个tensor(高维的vector)。为什么要给三维的vector?因为image的长宽高各是一维,若是彩色的话就是第三维。所以要给三维的tensor
model.add(Convolution2D( 25, 3, 3)
25代表有25个filter,3 *3代表filter是一个3 *3的matrix
Input_shape=(28,28,1)
假设我要做手写数字辨识,input是28 *28的image,每个pixel都是单一颜色。所以input_shape是(1,28,28)。如果是黑白图为1(blacj/white),如果是彩色的图时为3(每个pixel用三个值来表述)。
MaxPooling2D(( 2, 2 ))
2,2表示把2*2的feature map里面的pixel拿出来,选择max value
假设我们input一个1 *28 * 28的image,你就可以写model.add(Convolution2D( 25, 3, 3, Input_shape=(28,28,1)))。通过convplution以后得到output是25 *26 26(25个filter,通过3 *3得到26 * 26)。然后做max pooling,2 *2一组选择 max value得到 25 *13 * 13
然后在做一次convolution,假设我在这选50个filter,每一个filter是3 *3时,那么现在的channel就是50。13 *13的image通过3 *3的filter,就成11 *11,然后通过2 *2的Max Pooling,变成了50 *5 *5
在第一个convolution layer里面,每一个filter有9个参数,在第二个convolution layer里面,虽然每一个filter都是3 *3,但不是3 *3个参数,因为它input channel 是25个,所以它的参数是3 *3 *25(225)。
通过两次convolution,两次Max Pooling,原来是1 *28 *28变为50 *5 *5。flatten的目的就是把50 *5 *5拉直,拉直之后就成了1250维的vector,然后把1250维的vector丢到fully connected。
import numpy as np
import keras
# 导入Keras库
from keras.models import Sequential
# 导入Keras层
from keras.layers import Dense,Dropout,Activation,Convolution2D,MaxPooling2D,Flatten
# 导入Keras优化器
from keras.optimizers import SGD,Adam
# 导入Keras数据集
from keras.utils import to_categorical
from keras.datasets import mnist
print(np.__version__) # 1.23.5
print(keras.__version__) # 2.13.1
# 加载mnist数据集,将训练集和测试集分别赋值给x_train,y_train,x_test,y_test
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 将训练集和测试集的形状转换为(60000,28,28,1)和(10000,28,28,1)
x_train = x_train.reshape(60000,28,28,1)
x_test = x_test.reshape(10000,28,28,1)
# 将训练集和测试集的标签转换为one-hot编码
y_train = to_categorical(y_train,10)
y_test = to_categorical(y_test,10)
# x_train=x_train/255
# x_test=x_test/255
# 定义一个模型
model2 = Sequential()
# model2.add(Dense(input_dim=28*28, units=633,activation='relu'))
# 添加一个卷积层,输入维度为28*28,输出维度为633,激活函数为relu
model2.add(Convolution2D(25,3,3,input_shape=(28,28,1)))
# 添加一个池化层,池化核为2*2
model2.add(MaxPooling2D((2,2)))
# 添加一个Flatten层,将输入的维度转换为(None,7*7*633)
model2.add(Flatten())
# 添加一个全连接层,输出维度为100,激活函数为relu
model2.add(Dense(units=100,activation="relu"))
# 添加一个全连接层,输出维度为100,激活函数为relu
model2.add(Dense(units=100,activation="relu"))
# 添加10个全连接层,输出维度为633,激活函数为relu
# for i in range(10):
# model2.add(Dense(units=633,activation="relu"))
# 添加一个全连接层,输出维度为10,激活函数为softmax
model2.add(Dense(units=10,activation="softmax"))
# model2.compile(loss="categorical_crossentropy",optimizer=SGD(learning_rate=0.1), metrics=['accuracy'])
# 编译模型,损失函数为categorical_crossentropy,优化器为SGD,学习率为0.1,指标为accuracy
model2.compile(loss="categorical_crossentropy",optimizer="adam", metrics=['accuracy'])
# 训练模型,batch_size为100,epochs为20
model2.fit(x_train, y_train, batch_size=100, epochs=20)
# 计算模型的训练精度
restult = model2.evaluate(x_train, y_train,batch_size=10000)
print("Train Acc:",restult[1])
# 计算模型的测试精度
restult = model2.evaluate(x_test, y_test)
print("Test Acc:",restult[1])