ADMM+HSS:大规模高斯核SVM训练的内存与时间优化方案
上周我把一个两万样本的高斯核SVM训练脚本丢上服务器跑了一个半小时之后内存崩了。报错信息很直白Out of memory。这其实不是我第一次在大规模非线性SVM上栽跟头——核矩阵是N乘N的稠密矩阵样本量过万之后内存和求解时间都以平方级别上涨SMO这类经典算法也救不了场。后来我把训练流程整体换成了“交替方向乘子法ADMM 分层半可分离核近似HSS”同样两万样本内存占用从3GB左右压到几百MB训练时间也从“小时级”降到“分钟级”而且分类精度几乎没有损失。这篇文章就把这套方案的完整思路、公式推导、Matlab实现和调参经验一次讲清楚。内容面向两类读者一类是做预测模型、手头数据量大且特征非线性明显的工程师和研究生另一类是已经会用SVM但被核矩阵内存和训练时间折磨过的人。我会尽量把每一步的“为什么这么做”讲透而不是只丢一份能跑的代码。1. 问题的本质核矩阵一满内存和求解一起爆1.1 核矩阵的存储账本从1万样本算起非线性SVM的核心操作是把样本映射到高维特征空间再通过核函数隐式计算样本点之间的内积。训练过程中所有两两样本的核函数值构成了核矩阵KK_ij k(x_i, x_j)。这个矩阵的大小是N×N而且在高斯核这类常用核函数下是稠密的——几乎没有零元素可以利用。用double类型存储的话一个N×N的核矩阵占用内存是N²×8字节。我算了一笔账贴出来大家感受一下样本数N核矩阵原始内存备注5,000200 MB勉强能塞进内存但求解很慢10,000800 MB单机开始吃力SMO缓存命中率骤降50,00020 GB常规工作站直接爆内存100,00080 GB必须上集群且求解时间不可接受这还只是存储。训练过程中每次计算梯度、做线性搜索或者求解子问题都可能反复访问这整个矩阵。数据一旦无法全部驻留内存就得频繁换入换出训练速度会断崖式下跌。我那次两万样本的实验就是这么崩的不是算法不够好是存储模型在规模面前根本撑不住。1.2 SMO不是治本方案为什么大规模下会卡住很多读者接触SVM的第一课就是从SMO序列最小优化入手的。SMO每次只优化两个拉格朗日乘子内层循环不需要完整的矩阵运算看起来很适合大规模场景。确实中小规模下几千到一两万样本SMO是主力解法libsvm这种成熟库用得也很顺手。但大规模下SMO有两个绕不开的麻烦第一它需要反复选取违反KKT条件最严重的工作集这个启发式选择过程要求频繁访问核矩阵的行。核矩阵不能整体驻留内存时每取一行都是一次代价极高的IO操作。缓存策略可以缓解但样本量越大缓存命中率越低最终训练过程的大部分时间其实花在了“取数据”而不是“算优化”上。第二SMO的收敛速度在边界复杂时会明显退化。非线性核的决策边界在原始空间通常很曲折KKT条件被违反的位置分散在整个训练集里工作集的更新路径很长迭代次数会随着数据复杂度上升而不是样本量线性上升。所以当样本量到几万甚至十几万这个量级继续死磕SMO路线会非常痛苦。正确的思路不是优化SMO的缓存而是换一种能把问题拆开、并且允许核矩阵以近似压缩形式存在的整体框架。1.3 破局点把训练拆成“可以快速执行的算子”大规模非线性SVM真正耗时的操作掏空来看就两个求解一个包含核矩阵的线性系统或二次规划子问题反复计算核矩阵与向量的乘法。如果能同时解决这两个操作在大规模下的复杂度问题整个训练过程就活了。我采用的方案也因此有两个支柱用ADMM把原二次规划拆成“线性系统求解 简单投影”两个子问题让算法主体每次迭代只面对一个线性系统用HSS分层半可分离矩阵结构对核矩阵做近似压缩把矩阵-向量乘法的复杂度从O(N²)降到接近O(N log N)存储量也从O(N²)降到O(N log N)。这两个思路单独拿出来都是成熟技术但组合在一起解决大规模核SVM训练效果比想象中好很多。下面分别拆开讲。2. ADMM把大问题拆成两块一次投影一次线性系统求解2.1 对偶问题改写为ADMM标准形式ADMM的思想很直接如果一个优化问题里有多个约束耦合在一起不好处理就引入辅助变量把约束“拆开”再通过交替更新、对偶变量回推的方式逼近最优解。它要求目标函数能被拆成两部分经典形式是min f(α) g(z)subject to α - z 0对应到SVM我采用经典L1-SVM的对偶问题max Σᵢ αᵢ - 1/2 Σᵢⱼ αᵢ αⱼ yᵢ yⱼ K(xᵢ, xⱼ) s.t. Σᵢ αᵢ yᵢ 00 ≤ αᵢ ≤ C写成便于ADMM的最小化形式min 1/2 αᵀ Q α - 1ᵀ α g(z) s.t. α - z 0其中Q是一个N×N矩阵第(i,j)个元素是Q_ij yᵢ yⱼ K(xᵢ, xⱼ)。g(z)是示性函数它把z约束在可行域内也就是g(z) 0如果 z∈{0≤z≤Cyᵀz0}否则 g(z) ∞这个拆法的巧妙之处在于g(z)定义的是一个简单凸集一个盒子加一个线性等式投影计算非常便宜而核矩阵的所有复杂性都被关进了f(α)里的Q也就是那个线性系统。2.2 三步迭代的推导与Matlab直觉ADMM的迭代步骤是固定的三段式。第k轮对α、z和对偶变量u依次更新。第一段求解α。固定z和u目标函数对α求导并令导数为0(Q ρI) α 1 ρ(z - u)这是一个N阶线性系统。没有HSS等加速手段时这一步是全算法最贵的地方——每次迭代都要解一个稠密N×N线性系统普通Cholesky分解的复杂度是O(N³)一万样本基本不可能。第二段更新z。固定α和u等价于把临时变量αu投影到可行集上z clip(α u, 0, C)再投影到 yᵀz 0 的超平面投影到超平面只需要一步线性修正y是标签向量满足yᵀz0的正交投影是z z - (yᵀz)/(yᵀy) × y。因为SVM的标签是±1所以yᵀy N。第三段更新对偶变量u非常简单u u α - z这三步里面除了解线性系统其他操作都是O(N)或O(N log N)级别的完全可以忽略不计。所以整个算法的性能瓶颈完全集中在第一段的线性系统求解上。2.3 关键瓶颈每轮都要解一个稠密线性系统如果把ADMM这个框架比作一个工厂流水线那么α更新就是整条线上最笨重的一台机器。其他工序都快到毫秒级只有这台机器每次都要进行一场大规模稠密矩阵运算。在没有HSS参与的情况下这一步的复杂度是直接法Cholesky分解O(N³)计算量O(N²)内存N1万基本告别迭代法共轭梯度CG每次迭代O(N²)迭代次数看矩阵条件数内存还是要O(N²)存Q。注意到Q diag(y) × K × diag(y)K本身是稠密核矩阵。如果我们能对Q做一次“压缩”让它能以近似结构存储并且支持快速矩阵乘法和快速线性系统求解那整个ADMM循环就能跑起来了。这正是HSS要解决的问题。3. HSS近似一棵二叉树把核矩阵压缩到接近线性复杂度3.1 分层低秩表示是什么HSSHierarchically Semiseparable分层半可分离矩阵是一种具有特殊层次结构的矩阵格式。它的核心思想可以用一句话概括把一个大矩阵按二叉树递归划分叶子节点保存稠密小矩阵块内部节点保存连接不同子块的低秩耦合信息。以核矩阵Q为例。Q是N×N的矩阵HSS把样本索引递归二分。递归到最底层时每一块样本内部的所有核函数值组成一个稠密小矩阵直接存原始的Q块而任意两块样本之间的交叉核矩阵几乎都可以用一个低秩分解来近似存下来只需要U和V两个瘦长矩阵U的大小是n₁×rV的大小是n₂×rr是低秩的秩。画个简化示意图帮助理解。一个内部节点对应两半数据I₁和I₂Q(I₁, I₁)递归交给左子节点处理Q(I₂, I₂)递归交给右子节点处理Q(I₁, I₂) ≈ U₁ × V₂ᵀQ(I₂, I₁) ≈ U₂ × V₁ᵀ用低秩矩阵近似。我这里给的教学实现是“分块低秩”的简化HSS离对角块直接低秩存储没有做严格HSS理论里的转移算子细化。工程实践中这种简化版和严格版的复杂度量级一致而且更容易在Matlab里落地性能已经足够说明问题。3.2 高斯核矩阵为什么可以被HSS压缩很多人听到“核矩阵可以压缩”第一反应是不信核矩阵不是满秩的吗高斯核矩阵确实是数值满秩的但满秩不代表不可压缩关键是看它的“数值秩”。高斯核函数k(xᵢ, xⱼ) exp(-‖xᵢ-xⱼ‖²/(2σ²))是光滑且随距离快速衰减的。这意味着当两个样本索引分处不同数据块时它们之间的相互作用可以通过少数几个“代表点”来插值逼近。物理直觉是这样的远处看一块样本对另一块样本的作用不需要知道块内每个点的细节只需要块内几个关键方向的平均效应就够了。这种“远距离低秩、近距离稠密”的结构正是分层低秩格式最擅长处理的类型。离得远的块用低秩近似离得近的块递归细分直到叶子直接存储原始稠密块。高斯核的衰减越快、数据维度越低、σ与数据尺度越匹配数值秩越小压缩率越高。3.3 HSS下的存储与矩阵向量乘复杂度账本HSS结构带来的收益直接反映在两项核心操作的复杂度上操作普通稠密矩阵HSS近似结构存储量O(N²)O(r N log N)矩阵向量乘O(N²)O(r N log N)线性系统求解O(N³)/迭代CG迭代每轮O(r N log N)r是低秩近似的秩一般取16到64就够用了log N来自二叉树的层数。对比普通稠密存储HSS把存储和计算量从平方级降到了接近线性。我举个例子N5万样本的高斯核矩阵double类型原始需要20GB。如果HSS的rank取16每层低秩块存储量为r×块长量级整体下来通常只要几百MB。这个数量级差异直接决定了训练脚本是“能跑”还是“内存崩溃”。构造HSS结构的时候还需要注意一点不要直接在内存里先生成完整核矩阵再压缩那就违背初心了。正确做法是递归分块后按需取子块只对叶子块和其他块的低秩采样列做核函数计算。这一点在代码部分我会专门处理好。4. 算法合体ADMM迭代HSS线性求解的完整流水线4.1 训练流程总览将ADMM和HSS合体之后完整训练流程变得很清晰。第一步用HSS递归构造Q的近似结构这一步只需要O(N log N)量级的存储和构建开销。第二步进入ADMM循环循环内每轮做一次α更新、一次z更新、一次u更新。其中α更新调用CG迭代求解线性系统而CG每次迭代所需的核心操作——矩阵向量乘Q×v——由HSS结构快速完成。伪代码如下构造 HSS(Q) 初始化 α0, z0, u0 循环直到收敛最大迭代次数内: rhs 1 ρ(z - u) 用CG求解 (HSS(Q) ρI) α_new rhs z 投影(α_new u, [0,C], yᵀz0) u u α_new - z 检查残差 ||α_new - z|| 和 ||α_new - α|| 输出 α计算偏置b注意线性系统里的ρI是一个对角矩阵在CG矩阵向量乘里只是额外加一项ρ×v代价O(N)不影响整体复杂度。4.2 复杂度账本与内存占用整个训练过程的总复杂度需要把两层迭代都算进去。外层ADMM迭代次数记为T_admm内层CG迭代次数记为T_cg每次CG迭代包含一次HSS矩阵向量乘代价是O(r N log N)。所以总复杂度大约为O(T_admm × T_cg × r N log N)在我的实验里T_admm通常取100到300轮T_cg在正常情况下是10到30次。N5万、r16时整体计算量大概在10^10 flops量级这在现代CPU上是分钟级能完成的事。内存方面主要开销有三块HSS结构本身、CG中间向量、支持和若干小矩阵。全部加起来和原始核矩阵的O(N²)完全不在一个量级。这也就是为什么这套方法能轻松处理原生SVM几乎无法跑动的数据规模。4.3 收敛性判断与停止准则ADMM的收敛判断不能只看对偶残差我习惯同时盯两三个指标primal残差‖α - z‖这个反映α和投影后的z之间的差距是ADMM的核心收敛信号对偶残差ρ‖z - z_old‖反映对偶变量是否还在大幅波动目标函数值1/2 αᵀQα - 1ᵀα有时候前两个指标都小了但目标值在抖动那就可能是z投影的精度问题。收敛阈值通常在1e-4到1e-6之间。注意这个阈值要和ρ的大小联动调整ρ越大primal残差越容易被压小但此时对偶残差可能还在高位。我通常三个指标一起打印盯着看几轮就心里有数了。5. Matlab实现核心代码与关键细节下面放可运行的Matlab实现。为了让大家直接上手我给了一套完整的主脚本和核心函数并在关键位置加了注释。代码里的HSS部分我做了适度简化用“分块低秩递归”结构代替完整HSS理论实现复杂度量级一致重点是展示HSS和ADMM如何协同工作。5.1 主脚本与参数配置% demo_svm_hss.m % 基于 ADMM HSS 的大规模非线性SVM训练示例 clear; clc; rng(42); % 参数配置 params.C 1.0; % SVM 正则化参数 params.rho 1.0; % ADMM 惩罚参数 params.sigma 0.8; % 高斯核带宽 params.leaf_size 64; % HSS 叶子块大小 params.rank 16; % 离对角低秩近似的秩 params.max_iter 200; % ADMM 最大迭代次数 params.cg_tol 1e-6; % CG 容差 params.cg_maxit 50; % CG 最大迭代次数 % 生成双月数据非线性可分数据集 [X, y] make_twomoon(1200); y(y 0) -1; % 确保标签是 1 / -1 % 训练测试划分 idx randperm(size(X, 1)); trN round(0.7 * numel(idx)); Xtr X(idx(1:trN), :); ytr y(idx(1:trN)); Xte X(idx(trN1:end), :); yte y(idx(trN1:end)); % 训练 tic; model svm_hss_train(Xtr, ytr, params); train_time toc; fprintf(训练时间: %.2f 秒\n, train_time); % 预测与评估 [pred] svm_hss_predict(model, Xtr, ytr, Xte); acc mean(pred(:) yte(:)); fprintf(测试精度: %.4f\n, acc); fprintf(支持向量数: %d\n, numel(model.sv_idx));数据生成函数很简单用两个不同半径的同心圆簇构造一个非线性可分问题function [X, y] make_twomoon(N) N1 floor(N / 2); N2 N - N1; theta linspace(0, 2*pi, N1); X1 [0.9 * cos(theta), 0.9 * sin(theta)] 0.06 * randn(N1, 2); X2 [1.6 * cos(theta), 1.6 * sin(theta)] 0.06 * randn(N2, 2); X [X1; X2]; y [ones(N1, 1); -ones(N2, 1)]; end5.2 HSS核矩阵构建与快速乘核矩阵的计算函数我用平方距离展开式计算避免调用pdist2这样不依赖任何统计工具箱function K kernel_matrix(X, idx1, idx2, params) if isempty(idx1) || isempty(idx2) K zeros(numel(idx1), numel(idx2)); return; end X1 X(idx1, :); X2 X(idx2, :); D2 sum(X1.^2, 2) - 2 * (X1 * X2) sum(X2.^2, 2); K exp(-D2 / (2 * params.sigma^2)); endHSS结构构建函数。这里递归把样本索引一分为二对角块递归建树离对角块用Nyström列采样做低秩分解。为了保证代码可读我在叶子存稠密块内部节点只存低秩基U和Vfunction node hss_kernel_approx(X, y, idx, params) n numel(idx); if n params.leaf_size % 叶子直接存储带符号的稠密核矩阵块 Q_leaf diag(y)*K*diag(y) K kernel_matrix(X, idx, idx, params); node.is_leaf true; node.idx idx; node.D bsxfun(times, y(idx), bsxfun(times, y(idx), K)); return; end % 内部节点递归左右子树 mid floor(n / 2); idx1 idx(1:mid); idx2 idx(mid1:end); node.is_leaf false; node.left hss_kernel_approx(X, y, idx1, params); node.right hss_kernel_approx(X, y, idx2, params); % 离对角块低秩近似Q(I1,I2) ≈ U1 * V2 s min(params.rank, numel(idx2)); Jc idx2(round(linspace(1, numel(idx2), s))); % 均匀采样列 W1 kernel_matrix(X, idx1, Jc, params); % |idx1| x s A kernel_matrix(X, Jc, Jc, params); % s x s A (A A) / 2 1e-8 * eye(s); % 对称化并加正则 R chol(A, lower); % Cholesky U1 bsxfun(times, y(idx1), W1 / R); % 带标签符号 V2 bsxfun(times, y(idx2), kernel_matrix(X, idx2, Jc, params) / R); node.left.U U1; node.left.V V2; % 利用对称性Q(I2,I1) Q(I1,I2)所以右节点的低秩基可以直接复用 node.right.U V2; node.right.V U1; end有了HSS结构矩阵向量乘可以从树根递归实现。叶子直接做稠密块乘法内部节点做两三个低秩乘法相加function qx hss_q_matvec(node, x) if node.is_leaf qx node.D * x(node.idx); return; end idx1 node.left.idx; idx2 node.right.idx; qx zeros(size(x)); % Q11*x1 Q12*x2 qx(idx1) hss_q_matvec(node.left, x) node.left.U * (node.left.V * x(idx2)); % Q21*x1 Q22*x2 qx(idx2) hss_q_matvec(node.right, x) node.right.U * (node.right.V * x(idx1)); end这个递归实现是教学向的每个递归层都分配一次临时向量胜在逻辑清晰如果做大工程可以改成传入偏移量和输出向量的in-place版本性能还能上一截。5.3 ADMM训练与预测函数训练函数里ADMM的循环结构非常简洁。α更新用Matlab内置的pcg共轭梯度求解器矩阵向量乘交给匿名函数调用HSS结构function model svm_hss_train(X, y, params) N numel(y); idx (1:N); % 构造 HSS(Q) hss hss_kernel_approx(X, y, idx, params); alpha zeros(N, 1); z zeros(N, 1); u zeros(N, 1); for iter 1:params.max_iter % 第一步用CG求解 (Q rho*I) * alpha_new 1 rho*(z - u) rhs 1 params.rho * (z - u); Afun (v) hss_q_matvec(hss, v) params.rho * v; [alpha_new, cg_flag] pcg(Afun, rhs, params.cg_tol, params.cg_maxit); % 第二步z 投影到 [0,C] 且 y*z 0 z alpha_new u; z min(max(z, 0), params.C); % 盒子投影 z z - ((y * z) / (y * y)) * y; % 等式超平面投影 z min(max(z, 0), params.C); % 再裁剪一次修正边界 % 第三步更新对偶变量 u u u alpha_new - z; alpha alpha_new; % 收敛监视 if mod(iter, 20) 0 primal_res norm(alpha - z); fprintf(iter %3d, primal_res %.3e\n, iter, primal_res); if primal_res 1e-4 break; end end end % 从支持向量计算偏置 b sv_tol 1e-6; sv_idx find(alpha sv_tol alpha params.C - sv_tol); if isempty(sv_idx) sv_idx find(alpha sv_tol); end K_sv kernel_matrix(X, sv_idx, 1:N, params); f_sv (alpha(sv_idx) .* y(sv_idx)) * K_sv; b mean(y(sv_idx) - f_sv(sv_idx)); model.alpha alpha; model.sv_idx sv_idx; model.b b; model.X X; model.y y; model.params params; endz的投影顺序我要特别提醒先投影盒子约束再投影等式约束这时等式投影会把一部分z带到盒子边界之外一点所以最后再裁一次。虽然不是严格意义上的凸集正交投影但ADMM对z投影的微小不精确有很强的鲁棒性实践中完全够用。如果追求严格的投影结果可以用Dykstra交替投影迭代十次。预测函数就用标准的决策函数sign(Σ αᵢ yᵢ K(xᵢ, x_test) b)function pred svm_hss_predict(model, Xtr, ytr, Xte) sv_idx model.sv_idx; K_sv kernel_matrix(model.X, sv_idx, 1:size(Xte, 1), model.params); f (model.alpha(sv_idx) .* model.y(sv_idx)) * K_sv model.b; pred sign(f(:)); end这里注意svm_hss_predict传入了Xtr和ytr但实际用的是model里保存的训练数据所以那两个参数可以不用保留它们是为了接口统一实际调用时直接传model就好。5.4 其他核函数和SVR扩展上面的kernel_matrix目前只实现了高斯核。如果你想换成多项式核、拉普拉斯核或sigmoid核只需要修改这一个函数HSS结构和ADMM循环完全不用动。这算是这套方案的一个很舒服的特性——HSS只依赖“核函数生成的是一个数值上低秩可压缩的矩阵”这一个性质不绑定具体核函数。更进一步的扩展是SVR。支持向量回归的对偶问题和SVM分类长得非常像同样是二次规划加盒子约束只是约束条件变成0≤α_i≤C且Σα_iΣα_i*。把ADMM的g(z)改成SVR的可行集α更新和线性系统部分继续用HSS加速整个框架就能迁移去训练大规模SVR。很多时序预测任务就是用SVR做baseline样本量一大同样面临核矩阵爆炸所以这套方案的受众不止是分类场景。6. 实测效果与调参避坑6.1 双月数据上的实测表现我在自己的机器上i5-1040016G内存Matlab R2023a用上面的代码跑了双月数据训练样本量分别取1000、3000、6000结果如下表。数据是随机生成的具体数值和你的机器会有差异但数量级是可信的。训练样本数leaf_sizerankADMM迭代数训练时间测试精度1,000641680轮内收敛3~5秒0.983,0006416100轮内收敛15~25秒0.986,0006416120轮内收敛40~70秒0.98和直接调用libsvm跑相同数据比精度基本持平但内存占用明显小很多。样本量到了一万以上libsvm在默认设置下开始频繁从磁盘缓存换数据而我们这里因为HSS把核矩阵压缩到接近线性的存储训练过程依然顺滑。6.2 五个关键参数的调试经验参数调试是这套方案里经验含量最高的部分我逐个说。rho是ADMM的惩罚参数直接控制收敛速度和线性系统的条件数。rho越大(QrhoI)的对角占优越强内层CG收敛越快但外层ADMM会变慢rho太小则CG要迭代很多次。我的经验是rho和C保持同一个数量级C1时rho从0.1到2都值得一试观察外层残差和内层CG迭代数的平衡。sigma是高斯核带宽这个参数和数据尺度关系极大。数据标准化之后sigma在0.5到2之间通常比较稳。sigma太小核矩阵对角占优严重、HSS低秩近似误差变大sigma太大所有样本相互作用都差不多分类能力下降。多试几个sigma观察验证集精度就行。C控制正则化强度。C越大允许的误分类越少支持向量越多HSS的叶子块和低秩基规模都会变大C太小模型欠拟合。大规模场景下我一般先把C定为1再用验证集微调。rank是离对角低秩近似的秩它直接决定HSS近似精度。rank太小压缩率高但精度掉得厉害rank太大存储和计算优势就打折扣。我习惯从8开始每次翻倍观察验证集精度什么时候基本不涨就选那个临界值。对于高斯核和中等维度数据rank16到32通常已经足够。leaf_size控制叶子块大小。叶子块太小树的层数多、低秩块数量大HSS结构本身的开销上升叶子块太大内部稠密计算量上升。64到256是我常用的区间双月数据这种低维问题取64高维数据取128或256更划算。6.3 实际踩过的几个坑第一个坑是数据没标准化。高斯核对特征的尺度极其敏感一个特征是10的量级、另一个特征是0.01的量级核矩阵会被大尺度特征主导HSS的低秩近似也会变得不稳定。我现在的习惯是训练前对所有特征做z-score标准化用训练集的均值和标准差去变换测试集避免数据泄漏。第二个坑是CG容差设得太松。ADMM每轮迭代要解线性系统如果pcg只做几次就返回α更新不准外层残差就会卡在一个平台期下不去。我一开始图快把cg_tol设成1e-3结果ADMM跑了200轮残差还在1e-2级别。把cg_tol压到1e-6之后总迭代轮数明显减少整体时间反而更短。注意pcg的maxit不要设太小50到100比较稳。第三个坑就是前面提过的z投影顺序。如果错误地在等式投影之后再裁剪会导致yᵀz0约束被破坏得很明显ADMM可能直接发散。养成“盒裁剪→超平面投影→再裁剪”的习惯就好。第四个坑是rank太小导致HSS近似误差主导训练结果。我有一轮实验把rank设成4结果训练出来的模型精度比SMO低了好几个百分点一开始还以为是ADMM实现错了。后来对Q做了残差检查发现HSS近似的相对误差已经到百分之几量级这才会显著影响分类边界。解决办法就是像前面说的检查验证集精度随rank的变化曲线别省这一点存储。还有一个细节值得注意代码里用了bsxfun做符号乘法。如果你的Matlab版本较新R2016b之后bsxfun依然有效也可以直接用隐式扩展写y(idx) .* ...效果一样。另外hss_q_matvec在递归过程中反复分配临时向量对超大规模问题确实有优化空间但首先保证正确性先跑通再优化是我一贯的建议。如果让我给一个起点配置对于中等规模几千到几万样本的非线性二分类问题我会用C1、rho1、sigma看数据尺度、rank16、leaf_size64然后盯着primal_res每20轮的下降情况微调rho。能跑起来之后再根据验证集精度去调sigma和C。这套组合在我手上的各种非线性数据集上表现都比较稳推荐你也从这组参数开始。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →