基于Python的卷积神经网络和特征提取
基于Python的卷积神经网络和特征提取
作者:Christian S.Peron
译者:刘帝伟
摘要:本文展示了如何基于nolearn使用一些卷积层和池化层来建立一个简单的ConvNet体系结构,以及如何使用ConvNet去训练一个特征提取器,然后在使用如SVM、Logistic回归等不同的模型之前使用它来进行特征提取。
卷积神经网络(ConvNets)是受生物启发的MLPs(多层感知器),它们有着不同类别的层,并且每层的工作方式与普通的MLP层也有所差异。如果你对ConvNets感兴趣,这里有个很好的教程CS231n – Convolutional Neural Newtorks for Visual Recognition。CNNs的体系结构如下所示:

常规的神经网络(来自CS231n网站)

ConvNet网络体系结构(来自CS231n网站)
如你所见,ConvNets工作时伴随着3D卷积并且在不断转变着这些3D卷积。我在这篇文章中不会再重复整个CS231n的教程,所以如果你真的感兴趣,请在继续阅读之前先花点时间去学习一下。
Lasagne 和 nolearn
Lasagne和nolearn是我最喜欢使用的深度学习Python包。Lasagne是基于Theano的,所以GPU的加速将大有不同,并且其对神经网络创建的声明方法也很有帮助。nolearn库是一个神经网络软件包实用程序集(包含Lasagne),它在神经网络体系结构的创建过程上、各层的检验等都能够给我们很大的帮助。
在这篇文章中我要展示的是,如何使用一些卷积层和池化层来建立一个简单的ConvNet体系结构。我还将向你展示如何使用ConvNet去训练一个特征提取器,在使用如SVM、Logistic回归等不同的模型之前使用它来进行特征提取。大多数人使用的是预训练ConvNet模型,然后删除最后一个输出层,接着从ImageNets数据集上训练的ConvNets网络提取特征。这通常被称为是迁移学习,因为对于不同的问题你可以使用来自其它的ConvNets层,由于ConvNets的第一层过滤器被当做是一个边缘探测器,所以它们可以用来作为其它问题的普通特征探测器。
加载MNIST数据集
MNIST数据集是用于数字识别最传统的数据集之一。我们使用的是一个面向Python的版本,但先让我们导入需要使用的包:
[py] view plaincopy
- import matplotlib
- import matplotlib.pyplot as plt
- import matplotlib.cm as cm
- from urllib import urlretrieve
- import cPickle as pickle
- import os
- import gzip
- import numpy as np
- import theano
- import lasagne
- from lasagne import layers
- from lasagne.updates import nesterov_momentum
- from nolearn.lasagne import NeuralNet
- from nolearn.lasagne import visualize
- from sklearn.metrics import classification_report
- from sklearn.metrics import confusion_matrix
正如你所看到的,我们导入了用于绘图的matplotlib包,一些用于下载MNIST数据集的原生Python模块,numpy, theano,lasagne,nolearn 以及 scikit-learn库中用于模型评估的一些函数。
然后,我们定义一个加载MNIST数据集的函数(这个功能与Lasagne教程上使用的非常相似)
[py] view plaincopy
- def load_dataset():
- url = 'http://deeplearning.net/data/mnist/mnist.pkl.gz'
- filename = 'mnist.pkl.gz'
- if not os.path.exists(filename):
- print("Downloading MNIST dataset...")
- urlretrieve(url, filename)
- with gzip.open(filename, 'rb') as f:
- data = pickle.load(f)
- X_train, y_train = data[0]
- X_val, y_val = data[1]
- X_test, y_test = data[2]
- X_train = X_train.reshape((-1, 1, 28, 28))
- X_val = X_val.reshape((-1, 1, 28, 28))
- X_test = X_test.reshape((-1, 1, 28, 28))
- y_train = y_train.astype(np.uint8)
- y_val = y_val.astype(np.uint8)
- y_test = y_test.astype(np.uint8)
- return X_train, y_train, X_val, y_val, X_test, y_test
正如你看到的,我们正在下载处理过的MNIST数据集,接着把它拆分为三个不同的数据集,分别是:训练集、验证集和测试集。然后重置图像内容,为之后的Lasagne输入层做准备,与此同时,由于GPU/theano数据类型的限制,我们还把numpy的数据类型转换成了uint8。
随后,我们准备加载MNIST数据集并检验它:
[py] view plaincopy
- X_train, y_train, X_val, y_val, X_test, y_test = load_dataset()
- plt.imshow(X_train[0][0], cmap=cm.binary)
这个代码将输出下面的图像(我用的是IPython Notebook)

一个MNIST数据集的数字实例(该实例是5)
ConvNet体系结构与训练
现在,定义我们的ConvNet体系结构,然后使用单GPU/CPU来训练它(我有一个非常廉价的GPU,但它很有用)
[py] view plaincopy
- net1 = NeuralNet(
- layers=[('input', layers.InputLayer),
- ('conv2d1', layers.Conv2DLayer),
- ('maxpool1', layers.MaxPool2DLayer),
- ('conv2d2', layers.Conv2DLayer),
- ('maxpool2', layers.MaxPool2DLayer),
- ('dropout1', layers.DropoutLayer),
- ('dense', layers.DenseLayer),
- ('dropout2', layers.DropoutLayer),
- ('output', layers.DenseLayer),
- ],
- # input layer
- input_shape=(None, 1, 28, 28),
- # layer conv2d1
- conv2d1_num_filters=32,
- conv2d1_filter_size=(5, 5),
- conv2d1_nonlinearity=lasagne.nonlinearities.rectify,
- conv2d1_W=lasagne.init.GlorotUniform(),
- # layer maxpool1
- maxpool1_pool_size=(2, 2),
- # layer conv2d2
- conv2d2_num_filters=32,
- conv2d2_filter_size=(5, 5),
- conv2d2_nonlinearity=lasagne.nonlinearities.rectify,
- # layer maxpool2
- maxpool2_pool_size=(2, 2),
- # dropout1
- dropout1_p=0.5,
- # dense
- dense_num_units=256,
- dense_nonlinearity=lasagne.nonlinearities.rectify,
- # dropout2
- dropout2_p=0.5,
- # output
- output_nonlinearity=lasagne.nonlinearities.softmax,
- output_num_units=10,
- # optimization method params
- update=nesterov_momentum,
- update_learning_rate=0.01,
- update_momentum=0.9,
- max_epochs=10,
- verbose=1,
- )
- # Train the network
- nn = net1.fit(X_train, y_train)
如你所视,在layers的参数中,我们定义了一个有层名称/类型的元组字典,然后定义了这些层的参数。在这里,我们的体系结构使用的是两个卷积层,两个池化层,一个全连接层(稠密层,dense layer)和一个输出层。在一些层之间也会有dropout层,dropout层是一个正则化矩阵,随机的设置输入值为零来避免过拟合(见下图)。

Dropout层效果(来自CS231n网站)
调用训练方法后,nolearn包将会显示学习过程的状态,我的机器使用的是低端的的GPU,得到的结果如下:
[py] view plaincopy
- # Neural Network with 160362 learnable parameters
- ## Layer information
- # name size
- --- -------- --------
- 0 input 1x28x28
- 1 conv2d1 32x24x24
- 2 maxpool1 32x12x12
- 3 conv2d2 32x8x8
- 4 maxpool2 32x4x4
- 5 dropout1 32x4x4
- 6 dense 256
- 7 dropout2 256
- 8 output 10
- epoch train loss valid loss train/val valid acc dur
- ------- ------------ ------------ ----------- --------- ---
- 1 0.85204 0.16707 5.09977 0.95174 33.71s
- 2 0.27571 0.10732 2.56896 0.96825 33.34s
- 3 0.20262 0.08567 2.36524 0.97488 33.51s
- 4 0.16551 0.07695 2.15081 0.97705 33.50s
- 5 0.14173 0.06803 2.08322 0.98061 34.38s
- 6 0.12519 0.06067 2.06352 0.98239 34.02s
- 7 0.11077 0.05532 2.00254 0.98427 33.78s
- 8 0.10497 0.05771 1.81898 0.98248 34.17s
- 9 0.09881 0.05159 1.91509 0.98407 33.80s
- 10 0.09264 0.04958 1.86864 0.98526 33.40s
正如你看到的,最后一次的精度可以达到0.98526,是这10个单元训练中的一个相当不错的性能。
预测和混淆矩阵
现在,我们使用这个模型来预测整个测试集:
[py] view plaincopy
- preds = net1.predict(X_test)
我们还可以绘制一个混淆矩阵来检查神经网络的分类性能:
[py] view plaincopy
- cm = confusion_matrix(y_test, preds)
- plt.matshow(cm)
- plt.title('Confusion matrix')
- plt.colorbar()
- plt.ylabel('True label')
- plt.xlabel('Predicted label')
- plt.show()
上面的代码将绘制下面的混淆矩阵:

