资讯动态

感知机学习策略:损失函数、梯度下降与收敛性分析

发布时间:2026/10/19 22:23:34 来源:尧图企业网站定制
上周刚把感知机的模型结构聊完不少朋友在后台问模型会算了权重和偏置也有了但它到底是靠什么规则一步步学会分类的为什么大家一提到感知机学习的损失函数都是所有误分类点到超平面的距离之和而不是更直观的误分类点的个数这期就把这块补上认真拆一拆感知机的学习策略从损失函数怎么定到梯度下降为什么能用再到学习率到底怎么调、收敛性怎么理解我都结合自己跑过的实验和踩过的坑来聊不整虚的。顺便说一句这篇是感知机(2)默认你已经知道感知机的前向计算输入特征加权求和过符号函数输出正负类。如果还没看过上一篇建议先回去把模型部分过一遍否则下面好几处推导会跟不上。1. 损失函数为什么是误分类点到超平面的距离而不是误分类点的个数1.1 误分类点的个数为什么没法直接优化先做个小实验假设你手里只有两个特征数据分布大致能被一条直线分开但初始权重随便给得很离谱。你心算一下现在分错3个点下一轮想让它变成分错2个点。听起来很合理但问题是分错3个点到分错2个点之间权重需要变化多少这个变化方向是不连续的。这个连续性的问题很关键。训练的本质是反复微调参数希望损失函数的值一点点变小。如果损失函数像台阶一样参数稍微动一点损失值要么不变、要么突然跳一格那梯度这个概念就没有意义了——因为台阶面上没有坡你不知道往哪个方向推能走下台阶。我最早做这个实验的时候也觉得感知机判断对错就是一个符号函数的事预测错了我给它一个惩罚预测对了不惩罚这很自然啊。但把损失函数画出来之后就明白了误分类点个数这种计数式的损失是阶跃的处处不连续没有梯度信息对它做梯度下降无从谈起。这就像你站在一个棋盘的格子上你能看到下一格是黑是白但你没法判断稍微往东南方向挪一点会怎样——因为棋盘格是离散的。1.2 点到超平面的距离公式是怎么来的那为什么要用距离呢因为距离是连续的。一个点离超平面多远这个数值随权重平滑变化你稍微动一点点权重距离就跟着动一点点梯度就有了方向。点到超平面的距离公式本身不复杂。如果超平面是 (w \cdot x b 0)那么空间中任意一点 (x_0) 到这个超平面的距离是[ \frac{|w \cdot x_0 b|}{|w|} ]这里的分子是点的函数值的绝对值分母是权重向量的模长。这个式子几何上很清楚你把权重向量当成超平面的法向量函数值 (w \cdot x_0 b) 实际上描述了点在法方向上的偏移量除以法向量的长度就得到了真正的垂直距离。对于被误分类的点还有一个额外的性质它的函数值符号和真实标签相反。也就是当 (y_i 1) 的时候(w \cdot x_i b) 算出来是负的当 (y_i -1) 的时候(w \cdot x_i b) 算出来是正的。既然符号相反那么乘上标签之后(y_i(w \cdot x_i b)) 必然是一个负数套上负号就得到正值[ -y_i(w \cdot x_i b) |w \cdot x_i b| ]所以误分类点到超平面的距离可以直接写成[ \frac{-y_i(w \cdot x_i b)}{|w|} ]这个写法里没有任何绝对值符号光滑性更好而且梯度的计算路径非常直接。当时我推到这里才反应过来所谓距离之和并不是额外引入了一个复杂的几何概念它本质上就是把误分类点这个条件本身携带的信息用数学形式表达出来了。1.3 去掉范数简化但本质没变原始形式里损失函数定义为[ L(w, b) -\sum_{x_i \in M} y_i(w \cdot x_i b) ]注意分母的 (|w|) 被去掉了。这是我刚开始自学时最困惑的地方明明距离公式里有分母为什么损失函数里没有书上的解释是不考虑 (|w|)或者因为最终梯度会变号去掉后不影响优化方向但我觉得直接这样讲多少有点抽象。换个角度来理解我们要优化的是让所有误分类点都离超平面越远越好最终目标是让误分类点的个数变成0。在误分类点被修正的过程中权重向量的方向变化才是关键而长度大小本质上是冗余的——把权重向量等比例放大缩小超平面的位置其实没变。再深一层说如果保留 (|w|)梯度里会出现一个关于权重本身的分量优化行为会更复杂还可能出现某些数值上的病态情况。去掉它之后损失函数变得更线性梯度更干净算法的每一步更新都变得特别简单。这是感知机能够在1960年代就提出的重要原因——它追求的不是理论上的最漂亮而是工程上的最可行。注意这个损失函数是只针对误分类点来算的。也就是说每次更新之前你得先找出现在哪些点分错了如果所有点都分对了损失就是0也就没有梯度可算了。这个分对就不管、分错才惩罚的思路和后面要讲的对偶形式、支持向量机的最大间隔思想都有联系但感知机没有考虑分类正确但有风险的点这也是它和SVM的一个核心区别。2. 梯度下降在感知机里到底是在做什么2.1 从损失函数到梯度一步步求偏导有了损失函数接下来就是标准的梯度下降流程。损失函数 (L(w, b)) 只对误分类集合 (M) 求和那么对 (w) 求梯度得到[ \nabla_w L -\sum_{x_i \in M} y_i x_i ]这个式子比很多带正则项、带非线性激活函数的模型要简单太多。没有链式法则套好几层没有很复杂的矩阵运算就是一个简单的累加把误分类点的特征向量乘以它的标签正负1加总再取个负号。同理对偏置 (b) 求梯度[ \nabla_b L -\sum_{x_i \in M} y_i ]所以梯度下降的更新公式就是顺着负梯度方向调整(w \leftarrow w \eta \sum_{x_i \in M} y_i x_i)(b \leftarrow b \eta \sum_{x_i \in M} y_i)我当时第一次手推这个更新公式的时候愣了一下原来这就是用误分类点本身来修正权重把一个误分类点对应的特征向量的方向加到权重上相当于让超平面朝更能包容这个点的方向转动。这个行为非常直观一个点分错了说明当前超平面在它身上站错了边那就把超平面往它的方向拉一拉。2.2 为什么不直接解解析解而要用梯度下降有一个问题大家可能也会想损失函数看着挺简单误分类点的求和也不复杂能不能直接令梯度等于0解出来理论上可以写出解析式但实际上有个硬伤——误分类集合 (M) 是依赖当前 (w, b) 的也就是说求和的集合本身会随着参数变化而变。这导致整个优化目标不是一个固定的二次函数或者线性函数而是一个分段的目标参数变一下哪些点算误分类就变一下目标函数也跟着换一套。这种情况下直接解方程是不现实的只能迭代着来。这是感知机学习策略里最微妙的地方。每次更新之后原本分错的点可能分对了另外又有新的点被判错误分类集合一直在变。梯度下降的做法是走一步看一步这一刻朝着能减少当前误分类点损失的方向走走完一步重新算谁分错了再决定下一步往哪走。它不强求一步到位而是通过反复迭代让误分类点的数量逐步减少。我拿一个有20个线性可分样本的模拟数据集跑过一个小实验把每一次迭代后的误分类点个数记录下来。最直观的感觉是前几次迭代误分类点数掉得很快可能从20掉到8再从8掉到3但后面几轮的修正往往是在不同点之间反复横跳甚至某一次迭代之后误分类点数不降反升但是再迭代一步又降下来了。这也是迭代类优化算法的典型特征损失不会严格单调下降但整体趋势向下。2.3 一个样本一个样本更新 vs 一批一批更新感知机学习策略里其实有两种更新方式一种是每次随机挑一个误分类点就更新一次这是课本上最常说的随机梯度下降形式另一种是把当前所有误分类点的梯度加起来做一次批量更新。随机方式的做法是随机选取一个误分类点 ((x_i, y_i))(w \leftarrow w \eta y_i x_i)(b \leftarrow b \eta y_i)批量方式的做法是找出本轮所有误分类点累加所有 (y_i x_i) 和 (y_i)统一更新一次两种方式理论上都能收敛只要数据线性可分、学习率合适。但从实际感受来说随机方式收敛路径更蜿蜒每次只基于一个点来决定更新方向常常是这个点刚拉回来、那个点又出去了。批量方式每步的信息量更大方向更平稳但是计算成本也高而且有一个问题如果数据集很大每轮都遍历全部样本找误分类点开销不小。我自己在实验里倾向于随机方式原因有两条一是它符合感知机这个算法的历史定位简单、内存占用小二是在线学习场景下数据本来就是流式进来的你也不可能等全量数据算完再更新。但如果你是跑一个离线的小规模实验想更快看到稳定的收敛曲线批量方式也不差尤其是数据量只有几百条的时候两者的差别主要是风格上的。3. 学习率怎么调才算不翻车3.1 学习率太大太小的实际表现感知机的更新公式里只有一个超参数学习率 (\eta)。它的作用直接决定每一步权重被拽多远。按理说只要线性可分任意正的学习率都能收敛这一点后面收敛性章节会展开但能收敛和实际体验好是两回事。学习率设得太大比如 (\eta 1) 甚至更大每次更新权重就会剧烈摆动。我做过一个对比实验同一份数据(\eta) 从0.1调到1.0误分类点个数的曲线从平滑下降变成了锯齿状跳动虽然最后也能收敛但中间会在分界面附近来回震荡很久。学习率设得太小比如 (\eta 0.001)问题就完全反过来了——每一步都挪得跟蚂蚁一样。数据量小的时候可能感觉不明显几千次迭代总能磨到收敛但数据量一大训练时间会成倍增长而且在某些极端初值下可能会有卡在一个位置很久不动的观感。其实就是每一步的修正量太小不足以让当前误分类点翻过超平面这一边。经验值大多数感知机实现里学习率取0.1到1之间比较稳。我自己最常用的是0.1如果想看更平滑的收敛过程就调小一点如果想快速看个大概结果就设成1。但设成1的时候如果初始权重较大前期震荡会很明显你可能需要先对特征做归一化否则一个特征量纲很大、另一个很小更新方向会被大量纲特征主导收敛路径会很诡异。3.2 步长对收敛路径的影响一个可视化的记忆为了搞清楚学习率到底怎么影响行为我做过一个二维数据的可视化实验。数据集是两个高斯分布生成的点簇蓝色一堆在左下橙色一堆在右上线性可分。初始权重设成零向量偏置也是0。然后用不同学习率各跑一遍把每一轮的超平面画出来。学习率0.01的时候超平面像是一帧一帧地慢慢挪动从初始位置一路平移到正中间中间几乎没走过回头路。学习率0.5的时候超平面会在两簇点之间来回甩幅度很大每一帧都像在跳探戈但肉眼观察下来它的平均位置一直在朝正确的分隔带移动。学习率1.5的时候就更夸张了超平面直接转过了头又从另一侧转回来好几轮都在正-负-正之间抽搐但最终依然能稳定在分界线上。这个实验让我对学习率不敏感有了一个更立体的认知感知机对学习率的容忍度比很多现代模型都要高因为它的更新完全由误分类点驱动不存在局部最优陷阱。你不需要像调深度网络那样精心选择学习率只要别选到极端离谱的数值它总能收敛差别只在路径好不好看、收敛快不快。3.3 我常用的几个调试技巧关于学习率调参说几条自己的经验。第一条如果数据没有归一化先把特征缩放到相似的范围。感知机的更新公式对特征尺度非常敏感如果一个特征取值范围是0到1另一个是0到10000那么每次更新中第二个特征对应的权重分量会被拉得极快第一个特征几乎学不到东西。归一化之后学习率的可调范围会宽很多。第二条观察振荡幅度。如果训练误差曲线有规律地上下抖动且幅度不小可以先考虑把学习率调小一半再跑。如果振荡一直不收敛别急着继续调学习率先检查数据到底是不是线性可分的——不可分数据即使学习率调到0.0001也永远收敛不了这个要分清。第三条初期可以故意加大学习率让权重迅速从一个比较差的初值区域走出来。比如先用 (\eta 1) 跑几个epoch让分类面大致到位再降低到 (\eta 0.1) 做精细调整。这种粗调再精调的思路在很多优化算法里通用用在感知机上虽然简单但效果意外地好。4. 感知机收敛性的直觉与边界条件4.1 线性可分性一切讨论的前提感知机学习策略的一个核心定理是如果训练数据集是线性可分的那么感知机学习算法在有限次迭代后一定能收敛找到能把所有数据正确分类的超平面。这个结论在数学上有严格证明但更值得琢磨的是它的前提——线性可分。线性可分的意思是存在一个超平面 (w^* \cdot x b^* 0)对所有训练样本都有 (y_i(w^* \cdot x_i b^*) 0)。注意这里的符号是严格大于0也就是说样本不在超平面上。如果某个样本恰好落在超平面上它的函数值是0感知机的符号函数输出结果无法定义这种情况要排除。我跑过很多实验之后才意识到教科书上的定理证明看起来有点绕但实际上它抓住了一个非常朴素的直觉既然有一个参考超平面能把所有点分对那感知机每次走错一步后它的权重向量和这个参考超平面的法向量之间的夹角都会变小一点点。只要夹角的减小是有下界的那么有限的修正次数之内就能把方向拧到正确的那一侧。4.2 收敛性定理的核心思想夹角在缩小为了更好地理解这个收敛过程拿二维空间举个例子。假设参考法向量是 (w^*)当前权重是 (w_t)。(w_t) 在每次更新后按下式变化[ w_{t1} w_t \eta y_i x_i ]从内积的角度看(w_{t1}) 与 (w^*) 的内积可以展开成[ w^* \cdot w_{t1} w^* \cdot w_t \eta y_i (w^* \cdot x_i) ]因为参考超平面能正确分类所以 (y_i(w^* \cdot x_i) 0)。这意味着每修正一次当前权重和参考权重在方向一致性上就提升一点。但权重向量的长度也在增长因为每次更新都加了一个向量所以不能直接说内积变大就代表角度变小还得看模长的增长速度这也是证明里要用到柯西不等式和Rademacher复杂度之类工具的原因。不过直觉上你可以这样理解每一步修正都在把当前超平面朝能容纳更多误分类点的方向推而参考超平面是一个永远能容纳所有点的目标所以你的修正方向在统计意义上和通往目标的方向是一致的。这种一致性导致了收敛性的成立。证明里还有几个常数值得注意一个是参考权重向量到原点的距离或者说间隔另一个是数据点特征向量的最大模长。如果数据点离可分的超平面特别近、间隔特别小收敛所需迭代次数就会变大如果特征向量的模长特别大每一步的更新幅度也会变大收敛次数的上界也会受影响。用大白话说数据越好分间隔大感知机收敛得越快数据越勉强可分间隔小你就得等更久。4.3 数据不可分时的实际表现永远停不下来如果数据不是线性可分的感知机就永远不会收敛。这个话听着像废话但实际跑起来你会看到一个特别真实的现象训练过程会一直循环往复误分类点集合换来换去损失函数呈现周期性或者不规则振荡永远到不了0。我在一次实验里故意用了两个有重叠的高斯分布数据让它们本质上不可分。跑感知机的时候训练误差曲线稳定在大概20%左右不下来每一轮都会有一小撮点反复横跳今天分对了、明天又分错了算法永远找不到一个能让所有点都满意的超平面。这个现象是感知机最明显的短板也是后来大家为什么转向支持向量机、核方法、以及更复杂的模型的重要原因——SVM的核心思想之一就是在硬间隔不行的时候引入软间隔允许一部分点犯错而不是像感知机这样死磕。如果你在实战里遇到感知机训练不收敛第一反应应该是数据可能不是线性可分的。这时候有几个务实的选择一是换个特征空间看看能不能通过某种变换让数据变得线性可分二是直接用支持向量机等模型来替代三是如果业务场景允许一定误差可以用经过改进的带正则化或者带边距的感知机变体。我在一个二分类小项目里就曾经踩过这个坑当时以为是自己学习率没调好调了一下午后来画了一下数据分布才发现两个类别重叠得很厉害根本不是调参能解决的事。5. 写一个最小实现从零跑通再谈优化5.1 核心代码逻辑二十行搞定说了这么多理论实际上感知机的最小实现真的特别短。我用Python写过一个最朴素的版本核心逻辑基本就是两层循环加一个条件分支。import numpy as np class Perceptron: def __init__(self, learning_rate0.1, max_epochs100): self.lr learning_rate self.max_epochs max_epochs self.w None self.b 0.0 def fit(self, X, y): n_samples, n_features X.shape self.w np.zeros(n_features) self.b 0.0 for epoch in range(self.max_epochs): misclassified 0 for i in range(n_samples): # 线性输出 linear_output np.dot(self.w, X[i]) self.b # 如果误分类就更新 if y[i] * linear_output 0: self.w self.lr * y[i] * X[i] self.b self.lr * y[i] misclassified 1 if misclassified 0: print(f第 {epoch 1} 轮收敛) break def predict(self, X): linear_output np.dot(X, self.w) self.b return np.where(linear_output 0, 1, -1)这个实现里最需要注意的就是误分类的条件判断。感知机的标签是 (1) 和 (-1)不是 (0) 和 (1)。如果你习惯用0和1做标签条件判断要写成预测类别和真实类别是否相同但更推荐直接统一成 (\pm1)这样 (y_i \cdot (w \cdot x_i b) \le 0) 就是误分类逻辑最干净。5.2 我踩过一次的调试坑偏置没更新写感知机最容易出bug的地方我觉得不是权重 (w)而是偏置 (b)。有一回我在一个模拟数据上跑实验发现怎么训练都收敛不了损失函数在某个数值附近晃来晃去。我查了很久最后发现是更新公式里只写了 (w) 的更新忘了更新 (b)。这个错误特别隐蔽因为只更新 (w) 的情况下超平面仍然会转动只是它永远被限制在必须过原点。如果数据分布不过原点那不管权重怎么调都有一个方向的点死活分不对。当时我盯着代码看了一段时间才反应过来后来我把偏置更新的代码写进去问题立刻解决收敛得很好。所以如果你自己写感知机建议先在一个合成数据上做单元测试。生成两个明显分开的簇一个中心在正区域一个中心在负区域跑几步看看能不能线性分开。这个测试跑不过基本就是实现里有bug而不是算法的锅。5.3 怎么检查你的实现没问题可视化与收敛日志除了正确性检查我建议你在调试时不要只盯着最后的准确率要去看这个过程。第一是打印每一轮的误分类个数。如果数字一路上升或者震荡幅度特别大说明实现可能有问题或者学习率不合理。第二是如果特征是二维的把每一轮的决策边界画出来。你可以用matplotlib每隔几轮画一条线肉眼看超平面是否在逐渐逼近两个类别中间的间隙。这个过程非常直观比任何指标都有说服力。还有个细节epoch的定义。上面这个实现里一个epoch是指完整遍历一遍所有样本。但如果在一轮遍历过程中更新了很多次权重那么之前已经分对过的样本可能在更新后被重新分错同一轮里可能还要再次修正。感知机经典的收敛性证明讨论的是更新次数不是一个epoch的次数。所以我写代码的时候习惯打印两种指标每一轮结束时的误分类点个数以及到目前为止的总更新次数。前者看趋势后者和理论上的收敛上界做对比能帮你判断离边界还有多远。另外提一句数据顺序的影响。如果按固定顺序遍历样本感知机学到最终超平面会偏向数据顺序如果是随机打乱顺序遍历或者随机挑选误分类点最终得到的超平面会在不同次运行之间略有差异。这不是bug而是感知机的固有特性——它找到的是一个可行解不是最优解。我在实际项目中曾经为了让结果可复现固定了随机种子。如果实验报告里不提这个细节别人复现的时候可能得到不同的结果这一点需要留意。5.4 收敛之后还能做什么最后说说感知机训练完成后的延伸。一个最常规的操作是把感知机的权重当作初始化再套到别的线性分类器上。因为感知机收敛快可以在前期快速得到一个不错的起始边界然后切换成SVM、Logistic回归等模型做精细优化。我试过几次在某些数据集上能明显减少后续模型的收敛时间。另外一个延伸方向是感知机的对偶形式这也是很多教材紧跟在这块后面讲的内容。对偶形式里权重不再显式存储而是用训练样本的线性组合来表达更新对象变成了每个样本对应的系数 (\alpha_i)最终模型变成[ w \sum_{i1}^{N} \alpha_i y_i x_i ]对偶形式的实际价值在于它把后续要用的样本内积累计出来了为引入核函数做铺垫。如果你现在只是想把感知机用到自己的任务里原始形式就够了但如果你想沿着这条路线往SVM走对偶形式绝对值得仔细推导一遍。我自己当初就是从这里开始体会到对偶这个概念的妙处的它是一种换个角度看同一个问题的方式常常能打开新的思路。我在实际跑数据的过程中最大的体会是感知机虽然简单但它的学习策略里的每一个选择——用距离定义损失、只惩罚误分类点、随机梯度下降、线性可分才收敛——都不是拍脑袋定的每一个决策背后都有数学上的合理性和工程上的必要性。把这一套逻辑真正搞懂了后面学SVM、学逻辑回归、学神经网络都会顺很多。至少你再看其他模型的损失函数设计时会知道为什么选这个损失和为什么能用梯度下降这两个问题的分量有多重。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