本博客来源于CSDN机器鱼,未同意任何人转载。
更多内容,欢迎点击本专栏目录,查看更多内容。
目录
目录
0.引言
在博客【深度核极限学习机】与里我们讲述了【麻雀优化深度核极限学习机】,今天我们继续对其进行改进:原始的深度核极限学习机顶层采用核极限学习机进行分类,现在我们将其改成混合核的极限学习机(线性核、RBF、多项式、小波核两两组合),因为应用了多个核函数,相比于之前的超参数更多,手工调参较为困难。
有人问,为啥要这么改进,我只想说灌水懂不!!!
1-算法原理
首先回顾一下深度核极限学习机原理,详细的看我之前的博客。
1.1 深度核极限学习机结构
1.2 深度混合核极限学习机
与深度核极限学习机结构一样,区别在于顶层KELM采用多个核进行加权,称为混合核极限学习机或者多核极限学习机,简称HKELM。比如,KELM的输入是核矩阵,这里K可以是线性核、RBF、多项式、小波核中的任意一种,而HKELM的输入是
,其中K1和K2都是线性核、RBF、多项式、小波核中的任意一种,w是核1所占的权重,即HKELM的输入是两个核矩阵加权。
这样的模型我们称之为深度混合核极限学习机,简称DHKELM。
1.3 应用代码
1.3.1 ELM-AE无监督训练
DHKELM与DKELM、DELM前级的ELM-AE是一样的,主要区别在于顶层的HKELM层。
function delm=dhkelmtrain(P_train,T_train,hidden,lambda1,TF,...
lambda2,w,ker1_para,kernel1,ker2_para,kernel2)
input_num=size(P_train,1);
for u = 1 : numel(hidden)
weight = ELMAEtrain(hidden(u),P_train,lambda1,TF);
delm.elmae{u}=pinv(weight);
input_num=hidden(u);
P_train=P_train*delm.elmae{u};
end
weight=top_HKELMtrain(P_train,T_train,lambda2,w,ker1_para,kernel1,ker2_para,kernel2);
delm.output=weight;
end
function [beta]=ELMAEtrain(N,P,lambda,TF)
% 极限学习机-自动编码器的训练 输入=输出
%INPUT N:隐含层节点数
% P:输入数据
% lambda:L2正则化系数
% TF:激活函数种类
%OUTPUT beta:输出权值
[R,Q] = size(P);
mu=rand;
b=log(1+exp(0.1*rand));
%%
B = orth(rand(N,1));
BiasMatrix = repmat(B,1,R)';
if Q>=N
IW =orth(mu+b*rand(Q,N));%根据文献 将输入权值赋值到-1,1
else
IW =(mu+b*rand(Q,N))';
IW=orth(IW)';
end
tempH = P*IW+BiasMatrix;
switch TF %激活函数
case 'sig'
H = 1 ./ (1 + exp(-tempH));
case 'sin'
H = sin(tempH);
case 'hardlim'
H = hardlim(tempH);
end
beta = (pinv((H'*H+eye(N)/lambda)')*H')*P;
end
1.3.2 顶层HKELM训练
实现方式很简单,DKELM的直接计算kernel_matrix就行了,DHKELM这里计算了两个kernel_matrix,然后分别乘以自己的权重,在加起来得到一个最终的kernel_matrix。
function OutputWeight=top_HKELMtrain(P_train,T_train,lambda,w,ker1_para,kernel1,ker12_para,kernel2)
n=size(T_train,1);%样本数
Omega1 = kernel_matrix(P_train,kernel1, ker1_para);%隐含层输出
Omega2 = kernel_matrix(P_train,kernel2, ker12_para);%隐含层输出
Omega_train=w*Omega1+(1-w)*Omega2;
OutputWeight=((Omega_train+speye(n)/lambda)\(T_train));
end
function omega = kernel_matrix(Xtrain,kernel_type, kernel_pars,Xt)
nb_data = size(Xtrain,1);%nxm 样本数nb
if strcmp(kernel_type,'RBF_kernel'),
if nargin<4,
XXh = sum(Xtrain.^2,2)*ones(1,nb_data);%nxn
omega = XXh+XXh'-2*(Xtrain*Xtrain');
omega = exp(-omega./kernel_pars(1));
else
XXh1 = sum(Xtrain.^2,2)*ones(1,size(Xt,1));
XXh2 = sum(Xt.^2,2)*ones(1,nb_data);
omega = XXh1+XXh2' - 2*Xtrain*Xt';
omega = exp(-omega./kernel_pars(1));
end
elseif strcmp(kernel_type,'lin_kernel')
if nargin<4,
omega = Xtrain*Xtrain';
else
omega = Xtrain*Xt';
end
elseif strcmp(kernel_type,'poly_kernel')
if nargin<4,
omega = (Xtrain*Xtrain'+kernel_pars(1)).^kernel_pars(2);
else
omega = (Xtrain*Xt'+kernel_pars(1)).^kernel_pars(2);
end
elseif strcmp(kernel_type,'wav_kernel')
if nargin<4,
XXh = sum(Xtrain.^2,2)*ones(1,nb_data);
omega = XXh+XXh'-2*(Xtrain*Xtrain');
XXh1 = sum(Xtrain,2)*ones(1,nb_data);
omega1 = XXh1-XXh1';
omega = cos(kernel_pars(3)*omega1./kernel_pars(2)).*exp(-omega./kernel_pars(1));
else
XXh1 = sum(Xtrain.^2,2)*ones(1,size(Xt,1));
XXh2 = sum(Xt.^2,2)*ones(1,nb_data);
omega = XXh1+XXh2' - 2*(Xtrain*Xt');
XXh11 = sum(Xtrain,2)*ones(1,size(Xt,1));
XXh22 = sum(Xt,2)*ones(1,nb_data);
omega1 = XXh11-XXh22';
omega = cos(kernel_pars(3)*omega1./kernel_pars(2)).*exp(-omega./kernel_pars(1));
end
end
end
1.3.3 DHKELM预测
一目了然,首先将输入乘上elm-ae训练得到的权重,得到隐含层的输出,然后计算它的核矩阵,最后乘上输出层的权重,得到输出。与DKELM类似,即使是输入测试集,也要一同计算训练集的隐含层输出,用来计算核矩阵。
function output=dhkelmpredict(delm,w,ker1_para,kernel1,ker2_para,kernel2,P_test,P_train)
num_hidden=numel(delm.elmae);
for i=1:num_hidden
P_test=P_test*delm.elmae{i};
P_train=P_train*delm.elmae{i};
end
Omega_1_test = kernel_matrix(P_train,kernel1, ker1_para,P_test);
Omega_2_test = kernel_matrix(P_train,kernel2, ker2_para,P_test);
Omega_test=w*Omega_1_test+(1-w)*Omega_2_test;
output=Omega_test' * delm.output;
end
1.3.4 主程序调用
%% DHKELM
close all;clear;format compact;format short;clc;rng('default')
%% 加载数据
load data
P_train = double(train_x) ;%200*1000
P_test = double(test_x) ;%200*500
T_train = double(train_Y);%5*1000
T_test = double(test_Y);%5*500
%% 参数设置
%elm-ae的参数
h=[100 ,50];%各隐含层节点数
TF='sig';%ELM-AE激活函数
lambda1=inf;%elm-ae的L2正则化系数
%顶层核极限学习的惩罚系数lambda与核参数ker1
lambda2=100;%正则化系数
w=0.5;%混合核kernel1的权重w,kernel2就是1-w
%核函数类型1.RBF_kernel 2.lin_kernel 3 poly_kernel 4 wav_kernel
kernel1='RBF_kernel';
kernel2='poly_kernel';
% 核参数设置 详情看kernel_matrix
if strcmp(kernel1,'RBF_kernel')
ker1_para=1; %rbf有一个核参数
elseif strcmp(kernel1,'lin_kernel')
ker1_para=[]; %线性没有核参数
elseif strcmp(kernel1,'poly_kernel')
ker1_para=[1,1]; %多项式有2个核参数
elseif strcmp(kernel1,'wav_kernel')
ker1_para=[1,1,1]; %小波核有3个核参数
end
% 核参数设置 详情看kernel_matrix
if strcmp(kernel2,'RBF_kernel')
ker2_para=1; %rbf有一个核参数
elseif strcmp(kernel2,'lin_kernel')
ker2_para=[]; %线性没有核参数
elseif strcmp(kernel2,'poly_kernel')
ker2_para=[1,1]; %多项式有2个核参数
elseif strcmp(kernel2,'wav_kernel')
ker2_para=[1,1,1]; %小波核有3个核参数
end
%%
dhkelm=dhkelmtrain(P_train,T_train,h,lambda1,TF,...
lambda2,w,ker1_para,kernel1,ker2_para,kernel2);
% T1=dhkelmpredict(dhkelm,w,ker1,kernel1,ker2,kernel2,P_train,P_train);%训练集的预测结果
T2=dhkelmpredict(dhkelm,w,ker1_para,kernel1,ker2_para,kernel2,P_test,P_train);%测试集的预测结果
2 问题引出
观察1.3.4可知,构建一个DHKELM需要设置的有:DHKELM各隐含层节点数,预训练时ELM-AE的L2 正则化系数,顶层HKELM的惩罚系数、核1的权重以及K1、K2的核参数,且核函数类型不同,核参数的数量也不一样,因此通过手动选择往往无法得到最优的结果,让我手调是调不出来的。
为此采用鲸鱼算法对DHKELM的超参数进行优化,简称WOA-DHKELM。
2.1 优化原理
鲸鱼优化算法的原理到处都是,就不详细说明了。我们只需要知道一点,任何一个优化算法,都是在他的寻优空间内,基于特定公式进行寻优,具体什么公式我们可以不用管,只要网上能找到可以跑得通的原始代码,我们拿过来改一下就能用,我要调整的就是寻优参数的维度,每个参数的寻优范围,以及适应度函数。
具体步骤如下
2.2 具体实现
步骤1:确定寻优参数与寻优的范围。如下代码所示,我们要优化的是各隐含层节点数,ELM-AE的正则化系数,顶层kelm的惩罚系数,核1的权重系数,与两个核的核参数。其中隐含层数量与核函数类型是预先设定好的,一般设置成3层与RBF核与Poly多项式核函数即可。由于每个参数的代码的含义不一样,因此我们需要针对每个参数设定寻优范围lb与ub,详细的看下列代码:
function [Leader_pos,Convergence_curve,process]=woafordhkelm(p_train,p_test,t_train,t_test,layers,TF,kernel1,kernel2)
% 定义寻优边界
%由于隐含层层数与核函数类型会影响寻优维度,所以下面我们分段进行寻优范围设置
xmin=[];xmax=[];
% 首先是各层节点数,范围是1-100,整数
xmin=[xmin ones(1,layers)];
xmax=[xmax 100*ones(1,layers)];
% 然后是ELM-AE的正则化系数【范围是0.001-1000】与 hkelm的惩罚系数【范围是0.001-1000】 与kernel1的权重系数w【范围是0-1,kernel2的权重系数是1-w】
xmin=[xmin 1e-3 1e-3 0];
xmax=[xmax 1e3 1e3 1];
% 最后是hkelm的核参数
% 核参数设置 详情看kernel_matrix
if strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'RBF_kernel')
xmin=[xmin 1e-3 1e-3 ];xmax=[xmax 1e3 1e3 ];%两个 RBF_kernel的 各一个核参数 范围都是1e-3到1e3
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'lin_kernel')
xmin=[xmin 1e-3 ];xmax=[xmax 1e3 ];%线性核没有核参数,只有RBF有一个参数,范围是1e-3到1e3
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'poly_kernel')
xmin=[xmin 1e-3 1e-3 1 ];xmax=[xmax 1e3 1e3 10 ];%RBF_kernel一个核参数 而poly_kernel一共2个,第一个和rbf一样是1e-3到1e3。而第二个参数是幂指数,我们定义他的范围是1-10
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'wav_kernel')%
xmin=[xmin 1e-3 1e-3 1e-3 1e-3 ];xmax=[xmax 1e3 1e3 1e3 1e3 ];%RBF_kernel一个核参数 wav_kernel有3个核参数
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'lin_kernel')
xmin=xmin;xmax=xmax;
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'poly_kernel')
xmin=[xmin 1e-3 1 ];xmax=[xmax 1e3 10];
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'wav_kernel')
xmin=[xmin 1e-3 1e-3 1e-3 ];xmax=[xmax 1e3 1e3 1e3 ];
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'poly_kernel')
xmin=[xmin 1e-3 1 1e-3 1 ];xmax=[xmax 1e3 10 1e3 10 ];
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'wav_kernel')
xmin=[xmin 1e-3 1 1e-3 1e-3 1e-3 ];xmax=[xmax 1e3 10 1e3 1e3 1e3 ];
elseif strcmp(kernel1,'wav_kernel') && strcmp(kernel2,'wav_kernel')
xmin=[xmin 1e-3 1e-3 1e-3 1e-3 1e-3 1e-3 ];xmax=[xmax 1e3 1e3 1e3 1e3 1e3 1e3 ];
end
dim=length(xmin);
SearchAgents_no=5;%种群数量
Max_iter=20;%寻优代数
%% 初始化
Leader_pos=zeros(1,dim);
Leader_score=inf;
for i=1:SearchAgents_no%随机初始化速度,随机初始化位置
for j=1:dim
Positions( i, j ) = (xmax(j)-xmin(j))*rand+xmin(j);
end
end
Convergence_curve=zeros(1,Max_iter);
%% 主循环
for t=1:Max_iter
a=2-t*((2)/Max_iter);
a2=-1+t*((-1)/Max_iter);
lambda=3;
mu=2;
adapative_p= 1-(1/(lambda+mu)*(lambda*t^lambda+mu*mu^lambda)/(Max_iter^lambda));
for i=1:size(Positions,1)
r1=rand();
r2=rand();
A=2*a*r1-a;
C=2*r2;
b=1;
l=(a2-1)*rand+1;
p = rand();
for j=1:size(Positions,2)
if p<0.5
if abs(A)>=1
rand_leader_index = floor(SearchAgents_no*rand()+1);
X_rand = Positions(rand_leader_index, :);
D_X_rand=abs(C*X_rand(j)-Positions(i,j));
Positions(i,j)=X_rand(j)-A*D_X_rand;
elseif abs(A)<1
D_Leader=abs(C*Leader_pos(j)-Positions(i,j));
Positions(i,j)=Leader_pos(j)-A*D_Leader;
end
elseif p>=0.5
distance2Leader=abs(Leader_pos(j)-Positions(i,j));
Positions(i,j)=distance2Leader*exp(b.*l).*cos(l.*2*pi)+Leader_pos(j);
end
end
Positions(i, : ) = Bounds( Positions(i, : ), xmin, xmax );%对超过边界的变量进行去除
fit=fitness(Positions(i,:),p_train,t_train,p_test,t_test,layers,TF,kernel1,kernel2);
% 更新
if fit<Leader_score
Leader_score=fit;
Leader_pos=Positions(i,:);
end
end
Convergence_curve(t)=Leader_score;
process(t,:)=Leader_pos;
end
end
function y=Bounds(x,xmin,xmax)
for i =1:length(xmin)
if x(i)>xmax(i) || x(i)<xmin(i)
x(i)=(xmax(i)-xmin(i))*rand+xmin(i);
end
end
y=x;
end
步骤2:确定适应度函数。观察woafordhkelm函数中更新leader_score与leader_pos可知,这个优化算法是找适应度函数最小时对应的值,是一个最小化问题,因此我们的适应度函数也要写成最小化误差的方式。代码如下,首先将woa的position传进来,进行解码,得到各隐含层节点数、L2正则化系数、顶层kelm的惩罚系数,kernel1的权重系数,与两个核核参数,然后用这些参数训练、预测DHKELM,得到预测值,分类任务就计算分类错误率,回归任务就计算均方差。
function accuracy=fitness(xx,P_train,T_train,P_valid,T_valid,layers,activatation,kernel1,kernel2)
rng('default')
hidden=round(xx(1:layers)); %各隐含层节点数,且取整
lambda1=xx(layers+1); %ELM-AE的L2正则化系数
lambda2=xx(layers+2); %顶层hkelm惩罚系数
w=xx(layers+3); %kernel1的权重系数
%现在开始分配核参数
pop=xx(layers+4:end);
if strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'RBF_kernel')
ker1_para=pop(1); ker2_para=pop(2);
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'lin_kernel')
ker1_para=pop(1); ker2_para=[];
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=pop(1); ker2_para=pop(2:3); ker2_para(2)=round(ker2_para(2));%幂指数要取整
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1); ker2_para=pop(2:4);
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'lin_kernel')
ker1_para=[]; ker2_para=[];
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=[]; ker2_para=pop(1:2); ker2_para(2)=round(ker2_para(2));
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=[]; ker2_para=pop(1:3);
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=pop(1:2); ker2_para=pop(3:4); ker1_para(2)=round(ker1_para(2));ker2_para(2)=round(ker2_para(2));
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1:2); ker2_para=pop(3:5); ker1_para(2)=round(ker1_para(2));
elseif strcmp(kernel1,'wav_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1:3); ker2_para=pop(4:6);
end
dhkelm=dhkelmtrain(P_train,T_train,hidden,lambda1,activatation,...
lambda2,w,ker1_para,kernel1,ker2_para,kernel2);
T2=dhkelmpredict(dhkelm,w,ker1_para,kernel1,ker2_para,kernel2,P_valid,P_train);
% 如果是分类任务
[~ ,J2]=max(T2,[],2);
[~ ,J3]=max(T_valid,[],2);
accuracy=1-sum(J2==J3)/length(J2); %以最小化分类错误率作为适应度值,优化算法的目的就是找到一组最优超参数,用这组超参数训练 的网络,可以使得使得网络的错误率最低
% 如果是回归任务
%[m,m]=size(T2);
%T2=reshape(T2,[1,m*n]);
%T_valid=reshape(T_valid,[1,m*n]);
%accuracy=mse(T2,T_valid); %以最小化预测值与实际值的均方差作为适应度值,优化算法的目的就是找到一组最优超参数,用这组超参数训练 的网络,可以使得使得网络的预测误差最低
rng(sum(100*clock))
步骤3:主程序调用。
%% 此程序为 鲸鱼优化DHKELM超参数
close all;clear;format compact;format short;clc;warning off
%% 加载数据
load data
P_train = double(train_x) ;%200*1000
P_test = double(test_x) ;%200*500
T_train = double(train_Y);%5*1000
T_test = double(test_Y);%5*500
%% 参数设置
layers=3;%3层隐含层
TF='sig';%ELM-AE激活函数类型
%顶层混合核极限学习机类型
kernel1='poly_kernel';%核函数类型 RBF_kernel lin_kernel poly_kernel wav_kernel
kernel2='wav_kernel';%核函数类型 RBF_kernel lin_kernel poly_kernel wav_kernel
%% 开始优化
[xx,trace,result]=woafordhkelm(P_train,P_test,T_train,T_test,layers,TF,kernel1,kernel2);
figure
plot(trace)
title('适应度曲线')
xlabel('优化代数')
ylabel('适应度值')
%% 解码得到优化得到的各个参数,然后重新训练一个DHKELM
hidden=round(xx(1:layers)) %各隐含层节点数,且取整
lambda1=xx(layers+1) %L2正则化系数
lambda2=xx(layers+2) %顶层kelm惩罚系数
w=xx(layers+3) %kernel1的权重
%现在开始分配核参数
pop=xx(layers+4:end);
if strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'RBF_kernel')
ker1_para=pop(1); ker2_para=pop(2);
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'lin_kernel')
ker1_para=pop(1); ker2_para=[];
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=pop(1); ker2_para=pop(2:3); ker2_para(2)=round(ker2_para(2));%幂指数要取整
elseif strcmp(kernel1,'RBF_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1); ker2_para=pop(2:4);
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'lin_kernel')
ker1_para=[]; ker2_para=[];
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=[]; ker2_para=pop(1:2); ker2_para(2)=round(ker2_para(2));
elseif strcmp(kernel1,'lin_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=[]; ker2_para=pop(1:3);
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'poly_kernel')
ker1_para=pop(1:2); ker2_para=pop(3:4); ker1_para(2)=round(ker1_para(2));ker2_para(2)=round(ker2_para(2));
elseif strcmp(kernel1,'poly_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1:2); ker2_para=pop(3:5); ker1_para(2)=round(ker1_para(2));
elseif strcmp(kernel1,'wav_kernel') && strcmp(kernel2,'wav_kernel')
ker1_para=pop(1:3); ker2_para=pop(4:6);
end
dhkelm=dhkelmtrain(P_train,T_train,hidden,lambda1,TF,...
lambda2,w,ker1_para,kernel1,ker2_para,kernel2);
% T1=dhkelmpredict(dhkelm,w,ker1,kernel1,ker2,kernel2,P_train,P_train);%训练集预测输出
T2=dhkelmpredict(dhkelm,w,ker1_para,kernel1,ker2_para,kernel2,P_test,P_train);%测试集预测输出
3.结语
至此,我们完成了DELM系列。更多内容点击专栏。