首页 / 资讯中心 / 文章详情

极限学习机结合流形正则:SS-US-ELM在少量标签场景下的半监督与无监督分类实现

极限学习机结合流形正则:SS-US-ELM在少量标签场景下的半监督与无监督分类实现 ★ FEATURED ARTICLE
手里只有30个带标签的样本剩下四千多条数据安安静静躺在硬盘里没有标签但明显不是噪声。标签费时费力丢掉这些无标签数据又可惜这是我在做一个分类任务时最真实的窘境。后来我翻到半监督极限学习机SS-ELM和无监督极限学习机US-ELM的实现思路才意识到原本纠结的问题可以用一个统一框架解决给极端学习机加一顶“流形正则化”的帽子让少量标签和大量无标签数据在同一个训练闭环里协作。这篇文章我按自己的理解把SS-US-ELM的原理、公式到Matlab代码串了一遍适合手里有Matlab、想快速复现又不想啃一堆论文的读者。这个标题下的代码解决的核心问题说白了就是需求是“分类器”但只有少量标注数据时怎么把无标签样本用起来需求是“聚类”但完全不想碰标签时怎么让ELM也能输出有意义的嵌入和簇。两种需求共用同一套隐层映射逻辑只是目标函数里取舍不同。我下面会先讲清楚ELM为什么快、SS-ELM三个目标项是怎么协同的、US-ELM如何退化成特征分解问题再给一套能直接跑的Matlab代码段最后说说我调参和踩坑的实际经验。1. 理解ELM的核心逻辑随机映射加上最小二乘输出1.1 ELM为什么快把“反向传播学习隐层”变成“随机生成隐层”传统的BP神经网络训练慢很大程度是因为隐层的权重和偏置要通过误差反传反复迭代调整每调一次都要跑一轮前向和反向。极限学习机ELM做了一个非常大胆的简化隐层的输入权重和偏置不需要学习直接随机生成训练阶段只学习隐层到输出的那一层线性权重。这样一来训练从“非线性迭代优化”降维成“线性最小二乘求解”速度能提升几个数量级。ELM的过程可以拆成两步。第一步给定输入数据矩阵Xn×d随机生成输入权重Wd×L和偏置b1×L经过激活函数得到隐层输出矩阵Hn×L H g(X·W b) 这里g就是激活函数可以是sigmoid、tanh、rbf等。矩阵的每一行是对应样本的隐层特征每一列对应一个随机映射后的“特征维度”。为什么说这是特征学习因为ELM把原始输入投射到一个随机的高维空间里在这个空间里再做线性分类或回归等价于用一组基函数去拟合目标。第二步求解隐层到输出的权重βL×m目标是最小化训练误差 min ||H·β - T||² T是标签矩阵n×m。这是个典型的线性最小二乘问题解是β H†·T其中H†是H的伪逆。伪逆的Matlab实现非常简单一行pinv(H) * T就跑完了。当年我第一次跑通这个流程时感觉训练时间几乎可以忽略不计这就是ELM的核心价值所在。1.2 输出权重求解从伪逆到正则化如果H的列数比较少、样本量很大伪逆求解的稳定性其实还可以。但一旦隐层节点数L接近或超过样本数n问题就变成欠定直接用伪逆容易过拟合。实践里我在ELM后面加上一个L2正则项把训练目标改成 min (1/2)·||β||² (C/2)·||Hβ - T||² 这个C就是正则化系数控制模型复杂度和训练误差的平衡。解变成 β (I/C HᵀH)⁻¹·HᵀT 等价形式是β (I C·HᵀH)⁻¹·HᵀT。这里I是单位矩阵维度等于隐层节点数L。这样既保证模型有足够表达能力又不会因为隐层太大而把噪声也一并记下来。这个正则化思想在后面SS-ELM和US-ELM里会被放大成更具灵活性的框架理解这一步很重要。1.3 为什么在ELM之上做半监督和无监督ELM虽然快但它本身是有监督的必须给标签。实际场景里标签往往是最贵的资源。我碰到的项目里有时专家手工标注一批数据需要一两天而无标签数据每天都在产生。这时候有两个直接需求半监督用少量有标签样本加大量无标签样本共同训练让无标签样本通过数据分布和邻居关系“间接”参与训练。无监督完全没有标签但希望ELM能输出一个可聚类的嵌入表示。标准ELM在这两种场景下都无能为力但它的隐层随机映射机制给半监督和无监督留了一扇窗既然隐层是固定的、不需要额外学习那我们只需要在输出权重β上做文章。把流形正则化加进目标函数就能让无标签样本的几何结构影响β的求解。这就是SS-US-ELM这套框架的出发点也是我下文要展开的核心。2. 半监督极限学习机三个目标项如何在闭环中协作2.1 目标函数拆解每一项代表一个需求半监督极限学习机SS-ELM的目标函数在我看来就是三个需求的叠加结构风险、经验风险、流形正则。写成公式就是min (1/2)·||β||² (C/2)·Σᵢ₌₁ˡ ||eᵢ||² (λ/2)·Tr(Fᵀ·L·F)三个项各自的分工非常明确。第一项(1/2)·||β||²是L2正则约束模型的复杂度防止在标签极少时输出权重出现极端取值。第二项是有标签样本的误差项Σᵢ₌₁ˡ ||eᵢ||²只对前面l个有标签样本计算误差eᵢ Tᵢ - Hβ。C控制这部分在整体目标里的权重——标签越可靠、越重要C就越大。第三项是流形正则对所有样本不管标没标标签的隐层输出F Hβ做平滑性约束Tr(FᵀLF)表示预测结果在数据流形上的“变化剧烈程度”。这个三项结构最妙的地方在于它不是把无标签样本强行赋予伪标签来参与训练而是让无标签样本通过图结构“告诉”模型彼此的预测应该保持在怎样的相对关系里。标签只负责校准绝对位置流形负责维持相对结构。2.2 图拉普拉斯正则的直观含义——平滑性假设很多人第一次看Tr(FᵀLF)会觉得抽象。实际上这个式子做一次恒等变形就好理解了Tr(FᵀLF) (1/2)·Σᵢⱼ Wᵢⱼ·||Fᵢ - Fⱼ||²Fᵢ是样本i的网络输出。Wᵢⱼ是样本i和样本j之间的相似度权重。这个式子的意思是如果两个样本在原始空间里离得近Wᵢⱼ大那它们对应的输出Fᵢ和Fⱼ也应该尽量接近。这个假设就是半监督学习里的平滑性假设——数据分布的密度高区域内部标签变化不应太剧烈。用生活里的类比来说一条街道相邻门牌号的房价不会突然从五位数跳到大七位数邻居之间总是渐变的。构造L的过程有两种常用做法。第一种是k近邻图加高斯核权重先对每个样本找它的k个近邻然后对近邻对赋予高斯相似度Wᵢⱼ exp(-||xᵢ - xⱼ||²/(2σ²))非近邻权重为0。第二种是直接用全连接高斯核但样本量大时矩阵会太密我一般用k近邻图。得到相似度矩阵W后令度矩阵D为对角阵对角线元素Dᵢᵢ Σⱼ Wᵢⱼ拉普拉斯矩阵L D - W。实践中我也常用对称归一化的版本L_sym D^(-1/2)·L·D^(-1/2)数值上更稳定后面代码里就用这个。2.3 闭式解的推导思路与Matlab代码中的矩阵组装SS-ELM的优点之一是求解过程仍然是解析解不需要迭代。把目标函数的误差项写成矩阵形式误差向量只对有标签样本有效这里用一个对角矩阵J来实现维度n×n对角线前l个元素为1后u个元素为0。T̃是同标签矩阵有标签部分放真实标签无标签部分置0即可因为对应的误差项被J屏蔽了。整体优化问题等价于min (1/2)·||β||² (C/2)·||J^(1/2)(Hβ - T̃)||² (λ/2)·βᵀHᵀLHβ对β求导并令导数为0整理后就得到闭式解β (I C·Hᵀ·J·H λ·Hᵀ·L·H)⁻¹·Hᵀ·J·T̃从公式可以看出HᵀJH是在有标签样本上积累信息HᵀLH是在全部样本上做流形正则。两者共享同一套隐层特征H只是作用方式不同。在我实际写的Matlab代码里整个求解过程不过三五行矩阵操作。预计算H构造J、L然后一次左除“\”就解出β训练过程仍然保持了ELM系列“快”的基因。我强烈建议你自己推一遍这个求导过程哪怕只在草稿纸上写写符号也会对矩阵组装的理解深很多。3. 无监督极限学习机没有标签时的嵌入与聚类3.1 去掉误差项之后目标函数还剩什么无监督极限学习机US-ELM是SS-ELM的一个极端情形没有标签目标函数里去掉经验误差项只剩结构风险和流形正则。于是优化目标变成min (1/2)·||β||² (λ/2)·Tr(Fᵀ·L·F)但这里藏着一个致命问题如果不加任何约束β 0就是最小值。L·F里如果F全为0Tr(FᵀLF)0||β||²也是0模型直接学出一个全零输出。这是我在初学US-ELM时踩过的最大坑——跑出来所有样本的嵌入一模一样聚类结果自然一团糟。要避免平凡解必须给输出嵌入加上约束。常见做法是要求嵌入正交归一FᵀF I或者说(Hβ)ᵀ(Hβ) I。正交约束的意思是不同维度上的嵌入要彼此独立且保持单位能量。这样模型被迫在“保持流形平滑”和“输出不退化”之间寻找平衡学出来的嵌入才有辨识度。3.2 正交约束与广义特征值问题加上约束后原本的简单最小二乘问题变成了约束优化问题用拉格朗日乘子法可以转成一个广义特征值问题。推导思路大致是构造拉格朗日函数对β求导置零整理后得到Hᵀ·L·H·β λ·Hᵀ·H·β这是一个标准的广义特征值问题A HᵀLHB HᵀH求解使得Aβ λBβ的β。最优解取最小的m个特征值对应的特征向量m是希望的嵌入维度或聚类簇数。这背后有谱聚类的思想拉普拉斯矩阵最小的非零特征向量恰恰对应着数据流形上最“平滑”的切分方向把这些方向作为嵌入特征后续聚类更容易分开。在实际Matlab代码里求广义特征值用的是eigs函数[V, ~] eigs(A, B, m, smallestabs); beta V;一个很容易被忽略的细节是eigs默认返回模最大的特征值必须显式指定smallestabs才是最小特征值方向。我一开始没注意结果聚类效果诡异排查了半天才发现是特征值选向反了。3.3 聚类嵌入怎么落地成最后的类别拿到β之后把所有样本映射到嵌入空间F H·β。F是n×m的矩阵每一行是一个样本的低维嵌入。对F直接跑kmeans聚类就能得到每个样本的簇标签。这个流程跟谱聚类的embedd后接kmeans是一样的套路只是这里嵌入的基来自ELM随机映射后的H空间而不是原始特征空间。嵌入维度m通常取簇数本身但也可以稍微大一点再做PCA降维。在我的实验里m等于簇数时效果最直接如果数据簇内结构复杂可以取2~3倍的簇数然后聚类前用PCA压缩到簇数维度往往更稳。后面我会在代码段里展示完整步骤。4. Matlab实现细节我的这套代码长什么样4.1 输入输出约定与核心参数先说我代码里约定的输入输出这样你拿到任何一份SS-US-ELM代码都能快速对齐。输入是特征矩阵Xn×d标签矩阵Yn×cc是类别数或输出维度。半监督场景下无标签样本在Y中的对应行全部置0纯监督场景下每行是one-hot标签。输出是输出权重β和一个结构体model里面存了随机权重W、偏置b、激活函数类型等方便预测阶段复用。核心参数包括num_hidden隐层节点数L我常用范围100~1000C经验风险正则系数常用1e-3~1e3lambda流形正则系数常用1e-4~1e-1kk近邻图中近邻个数常用5~20gamma高斯核宽度参数σ影响相似度权重的衰减速度我通常这样组织代码一个主函数一个隐层计算函数一个图拉普拉斯构造函数。数据量不大时全部用普通矩阵即可超过一万条样本我建议把W和L都转成sparse矩阵内存会舒服很多。4.2 隐层映射与图拉普拉斯构造代码隐层映射函数我一般写成这样function H compute_hidden(X, W, b, activation) % X: n*d % W: d*L % b: 1*L % activation: sigmoid | tanh | rbf n size(X, 1); if strcmp(activation, sigmoid) H 1 ./ (1 exp(-(X * W repmat(b, n, 1)))); elseif strcmp(activation, tanh) H tanh(X * W repmat(b, n, 1)); elseif strcmp(activation, rbf) % 高斯核映射需要额外传gamma参数这里简化 H exp(-gaussian_dist(X * W)); else error(unknown activation); end end其中W和b可以用rand(d, L)*2-1和rand(1, L)生成。这里需要提醒偏置b的维度要和W的输出维度匹配我见过有人把b生成成n×L直接让隐层计算变成全零排查老半天。图拉普拉斯构造函数function Lap compute_laplacian(X, k, gamma) n size(X, 1); idx knnsearch(X, X, K, k1); % 不包括自身 idx idx(:, 2:end); % 去掉自近邻 W zeros(n, n); for i 1:n diff X(idx(i,:), :) - repmat(X(i,:), k, 1); dist2 sum(diff .^ 2, 2); W(i, idx(i,:)) exp(-dist2 / (2 * gamma^2)); W(idx(i,:), i) W(i, idx(i,:)); end D diag(sum(W, 2)); Lap D - W; % 对称归一化 D_inv_sqrt diag(1 ./ sqrt(sum(W, 2) eps)); Lap D_inv_sqrt * Lap * D_inv_sqrt; end这个函数看起来简单但我建议实际项目里把W和L都声明成sparse类型尤其n超过五千时稠密W矩阵会瞬间吃掉几个GB内存。我曾在一个两万样本的任务里用稠密矩阵直接内存溢出改成sparse(n,n)后才跑通。4.3 SS-ELM求解核心代码主函数半监督部分核心代码很短function beta train_sselm(X, Y, label_mask, opts) n size(X, 1); L opts.num_hidden; % 1. 随机生成隐层参数 W rand(size(X,2), L) * 2 - 1; b rand(1, L); H compute_hidden(X, W, b, opts.activation); % 2. 构造图拉普拉斯 Lap compute_laplacian(X, opts.k, opts.gamma); % 3. 半监督标识矩阵 J J diag(label_mask); % label_mask是n*1逻辑列向量 T Y; % 无标签行全0 % 4. 组装矩阵求解 A eye(L) opts.C * (H * J * H) opts.lambda * (H * Lap * H); B H * J * T; beta A \ B; end这里的label_mask尤其关键它同时体现在J矩阵和T矩阵的处理里。J让无标签样本的误差项不进入目标函数T的无标签行虽然置0但不影响最终结果因为对应的J在乘法中把贡献屏蔽了。很多复现SS-ELM的人在这块容易绕晕我以为当初写代码时也怀疑过“无标签行的标签设成0不会误导吗”推导一遍公式就明白了——它只是占位真正决定无标签样本影响力的是流形正则项的L。4.4 US-ELM求解和聚类代码US-ELM部分区别更大因为不需要J和T目标函数直接变成广义特征值问题function cluster_labels train_uselm(X, num_clusters, opts) n size(X, 1); L opts.num_hidden; W rand(size(X,2), L) * 2 - 1; b rand(1, L); H compute_hidden(X, W, b, opts.activation); Lap compute_laplacian(X, opts.k, opts.gamma); % 广义特征值问题 A H * Lap * H; B H * H; [V, ~] eigs(A, B, num_clusters, smallestabs); beta V; embedding H * beta; % 对嵌入做kmeans聚类 cluster_labels kmeans(embedding, num_clusters, Replicates, 10); end这里有一个重要细节取最小特征向量时要不要剔除全零的特征向量如果是归一化拉普拉斯最小的特征值对应0它的特征向量是常数向量输入给kmeans没有区分度。但eigs返回的前几个特征向量通常会包含这个常数方向我建议要么从第二个特征向量开始取要么直接对所有特征向量做一次小方差过滤。我自己的做法是取num_clusters1个最小特征向量去掉第一个常数方向保留后面num_clusters个。如果你发现聚类结果某簇特别大其他簇特别小往往就是常数方向混进去了。5. 参数选择与调优经验含踩坑记录5.1 五个关键参数的推荐范围和一个建议调试顺序SS-US-ELM看起来很省事但参数一多还是容易迷茫。我整理了一个比较实用的参考范围表参数推荐范围说明num_hidden100 ~ 1000太小欠拟合太大计算变慢效果不一定更好C1e-3 ~ 1e3有标签样本越多、越可靠C可适当调大lambda1e-4 ~ 1e-1无标签数据越有结构lambda的作用越明显k5 ~ 20数据越密k可以稍大但要避免图过密失去局部性gamma数据特征尺度可以用所有样本对距离的中位数做初始值调试顺序我建议固定成先用纯监督ELMλ0跑一遍有标签数据确认隐层节点数和C在一个合理范围然后逐步增大λ观察交叉验证或聚类指标。不要一开始就所有参数同时调否则出了问题根本定位不到是哪个参数引起的。5.2 激活函数和近邻图的数值稳定性问题激活函数的选择会直接影响隐层输出H的数值范围。sigmoid输出在(0,1)之间tanh在(-1,1)之间RBF高斯核在(0,1)之间但衰减速度受gamma影响很大。如果H的数值范围太小HᵀH和HᵀLH的数值就会接近机器精度求解β时容易出现奇异或矩阵不可逆。我在一个任务里用RBF核时把gamma设得太小导致H几乎全为0A矩阵条件数爆炸A\B警告矩阵接近奇异。解决方法很简单把gamma调到数据距离的中位数量级并检查H的方差是否在一个可接受范围。近邻图构造的数值问题同样常见。knnsearch默认用欧氏距离在特征维度很高时距离几乎全部集中在某个均值附近高斯核权重会退化成近似常数流形正则项失去区分度。这种情况我会先对X做标准化或PCA降维再构造图。注意标准化一定要用全部样本的均值和方差计算不能用有标签子集单独算否则会把分布信息泄露给训练过程。5.3 新手最常遇到的问题和排查思路第一个高频问题是“无标签样本的预测输出全为0”。大多数人会怀疑是代码bug但其实先检查J矩阵和T̃矩阵是否正确。如果J的对角线在无标签位置不是0而是1那无标签样本的误差就被强行加进了目标函数模型为了拟合那些全0标签当然输出全0。排查方式是打印所有样本的误差均值看有标签和无标签样本的误差是否分布合理。第二个高频问题是“eigs收敛失败或警告”。这多半是A或B矩阵不是对称半正定的。HᵀLH理论上是对称半正定的但数值上如果拉普拉斯矩阵的对称性被破坏了比如图构造时只给W设了上三角没设下三角对称性就没了。我建议构造L后直接检查norm(L - L)如果大于1e-10说明对称性有问题需要对L做一次(LL)/2处理。第三个问题是“隐层节点数一多SS-ELM效果反而变差”。这在半监督场景很常见无标签数据占比高时隐层过大意味着模型参数太多而流形正则虽然提供了约束但约束强度不够时噪声仍然会被记住。解决思路不是简单调小隐层而是同步调大λ让流形正则的约束力跟模型容量匹配。6. 应用场景扩展与我的使用体会6.1 适合SS-US-ELM的任务特征结合我自己的跑题经验SS-ELM特别适合“标签极少、无标签数据量大、数据本身有清晰流形结构”的任务。典型的比如文本分类里只有几十篇人工标注但语料库里躺着几万篇未标注文档词语的分布本身就带着很强的结构线索再比如工业场景的传感器故障诊断正常样本随手可得故障样本要等真实故障出现才有一条。US-ELM则适合那些“不知道有几类、需要快速看结构”的探索性分析比如用户行为分群、图像特征预聚类在正式标注之前先跑一遍US-ELM看看簇与簇之间分得开不开能省不少标注预算。一个我自己印象很深的例子实验室A同学要给一组高维传感器数据做状态识别手里只有20来个有效标注样本剩下四千多个样本不知道属于哪类。他一开始直接用监督ELM训练测试准确率最高只有61%而且很不稳定。后来我把SS-ELM的框架给他无标签样本参与流形正则后准确率在同一个数据集上直接到了82%。差别就在于那四千个样本虽然没有标签但它们在高维空间里的近邻关系把不同状态之间的边界“撑开”了分类器因此变得稳定很多。6.2 扩展思路集成、多核、在线学习SS-US-ELM的框架还能往几个方向扩展。第一个是多核ELM把不同激活函数sigmoid、tanh、RBF生成的隐层拼接在一起相当于构造一个更宽的特征空间之后再套SS-ELM或US-ELM往往能提升流形正则项的表达力。第二个是集成学习每次随机生成不同的W和b重复训练多个SS-ELM模型最后对输出投票或取平均。ELM本身带随机性集成能显著平滑这种方差。第三个是在线学习如果数据流式到来可以分批更新图拉普拉斯和H矩阵再用增量方式更新β不必每次全量重算。不过扩展时也要注意控制成本。多核拼接会让L成倍增加HᵀLH的计算代价上涨很快集成训练则是拿时间换稳定性。我一般会先跑一个单模型判断方向再决定要不要上扩展方案。6.3 关于这套框架的使用体会写了这么多代码和公式最后聊聊我对SS-US-ELM的个人判断。它的定位不是要替代深度神经网络而是给“标签稀缺、算力有限、需要快速出结果”的场景提供一个高性价比选项。优点非常明显闭式解、不迭代、Matlab代码几十行能跑通、原理透明可控。瓶颈也很清楚图拉普拉斯构造依赖k近邻样本量大了以后计算量是瓶颈随机隐层虽然快但可复现性不如全监督学习的显式特征提取需要多次随机种子做对比才能确定结果稳定。我自己的实践心得是拿到一份新的SS-US-ELM代码千万不要直接拿默认参数去跑业务数据。先在一个小规模子集上把隐层节点数、C、λ分别扫一遍画出一条简单的性能曲线确认这套代码在你的数据分布下确实work再放大到全量数据。ElM系列最迷惑人的地方在于它训练得太快了以至于样本处理流程里细微的错误会被速度掩盖。只有先在小样本上把机制跑通心里才有底。最后再分享一个小技巧如果你不太确定无标签数据的图结构应该用多少近邻数不妨把k从5到20扫一遍观察β矩阵的变化幅度。如果k在这个区间内β的数值变化很小说明流形正则对k不敏感结果可靠如果β变化巨大那说明你的特征尺度或归一化方式有问题先回去修数据预处理比调k更重要。这套代码的价值不在于“跑通”而在于你通过调整λ和k真正理解了无标签数据是如何通过图结构把自己的先验知识传递给分类器的。
阅读完成 · 觉得有帮助?
咨询建站