基于keras的CNN网络搭建(数字识别为例)
本文章可能仅对于有一点机器学习底子的同学有帮助,很多知识点都没有详细的去说明,当然这只是keras搭建一个简单的cnn网络,如果有错误,欢迎大佬指正。一起交流一起进步
-
导包
-
from keras.models import Sequential
网络模型实例化,用来存放cnn网络结构
-
from keras.layers import Deconv2D,MaxPool2D,Dense,Flatten
分别为卷积层,最大值池化层,全连接层,压缩层
-
from keras.optimizers import Adam
亚当算法,用来做优化方法,也可以不导入,通过keras的反向映射机制来确定优化方法
-
from keras.metrics import mse
均方误差,用来做损失函数
-
from keras.losses import categorical_crossentropy
评价函数,交叉熵,也可用来做损失函数
-
from keras.activations import relu,softmax
激活函数,用于神经元的激活,relu比sigmoid更好,不会梯度消散
-
import numpy as np
用来做数据类型的转换,转换成float32类型
本次例子为sklearn的自带数据集-load_digits
由于是多分类问题,结果评价用cohen_kappa作为参考
-
-
数据预处理阶段
-
第一步数据类型转化和数据维度变换
ld.images.reshape(1797,8,8,1).astype(np.float32)
将数据维度转换成四维(神经网络的需求),转成np.float32类型,当然用tf也行
-
第二步数据结果做独热编码
可以自己写几行代码来实现,也可以通过keras.utils中的to_categorical来实现
-
-
网络搭建
-
首先实例一个model,来存放网络结构
model = Sequential()
-
添加第一层卷积层和池化层
-
model.add(Deconv2D(8, (3, 3), input_shape=(8, 8, 1), strides=(1, 1), padding='SAME', activation=relu)) model.add(MaxPool2D((2,2)))
添加8个3x3的卷积核作为第一层,输入层为8x8单通道,每次卷积核移动一个像素单位,padding表示零填充,当卷积核到图像边缘不够时以0补充,最后激活函数为relu
-
model.add(MaxPool2D((2,2)))
-
-
添加第二层卷积层和池化层
-
model.add(Deconv2D(5, (3, 3), padding='SAME', activation=relu))
内容同上,和第一层差不多,不过减少了卷积核的数量
-
model.add(MaxPool2D((2,2)))
添加一个2x2的最大值池化滤波窗口
-
-
添加压缩层,将多维的数据压缩至一维
-
model.add(Flatten())
-
-
添加全连接层,输出结果
-
model.add(Dense(96, activation=relu)) model.add(Dense(10, activation=softmax))
添加两个全连接层,防止下降特征太迅速,中间多一层,先用relu激活,最后结果输出时用softmax激活,将结果转换成概率的形式,最大概率值的下标,即为识别的数字结果
-
可以添加一层model.summary() 可以查看各层的详细信息
-
-
-
网络模型的完成与训练
-
实例一个亚当算法模型,op_t = Adam(lr=0.01) 也可以不用实例,不果需要修改模型参数的话,就必须要实例,默认的lr为0.001
-
model.compile(optimizer=op_t,loss=mse,metrics=[categorical_crossentropy])
优化方法为adam,损失函数为均方误差法,评估方法为交叉熵损失函数,一般模型编译时,设置这三个参数就够了,我们这个例子本来就比较简单,所以值给定这三个就行
-
model.fit(train_data, target,batch_size=40,epochs=50)
模型训练,和skleearn的写法差不多,先传训练数据,再给训练数据集的结果,然后确定训练的次数,理论上来说,训练的次数越多,结果会越好,但是次数越多,多电脑的消耗就会越大,因此这里自己可以更具情况来决定,最后时批量梯度下降时采取的样本数量,默认为1,可以修改,我们给定的是50
-
-
最后预测,评估模型的优劣性
- 由于最后的输出结果是概率的形式,并不是我们想要的数字形式,所以这里需要转换。利用argmax()方法来获取条array的最大值下标,这就是我们想要的结果。存放在同一组的list中即可
- 评估方法cohen_kappa和f1差不多,不过一个是可以用于多分类问题,一个是用于二分类问题。cohen_kappa_score的得分越接近-1,模型识别的结果就和正式结果相差越大;越接近0,模型几乎是随机识别;越接近1,则模型识别越准确。当然,也可以用正确率来当模型的评估结果。
代码
from keras.models import Sequential
from keras.layers import Deconv2D,MaxPool2D,Dense,Flatten
from keras.optimizers import Adam
from keras.metrics import mse
from keras.losses import categorical_crossentropy
from keras.utils import to_categorical
from keras.activations import relu,softmax
import numpy as np
from sklearn.datasets import load_digits
from sklearn.metrics import cohen_kappa_score
ld = load_digits()
# print(ld.images.shape)
train_data = ld.images.reshape(1797, 8, 8, 1).astype(np.float32)
target = to_categorical(ld.target)
# 搭建网络
model = Sequential()
model.add(Deconv2D(10, (3, 3), input_shape=(8, 8, 1), strides=(1, 1), padding='SAME', activation=relu))
model.add(MaxPool2D((2,2)))
model.add(Deconv2D(8, (3, 3), padding='SAME', activation=relu))
model.add(MaxPool2D((2,2)))
model.add(Flatten())
model.add(Dense(96, activation=relu))
model.add(Dense(10, activation=softmax))
# model.summary()
op_t = Adam(lr=0.01)
model.compile(optimizer=op_t,loss=mse,metrics=[categorical_crossentropy])
model.fit(train_data[:len(train_data)-500], target[:len(target)-500],batch_size=40,epochs=50)
pre = model.predict(train_data[len(train_data)-500:])
result_list = []
for value in pre:
index = value.argmax()
result_list.append(index)
print('正确结果', ld.target[len(ld.target)-500:])
print('预测结果', np.array(result_list))
print('kappa::',cohen_kappa_score(ld.target[len(ld.target)-500:], result_list))
代码是将数据集分为训练集和测试集,手动分割,取数据集的后500个作为测试集,当然也可以用train_test_split来分割,最有kappa得分有0.94左右,准确率接近96%。整体来说还行。
当然如果有需要,例如验证码识别或者数字的图片规格更大,可以修改网络参数,增加网络的深度。