恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Matlab实现正则化逻辑回归:微芯片质检预测模型实战
首页
资讯中心
/
Matlab实现正则化逻辑回归:微芯片质检预测模型实战
Matlab实现正则化逻辑回归:微芯片质检预测模型实战
发布时间:2026/10/11 9:07:30
前阵子一批微芯片出厂前质检的数据被送到了我手上每一颗芯片都记录了工程测试环节的两个关键读数标签就是“通过”和“不通过”。工程师把散点图拿来的时候我一眼就看出问题——两类点交缠在一起一条直线根本切不开。最后我交回的方案是用Matlab实现的基于正则化逻辑回归的微芯片质检预测模型。训练集准确率能到八成以上而且模型直接输出每颗芯片的合格概率把疑似不合格的点提前捞出来供人工复检比简单扔一个“过/不过”的结论实用得多。这篇主要写给三类人一类是在产线上做质量数据分析、想快速建立二分类预测规则的工程师一类是学机器学习课程、正在为课程设计或论文实验挠头的学生还有一类是把Matlab当成日常工具、想搞明白逻辑回归损失函数和正则化系数到底怎么落地的人。下面把完整思路、关键代码和几个坑都摊开讲。先解释一个绕嘴的地方逻辑回归名字里带“回归”俩字干的却是分类的活儿。它在特征加权和之后套了一个sigmoid函数输出0到1之间的概率概率大于0.5就判成正例否则判成负例。理解了这点后面看损失函数和梯度公式就都顺了。1. 这个质检问题的底子数据长什么样、难在哪1.1 现场数据形态与二分类目标先说我手里这份数据的基本结构。每颗芯片有两个连续型测量指标这里就叫它们Test 1和Test 2标签y取值只有两个1表示芯片质量合格可以流到下一道工序0表示不合格需要报废或返工。样本量不大总共118个点左右放在真实产线上也就是一波抽检的量。目标是建立一个预测模型给定一对Test 1, Test 2读数模型输出该芯片合格的概率。这类问题在质量工程里非常典型。你不一定真的只有两个测量指标可能还有温度、电压、频率响应等十几个指标但问题本质是一样的用可测的连续特征去预测不可直接观测的质量状态并把不确定性转化成概率。芯片出厂前的电学测试、PCB焊点的阻抗检查、注塑件的外观尺寸测量抽象出来都是同一个骨架。这也是我坚持用这个简单数据集讲完整流程的原因——把骨架搭对了换数据只是换表格的问题。1.2 线性可分性的快速研判拿到数据第一件事不是建模而是快速看数据长什么样。我用原始两个特征画了散点图正例用加号、负例用圆圈结果一目了然两类点在二维平面上相互交织存在明显重叠区域没有任何一条直线能把它们干净分开。我顺手用没加任何处理的线性逻辑回归试了一版θ只有三个参数训练集准确率撑死六成多跟瞎猜强不了太多。这个结果不意外。线性模型在二维平面上只能画直线而这批数据的真实分界显然是一条曲线甚至更复杂的闭合区域。如果你在自己的数据上也画出这种“线性不可分”的形态接下来的动作基本是两条路要么换非线性模型要么在特征上做文章。我这个项目走的是后者因为特征侧的空间还没挖掘完。1.3 特征映射把直线切不动的问题抬到高维去切特征侧做的文章叫特征映射英文里常写成mapFeature。想法很朴素原来只有x1、x2两个特征线性模型的形式是θ0 θ1·x1 θ2·x2只能在二维空间画直线但如果把特征扩展成x1、x2、x1²、x1·x2、x2²、x1³、x1²·x2……一直到6次项的组合线性公式就变成一个28维空间里的超平面。高维空间里的超平面投影回原来的二维平面就是一条弯弯曲曲的曲线正好可以贴合那些非线性边界。用生活里的例子理解你在平面地图上画一条直线分不开两个区域但如果把高度信息加进来变成三维一个平面可能就把山区和平原切开了。特征映射就是这个思路。代价也很直观参数从3个变成28个模型表达能力上去了过拟合风险也跟着上去了后面正则化就是用来压住这种风险的。关于为什么选6阶而不是更高阶我自己的习惯是从3阶开始往上涨每涨一阶就在验证集上看误差变化。6阶对118个样本来说已经足够表达复杂边界再往上特征数量急剧增加比如10阶会扩展到66维更容易触发过拟合收益却很小。具体代码实现我在第三节给完整版本。2. 为什么偏偏是正则化逻辑回归2.1 小样本二分类场景下的选型对比说实话拿到这个题目我第一反应不是“上什么高级模型”而是“什么样的模型在这个场景里最稳”。我把常见方案放在一张表里横向比过模型能否表达非线性边界输出概率小样本表现可解释性实施成本逻辑回归 特征映射能自然输出较好配正则化后很稳高系数和边界可直接展示低Matlab原生可跑支持向量机核函数能需要额外Platt缩放较好但调参维度更多中核函数不可直观解释中决策树 / 随机森林能可估计小样本下树结构不稳定中高低神经网络能软输出样本少时极易过拟合低黑盒高这张表已经很说明问题工业质检这种要求“能解释、能快速部署、样本量不大”的场景正则化逻辑回归几乎是性价比天花板。它不需要昂贵的训练资源也不会因为一两百条样本就学飞。更重要的是逻辑回归天然输出概率这个概率在质检流程里能做分级处理而不只是一个硬分类标签。相比之下SVM的核函数不好跟同事解释树模型的切分点对样本扰动太敏感神经网络在这个样本量下基本是过拟合表演现场。我的底线是模型复杂程度与数据规模匹配。样本量就这么点先把能完全解释的模型跑通后面样本积累到几千条再升级模型结构完全来得及。2.2 逻辑回归损失函数里的两个部分老规矩先从损失函数说起。逻辑回归的代价函数由两部分组成第一部分是交叉熵项衡量预测概率和真实标签的偏差第二部分是正则项惩罚参数的模长。写成公式就是J(θ) -(1/m)·[yᵀlog(h) (1-y)ᵀlog(1-h)] (λ/2m)·Σ_{j2}^{n} θ_j²其中h是sigmoid(Xθ)m是样本数λ是正则化系数θ_1也就是偏置项不参与惩罚。第一部分保证模型尽量拟合训练数据第二部分保证模型参数不会长得太离谱两者互相拉扯λ就是那条拉锯的调节杆。很多初学者会把正则项写成对所有θ包括θ_0都惩罚这在某些框架里也没错但在经典的逻辑回归实现里偏置项单独排除是更常见的做法。原因很简单偏置项只负责整体平移不参与边界的弯曲程度惩罚它没有实际意义还可能让模型在没有数据覆盖的区域产生奇怪的偏移。2.3 L1、L2、弹性网正则化的取舍正则化不是只有L2一种。L1正则化会把不重要的系数直接压成0得到稀疏解适合特征成百上千、想顺带做特征选择的场景L2正则化则是把系数整体往0的方向均匀收缩不会真的归零适合特征维度不算高、特征间相关性强的场景弹性网正则化是两者的线性组合兼顾稀疏和稳定。我这个项目特征映射到6阶之后一共28个特征维度不高而且多项式展开出来的特征之间高度相关硬套L1反而容易在一组高度相关的特征里只保留一个导致决策边界不稳定。所以我选L2作为基准方案这也是标题里“正则化逻辑回归”最常见的默认形态。如果你的业务特征特别多比如直接上10阶映射或者加入几十个原始传感器指标那时候再考虑弹性网也不迟。2.4 不强行给高级模型加戏的底气我见过不少同行一上来就想用随机森林或者深度学习理由往往是“效果可能更好”。但在118个样本的质检场景里树模型的切分点对样本扰动极其敏感换一批数据边界就全变了神经网络在这个样本量下基本就是过拟合表演现场除非做极强的数据增强和早停否则训练集漂亮、测试集翻车是家常便饭。反观正则化逻辑回归参数少、优化目标凸只要特征映射和λ选得合适训练过程非常可控。到了实际部署阶段你能从训练好的权重里看出哪些高次项在起作用还能把决策边界画给质量部门的同事看。这种透明性在产线审核时极其加分因为大家不只想看结论还想知道模型为什么这么做判断。3. Matlab代码实现全流程从损失函数到收敛3.1 环境、数据读取与变量保护我用的是较新版本的Matlab但下面的代码在R2016b之后的版本基本都能跑主要依赖是优化工具箱里的fminunc函数。如果你手头没有这个工具箱第四节我也会给一段纯手写的梯度下降替代代码。先读数据。假设数据文件是文本格式或CSV前三列分别是Test1、Test2和标签ydata load(microchip_data.txt); x1 data(:, 1); x2 data(:, 2); y data(:, 3);这里有个我一开始就踩过的坑千万别急着把原始特征覆盖掉。后面画散点图和决策边界都要用到原始的x1和x2一旦你把它们mapFeature之后再赋值给X原始分布就丢了再想画图就得重新读文件。所以上面我特意把x1、x2单独保存这是很便宜的自我保护。如果你用Excel表格存数据换成readmatrix(microchip_data.xlsx)同样能读格式上没什么可说主要是数据结构要统一成“特征列在前、标签列在后”的规整形式后面建模更省心。3.2 特征映射函数mapFeature特征映射的代码非常短但逻辑要讲清楚。我们要生成从1次到6次的全组合多项式项每一项的形式是x1^(i-j)·x2^j其中i从1到6j从0到i。比如i2时会生成x1²、x1·x2、x2²三项i3时生成x1³、x1²·x2、x1·x2²、x2³四项。加上开头全1的偏置列总共112345628列对应28个参数。function out mapFeature(X1, X2, degree) out ones(size(X1(:, 1))); for i 1:degree for j 0:i out(:, end1) (X1.^(i-j)) .* (X2.^j); end end end调用方式就一行X mapFeature(x1, x2, 6);这个实现简单粗暴但对当前数据量性能完全够用。如果以后维度涨到十几阶、样本量涨到几万再用arrayfun或向量化技巧去优化。现在这个阶段保持可读性比追求极致性能重要特别是要拿给同事评审代码的时候两个嵌套循环一眼就能看懂在做什么。3.3 代价函数与梯度正则化项的正确写法核心是代价函数和梯度的Matlab实现。我直接给最终代码function [J, grad] costFunctionReg(theta, X, y, lambda) m length(y); h sigmoid(X * theta); J -(1/m) * (y * log(h) (1 - y) * log(1 - h)) ... (lambda / (2 * m)) * sum(theta(2:end).^2); grad (1/m) * (X * (h - y)); grad(2:end) grad(2:end) (lambda / m) * theta(2:end); end配合的sigmoid函数function g sigmoid(z) g 1 ./ (1 exp(-z)); end两个细节值得啰嗦一句。第一正则项只对theta(2:end)求和也就是跳过偏置项这与前面说的逻辑一致第二梯度的正则化部分同样只加到grad(2:end)上否则梯度检查和收敛曲线都会出问题。如果你想把正则化从L2换成弹性网只需要把sum(theta(2:end).^2)这一项改成平方项加绝对值项的线性组合原理一样只是惩罚函数变了。3.4 训练主流程fminunc与手写梯度下降双方案有了代价函数接下来调用优化器。我推荐fminunc它内部用拟牛顿法收敛速度和稳定性都比手写梯度下降好很多initial_theta zeros(size(X, 2), 1); lambda 1; options optimset(GradObj, on, MaxIter, 400); [theta, cost] fminunc((t) costFunctionReg(t, X, y, lambda), ... initial_theta, options);注意匿名函数(t) costFunctionReg(t, X, y, lambda)里X、y、lambda是从工作区捕获的theta则是每次迭代传入的变量。如果你没有优化工具箱可以换成最基础的批量梯度下降alpha 0.1; iters 5000; theta initial_theta; for iter 1:iters grad (1/m) * X * (sigmoid(X * theta) - y); grad(2:end) grad(2:end) (lambda / m) * theta(2:end); theta theta - alpha * grad; end手写版本的关键是观察代价是否每轮下降如果上升就把alpha调小。特征映射后特征范围差异大建议alpha从0.01开始试别一上来就0.5否则很容易发散。另外训练前强烈建议做一次梯度检查用数值差分验证解析梯度没有写错。这个检查我每次换代价函数结构都会跑十分钟能省一下午的排错时间。4. lambda调参实战一组对照实验4.1 实验设计相同特征映射只动λ特征映射固定为6阶训练数据固定唯一变化的是正则化系数λ。我分别跑了λ0、λ1、λ100三组每组都用完整流程训练、预测、画边界、看准确率。目的不是比谁数字高而是看λ怎么通过正则化项改变模型的性格。这里说明一下下面表里的准确率是我在自己数据划分下的一次典型运行结果不同随机划分会有几个百分点的波动所以读这张表重点看趋势不要死记数字。λ训练集准确率验证集准确率决策边界形态诊断087.5%66.7%贴着样本点扭曲成蚯蚓过拟合183.7%79.4%平滑贴合主体分布泛化良好10061.2%63.8%几乎退化成一条直线欠拟合4.2 λ0没有约束的表达力失控λ0时正则项完全消失模型只剩交叉熵部分28个参数被训练数据任意摆布。训练集准确率冲到87.5%决策边界几乎包住每一个正例样本边界上出现大量向内向外凸起的锯齿。问题在于这些凸起大多是噪声的投影不是真实的质量规律。验证集准确率掉到66.7%这就是典型的过拟合信号。模型在训练集上背答案换个环境就露馅。如果你画出来的边界长这样别管训练集数字多好看先告诉自己这个模型不能上线。反过来说也别忘了正则化在这里的价值——它逼着模型放弃那些“只对训练集成立”的细节去学真正稳定的结构。4.3 λ1边界平滑、泛化最好λ1是经典示例里的默认值跑出来的决策边界不紧贴样本而是顺着两类点的整体趋势平滑地绕过去。训练集准确率降到83.7%看上去比λ0低了几个点但验证集准确率反而到了79.4%。这才是我们要的模型牺牲一点训练集上的死记硬背换来了在未知数据上的稳定表现。有些同学会觉得83.7%和87.5%差不多不以为然。但质检场景里真正要命的是验证集那代表未来到货的一批芯片的预测水平。验证集从66.7%到79.4%相当于每100颗芯片少错12.7颗这个改善是实打实的收益。所以说调λ不是在追求训练集上的好看而是在数据分布和模型复杂度之间找一个平衡点。4.4 用验证集选择λ的标准流程现在问题变成λ到底取多少答案不能靠“觉得”要靠验证集。我把数据按60%/20%/20%分成训练集、验证集、测试集对一组候选λ比如0.001、0.01、0.1、1、10、100逐一训练每次都用训练集拟合参数、用验证集计算误差最后选验证集误差最小的λ再用这个λ在测试集上做一次最终评估。这里有个很容易犯的错误直接用测试集来选λ选完又把测试集当“新数据”汇报准确率。这是信息泄漏等于考试前看了答案还说自己是裸考。正确姿势是测试集全程焊死只在最后碰一次。我自己的习惯是先把测试集文件放到另一个文件夹调参阶段根本不开它避免顺手就试的冲动。λ和验证集误差的关系不是单调的画出来通常是一条U形曲线左边过拟合误差高右边欠拟合误差也高中间有个谷底。找到谷底对应的λ就找到了合适的正则化强度。如果你画出来的曲线没有谷底反而持续下降那说明候选λ范围整体偏大往里加更小的值再试一轮。5. 决策边界可视化与质检指标解读5.1 用contour画出决策边界模型训练好之后我最喜欢做的一件事就是把决策边界画出来边给质量部门的同事看边解释边界形状。Matlab里画等高线的思路是在原来两个特征的取值范围内构造一个网格对网格上每个点计算θ的线性组合用contour函数取等高线为0的位置那就是决策面的投影。u linspace(-1.0, 1.5, 200); v linspace(-1.0, 1.5, 200); [U, V] meshgrid(u, v); Z arrayfun((a, b) mapFeature(a, b, 6) * theta, U, V); contour(U, V, Z, [0 0], LineWidth, 2); hold on; plot(x1(y1), x2(y1), k, LineWidth, 2, MarkerSize, 8); plot(x1(y0), x2(y0), ko, MarkerFaceColor, y, MarkerSize, 8);这里为什么用arrayfun而不是双重for循环效果一样但arrayfun一行代码更整洁而且U、V的维度顺序不会错。如果你非要双重for循环记得z(j,i)的行列索引别写反否则边界会转置画出来跟数据对不上。我把λ1画出来的边界给同事看时直观感受是它从右上方绕开了一片负例密集区又在左侧兜住一个正例聚集带整体平滑没有奇形怪状的触手。这个图比任何准确率数字都有说服力因为它直接把模型对数据分布的理解摆在了桌面上。5.2 准确率之外混淆矩阵与漏检率准确率是方便汇报的指标但它会掩盖关键问题如果正负例比例失衡算出来的准确率可能是虚高的。所以质检场景里我通常还要看混淆矩阵并从中算精确率、召回率和F1。要不依赖工具箱手工算就行pred sigmoid(X * theta) 0.5; TP sum((pred 1) (y 1)); FP sum((pred 1) (y 0)); TN sum((pred 0) (y 0)); FN sum((pred 0) (y 1)); precision TP / (TP FP); recall TP / (TP FN); F1 2 * precision * recall / (precision recall);在芯片质检里我特别看重召回率也就是“真正不合格的芯片里我们抓到了多少”。漏掉一颗不合格芯片让它流出比误杀一颗合格芯片的代价高得多。如果漏检惩罚更重我会把判定阈值从0.5往下调比如0.4甚至0.3。注意这不是重新训练只是移动概率阈值的滑杆几行代码就能实现而代价是误检增多需要后续人工复核来兜底。另一个实用技巧是“复检区”概念。sigmoid输出的概率在0.4到0.6之间的样本模型其实相当犹豫我不会硬判而是打个“待复检”标签交给线下抽检。这个区间通常包含大量边界上的难分样本人工介入能以最少的成本消除最不确定的部分。这个玩法在产线现场很受欢迎比一刀切的二分类实用得多。5.3 从演示模型到产线部署的过渡演示模型要真正跑起来还有几件琐碎但重要的事。第一把特征命名、输入顺序、数据单位都写清楚部署时如果读到的特征顺序和训练时不一致模型静默出错是最可怕的第二把训练好的θ系数、特征映射的degree、标准化参数如果做了标准化一起导出用writematrix或csvwrite写成一个模型文件第三记录模型的版本号和训练数据批次后面针对性抽检不达标时能回溯。部署形态可以很灵活。最简单的就是在Matlab里写一个批处理脚本输入新一批芯片的Test1、Test2直接输出概率和判定复杂一点可以用Matlab Compiler打包成独立程序或者把系数导成JSON给产线的其他系统调用。模型本身不复杂迁移门槛很低难点往往在流程和数据规范上。我见过不少项目死在最后一步不是因为模型不准而是因为没人记录特征顺序和版本一个月后自己都看不懂当时的模型文件里存的是什么。6. 踩坑记录与省时建议6.1 特征标准化到底要不要做这是我在真实数据上栽过跟头的地方。多项式展开之后各列特征的量级天差地别比如x1^6和x1可能差出几个数量级。L2正则化对量级敏感一个特征量级大对应参数稍微大一点就被狠罚这会变相压缩高次项的贡献破坏特征映射本来的意义。我在这份演示数据上没做标准化也能跑因为原始特征范围接近[-1, 1]多项式高次项还不算失控。但迁移到真实产线数据强烈建议先对原始特征做z-score标准化再做mapFeature。这里有一个配套的坑一旦训练时做了标准化预测和画决策边界时也必须用同一组mean和std对输入做转换否则模型在推理阶段就像换了坐标系表现会莫名其妙变差。我一般会把mean_x1、std_x1这些参数和θ一起存进模型文件部署时一并读取这样就不会出现训练和预测两条链路标准不一致的情况。6.2 画图时原始数据被覆盖前面我特意强调先保存x1、x2因为确实吃过亏。第一次跑这个项目时我图省事直接写X mapFeature(x1, x2, 6)然后所有后续操作都用X等到画散点图的时候才想起原始特征已经被28列的高维特征覆盖了只能回头重新读文件。这不是技术难题纯粹是变量管理的小教训花十分钟重读文件无害但如果你在数据读取函数后面接了一大段自动化流程这种覆盖会让你排查老半天。建议一进脚本就把原始列数据复制到带raw前缀的变量里比如rawX1 data(:,1)后面怎么折腾都不心慌。6.3 优化器选型和梯度下降的收敛细节fminunc能用就用这是Matlab自带的高质量实现。但有些电脑没有优化工具箱手写梯度下降就必须注意三点学习率从0.01起步迭代轮数不要吝啬至少三四千轮每轮记录代价函数值画出下降曲线如果曲线锯齿状就调小学习率最后要确认梯度范数压到一个很小的阈值比如1e-6再停止迭代。如果嫌手写梯度下降慢可以自己做个简单的动量更新方向上加一个上一轮的加权速度项收敛会快不少。不过在28个参数、几百个样本的规模下这个优化其实一分钟内就完成了没必要过度优化算法本身。更多时候问题出在特征没标准化导致梯度震荡而不是优化器不够高级。先检查数据和梯度再考虑换优化器这是省时间的正确顺序。6.4 正则化项别写错位置正则化相关的代码错误非常隐蔽最常见的有两个一是忘记跳过θ_1把偏置项也惩罚了导致边界整体偏移但曲线形状没变化准确率下降却不明显二是梯度里忘了加正则化项导致fminunc算出来的梯度方向不对代价函数明明在下降但参数却越走越偏。这两种错的排查方法都是梯度检查数值梯度与解析梯度的范数一旦超过1e-4就要怀疑是不是把θ_1算进去了。我把梯度检查当成固定流程每次改完代价函数都跑一遍。它不费多少时间却能精准定位那些“看起来对、实际上错”的数学实现问题。这也是为什么标题里“研究”两个字在我看来是有分量的反复验证自己的实现是正确的比跑通一个漂亮结果更值钱。尤其在这类教学型项目里跑通一次不稀罕把每个细节都验证明白才说明你真正吃透了。6.5 把这套方法挪到其他质检任务的通用步骤这套流程的通用性比想象中强。拿PCB焊点检测来说特征是阻抗、电容等测试参数标签是焊接质量合格与否拿电机振动监测来说特征是不同频段的振幅特征标签是是否存在异常。换场景时只要把“特征映射 正则化逻辑回归 验证集选λ”这条链路原样搬运第一步换成业务数据清洗第二步换成特征工程后面几乎不用动。唯一要特别小心的是样本不平衡。如果合格芯片占95%、不合格只占5%直接训练逻辑回归会倾向于把什么都判成合格。处理办法有两个一是对负例做过采样或对正例做下采样二是在代价函数里给两类样本赋予不同权重。这类调整不影响正则化逻辑回归的主干逻辑但能把模型从“大概率正确”拉到“对小概率事件也敏感”的正确轨道上。我自己在实际项目里逐渐形成的习惯是小样本、难解释、要快速上线时优先用这套方案等样本量上来、边界越来越复杂再把特征工程和模型一起升级。检验一个模型能不能进产线不是看它在训练集上多威风而是看它在没见过的数据面前有多稳。λ的调节、阈值的移动、复检区的设置本质上都是在往“稳”这个方向使劲。