最近利用MATLAB实现一些机器学习算法的时候想到,MATLAB的数学计算、可视化、工具箱都非常方便高效,但如果在C++中想实现同样的算法就非常麻烦,尤其是C++计算能力远远比不上MATLAB。如果在C++中调用MATLAB函数,就能在利用MATLAB数学计算能力的同时结合C++的高性能和系统级编程优势。
本文以PSO-LSSVM(基于粒子群优化算法的最小二乘支持向量机)为例,完整项目代码请看https://github.com/Frostyume/PSO-LLSVM
目录
2.mclInitializeApplication初始化失败
4.Invalid input:Null runtime instance
算法原理
首先关于PSO和LSSVM的算法原理有很多文章珠玉在前,在此就不再赘述,有兴趣可以参考这两篇文章:
PSO-LSSVM的MATLAB代码实现
具体实现可以分为两部分:LSSVM的实现,以及PSO的优化调参
LSSVM的实现
MATLAB 自带的工具箱已经完成了LSSVM的实现,但出于学习目的还是自己实现一遍
首先写好RBF核函数的定义,关于SVM的核函数可以参考:SVM的核函数
function K = rbf_kernel(X1, X2, gamma)
sqX1 = sum(X1.^2, 2);
sqX2 = sum(X2.^2, 2);
K = exp(-gamma * (bsxfun(@plus, sqX1, sqX2') - 2 * (X1 * X2')));
end
训练LSSVM的代码
function model = train_lssvm(trainX, trainY, C, gamma, taskType)
% 输入:
% trainX: 输入特征矩阵
% trainY: 输出标签
% C: 正则化参数
% gamma: RBF核函数参数
% taskType: 'classification' 或 'regression'
% 样本数量
[n, ~] = size(trainX);
% 计算RBF核矩阵
K = rbf_kernel(trainX, trainX, gamma);
% 添加正则化项以避免矩阵奇异
regularization_matrix = (1/C) * eye(n);
Omega = [zeros(1, n+1); [ones(n, 1), K + regularization_matrix]]; % 形成Omega矩阵
Y = [0; trainY]; % 形成Y向量
% 求解线性方程组
alpha_b = pinv(Omega) * Y; % 使用伪逆计算
model.alpha = alpha_b(2:end); % 拉格朗日乘子
model.b = alpha_b(1); % 偏置项
model.gamma = gamma; % 核函数参数
model.C = C; % 正则化参数
model.trainX = trainX; % 保存训练数据
model.isClassification = strcmp(taskType, 'classification'); % 根据输入判断任务类型
% 检查输出有效性
if any(isnan(model.alpha)) || isinf(model.b)
error('模型训练失败,输出参数无效。');
end
end
lssvm_fitness用于计算损失,一开始只写了用于回归任务的代码,回归任务的损失函数就用均方差,想着也可以适用于分类任务就简单地加了个交叉熵损失。
function score = lssvm_fitness(trainX, trainY, C, gamma, taskType)
% 输入:
% trainX: 输入特征矩阵
% trainY: 输出标签
% C: 正则化参数
% gamma: RBF核函数参数
% taskType: 'classification' 或 'regression'
% 训练LS-SVM并计算其在训练集上的损失
% 训练LS-SVM
model = train_lssvm(trainX, trainY, C, gamma, taskType);
% 预测训练集的类别和概率
[predictedY, scores] = predict_lssvm(model, trainX);
if model.isClassification
% 计算交叉熵损失
numClasses = length(unique(trainY));
trueLabels = full(sparse(1:length(trainY), trainY, 1, length(trainY), numClasses)); % One-hot编码
score = -mean(sum(trueLabels .* log(scores + eps), 2)); % 交叉熵损失
else
% 计算均方误差
score = sqrt(mean((predictedY - trainY).^2)); % 均方误差
end
end
LSSVM的预测函数,对于多分类任务引入softmax
function [predicted, scores] = predict_lssvm(model, testX)
% 输入:
% model: 训练好的LS-SVM模型
% testX: 待预测的输入数据
% 输出:
% predicted: 预测的输出结果(类别标签或回归值)
% scores: 每个类别的概率分布(仅在分类任务中)
% 计算RBF核矩阵
K = rbf_kernel(testX, model.trainX, model.gamma);
% 计算决策值
decisionValues = K * model.alpha + model.b;
if model.isClassification
% 通过Softmax将决策值转换为概率
scores = softmax(decisionValues);
% 返回预测的类别标签
[~, predicted] = max(scores, [], 2); % 选择概率最大的类别
else
% 回归任务直接返回决策值
predicted = decisionValues; % 直接返回预测值
scores = []; % 对于回归,不需要概率分布
end
end
function p = softmax(z)
% 计算Softmax概率分布
expZ = exp(z - max(z, [], 2)); % 数值稳定性处理
p = expZ ./ sum(expZ, 2);
end
PSO优化调参的实现
接下来实现PSO算法并用于LSSVM的调参(C和gamma),一些参数可能需要根据实际任务数据进行调整
function [best_C, best_gamma] = pso_optimize(X_train, y_train, num_particles, max_iters, C_min, C_max, gamma_min, gamma_max, taskType)
% 输入:
% X_train: 训练集特征矩阵
% y_train: 训练集标签
% num_particles: 粒子数量
% max_iters: 最大迭代次数
% C_min: 正则化参数C的最小值
% C_max: 正则化参数C的最大值
% gamma_min: 核函数参数gamma的最小值
% gamma_max: 核函数参数gamma的最大值
% taskType: 任务类型('classification' 或 'regression')
% 输出:
% best_C: 优化后的正则化参数C
% best_gamma: 优化后的核函数参数gamma
% 设置随机种子以便重现结果
rng(1); % 你可以选择任何整数作为种子
% 初始化粒子的位置和速度
particles = rand(num_particles, 2) .* [C_max - C_min, gamma_max - gamma_min] + [C_min, gamma_min];
velocities = rand(num_particles, 2) * 0.1; % 初始化速度为小的随机值
personal_best_positions = particles;
personal_best_scores = inf(num_particles, 1);
global_best_position = particles(1, :);
global_best_score = inf;
% PSO参数
w = 0.8; % 惯性权重
c1 = 1.5; % 个体加速度系数
c2 = 1.5; % 群体加速度系数
for iter = 1:max_iters
for i = 1:num_particles
% 计算当前粒子的适应度
C = particles(i, 1);
gamma = particles(i, 2);
score