一、为何要用Keras
如今在深度学习大火的时候,第三方工具也层出不穷,比较出名的有Tensorflow,Caffe,Theano,MXNet,在如此多的第三方框架中频繁的更换无疑是很低效的,只要你能够好好掌握其中一个框架,熟悉其原理,那么之后因为各种要求你想要更换框架也是很容易的。
那么sherlock用的是哪个框架呢?sherlock使用的是Google的开源框架Tensorflow,因为Google开源了tensorflow之后其社区非常活跃,而且版本更新也非常稳定,所以我就选择了这个框架。对于框架之争,在知乎上已经有很多人在撕逼了,这个就好比哪种编程语言好这个问题一样。对于我们来讲,选择一个稳定的框架,好好的学习deep learning才是重中之重,对于哪种框架更好的问题,我们学好之后自然有自己的见解,所以前期切忌在刷知乎听完大神撕逼之后频繁更换框架。
对于Tensorflow的安装,以及CPU和GPU版本,各种系统的安装网上已经有很多人详细的写过攻略了,可以自己去网上搜一搜,很容易就可以安装成功。
选择了Tensorflow之后,我们就可以愉快的开始我们的深度学习之旅了。去Tensorflow的中文社区,可以看到有一些新手教程,网上也有很多学习材料,推荐看看stanford大学cs224d的课件,http://cs224d.stanford.edu/lectures/CS224d-Lecture7.pdf, 很详细的介绍了tensorflow。然后你就可以写tensorflow的程序了。虽然说tensorflow已经是一个封装好的框架,但是你发现你写一个简单的神经网络也需要很多行才能够写完,这个时候,就有很多的第三方插架来帮助你写网络,也就是说你用tensorflow要写10行,第三方插架帮你封装了一个函数,就是把这10行集合在这个函数里面,那么你用1行,传入相同的参数,就能够达到10行相同的效果,如此简便并且节约时间,可以帮助很快的实现我们的想法。
Keras Documentation就是Keras的官方文档,里面可以查阅所有的函数,并且可以在github上看他的开源代码,非常方便。安装也很简单,打开终端,输入pip install keras 就可以等待安装了。
下面就给一个简单的例子,来看一看Keras到底有多简单。
- from keras.models import Sequential
- model = Sequential()
引入sequential,这个就是一个空的网络结构,并且这个结构是一个顺序的序列,所以叫Sequential,Keras里面还有一些其他的网络结构。
- from keras.layers import Dense, Activation
- model.add(Dense(units=64, input_dim=100))
- model.add(Activation('relu'))
- model.add(Dense(units=10))
- model.add(Activation('softmax'))
可以看到加入层很简单,只需要写.add,后面是要加的层的类型。
- model.compile(loss='categorical_crossentropy',
- optimizer='sgd',
- metrics=['accuracy'])
一旦你写好了网络之后,就可以用compile编译整个网络,看参数设置有没有问题
- model.compile(loss=keras.losses.categorical_crossentropy,
- optimizer=keras.optimizers.SGD(lr=0.01,momentum=0.9,nesterov=True))
你也可以自定义其中的优化函数,就像上面这样,’sgd’是Keras已经写好了一些默认参数的优化函数,你可以自己重新定义参数,得到一个优化函数。
- model.fit(x_train,y_train,epochs=5,batch_size=32)
这个就像scikit-learn一样训练模型。
- loss_and_metrics=model.evaluate(x_test,y_test,batch_size=128)
这个就是评估训练结果。
- classes=model.predict(x_test,batch_size=128)
或者是通过predict进行预测。
看了上面的代码,相信很多熟悉scikit-learn的同学都很亲切,因为确实很简便,跟scikit-learn也有着类似的语法。
二、开始学习CNN
在理解CNN之前,我们有必要先理解一下什么是神经网络,这样才能开始了解更高级的卷积神经网络。
要学习神经网络当然有很多途径,网上不少的大牛写了很多攻略,有的推崇从理论到工程完成深度学习,有的希望从工程出发发现问题,解决问题。各种各样的方式都有不同的人去尝试,攻略也是一大推,这使得不少的小白直接倒在了选择材料的路上,一直在补先修知识,待到热情结束就放弃了学习,连卷积网络都不知道是什么,大大地打击了大家的学习热情。今天,sherlock在这里给大家推荐一个学习材料,保证你能够快速入门cnn,出去装逼也能够和别人聊几句。
这个材料是什么呢,就是大名鼎鼎的standford的cs231n这门课程。CS231n Convolutional Neural Networks for Visual Recognitionstanford大学确实算是深度学习和人工智能领域非常牛逼的学校。
神经网络
废话不多说,开始学习我们的神经网络。
这是一张脑神经的图片,神经网络的发明也是由此开始的,这就是所谓的一个神经元,上面有各种接受突触,然后通过一个脑神经来接受,最后得到输出的结果。
那么由这张脑神经图能够抽象出来的神经网络是什么呢?就是下面这个神经网络模型。
这个怎么理解呢?就是输入一个向量,然后给向量的每一个元素分配一个权重,然后通过权重求和得到一个结果,然后将这个结果输入一个激活函数,得到最后的输出结果。
激活函数又是什么鬼?激活函数的出现是因为人脑的构造,人脑里面接受信息得到结果这个过程是非线性的,比如你看到一样东西,你不可能保留这个东西的全部特征,你会重点观察你感兴趣的地方,这就是非线性的,也就是说需要一个非线性变化将输入的结果变换为非线性的结果。现在常用的非线性函数就是Relu(x) = max(x, 0),就是将小于0的部分去掉,只保留大于0的部分。
这就是单元的输入和输出,将这些单元合在一起就是一个神经网络。
这就是简单的一层网络,也可以由多层网络
这里面的input layer就是所谓的单个训练集的维数,将所有的训练集输入就可以开始训练一个神经网络。
Keras实现简单的神经网络
知道了神经网络的基本结构和原理,我们就可以开始使用keras去实现一个简单的神经网络。
- import keras
- from keras.models import Sequential
- from keras.layers import Dense
- import numpy as np
导入必要的package
- x=np.array([[0,1,0],[0,0,1],[1,3,2],[3,2,1]])
- y=np.array([0,0,1,1]).T
设定输入的x和y
- simple_model=Sequential()
- simple_model.add(Dense(5,input_shape=(x.shape[1],),activation='relu',name='layer1'))
- simple_model.add(Dense(4,activation='relu',name='layer2'))
- simple_model.add(Dense(1,activation='sigmoid',name='layer3'))
输入一个三层的神经网络,中间的hidden layer的元素个数是5和4,最后一层输出一个结果
- simple_model.compile(optimizer='sgd',loss='mean_squared_error')
complie这个简单的模型
- simple_model.fit(x,y,epochs=20000)
训练20000次模型
- simple_model.predict(x[0:1])
可以预测一下第一个输入的x的结果与实际的是否相符。
上面就是一个简单三层网络的keras实现,接下来我们将正式进入Convolutional Neural Network
三、Convolutional Neural Network
前面给大家推荐了一门好课cs231n,本篇文章也是按照这个思路来的。
基本结构
首先解释一下什么是卷积,这个卷积当然不是数学上的卷积,这里的卷积其实表示的是一个三维的权重,这么解释起来可能不太理解,我们先看看卷积网络的基本结构。
通过上面的图我们清楚地了解到卷积网络和一般网络结构上面的差别,也可以理解为卷积网络是立体的,而一般的网络结构是平面的。
卷积层
了解完了基本的结构之后,我们就要了解cnn最重要的一个部分,也是最为创新的一个部分,卷积层。首先用一张图片来比较一下卷积网络到底创新在什么地方。
我们通过这个结构就可以清晰地看到卷积网络到底是怎么实现的。首先右边是传统的网络结构,在前面我们已经详细的解释过了。而左边的图片,我们首先看看图中最左边的结构,你肯定会好奇为什么是32x32x3的一块立体方块。这个32×32代表的是像素点,说白了也就是图片的大小,这个大小是你可以设置的,你可以设置为50×50,也可以是256×256,这都取决与图片的大小,那么3表示什么呢?3其实表示的是RGB的三个通道,RGB也是什么?RGB表示red,green,blue,这三种颜色的各种组合叠加可以形成各种各样的颜色,所以任何一张照片都可以用左边这种图形来表示。
那么中间这个小方块又表示什么呢?这个就是我们要重点讲的卷积。所谓的卷积,就是这种小方块,我们设置一个小方块的大小,但是这个小方块的厚度必须和左边的这个大方块的厚度是一样的,大方块每一个像素点由一个0到255的数字表示,这样我们就可以赋予小方块权重,比如我们取小方块的大小是3×3,我们要求厚度必须要和左边的大方块厚度一样,那么小方块的的大小就为3x3x3,我们就可以赋予其3x3x3个权重,然后我们就可以开始计算卷积的结果,将小方块从大方块的左上角开始,一个卷积小方块所覆盖的范围是3x3x3,然后我们将大方块中3x3x3的数字和小方块中的权重分别相乘相加,再加上一个偏差,就可以得到一个卷积的结果,可以抽象的写成Wx+b这种形式,这就是图上所显示的结果,然后我们可以设置小方块的滑动距离,每次滑动就可以形成一个卷积的计算结果,然后将整张大图片滑动覆盖之后就可以形成一层卷积的结果,我们看到图中的卷积结果是很厚的,也就是设置了很多层卷积。总结来说,每层卷积就是一个卷积核在图片上滑动求值,然后设置多个卷积核就可以形成多层的卷积层。
池化层
讲完卷积层,接下来就要讲一下池化层。为什么会有池化层的出现呢?是因为不断的做卷积,得到的中间结果会越来越厚,卷积就相当于提取图片中的特征,所以卷积层一般会设置得越来越厚,不然你就无法从前面的结果来提取更多的特征。这样就会导致中间的结果会越来越大,计算会越来越慢,所以提出了池化层。
所谓的池化层,就是将图片的大小缩小的一种处理方式。我们可以先看看下面的图片。
通过这个图片,我们可以清楚地看到池化层是怎么处理的。池化层也是需要先设置一个窗口,但是这个小窗口的厚度是1,而不再是前一层输出的结果的厚度。然后有两种处理方式,一种是取这个小窗口里面所有元素的最大值来代表这个小窗口,一种是取平均值,然后将小窗口滑动,在第二的位置再做同样的处理,上层网络输出方块的每一层做完之后就进入这个大方块的下一层做同样的操作,这个处理办法就可以让整个大方块的大小变小,可以看看上面的图片的左边。右边是一个简单的一层厚度,取最大值的例子。
实现Lenet
讲完了卷积网络的基本结构之后,你是不是已经迫不及待希望能够实现一个简单的神经网络了呢?卷积网络发展的特别迅速,最早是由Lecun提出来的,Lenet成为cnn的鼻祖,接下来他的学生Alex提出了层数更深的Alexnet,然后2013年又提出了VGGnet,有16层和19层两种,这些都只是在层数上面的加深,并没有什么其他的创新,而之后google提出了inception net在网络结构上实现了创新,提出了一种inception的机构,facebook ai 实验室又提出了resnet,残差网络,实现了150层的网络结构可训练化,这些我们之后会慢慢讲到。
接下来我们就来实现一下最简单的Lenet,使用mnist手写子体作为训练集。
- import keras
- from keras.datasets import mnist
- (x_train, y_train), (x_test,y_test) =mnist.load_data()
导入必要的库和数据集
- x_train=x_train.reshape(-1,28,28,1)
- x_test=x_test.reshape(-1,28,28,1)
- x_train=x_train/255.
- x_test=x_test/255.
- y_train=keras.utils.to_categorical(y_train)
- y_test=keras.utils.to_categorical(y_test)
处理数据,让数据的shape是(28, 28, 1),然后label做一个one-hot encoding处理,比如类别是3,那么变成[0, 0, 1 ,0, 0, 0, 0, 0, 0, 0]。
- from keras.layers import Conv2D,MaxPool2D,Dense,Flatten
- from keras.models import Sequential
- lenet=Sequential()
- lenet.add(Conv2D(6,kernel_size=3,strides=1,padding='same',input_shape=(28, 28, 1)))
- lenet.add(MaxPool2D(pool_size=2,strides=2))
- lenet.add(Conv2D(16,kernel_size=5,strides=1,padding='valid'))
- lenet.add(MaxPool2D(pool_size=2,strides=2))
- lenet.add(Flatten())
- lenet.add(Dense(120))
- lenet.add(Dense(84))
- lenet.add(Dense(10,activation='softmax'))
构建lenet
- lenet.compile('sgd',loss='categorical_crossentropy',metrics=['accuracy'])
编译
- lenet.fit(x_train,y_train,batch_size=64,epochs=50,validation_data=[x_test,y_test])
训练50次,得到结果如下
- lenet.save('myletnet.h5')
可以保存训练好的模型
总结
OK, 这就是我们写的一个超级简单的Lenet,训练50次得到的训练准确率已经达到0.9939,测试准确率达到0.9852。