上节我们谈到了三层自编码网络的特征学习,并将其学习到的特征用于实际分类实验中,并与传统的PCA特征提取方法作了实验对比,发现在三层网络下自编码学习的特征能更好用于分类,达到分类准确率更高,本节我们将再一次探索自编码网络的特征学习问题,并且是深层网络的自编码学习。

首先来探索四层网络的自编码问题,还是以手写体为例,网络抽象成下面这样子:

深度学习中的编码器 深度自编码网络_深度网络

假设我们第一层隐含层用200个神经元,第二层K个(可以固定也可以调),那么问题来了,对于这个多层网络怎么去自编码呢?这里就要引入很重要的一个思想,深度网络自编码问题的解决方案了:逐层编码思想。那么什么是逐层编码?

我们知道,正常的神经网络也可以是多层的,但是多层的神经网络如果一起训练会有一个问题,那就是误差弥散问题,因为网络的权值更新依靠的是误差反向传播来的,那么越靠近尾端,这个时候误差还有点作用,可以使得靠近尾端的权值更新的比较大,越往前面的网络,由于每一层往前以后,误差的作用就会消退一点,那么前面的网络权值基本上就更新不动了,这也就是误差在反向传播的时候会存在弥散作用。那么为了解决这一问题才诞生了逐层编码。

比如上面那个两层隐含层的网络,我可以把它拆分成两个部分,每一部分组成一个3层网络的自编码,在训练完以后把他们接在就可以了,整个过程如下所示:

深度学习中的编码器 深度自编码网络_深度学习_02

这是我们要自编码的网络,经过两层编码层,然后在经过两层译码层最终使得输出等于输入(当然不可能完全等于,尽可能的等于)。那么这是整体的效果,具体实现起来再拆分成下面两个过程:

深度学习中的编码器 深度自编码网络_深度学习_03

这里我们假设了第二层隐含层单元为100个,那么第一个过程就是训练787-200-784这个自编码了,在训练完以后,我们把200出来的特征值再拿来训练一个200-100-200的自编码层,这样两个过程合在一起就是上上个图所示了。

Ok这是两层隐含层自编码,更多层的自编码无非是在后面继续训练自编码而已了。那么这样做的好处在哪里呢?就可以使得每一层网络的权值都能尽可能准确,因为误差反向传播在只有一层的时候是没有什么损失的。

Ok逐层编码介绍完了,我们来进行实验吧,先把K设置为100,也就是训练一个784-196-100自编码(注意这里改为第一个隐含层196=14*14是为了可视化方便),同时我们也要看看每一层自编码学习到编码矩阵权值到底表示了什么含义,简单程序如下:

clc
clear
currentFolder = pwd;
addpath(genpath(currentFolder))

%% 选择样本
load mnist_uint8;
%% 规定自编码权值
choose_num_train = 20000;
train = double(train_x(1:choose_num_train,:))/255;
auto_net = [784 196 100];
sae = deep_autocoderW(train,auto_net);
visualize(sae.ae{1}.W{1}(:,2:end)')%可视化第一层编码矩阵的权值
figure;
visualize(sae.ae{2}.W{1}(:,2:end)')%可视化第二层编码矩阵的权值

子函数deep_autocoderW及其设置在上一节博客中可以找到,原封不动即可,可视化函数自带的不用管。好了来看看这两个可视化结果吧:

深度学习中的编码器 深度自编码网络_权值_04

第一个可视化结果,有14*14个图,每个图大小为28*28,可以看到学习到的权值所表示的含义还是很明了的。

深度学习中的编码器 深度自编码网络_权值_05

第二个可视化结果,有10*10个图,每个图大小为14*14,可以看到学习到的权值所表示的含义不怎么样,甚至看不出什么来。其实这个特征就是在上一个基础上在进行组合学习而来的,至于为什么会这样的效果,还不得而知。

在自编码完成以后,我们就用第二个隐含层出来的100个特征去进行SVM训练吧,也就是后接一个SVM分类器,抽象出来就是文章开头的那样:

深度学习中的编码器 深度自编码网络_深度学习_06


代码如下:

%--------------添加当前文件夹-------------
%% addpath .\DeepLearnToolbox-master\.
clc
clear
currentFolder = pwd;
addpath(genpath(currentFolder))
%% 选择样本
load mnist_uint8;
%% 规定自编码权值
choose_num_train = 20000;
train = double(train_x(1:choose_num_train,:))/255;
auto_net = [784 200 100];%确定网络结构
sae = deep_autocoderW(train,auto_net);%进行自编码训练
%%
num_train = 5000;%选取训练与测试样本
train_x = double(train_x(1:num_train,:))/255;
train_y = double(train_y(1:num_train,:));

num_test= 5000;
test_x = double(test_x(1:num_test,:))/255;
test_y = double(test_y(1:num_test,:));
%% 进行特征再提取
data_train_feature = deep_feature_data(sae,train_x);
data_test_feature = deep_feature_data(sae,test_x);
data_train = data_train_feature{numel(auto_net)-1};
data_test = data_test_feature{numel(auto_net)-1};

%% svm 训练
[~,train_y] = max(train_y');
model = svmtrain(train_y',data_train,'-t 0');
%% svm 测试
[~,test_y] = max(test_y');
predict1 = svmpredict(ones(numel(test_y),1),data_test,model);
accurary = numel(find(predict1 == test_y'))/numel(test_y)

得到的结果为:

accurary =

0.9172

可以看到用第二层网络出来的特征进行分类实验结果也还可以。

下面假设我们用两层特征融合在一起作为最终的特征送到SVM中进行实验,那么此时的模型就是下面这样子:

深度学习中的编码器 深度自编码网络_权值_07


相应的程序就是这个样子:

%--------------添加当前文件夹-------------
%% addpath .\DeepLearnToolbox-master\.
clc
clear
currentFolder = pwd;
addpath(genpath(currentFolder))
%% 选择样本
load mnist_uint8;
%% 规定自编码权值
choose_num_train = 20000;
train = double(train_x(1:choose_num_train,:))/255;
auto_net = [784 200 100];%确定网络结构
sae = deep_autocoderW(train,auto_net);%进行自编码训练
%%
num_train = 5000;%选取训练与测试样本
train_x = double(train_x(1:num_train,:))/255;
train_y = double(train_y(1:num_train,:));

num_test= 5000;
test_x = double(test_x(1:num_test,:))/255;
test_y = double(test_y(1:num_test,:));
%% 进行特征再提取
data_train_feature = deep_feature_data(sae,train_x);
data_test_feature = deep_feature_data(sae,test_x);
% data_train = data_train_feature{numel(auto_net)-1};
% data_test = data_test_feature{numel(auto_net)-1};
data_train = [data_train_feature{numel(auto_net)-2},data_train_feature{numel(auto_net)-1}];
data_test = [data_test_feature{numel(auto_net)-2},data_test_feature{numel(auto_net)-1}];

%% svm 训练
[~,train_y] = max(train_y');
model = svmtrain(train_y',data_train,'-t 0');
%% svm 测试
[~,test_y] = max(test_y');
predict1 = svmpredict(ones(numel(test_y),1),data_test,model);
accurary = numel(find(predict1 == test_y'))/numel(test_y)

其结果如下:

accurary =

0.9360

准确率达到了0.9360,也是目前实验最高的了,由此可见将两层学习到的特征综合在一起构成200+100=300维特征的时候效果更好。这种引入上一层或者上几层特征的做法在改进深度学习的特征上是经常做的。至此,深度网络多特征学习就到这里了,至于更深的网络,其实和这种二层网络类似操作了,大家可以试验试验。