1. Keras的简介
Keras是基于Theano的一个深度学习框架,它的设计参考了Torch,用Python语言编写,是一个高度模块化的神经网络库,支持GPU和CPU。使用文档网址:http://keras.io/
使用上的问题可以在github提出:https://github.com/fchollet/keras
下面简单以Mnist数据库为例,编写一个CNN网络结构。
2. Keras里的模块介绍
Optimizers顾名思义,Optimizers包含了一些优化的方法,比如最基本的随机梯度下降SGD,另外还有Adagrad、Adadelta、RMSprop、Adam,一些新的方法以后也会被不断添加进来。
keras<span class="hljs-preprocessor">.optimizers</span><span class="hljs-preprocessor">.SGD</span>(lr=<span class="hljs-number">0.01</span>, momentum=<span class="hljs-number">0.9</span>, decay=<span class="hljs-number">0.9</span>, nesterov=False)
上面的代码是SGD的使用方法,其中:
lr表示学习速率;
momentum表示动量项;
decay表示学习速率的衰减系数(每个epoch衰减一次);
nesterov的值是False或者True,表示使不使用nesterov momentum。
Objectives这是目标函数模块,keras提供了mean_squared_error,mean_absolute_error,squared_hinge,hinge,binary_crossentropy,categorical_crossentropy这几种目标函数。
这里binary_crossentropy和categorical_crossentropy也就是logloss。
Activations这是激活函数模块,keras提供了linear、sigmoid、hard_sigmoid、tanh、softplus、relu、softplus,另外softmax也放在Activations模块里(我觉得放在layers模块里更合理些)。此外,像LeakyReLU和PReLU这种比较新的激活函数,keras在keras.layers.advanced_activations模块里提供。Initializations这是参数初始化模块,在添加layer的时候调用init进行初始化。keras提供了uniform、lecun_uniform、normal、orthogonal、zero、glorot_normal、he_normal这几种。
layers模块包含了core、convolutional、recurrent、advanced_activations、normalization、embeddings这几种layer。
其中core里面包含了flatten(CNN的全连接层之前需要把二维特征图flatten成为一维的)、reshape(CNN输入时将一维的向量弄成二维的)、dense(就是隐藏层,dense是稠密的意思),还有其他的就不介绍了。convolutional层基本就是Theano的Convolution2D的封装。
Preprocessing这是预处理模块,包括序列数据的处理,文本数据的处理,图像数据的处理。重点看一下图像数据的处理,keras提供了ImageDataGenerator函数,实现data augmentation,数据集扩增,对图像做一些弹性变换,比如水平翻转,垂直翻转,旋转等。
Models这是最主要的模块,模型。上面定义了各种基本组件,model是将它们组合起来,下面通过一个实例来说明。
3.实例:用CNN分类Mnist
数据下载Mnist数据在其官网上有提供,但是不是图像格式的,因为我们通常都是直接处理图像,为了以后程序能复用,我把它弄成图像格式的,这里可以下载:http://pan.baidu.com/s/1qCdS6。其共有42000张图片。读取图片数据keras要求输入的数据格式是numpy.array类型(numpy是一个python的数值计算的库),所以需要写一个脚本来读入mnist图像,保存为一个四维的data,还有一个一维的label,代码:
<span class="hljs-comment">#coding:utf-8</span>
<span class="hljs-string">"""
Author:wepon
Source:https://github.com/wepe
file:data.py
"""</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-comment">#读取文件夹mnist下的42000张图片,图片为灰度图,所以为1通道,</span>
<span class="hljs-comment">#如果是将彩色图作为输入,则将1替换为3,图像大小28*28</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_data</span><span class="hljs-params">()</span>:</span>
data = np.empty((<span class="hljs-number">42000</span>,<span class="hljs-number">1</span>,<span class="hljs-number">28</span>,<span class="hljs-number">28</span>),dtype=<span class="hljs-string">"float32"</span>)
label = np.empty((<span class="hljs-number">42000</span>,),dtype=<span class="hljs-string">"uint8"</span>)
imgs = os.listdir(<span class="hljs-string">"./mnist"</span>)
num = len(imgs)
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(num):
img = Image.open(<span class="hljs-string">"./mnist/"</span>+imgs[i])
arr = np.asarray(img,dtype=<span class="hljs-string">"float32"</span>)
data[i,:,:,:] = arr
label[i] = int(imgs[i].split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">0</span>])
<span class="hljs-keyword">return</span> data,label
构建一个三个卷积层的CNN,代码如下:
<span class="hljs-preprocessor">#导入各种用到的模块组件</span>
from __future__ import absolute_import
from __future__ import print_function
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers.advanced_activations import PReLU
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from keras.optimizers import SGD, Adadelta, Adagrad
from keras.utils import np_utils, generic_utils
from six.moves import range
from data import load_data
<span class="hljs-preprocessor">#加载数据</span>
data, label = load_data()
print(data.shape[<span class="hljs-number">0</span>], <span class="hljs-string">' samples'</span>)
<span class="hljs-preprocessor">#label为0~9共10个类别,keras要求格式为binary class matrices,转化一下,直接调用keras提供的这个函数</span>
label = np_utils.to_categorical(label, <span class="hljs-number">10</span>)
<span class="hljs-preprocessor">###############</span>
<span class="hljs-preprocessor">#开始建立CNN模型</span>
<span class="hljs-preprocessor">###############</span>
<span class="hljs-preprocessor">#生成一个model</span>
model = Sequential()
<span class="hljs-preprocessor">#第一个卷积层,4个卷积核,每个卷积核大小5*5。1表示输入的图片的通道,灰度图为1通道。</span>
<span class="hljs-preprocessor">#border_mode可以是valid或者full,具体看这里说明:http://deeplearning.net/software/theano/library/tensor/nnet/conv.html#theano.tensor.nnet.conv.conv2d</span>
<span class="hljs-preprocessor">#激活函数用tanh</span>
<span class="hljs-preprocessor">#你还可以在model.add(Activation('tanh'))后加上dropout的技巧: model.add(Dropout(0.5))</span>
model.add(Convolution2D(<span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>, border_mode=<span class="hljs-string">'valid'</span>))
model.add(Activation(<span class="hljs-string">'tanh'</span>))
<span class="hljs-preprocessor">#第二个卷积层,8个卷积核,每个卷积核大小3*3。4表示输入的特征图个数,等于上一层的卷积核个数</span>
<span class="hljs-preprocessor">#激活函数用tanh</span>
<span class="hljs-preprocessor">#采用maxpooling,poolsize为(2,2)</span>
model.add(Convolution2D(<span class="hljs-number">8</span>,<span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, border_mode=<span class="hljs-string">'valid'</span>))
model.add(Activation(<span class="hljs-string">'tanh'</span>))
model.add(MaxPooling2D(poolsize=(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))
<span class="hljs-preprocessor">#第三个卷积层,16个卷积核,每个卷积核大小3*3</span>
<span class="hljs-preprocessor">#激活函数用tanh</span>
<span class="hljs-preprocessor">#采用maxpooling,poolsize为(2,2)</span>
model.add(Convolution2D(<span class="hljs-number">16</span>, <span class="hljs-number">8</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, border_mode=<span class="hljs-string">'valid'</span>))
model.add(Activation(<span class="hljs-string">'tanh'</span>))
model.add(MaxPooling2D(poolsize=(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))
<span class="hljs-preprocessor">#全连接层,先将前一层输出的二维特征图flatten为一维的。</span>
<span class="hljs-preprocessor">#Dense就是隐藏层。16就是上一层输出的特征图个数。4是根据每个卷积层计算出来的:(28-5+1)得到24,(24-3+1)/2得到11,(11-3+1)/2得到4</span>
<span class="hljs-preprocessor">#全连接有128个神经元节点,初始化方式为normal</span>
model.add(Flatten())
model.add(Dense(<span class="hljs-number">16</span>*<span class="hljs-number">4</span>*<span class="hljs-number">4</span>, <span class="hljs-number">128</span>, init=<span class="hljs-string">'normal'</span>))
model.add(Activation(<span class="hljs-string">'tanh'</span>))
<span class="hljs-preprocessor">#Softmax分类,输出是10类别</span>
model.add(Dense(<span class="hljs-number">128</span>, <span class="hljs-number">10</span>, init=<span class="hljs-string">'normal'</span>))
model.add(Activation(<span class="hljs-string">'softmax'</span>))
<span class="hljs-preprocessor">#############</span>
<span class="hljs-preprocessor">#开始训练模型</span>
<span class="hljs-preprocessor">##############</span>
<span class="hljs-preprocessor">#使用SGD + momentum</span>
<span class="hljs-preprocessor">#model.compile里的参数loss就是损失函数(目标函数)</span>
sgd = SGD(l2=<span class="hljs-number">0.0</span>,lr=<span class="hljs-number">0.05</span>, decay=<span class="hljs-number">1e-6</span>, momentum=<span class="hljs-number">0.9</span>, nesterov=True)
model.compile(loss=<span class="hljs-string">'categorical_crossentropy'</span>, optimizer=sgd,class_mode=<span class="hljs-string">"categorical"</span>)
<span class="hljs-preprocessor">#调用fit方法,就是一个训练过程. 训练的epoch数设为10,batch_size为100.</span>
<span class="hljs-preprocessor">#数据经过随机打乱shuffle=True。verbose=1,训练过程中输出的信息,0、1、2三种方式都可以,无关紧要。show_accuracy=True,训练时每一个epoch都输出accuracy。</span>
<span class="hljs-preprocessor">#validation_split=0.2,将20%的数据作为验证集。</span>
model.fit(data, label, batch_size=<span class="hljs-number">100</span>,nb_epoch=<span class="hljs-number">10</span>,shuffle=True,verbose=<span class="hljs-number">1</span>,show_accuracy=True,validation_split=<span class="hljs-number">0.2</span>)
<span class="hljs-preprocessor">#fit方法在达到设定的nb_epoch时结束,并且自动地保存了效果最好的model,之后你可以调用model.evaluate()方法对测试数据进行测试,</span>
<span class="hljs-preprocessor">#还有model.predict_classes,model.predict_proba等方法。</span>
代码使用与结果
在/DeepLearning Tutorials/keras_usage目录下包括data.py,cnn.py两份代码,下载Mnist数据后解压到该目录下,运行cnn.py这份文件即可。
结果如下所示,在Epoch 9达到了0.98的训练集识别率和0.97的验证集识别率:
以上总结了Keras的基本使用方法,主要包括以下三个内容:
训练CNN并保存训练好的模型。将CNN用于特征提取,用提取出来的特征训练SVM。可视化CNN卷积层后的特征图。
仍然以Mnist为例。
1. 加载数据
数据是图片格式,利用pyhton的PIL模块读取,并转为numpy.array类型。这部分的代码在data.py里。
2. 训练CNN并保存训练好的CNN模型
将上一步加载进来的数据分为训练数据(X_train,30000个样本)和验证数据(X_val,12000个样本),构建CNN模型并训练。训练过程中,每一个epoch得到的val-accuracy都不一样,我们保存达到最好的val-accuracy时的模型,利用Python的cPickle模块保持。(Keras的开发者最近在添加用hdf5保持模型的功能,我试了一下,没用成功,去github发了issue也没人回,估计还没完善,hdf5压缩率会更高,保存下来的文件会更小。)
这部分的代码在cnn.py里,运行:
python cnn<span class="hljs-preprocessor">.py</span>
在第Epoch 4得到96.45%的validation accuracy,运行完后会得到model.pkl这份文件,保存的就是96.45%对应的模型:
3.将CNN用于特征提取,用提取出来的特征训练SVM
上一步得到了一个val-accuracy为96.45%的CNN模型,在一些论文中经常会看到用CNN的全连接层的输出作为特征,然后去训练其他分类器。这里我也试了一下,用全连接层的输出作为样本的特征向量,训练SVM。SVM用的是scikit learn里的算法。
这部分代码在cnn-svm.py,运行:
1
python cnn-svm<span class="hljs-preprocessor">.py</spa
4.可视化CNN卷积层后的特征图
将卷积层和全连接层后的特征图、特征向量以图片形式展示出来,用到matplotlib这个库。这部分代码在get_feature_map.py里。运行:
python get_feature_map<span class="hljs-preprocessor">.py</span>