1.5 深度混合核极限学习机并采用鲸鱼算法优化超参数

本博客来源于CSDN机器鱼,未同意任何人转载。

更多内容,欢迎点击本专栏目录,查看更多内容。

目录

目录

0.引言

1-算法原理

1.1 深度核极限学习机结构

1.2 深度混合核极限学习机

1.3 应用代码

1.3.1 ELM-AE无监督训练

1.3.2 顶层HKELM训练 

1.3.3 DHKELM预测

1.3.4 主程序调用

2 问题引出

2.1 优化原理

2.2 具体实现

3.结语


0.引言

在博客【深度核极限学习机】与里我们讲述了【麻雀优化深度核极限学习机】,今天我们继续对其进行改进:原始的深度核极限学习机顶层采用核极限学习机进行分类,现在我们将其改成混合核的极限学习机(线性核、RBF、多项式、小波核两两组合),因为应用了多个核函数,相比于之前的超参数更多,手工调参较为困难

有人问,为啥要这么改进,我只想说灌水懂不!!!

1-算法原理

首先回顾一下深度核极限学习机原理,详细的看我之前的博客。

1.1 深度核极限学习机结构

1.2 深度混合核极限学习机

与深度核极限学习机结构一样,区别在于顶层KELM采用多个核进行加权,称为混合核极限学习机或者多核极限学习机,简称HKELM。比如,KELM的输入是核矩阵K(Hni,Hnj),这里K可以是线性核、RBF、多项式、小波核中的任意一种,而HKELM的输入是wK1(Hni,Hnj)+(1-w)K2(Hni,Hnj),其中K1K2都是线性核、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);%测试集的预测结果

问题引出

观察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系列。更多内容点击专栏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机器鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值