混淆矩阵
如你所视,对角线上的分类更密集,表明我们的分类器有一个良好的性能。
过滤器的可视化
我们还可以从第一个卷积层中可视化32个过滤器:
[py] view plaincopy
- visualize.plot_conv_weights(net1.layers_['conv2d1'])
上面的代码将绘制下面的过滤器:

第一层的5x5x32过滤器
如你所视,nolearn的plot_conv_weights函数在我们指定的层中绘制出了所有的过滤器。
Theano层的功能和特征提取
现在可以创建theano编译的函数了,它将前馈输入数据输送到结构体系中,甚至是你感兴趣的某一层中。接着,我会得到输出层的函数和输出层前面的稠密层函数。
[py] view plaincopy
- dense_layer = layers.get_output(net1.layers_['dense'], deterministic=True)
- output_layer = layers.get_output(net1.layers_['output'], deterministic=True)
- input_var = net1.layers_['input'].input_var
- f_output = theano.function([input_var], output_layer)
- f_dense = theano.function([input_var], dense_layer)
如你所视,我们现在有两个theano函数,分别是f_output和f_dense(用于输出层和稠密层)。请注意,在这里为了得到这些层,我们使用了一个额外的叫做“deterministic”的参数,这是为了避免dropout层影响我们的前馈操作。
现在,我们可以把实例转换为输入格式,然后输入到theano函数输出层中:
[py] view plaincopy
- instance = X_test[0][None, :, :]
- %timeit -n 500 f_output(instance)
- 500 loops, best of 3: 858 µs per loop
如你所视,f_output函数平均需要858µs。我们同样可以为这个实例绘制输出层激活值结果:
[py] view plaincopy
- pred = f_output(instance)
- N = pred.shape[1]
- plt.bar(range(N), pred.ravel())
上面的代码将绘制出下面的图:

输出层激活值
正如你所看到的,数字被认为是7。事实是为任何网络层创建theano函数都是非常有用的,因为你可以创建一个函数(像我们以前一样)得到稠密层(输出层前一个)的激活值,然后你可以使用这些激活值作为特征,并且使用你的神经网络作为特征提取器而不是分类器。现在,让我们为稠密层绘制256个激活单元:
[py] view plaincopy
- pred = f_dense(instance)
- N = pred.shape[1]
- plt.bar(range(N), pred.ravel())
上面的代码将绘制下面的图:

