写这篇东西之前我先交代一下背景。我是一名干了快十年的运维工程师K8s、Ansible、监控告警这些才是我的日常机器学习对我来说坦白讲一直是个“听起来很厉害但跟我没什么关系”的词。直到有一天接手了一套容器平台的容量预测需求——机器快满了要提前扩容光靠固定阈值根本压不住业务抖动我才意识到光会部署和运维工具不够了你得懂一点算法至少得知道模型在算什么。所以就硬着头皮开始啃机器学习。被各种大佬推荐先看西瓜书、再看吴恩达视频结果我卡在了第一关最小二乘法。这个看着简单的概念牵出来一连串数学推导。我花了好多时间才把整个逻辑串通今天这篇就把我从“数学公式恐惧症”到“能自己推一遍”的全过程写出来给同样干运维、又想迈入机器学习这道门的兄弟们做个参考。1. 运维老哥为什么要啃下最小二乘法1.1 运维场景里的“数学时刻”运维的日常是什么巡检查异常、处理告警、调优参数、发布上线。但只要你稍微深入一点就会发现很多东西其实都可以量化CPU使用率随时间怎么变化、明天磁盘还能剩多少容量、某个服务的响应时间是否有劣化趋势。这些问题的本质都是对一堆历史数据做拟合、预测。以前我们怎么做容量管理的凭感觉加预算买机器发现不够了再加。这种做法一来浪费钱二来容易被业务突增打脸。后来用阈值告警CPU超过80%就告警但很多业务的负载是有周期性变化的高峰期本来就容易超过阈值天天误报。真正需要的是预测未来几小时会不会超过阈值这就逼着我去搞一点预测类算法。最小二乘法就是一个特别基础又特别经典的预测方法。它不复杂逻辑非常朴素却是后面学线性回归、Ridge回归、逻辑回归、神经网络甚至推荐系统里矩阵分解的基石。把它啃透后面看再多的算法书都不会再被卡在第一个坎上。1.2 最小二乘法到底在解决什么问题用大白话说最小二乘法做的是这么一件事给你一堆已知的数据点我找一条线或者一个更复杂的函数关系让这条线上的预测值和真实值的误差平方和最小。“最小二乘”这个“二乘”指的就是平方。为什么不用绝对值、不用四次方偏偏用平方这个在后面推导里会有更细节的体会。简单说平方既保证误差贡献是正的、不会正负相抵又让目标函数平滑可导能直接用求导的办法找到最小值所在的位置。我学的时候最大的收获是最小二乘法不是一个“公式”而是一个求最优参数的框架。式子里的系数不是天上掉下来的是你自己去设定目标函数然后通过数学的极小化过程解出来的。这就是机器学习和“调参侠”的本质区别。1.3 前置知识到底要多少很多运维朋友一听说数学推导就被吓退了。我负责任地讲啃最小二乘法不需要高数基础太深只需要掌握三个工具求和符号“Σ”不超过高中数学难度偏导数求导核心就是“对一个变量求导、其他变量当常量”记住几个基本规则矩阵乘法最简单的行乘列规则向量转置、矩阵转置知道就行我翻了不少资料发现大多数人不是被数学打趴而是被推导里跳步太多打趴的。这篇文章我把每一步都尽量拆开写哪怕你全忘光了也能跟下来。2. 从一条直线跑起来一元线性回归推导2.1 建模先把误差写出来假设我们只有一个输入变量x和一个输出变量y要拟合一条直线y ax b这里的a是斜率b是截距。我们手上有一组样本数据(x₁,y₁), (x₂,y₂), ..., (xₙ,yₙ)。对第i个样本模型的预测值是y_i a x_i b真实值和预测值的差距就是误差也叫残差e_i y_i - a x_i - b有些教材写成 y_i a x_i b e_i一个意思。我们希望这个误差整体上尽量小。如果把所有误差相加会出问题误差有正有负相互抵消比如一个100和一个-100相加得0但两个点都偏了很远。所以要用平方来消灭符号问题L(a, b) Σ_{i1}^{n} (y_i - a x_i - b)²这个L通常叫损失函数或者目标函数。最小二乘法的“二乘”就体现在这里——它把误差平方求和再求最小值。到现在为止这个问题变成了什么样的a、b能让L(a,b)这个函数的值最小这就从拟合问题变成了优化问题。2.2 求最小值对a和b分别求偏导数一个可导函数在极值点处的导数必然为0这是高低数学都非常关键的一条定理。L是关于a和b的二元函数对a求偏导时把b看成常数反之亦然。令两个偏导数都为0就能解出使L最小的参数组合。先对a求偏导。注意L是连加结构我们可以把求和号放到前面先对通项求导再加总∂L/∂a Σ ∂/∂a[(y_i - a x_i - b)²]这要用到链式法则。设 u y_i - a x_i - b那么u²对a的导数是2u·(∂u/∂a)。而∂u/∂a -x_i。所以∂L/∂a Σ 2(y_i - a x_i - b) · (-x_i) -2 Σ x_i(y_i - a x_i - b)令它等于0去掉-2得到Σ x_i(y_i - a x_i - b) 0 Σ x_i y_i - a Σ x_i² - b Σ x_i 0这就是第一个方程记作式(1)。再对b求偏导。同理∂u/∂b -1∂L/∂b Σ 2(y_i - a x_i - b) · (-1) -2 Σ (y_i - a x_i - b)令它等于0去掉-2得到Σ (y_i - a x_i - b) 0 Σ y_i - a Σ x_i - n b 0这是第二个方程记作式(2)。注意这里出现了一个细节b求偏导时Σ b n·b因为求和是对i从1到n进行的b是常量加和n次就是n倍b。这个细节考试和推导时特别容易漏漏了后面方程组就解不出来了。2.3 解方程组拿到斜率与截距把式(1)和式(2)联立这就是一个标准的二元一次方程组。先看式(2)Σ y_i - a Σ x_i - n b 0 n b Σ y_i - a Σ x_i b (Σ y_i - a Σ x_i) / n令 x̄ Σ x_i / nx的均值ȳ Σ y_i / ny的均值可以写成更简洁的形式b ȳ - a x̄意思是直线必须经过点(x̄, ȳ)也就是数据点的“重心”。这个结论很有用实际建模时可以先用均值大致估计线会经过哪里。把b的表达式代入式(1)Σ x_i y_i - a Σ x_i² - (Σ x_i / n)(Σ y_i - a Σ x_i) 0这一步看着乱慢慢整理。先把它拆开Σ x_i y_i - a Σ x_i² - (Σ x_i · Σ y_i)/n (a(Σ x_i)²)/n 0把含a的项都移到右边a[(Σ x_i)²/n - Σ x_i²] (Σ x_i · Σ y_i)/n - Σ x_i y_i两边同时乘一个负号a[Σ x_i² - (Σ x_i)²/n] Σ x_i y_i - (Σ x_i · Σ y_i)/n于是a [Σ x_i y_i - (Σ x_i · Σ y_i)/n] / [Σ x_i² - (Σ x_i)²/n]分子分母同时乘以n就得到最经典的形式a (n Σ x_i y_i - Σ x_i Σ y_i) / (n Σ x_i² - (Σ x_i)²)这就是最小二乘估计的斜率和截距公式。写到这里我知道很多人会问这形式看着真的丑能记吗能有更优雅的写法吗有用协方差和方差的视角a Cov(x, y) / Var(x)其中Cov(x,y)是x和y的样本协方差Var(x)是x的样本方差。这是因为Cov(x,y) [Σ(x_i - x̄)(y_i - ȳ)] / n Σ x_i y_i / n - x̄ ȳ Var(x) [Σ(x_i - x̄)²] / n Σ x_i² / n - x̄²你看用这种方式记不仅能帮我们理解a的几何含义——斜率就是y和x的相关程度除以x的离散程度还方便往多元回归推广。2.4 顺带验证一下凸性有人可能会杠令偏导为0求出的是最大值还是最小值如果函数没有凸性找到的可能是极值但不是全局最优。所以我们需要验证L对a、b的二阶条件。对a求二阶偏导∂²L/∂a² 2 Σ x_i²对b求二阶偏导∂²L/∂b² 2n这两个二阶偏导都是非负的除非所有x都等于0这个退化情形说明函数是凸函数。也就是说我们求到的偏导为0的点确实就是全局最小值点。交叉二阶偏导 ∂²L/∂a∂b 2Σ x_i黑塞矩阵为[[2Σx_i², 2Σx_i], [2Σx_i, 2n]]只要矩阵半正定凸性就成立。实际数据里只要不是完全共线黑塞矩阵都是正定的。所以这个解的安全感是有保障的这也是为什么会有人把最小二乘解称为“解析解”。3. 拔高矩阵形式与正规方程3.1 数据多了就要用矩阵统一表达一元回归能解决的问题有限现实里预测磁盘用量可能还需要考虑周几、业务高峰、备份任务时间等多个因素。这时候就要上多元线性回归。假设我们有m个特征自变量n个样本。还是用原来的一元表达式显然不够。这时需要把线性关系写成一个矩阵方程y Xθ e其中y是n×1的观测值向量X是n×(m1)的特征矩阵第一列全为1对应截距项θ是(m1)×1的待求参数向量e是n×1的误差向量举个例子一个二维特征的情况矩阵X长这样X [[1, x₁₁, x₁₂], [1, x₂₁, x₂₂], ... [1, xₙ₁, xₙ₂]]这个第一列的“1”至关重要。在一元回归里截距b是单独存在的到了矩阵世界我们把它也当作一个参数唯一区别就是对应特征恒为1。这样所有参数就能统一成一个向量θ [b, a₁, a₂]ᵀ后续推导就非常通畅。3.2 展开损失函数的矩阵表达式损失函数在矩阵形式下写为L(θ) (y - Xθ)ᵀ (y - Xθ)为什么可以用转置相乘表示平方和因为向量v y - Xθv的每个元素就是第i个样本的误差vᵀv Σ v_i²正好等于误差平方和。这个等价关系是理解整个矩阵推导的关键。接下来是推导里最需要耐心的一步——展开这个式子。乘法分配率在矩阵里依然成立但要小心每一项的维度L(θ) (y - Xθ)ᵀ(y - Xθ) (yᵀ - θᵀXᵀ)(y - Xθ) yᵀy - yᵀXθ - θᵀXᵀy θᵀXᵀXθ注意中间两项。yᵀXθ是一个标量θᵀXᵀy也同样是标量而且它们互为转置。一个标量的转置还是它自己所以这两项其实相等yᵀXθ (yᵀXθ)ᵀ θᵀXᵀy合并后L(θ) yᵀy - 2θᵀXᵀy θᵀXᵀXθ这个形式看着干净多了一眼就能看到三项常数项、一次项、二次项。3.3 正规方程的完整推导现在对这个矩阵表达式求梯度。这里涉及矩阵求导的几个基本结论我直接列出来想深入看的可以翻矩阵论教材对常数项 yᵀy 求导得0对线性项 -2θᵀXᵀy求θ的偏导得 -2Xᵀy。因为Xᵀy是常数向量θᵀ乘这个向量的导数是该向量本身对二次项 θᵀXᵀXθ求θ的偏导得 2XᵀXθ。这是形如θᵀAθ的二次型求导A是对称矩阵(XᵀX确实对称)时的标准结果放在一起∇L(θ) -2Xᵀy 2XᵀXθ令梯度为0XᵀXθ Xᵀy如果XᵀX可逆两边同时左乘它的逆矩阵就得到最小二乘的矩阵解θ (XᵀX)⁻¹ Xᵀy这就是大名鼎鼎的正规方程。整个推导过程其实只是把一元情形下“求导令导数为0解方程”的套路移植到了矩阵世界。它的内涵一点都没变。到了这一步你会突然发现一元回归里得到的a、b公式其实就是这里θ的特例。用矩阵把多元情形的形式统一了这正是线性代数给机器学习带来的最大便利之一。3.4 正规方程 vs 梯度下降运维怎么选既然正规方程直接一步算出最优参数为什么还有一大堆梯度下降算法存在这个问题是几乎所有初学者都会卡的。区别主要在三个维度维度正规方程梯度下降计算方式解析解不需要迭代迭代逼近特征数m的影响m过大时 (XᵀX)⁻¹ 计算量大、内存压力大每轮只算梯度轻松应付大规模特征需要调参不需要学习率一步到位要选学习率可能陷入局部最优但线性回归的目标函数是凸的这里倒不用担心数值稳定性XᵀX条件数大时容易不稳定可以通过归一化让梯度计算更稳定我的建议是运维日常处理的数据量级特征数量在几千以下时直接上正规方程又省事又准确别动不动就上梯度下降。我自己以前也踩过这坑一上来就想着用梯度下降显得“高级”结果还要调学习率、做收敛判断反而把简单的线性回归问题搞复杂了。等真遇到海量高维数据再换随机梯度下降或者mini-batch梯度下降不迟。4. 换个角度看投影与列空间4.1 误差向量垂直这件事第一次学到正规方程时虽然能推导出来但我总觉得缺一个几何直觉。直到看到一个线性代数的讲解用“投影”重新看了一次才彻底通透。把X看成是由各列向量张成的空间列空间。我们的目标是找到一个θ让Xθ拟合y。但绝大多数情况下y并不完全落在X的列空间里否则误差就全为0了现实中几乎不可能。所以只能在列空间里找一个离y最近的向量。这个“最近的向量”就是y在列空间上的正交投影。为什么因为点到一个平面的最短距离就是过该点作平面的垂线。设投影点为Xθ误差向量为e y - Xθ它必须垂直于X的列空间中的每一个向量特别地垂直于X的每一列。垂直的代数表达就是内积为0Xᵀe 0代入eXᵀ(y - Xθ) 0 Xᵀy - XᵀXθ 0 XᵀXθ Xᵀy绕了一圈又回到了正规方程。原来“正规方程”这个名称里的“正规”指的其实就是“正交”或者说“垂直”——误差向量与特征列空间垂直。这一点我理解之后整个公式在脑子里就立体起来了。4.2 从投影公式推出正规方程在三维空间里做一个比喻。假设我们要在一张不平坦的桌子上放一个球球总会滚到最低点。列空间就是那张桌子的桌面有可能的各种倾斜方向y就是悬在空中的一个点桌面上离它最近的位置就是它的投影点。数学上投影矩阵P的经典形式是P X(XᵀX)⁻¹Xᵀ投影后的向量就是ŷ Py X(XᵀX)⁻¹Xᵀy因为ŷ Xθ̂两边对比不难看出θ̂ (XᵀX)⁻¹Xᵀy这又一次殊途同归到了正规方程。所以投影矩阵P和最小二乘解是同一个硬币的两面。明白了这一点以后我在看很多高级算法比如高斯过程、卡尔曼滤波时看到类似的(XᵀXλI)⁻¹结构就不会再发怵了。4.3 这样想有什么用有了几何直觉很多“记忆盲点”就不需要靠死记硬背了。举个例子一旦接受误差向量垂直列空间你自然就能推导正态方程以后在面试时被人问到哪怕公式忘了也能现场推出来。另一个好处是理解XᵀX不可逆时的原因。如果X的某一列和其他列线性相关比如两个特征完全成比例列空间的“维度”就降了y到列空间的投影虽然存在但投影参数θ不唯一。这时候矩阵不可逆根本不是什么意外而是数据本身就“不够格”。身后我们讲讲怎么处理这类问题。5. 用Python把推导敲一遍5.1 最小二乘法实现的完整代码数学推导不是目的能落代码才是运维工程师的浪漫。我习惯先把纯Python版写一遍确保自己真的懂了再用numpy快速验证。import numpy as np def least_squares_1d(x, y): 一元最小二乘y a*x b, 返回 (a, b) n len(x) sum_x sum(x) sum_y sum(y) sum_xx sum(xi * xi for xi in x) sum_xy sum(xi * yi for xi, yi in zip(x, y)) a (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x) b (sum_y - a * sum_x) / n return a, b def least_squares_matrix(X, y): 多元最小二乘θ (XᵀX)⁻¹Xᵀy, X第一列为1 X np.asarray(X, dtypefloat) y np.asarray(y, dtypefloat).reshape(-1, 1) theta np.linalg.inv(X.T X) X.T y return theta.flatten()这是最直接、最忠实于推导的写法。实际项目里记得用numpy的lstsq函数它在内部使用了SVD分解数值上比直接求逆稳定得多theta np.linalg.lstsq(X, y, rcondNone)[0]但理解原理时永远先从“手写公式”开始不然你连lstsq在背后干的是什么事都说不清。后面出了问题比如产生奇异值告警也不知道根源在哪。5.2 批量数据实操我拿一个我实际碰到的例子某服务的内存使用量GB随着时间天变化的数据一共7天x np.array([1, 2, 3, 4, 5, 6, 7]) y np.array([2.3, 2.8, 3.1, 4.0, 4.5, 4.9, 5.6])用上面的一元函数计算a, b least_squares_1d(x, y) print(f斜率a{a:.4f}, 截距b{b:.3f}) # 输出类似斜率a0.5214, 截距b1.7714预测第10天的值pred_10 a * 10 b print(f第10天预测内存: {pred_10:.2f} GB) # 输出类似第10天预测内存: 6.99 GB这是运维里很典型的趋势外推场景。如果第10天的预测值超过物理机内存容量就知道需要提前做扩容或者优化了。当然这只是演示真实场景里要结合业务曲线做季节性分解不能只用一个直线模型但方法论完全一致。5.3 用scipy和numpy对照为了确认手写代码没有bug通常我会和现成实现对照一下。scipy的linregress是专门做线性回归的接口from scipy import stats result stats.linregress(x, y) print(fslope{result.slope:.4f}, intercept{result.intercept:.3f})直接输出的斜率截距和我手写代码误差在浮点精度范围内。这里有一个很实用的检查习惯每次写自己的实现以后找标准库的结果做对拍误差超过1e-8就该警觉了。要么是公式差个正负号要么是数据没对齐。多元情形用numpy对拍也一样theta_np np.linalg.lstsq(X_multi, y_multi, rcondNone)[0] theta_manual least_squares_matrix(X_multi, y_multi)用数据对拍的方式学机器学习对运维老哥是再友好不过的路线——它跟我们在测试环境验证配置、在灰度集群核对预期效果是一模一样的思路。6. 运维视角的踩坑实录6.1 矩阵不可逆怎么办实操里最容易撞见的坑直接用正规方程结果np.linalg.inv(X.T X)报错LinAlgError: Singular matrix。常见原因特征矩阵里有两列完全线性相关比如一个特征是从另一个特征乘以系数得到的n小于m样本数比特征还少XᵀX必奇异某些特征全是0第一种情况在数据清洗阶段就该查掉。我建议在实际项目里别用inv直接用lstsq或者说pinv伪逆。伪逆的强大之处在于即使矩阵奇异它也能给出一个最小范数解虽然不是唯一的但至少模型能跑起来。更稳妥的方案是给XᵀX对角线加一个很小的正则项λI这就变成了岭回归Ridgeθ (XᵀX λI)⁻¹ Xᵀy这个公式看着眼熟吧在运维数据这种高噪声场景里加一点正则项不仅能解决矩阵奇异的问题还能抑制过拟合。我处理监控时序数据时λ取0.01~0.1效果就不错大家可以试试。6.2 量纲不同让结果一塌糊涂做过一个磁盘预测的模型特征里有“磁盘容量GB”和“写入次数次”两个数字差了好几个数量级。正规方程直接算出来的系数极不稳定第一轮在CPU上跑的浮点误差就很明显。这个问题的根源在于XᵀX里的数值跨度太大导致矩阵条件数很高。解决办法很粗暴也很有效先标准化。把每个特征减去均值再除以标准差让数据都落在零点附近、尺度一致。这样正规方程的条件数会大幅下降数值稳定性显著提升。实操时我习惯用sklearn.preprocessing.StandardScaler或手写X_scaled (X - X.mean(axis0)) / X.std(axis0)做完标准化预测的时候输入也要走同样的变换别忘了把预测结果切回原数据尺度再给业务方看不然会收到一连串“这是什么单位”的疑问。6.3 残差一定要看只算出一个R²就开始沾沾自喜是新手最容易犯的错。我之前用最小二乘拟合某个接口的响应时间曲线R²高达0.95直觉告诉我结果很好但画残差图的时候发现残差有明显的U型趋势——说明这个线性模型漏掉了非线性成分。残差分析的一个简单检查方法把预测值和残差画出来如果残差随机分布在0附近说明线性拟合是充分的如果呈现规律曲线或扇形说明模型不够、或者方差不稳定。这一步在运维里很容易被忽略因为大家习惯了只看阈值和平均值。具体操作import matplotlib.pyplot as plt residuals y - (a * x b) plt.scatter(x, residuals) plt.axhline(y0, colorred, linestyle--) plt.show()看到明显的“微笑曲线”或者“倒U”形就别硬套线性模型了考虑加多项式特征、周期性特征或者直接上树模型。6.4 千万别直接拿预测值提交告警这是最要命的运维修养问题。模型预测只是辅助手段不能直接替代告警链路。我的建议是把预测值和实际值一起做成指标只有预测值超过阈值且实际值处于持续上升趋势时才触发预扩容告警。否则一次模型输入数据的抖动就会导致误报把大家搞疲劳。真实业务里我还会把模型预测的置信区间也算出来。最小二乘法给出了点预测其实还可以推导出预测区间的表达式核心是误差方差σ²的估计σ² Σ(y_i - ŷ_i)² / (n - 2)有了这个可以用t分布构造95%预测区间。区间的上下界比单点预测值更能支撑运维决策——比如“内存会在3天后进入90%”比“内存3天后将达到89%”听起来更让人放心因为它带上了不确定性。我记得第一次在周会上给运维和开发同时展示这个带置信区间的预测曲线时大家的第一反应不是“这个算法好神奇”而是“这下我们终于知道该提前多久准备了”。那才是最小二乘法真正在运维场景发挥价值的一刻。7. 学完最小二乘法之后运维的路可以怎么走7.1 先别急着上深度学习我在啃完最小二乘后很长一段时间里都会被各种新名词轰炸XGBoost、LightGBM、LSTM、Transformer……每次看到这些心里都会慌。但实际用过之后我的最大体会是在监控预测、容量管理这类运维场景绝大多数需求用线性模型就解决得很好。为什么因为运维数据往往不是要找出什么惊人复杂关系而是要描述趋势、捕捉异常、预测容量。线性模型可解释性强出了问题可以追根溯源这在故障复盘时特别重要。你用一个深度神经网络预测出来的数值怎么跟业务方解释线性模型可以直接说每周磁盘上涨约200GB主要是备份文件增长贡献的建议清理旧备份。7.2 面向异常检测场景的进一步学习路线如果想继续深入我建议按这个顺序来逻辑回归学会分类思想就是“是否异常”的判定正则化方法Ridge/Lasso处理多特征、共线性问题时间序列分解把趋势、周期、残差拆开比直接线性拟合更精细聚类K-means/DBSCAN发现日志或指标里的模式分组在运维领域真正支撑起智能运维AIOps的往往不是最前沿的算法而是这些最扎实的统计模型组合。它们训练快、结果稳、易部署恰好匹配运维同学对确定性和可追溯性的追求。我到现在依然记得凌晨三点在工位上第一次跑通最小二乘代码时屏幕上打出的那一组预测曲线。说实话没有想象中波澜壮阔但它让我第一次觉得数学推导不是论文里的装饰品而是可以真正帮我提早一天发现问题、少接一个半夜告警电话的武器。推导过程没有捷径一步步列式子、一步步化简像极了定位一次复杂故障——前期越细后面越省心。希望你也能在搞懂这个公式之后找到属于自己的那种感觉。