基于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

  1. import matplotlib
  2. import matplotlib.pyplot as plt
  3. import matplotlib.cm as cm
  4. from urllib import urlretrieve
  5. import cPickle as pickle
  6. import os
  7. import gzip
  8. import numpy as np
  9. import theano
  10. import lasagne
  11. from lasagne import layers
  12. from lasagne.updates import nesterov_momentum
  13. from nolearn.lasagne import NeuralNet
  14. from nolearn.lasagne import visualize
  15. from sklearn.metrics import classification_report
  16. from sklearn.metrics import confusion_matrix

正如你所看到的,我们导入了用于绘图的matplotlib包,一些用于下载MNIST数据集的原生Python模块,numpy, theano,lasagne,nolearn 以及 scikit-learn库中用于模型评估的一些函数。

然后,我们定义一个加载MNIST数据集的函数(这个功能与Lasagne教程上使用的非常相似)

[py] view plaincopy

  1. def load_dataset():
  2. url = 'http://deeplearning.net/data/mnist/mnist.pkl.gz'
  3. filename = 'mnist.pkl.gz'
  4. if not os.path.exists(filename):
  5. print("Downloading MNIST dataset...")
  6. urlretrieve(url, filename)
  7. with gzip.open(filename, 'rb') as f:
  8. data = pickle.load(f)
  9. X_train, y_train = data[0]
  10. X_val, y_val = data[1]
  11. X_test, y_test = data[2]
  12. X_train = X_train.reshape((-1, 1, 28, 28))
  13. X_val = X_val.reshape((-1, 1, 28, 28))
  14. X_test = X_test.reshape((-1, 1, 28, 28))
  15. y_train = y_train.astype(np.uint8)
  16. y_val = y_val.astype(np.uint8)
  17. y_test = y_test.astype(np.uint8)
  18. return X_train, y_train, X_val, y_val, X_test, y_test

正如你看到的,我们正在下载处理过的MNIST数据集,接着把它拆分为三个不同的数据集,分别是:训练集、验证集和测试集。然后重置图像内容,为之后的Lasagne输入层做准备,与此同时,由于GPU/theano数据类型的限制,我们还把numpy的数据类型转换成了uint8。

随后,我们准备加载MNIST数据集并检验它:

[py] view plaincopy

  1. X_train, y_train, X_val, y_val, X_test, y_test = load_dataset()
  2. plt.imshow(X_train[0][0], cmap=cm.binary)

这个代码将输出下面的图像(我用的是IPython Notebook)

一个MNIST数据集的数字实例(该实例是5)

ConvNet体系结构与训练

现在,定义我们的ConvNet体系结构,然后使用单GPU/CPU来训练它(我有一个非常廉价的GPU,但它很有用)

[py] view plaincopy

  1. net1 = NeuralNet(
  2. layers=[('input', layers.InputLayer),
  3. ('conv2d1', layers.Conv2DLayer),
  4. ('maxpool1', layers.MaxPool2DLayer),
  5. ('conv2d2', layers.Conv2DLayer),
  6. ('maxpool2', layers.MaxPool2DLayer),
  7. ('dropout1', layers.DropoutLayer),
  8. ('dense', layers.DenseLayer),
  9. ('dropout2', layers.DropoutLayer),
  10. ('output', layers.DenseLayer),
  11. ],
  12. # input layer
  13. input_shape=(None, 1, 28, 28),
  14. # layer conv2d1
  15. conv2d1_num_filters=32,
  16. conv2d1_filter_size=(5, 5),
  17. conv2d1_nonlinearity=lasagne.nonlinearities.rectify,
  18. conv2d1_W=lasagne.init.GlorotUniform(),
  19. # layer maxpool1
  20. maxpool1_pool_size=(2, 2),
  21. # layer conv2d2
  22. conv2d2_num_filters=32,
  23. conv2d2_filter_size=(5, 5),
  24. conv2d2_nonlinearity=lasagne.nonlinearities.rectify,
  25. # layer maxpool2
  26. maxpool2_pool_size=(2, 2),
  27. # dropout1
  28. dropout1_p=0.5,
  29. # dense
  30. dense_num_units=256,
  31. dense_nonlinearity=lasagne.nonlinearities.rectify,
  32. # dropout2
  33. dropout2_p=0.5,
  34. # output
  35. output_nonlinearity=lasagne.nonlinearities.softmax,
  36. output_num_units=10,
  37. # optimization method params
  38. update=nesterov_momentum,
  39. update_learning_rate=0.01,
  40. update_momentum=0.9,
  41. max_epochs=10,
  42. verbose=1,
  43. )
  44. # Train the network
  45. nn = net1.fit(X_train, y_train)

如你所视,在layers的参数中,我们定义了一个有层名称/类型的元组字典,然后定义了这些层的参数。在这里,我们的体系结构使用的是两个卷积层,两个池化层,一个全连接层(稠密层,dense layer)和一个输出层。在一些层之间也会有dropout层,dropout层是一个正则化矩阵,随机的设置输入值为零来避免过拟合(见下图)。