稠密层激活值
现在,你可以使用输出的这256个激活值作为线性分类器如Logistic回归或支持向量机的特征了。
作者简介:Christian S.Peron,遗传算法框架Pyevolve(基于Python编写的)的作者,现任惠普软件设计师。可通过christian dot perone at gmail dot com 联系。
基于Python的卷积神经网络和特征提取的更多相关文章
- 基于 SoC 的卷积神经网络车牌识别系统设计(0)摘要
NOTES:现如今,芯片行业无比火热啊,无论是前景还是钱景,国家芯片战略的发布,公司四五十万的年薪,着实令人非常的向往,为了支持芯片设计者,集成了工作.科研.竞赛于一体的<基于 SoC 的卷积 ...
- 深度学习基础-基于Numpy的卷积神经网络(CNN)实现
本文是深度学习入门: 基于Python的实现.神经网络与深度学习(NNDL)以及动手学深度学习的读书笔记.本文将介绍基于Numpy的卷积神经网络(Convolutional Networks,CNN) ...
- 基于 SoC 的卷积神经网络车牌识别系统设计(1)概述
NOTES: 这是第三届全国大学生集成电路创新创业大赛 - Arm 杯 - 片上系统设计挑战赛(本人指导的一个比赛).主要划分为以下的 Top5 重点.难点.亮点.热点以及创新点:1.通过 Arm C ...
- visualization of filters keras 基于Keras的卷积神经网络(CNN)可视化
https://adeshpande3.github.io/adeshpande3.github.io/ https://blog.csdn.net/weiwei9363/article/detail ...
- 【python实现卷积神经网络】开始训练
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- Python CNN卷积神经网络代码实现
# -*- coding: utf-8 -*- """ Created on Wed Nov 21 17:32:28 2018 @author: zhen "& ...
- 【python实现卷积神经网络】卷积层Conv2D反向传播过程
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- 【python实现卷积神经网络】激活函数的实现(sigmoid、softmax、tanh、relu、leakyrelu、elu、selu、softplus)
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- 【python实现卷积神经网络】损失函数的定义(均方误差损失、交叉熵损失)
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
随机推荐
- BZOJ 1086 王室联邦 | BFS
BZOJ 1086 王室联邦 题意 把一棵树分块,每块大小在[B, 3B]之间(B由输入数据给出),每个块需要对应一个核心点,核心点可以在块内,这个点要满足块内每个点到核心点的路径上的点都属于这个块( ...
- Redux架构
深入Redux架构 阅读目录 关于redux API 中间件与异步操作 异步操作的基本思路 React-Redux的用法 回到顶部 关于redux 之前写了一篇通过一个demo了解Redux,但对 ...
- 【LOJ】#2265. 「CTSC2017」最长上升子序列
题解 点了一个新技能叫杨表(事实上集训的时候听过,但是一直不会 这道题就是让我们找到k个不上升子序列,要求长度加和最大 我们用杨表去维护,但是由于杨表的行数可能是n的,复杂度会炸 我们只维护前\(\s ...
- 【noip模拟赛2】牛跳
描述 John的奶牛们计划要跳到月亮上去.它们请魔法师配制了P(1 <= P <=150,000)种药水,这些药水必需安原来的先后次序使用,但中间可以跳过一些药水不吃.每种药水有一个“强度 ...
- 009 jquery过滤选择器-----------(表单对象属性过滤选择器 与 表单选择器)
1.表单对象属性选择器 2.程序 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"& ...
- vue.js阻止事件冒泡和默认事件
首先我们来看原生JS取消事件冒泡方法: e.stopPropagation(); //非IE浏览器window.event.cancelBubble = true; //IE浏览器 原生JS阻止默认事 ...
- CF1010D Mars rover [位运算,DP]
题目传送门 Mars Rover 格式难调,题面就不放了. 分析: 今天考试的时候考了这道题目的加强版,所以来做. 其实也并不难,我们建立好树形结构以后先把初始权值全部求出,然后就得到了根节点的初始值 ...
- 001.hadoop及hbase部署
一 环境准备 1.1 相关环境 系统:CentOS 7 #CentOS 6.x系列也可参考,转换相关命令即可. hadoop包:hadoop-2.7.0.tar.gz #下载官方地址:http://w ...
- React Hooks新特性学习随笔
React Hooks 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 前言 本篇主要以讲几个常用的api为主. 1.u ...
- [HDU4123]Bob’s Race
题目大意:给定一棵$n$个点并且有边权的树,每个点的权值为该点能走的最远长度,并输入$m$个询问,每次询问最多有多少个编号连续的点,他们的最大最小点权差小于等于$Q$. 思路:两趟DP(DFS)求出每 ...