Dropout层效果(来自CS231n网站)

调用训练方法后,nolearn包将会显示学习过程的状态,我的机器使用的是低端的的GPU,得到的结果如下:

[py] view plaincopy

  1. # Neural Network with 160362 learnable parameters
  2. ## Layer information
  3. # name size
  4. --- -------- --------
  5. 0 input 1x28x28
  6. 1 conv2d1 32x24x24
  7. 2 maxpool1 32x12x12
  8. 3 conv2d2 32x8x8
  9. 4 maxpool2 32x4x4
  10. 5 dropout1 32x4x4
  11. 6 dense 256
  12. 7 dropout2 256
  13. 8 output 10
  14. epoch train loss valid loss train/val valid acc dur
  15. ------- ------------ ------------ ----------- --------- ---
  16. 1 0.85204 0.16707 5.09977 0.95174 33.71s
  17. 2 0.27571 0.10732 2.56896 0.96825 33.34s
  18. 3 0.20262 0.08567 2.36524 0.97488 33.51s
  19. 4 0.16551 0.07695 2.15081 0.97705 33.50s
  20. 5 0.14173 0.06803 2.08322 0.98061 34.38s
  21. 6 0.12519 0.06067 2.06352 0.98239 34.02s
  22. 7 0.11077 0.05532 2.00254 0.98427 33.78s
  23. 8 0.10497 0.05771 1.81898 0.98248 34.17s
  24. 9 0.09881 0.05159 1.91509 0.98407 33.80s
  25. 10 0.09264 0.04958 1.86864 0.98526 33.40s

正如你看到的,最后一次的精度可以达到0.98526,是这10个单元训练中的一个相当不错的性能。

预测和混淆矩阵

现在,我们使用这个模型来预测整个测试集:

[py] view plaincopy

  1. preds = net1.predict(X_test)

我们还可以绘制一个混淆矩阵来检查神经网络的分类性能:

[py] view plaincopy

  1. cm = confusion_matrix(y_test, preds)
  2. plt.matshow(cm)
  3. plt.title('Confusion matrix')
  4. plt.colorbar()
  5. plt.ylabel('True label')
  6. plt.xlabel('Predicted label')
  7. plt.show()

上面的代码将绘制下面的混淆矩阵:

混淆矩阵

如你所视,对角线上的分类更密集,表明我们的分类器有一个良好的性能。

过滤器的可视化

我们还可以从第一个卷积层中可视化32个过滤器:

[py] view plaincopy

  1. visualize.plot_conv_weights(net1.layers_['conv2d1'])

上面的代码将绘制下面的过滤器:

第一层的5x5x32过滤器

如你所视,nolearn的plot_conv_weights函数在我们指定的层中绘制出了所有的过滤器。

Theano层的功能和特征提取

现在可以创建theano编译的函数了,它将前馈输入数据输送到结构体系中,甚至是你感兴趣的某一层中。接着,我会得到输出层的函数和输出层前面的稠密层函数。

[py] view plaincopy

  1. dense_layer = layers.get_output(net1.layers_['dense'], deterministic=True)
  2. output_layer = layers.get_output(net1.layers_['output'], deterministic=True)
  3. input_var = net1.layers_['input'].input_var
  4. f_output = theano.function([input_var], output_layer)
  5. f_dense = theano.function([input_var], dense_layer)

如你所视,我们现在有两个theano函数,分别是f_output和f_dense(用于输出层和稠密层)。请注意,在这里为了得到这些层,我们使用了一个额外的叫做“deterministic”的参数,这是为了避免dropout层影响我们的前馈操作。

现在,我们可以把实例转换为输入格式,然后输入到theano函数输出层中:

[py] view plaincopy

  1. instance = X_test[0][None, :, :]
  2. %timeit -n 500 f_output(instance)
  3. 500 loops, best of 3: 858 µs per loop

如你所视,f_output函数平均需要858µs。我们同样可以为这个实例绘制输出层激活值结果:

[py] view plaincopy

  1. pred = f_output(instance)
  2. N = pred.shape[1]
  3. plt.bar(range(N), pred.ravel())

上面的代码将绘制出下面的图:

输出层激活值

正如你所看到的,数字被认为是7。事实是为任何网络层创建theano函数都是非常有用的,因为你可以创建一个函数(像我们以前一样)得到稠密层(输出层前一个)的激活值,然后你可以使用这些激活值作为特征,并且使用你的神经网络作为特征提取器而不是分类器。现在,让我们为稠密层绘制256个激活单元:

[py] view plaincopy

  1. pred = f_dense(instance)
  2. N = pred.shape[1]
  3. plt.bar(range(N), pred.ravel())

上面的代码将绘制下面的图:

稠密层激活值

现在,你可以使用输出的这256个激活值作为线性分类器如Logistic回归或支持向量机的特征了。

作者简介:Christian S.Peron,遗传算法框架Pyevolve(基于Python编写的)的作者,现任惠普软件设计师。可通过christian dot perone at gmail dot com 联系。

基于Python的卷积神经网络和特征提取的更多相关文章

  1. 基于 SoC 的卷积神经网络车牌识别系统设计(0)摘要

    ​NOTES:现如今,芯片行业无比火热啊,无论是前景还是钱景,国家芯片战略的发布,公司四五十万的年薪,着实令人非常的向往,为了支持芯片设计者,集成了工作.科研.竞赛于一体的<基于 SoC 的卷积 ...

  2. 深度学习基础-基于Numpy的卷积神经网络(CNN)实现

    本文是深度学习入门: 基于Python的实现.神经网络与深度学习(NNDL)以及动手学深度学习的读书笔记.本文将介绍基于Numpy的卷积神经网络(Convolutional Networks,CNN) ...

  3. 基于 SoC 的卷积神经网络车牌识别系统设计(1)概述

    NOTES: 这是第三届全国大学生集成电路创新创业大赛 - Arm 杯 - 片上系统设计挑战赛(本人指导的一个比赛).主要划分为以下的 Top5 重点.难点.亮点.热点以及创新点:1.通过 Arm C ...

  4. visualization of filters keras 基于Keras的卷积神经网络(CNN)可视化

    https://adeshpande3.github.io/adeshpande3.github.io/ https://blog.csdn.net/weiwei9363/article/detail ...

  5. 【python实现卷积神经网络】开始训练

    代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...

  6. Python CNN卷积神经网络代码实现

    # -*- coding: utf-8 -*- """ Created on Wed Nov 21 17:32:28 2018 @author: zhen "& ...

  7. 【python实现卷积神经网络】卷积层Conv2D反向传播过程

    代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...

  8. 【python实现卷积神经网络】激活函数的实现(sigmoid、softmax、tanh、relu、leakyrelu、elu、selu、softplus)

    代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...

  9. 【python实现卷积神经网络】损失函数的定义(均方误差损失、交叉熵损失)

    代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...

随机推荐

  1. Java第三阶段学习(六、多线程)

    一.进程和线程的区别: 进程:指正在运行的程序,当一个程序进入内存运行,就变成一个进程. 线程:线程是进程的一个执行单元. 总结:一个程序运行后至少会有一个进程,一个进程可以有多个线程. 多线程:多线 ...

  2. 【AtCoder】ARC099题解

    C - Minimization 每次操作必然包含一个1 枚举第一次操作的位置计算两边即可 代码 #include <bits/stdc++.h> #define fi first #de ...

  3. 【Java】 子字符串的比较(substring的==与equal()使用)

    public class Test { public static void main(String[] args) { String str1="good"; System.ou ...

  4. centos 6.x x86 源码安装git-2.3.0

    (1) 添加rpmforge源 wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.i686 ...

  5. SQL数据库数据类型详解

    数据类型 类型 描 述 bit 整型 bit 数据类型是整型,其值只能是0.1或空值.这种数据类型用于存储只有两种可能值的数据,如Yes 或No.True 或Fa lse .On 或Off int 整 ...

  6. P2858 [USACO06FEB]奶牛零食Treats for the Cows

    P2858 [USACO06FEB]奶牛零食Treats for the Cows区间dp,级像矩阵取数, f[i][i+l]=max(f[i+1][i+l]+a[i]*(m-l),f[i][i+l- ...

  7. 机器学习入门 一、理解机器学习+简单感知机(JAVA实现)

    首先先来讲讲闲话 如果让你现在去搞机器学习,你会去吗?不会的话是因为你对这方面不感兴趣,还是因为你觉得这东西太难了,自己肯定学不来?如果你觉的太难了,很好,相信看完这篇文章,你就会有胆量踏入机器学习这 ...

  8. canvas入门级小游戏《开关灯》思路讲解

    游戏很简单,10行10列布局,每行每列各10盏灯,游戏初始化时随机点亮其中一些灯,点击某盏灯,其上下左右的灯及本身状态反转,如果点击前是灭着的,点击后即点亮,将所有灯全部点亮才算过关.游戏试玩: 下面 ...

  9. BZOJ.2938.[POI2000]病毒(AC自动机)

    题目链接 \(Description\) 给n个模式串,问是否存在长度无限的主串,使得任何一个模式串都没有在主串中出现. \(Solution\) 先建AC自动机. 假设我们有了一个无限长的安全代码, ...

  10. Codeforces.643E.Bear and Destroying Subtrees(DP 期望)

    题目链接 \(Description\) 有一棵树.Limak可以攻击树上的某棵子树,然后这棵子树上的每条边有\(\frac{1}{2}\)的概率消失.定义 若攻击以\(x\)为根的子树,高度\(ht ...