news 2026/10/27 14:46:13

从零手写梯度下降实现多变量二分类:逻辑回归训练与调参实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零手写梯度下降实现多变量二分类:逻辑回归训练与调参实战

手写一遍梯度下降做多变量二分类,这件事在我带新人的时候几乎每次都会被问到。有人觉得它太基础,几个公式就完了;也有人卡在中间——公式看得懂,代码也抄得下来,但模型跑起来损失不降、准确率在 0.5 附近晃,然后就不知道从哪儿下手了。我自己的经验是,梯度下降法做多变量二分类恰好是一个“分水岭”式的练习:它足够小,小到你可以把每一步都摊开来看;又足够真,真到你会在里面遇到特征尺度、学习率、数值溢出、类别不平衡这些真实项目里一定会撞上的问题。把这套东西从头到尾自己写一遍、调一遍,比看十篇推导文章都管用。下面这些内容,是我这几年反复做这件事、也反复教别人做这件事之后沉淀下来的东西,重点放在推导链条怎么接上、代码怎么写不坑、参数怎么定、出问题怎么查。适合刚学完线性回归想往下走一步的同学,也适合工作里需要快速搭一个基线模型、不想一上来就上重型框架的朋友。全篇用一个虚构的“客户流失预测”场景串起来,特征假设有 8 个维度,样本量按几万条算,你可以直接替换成自己手头的数据。

1. 先把问题摆清楚:多变量二分类到底在算什么

1.1 从一条直线说起:决策边界长什么样

很多人一上来就写代码,写到一半发现自己其实没想清楚模型在做什么。多变量二分类的本质,是在一个 n 维特征空间里找一张超平面,把两类点分开。二维的时候它是一条直线,三维是一个平面,八维就是一张你画不出来的超平面。模型输出的不是一个硬邦邦的“是/否”,而是一个 0 到 1 之间的概率值,代表“这个样本属于正类的可能性”。我们最后按 0.5 或者别的阈值切一刀,才得到类别标签。

这个理解方式很重要,因为它决定了后面所有设计的出发点:我们要优化的不是“分对多少个”,而是“预测出来的概率和真实标签差多少”。分类错误率是个阶梯函数,参数稍微动一动可能一点变化都没有,梯度全是零,没法迭代。所以必须换一个光滑的、可导的目标函数来间接优化它。这就是为什么逻辑回归用的是交叉熵而不是误分类率。同样,这也解释了为什么评估的时候不能只看准确率——模型输出的概率质量,比那个 0/1 结果包含的信息多得多。

我在实际项目里会习惯性地先做一件事:把每个特征单独和标签做个简单对照,看看有没有哪个特征本身就带着很强的信号。这一步不做模型,纯看数据,经常能提前发现特征工程的问题。比如某个特征的缺失率高达 60%,或者某一类的取值分布明显偏移,这些信息在训练时会被梯度下降“忠实”地学进去,但学到的可能是噪声。

1.2 为什么不用解析解,偏要迭代着来

线性回归有正规方程,一步到位求出闭式解,那逻辑回归为什么不行?原因很简单:交叉熵损失对参数求导之后,导数里带着 sigmoid 函数,方程写出来是非线性的,没有闭式解。你没法像解线性方程组那样直接算出来,只能一步一步逼近。

这恰恰是梯度下降的用武之地。它的思路朴素得不能再朴素:站在当前参数点上,看看往哪个方向走能让损失下降最快,就往那个方向迈一小步,重复下去。方向由负梯度给出,步子大小由学习率控制。整个过程就像蒙着眼睛下山,用脚感受哪边更陡,然后朝那个方向挪。

我见过有人问:既然有牛顿法、拟牛顿法收敛更快,为什么教程都从梯度下降讲起?原因有三个。一是梯度下降的实现极简,一次矩阵乘法就能算完整个梯度,代码不到二十行,方便把注意力放在原理上;二是它的内存开销和特征维度是线性关系,特征上千上万也能跑;三是它是几乎所有深度学习优化的母版,动量、自适应学习率这些改进都是它的变体,先把最朴素的版本吃透,后面看那些花哨的优化器才不会懵。当然代价也明确:收敛慢、对特征尺度敏感、学习率得调。这三个代价本篇文章后面会逐一处理。

1.3 数据形态和开工前的准备清单

在动手之前,先把数据整理成两个东西:一个形状为(m, n)的特征矩阵X,一个形状为(m,)的标签向量y,其中y的取值只允许 0 和 1。这里有个很低级但极其常见的坑:标签是字符串"yes"/"no"、"流失"/"留存",或者1/2。前两种要先映射成 0/1,第三种一定要把 2 改成 0,否则损失函数算出来是错的,模型也能“训练”,但结果毫无意义,而且不报错,你很难发现。

还有几件事必须在训练前确认:

  • 缺失值:梯度下降不会自动跳过 NaN,一个 NaN 传播开来会让整列参数全变成 NaN,表现为损失打印出nan。要么提前填充,要么用掩码处理。
  • 特征类型:类别型特征要做独热编码或者目标编码,不能直接把类别 ID 当数值喂进去,那等于人为制造了一个虚假的序关系。
  • 样本顺序:如果数据是按时间或者按类别排好序的,做 mini-batch 之前一定要打乱,否则每一批的分布都不一样,梯度方向会来回摆。
  • 标签分布:先统计一下正负样本比例。如果正类只占 2%,那准确率这个指标从一开始就废了,得提前想好用什么评估。

提示:开工前花十分钟写一个describe_data()函数,打印形状、缺失率、标签分布、每个特征的均值和标准差。这个函数在整个项目周期里你会调用上百次,早写早省事。

2. 数学骨架:损失函数、梯度与参数更新

2.1 从 Sigmoid 到交叉熵:目标函数是怎么定下来的

线性组合z = Xw + b的输出范围是负无穷到正无穷,没法直接当概率用。所以套一个 sigmoid 函数把它压到 (0, 1) 区间:p = 1 / (1 + exp(-z))。这个函数单调递增、处处可导,而且导数有个非常漂亮的性质:σ'(z) = σ(z)(1 - σ(z)),也就是可以用函数值本身算出导数值,省掉一次指数运算。这个性质在后面推梯度的时候会直接用到。

损失函数用负对数似然,也叫对数损失或交叉熵:

L = -(1/m) * Σ [ y_i * log(p_i) + (1 - y_i) * log(1 - p_i) ]

为什么是它?直观解释是:当真实标签y=1而模型预测p接近 0 时,log(p)会趋向负无穷,取负号后损失趋向正无穷,惩罚极重;反过来y=0而p接近 1 时同理。模型被逼着把概率往正确方向推,而且推得越离谱惩罚越大。相比之下,如果用平方误差(p - y)²,配合 sigmoid 会导致梯度里多出一个p(1-p)因子,当预测严重错误、p接近 0 或 1 时这个因子接近零,梯度反而消失了,学不动。这是逻辑回归不用平方误差的核心原因,不是习惯问题。

2.2 梯度推导:把链式法则一步步走完

这部分我建议每个人都亲手推一遍,推完你对整个模型的信心会完全不一样。从损失对某个参数w_j的偏导开始,走链式法则:

第一步,损失对预测值求导。对单个样本,L_i = -[y log p + (1-y) log(1-p)],则dL_i/dp = -y/p + (1-y)/(1-p) = (p - y) / (p(1-p))。

第二步,预测值对线性输出求导。dp/dz = p(1-p)。

第三步,两者相乘,p(1-p)正好约掉:dL_i/dz = p - y。这个结果干净得让人舒服——误差就是“预测减真实”。

第四步,z对w_j求导等于x_j,对b求导等于 1。

把单样本结果对所有样本取平均,得到最终梯度:

∂L/∂w = (1/m) * X^T (p - y) ∂L/∂b = (1/m) * Σ (p_i - y_i)

参数更新就是w := w - α * ∂L/∂w,b := b - α * ∂L/∂b。

这里有个符号问题每年都有人搞混:梯度指向损失上升最快的方向,我们要让损失下降,所以是减去梯度。如果你写成了加号,损失会一路飙升到无穷,这是最容易自查的错误之一。

2.3 矩阵形式:把 for 循环彻底干掉

上面的公式写成矩阵形式后,整个梯度计算只需要两行代码:一次X @ w得到所有样本的线性输出,一次X.T @ err得到所有特征的梯度。这一步的收益非常实在。我做过一个粗糙的对比:8 个特征、5 万条样本,用 Python 原生 for 循环逐样本累加梯度,一轮迭代大概 0.9 秒;换成矩阵运算,一轮 0.004 秒左右,差了两个数量级。迭代三千轮的话,一个是 45 分钟,一个是 12 秒。

这个差距的来源是底层线性代数库对矩阵乘法做了分块、向量化和多线程优化,而 Python 层的循环每走一次都有解释器开销。所以我的原则很明确:能用矩阵表达就绝不用循环,哪怕代码看起来没那么“直白”。训练循环里的唯一 for 循环应该只出现在“轮次”这一层。

还有一个细节值得提:计算X.T @ err时,err是长度 m 的向量,结果长度是 n。如果你的特征维度远大于样本量,可以先算err @ X的等价形式,避免中间结果的形状问题。不过在常规场景下(样本量几千到几百万,特征几十到几千),直接写就行。

2.4 学习率:一个参数决定你是散步还是摔跤

学习率 α 控制每一步迈多大。太小,收敛慢到让人怀疑代码写错了;太大,损失会震荡甚至直接发散成inf。我一般用“三档试法”起步:0.001、0.01、0.1 各跑两百轮,看损失曲线。如果 0.001 降得太慢、0.1 已经开始抖,那 0.01 附近就是合理区间,再在 0.03、0.05 这种中间值上细调。

这里有个我自己常用的判断技巧:把每轮的损失打出来,如果损失是单调下降且降幅逐渐变小,说明学习率合适;如果出现“降两轮涨一轮”的锯齿,说明偏大;如果前几十轮几乎不动,说明偏小。另外,特征做过标准化之后,各方向曲率比较接近,学习率可以取得更激进一些;如果没标准化,某些方向陡、某些方向平,学习率会被最陡的那个方向卡住,只能取很小的值,这时候收敛慢不是算法的问题,是数据没处理好。

3. 工程实现:从零写一版能跑起来的代码

3.1 特征标准化:这一步省不得

前面反复提到尺度问题,这里把原因说透。梯度下降的更新量是α * x_j * err。如果某个特征的量级是百万级(比如年收入),另一个是小数级(比如点击率),那么前者的梯度会比后者大好几个数量级。同一个学习率下,大尺度特征的参数一步就跨过头了,小尺度特征的参数几乎纹丝不动。损失的等高线图会变成一个极度扁长的椭圆,梯度方向几乎垂直于最优方向,走的是 zigzag 路径,收敛极慢甚至发散。

解决办法是标准化,把每个特征变成均值 0、标准差 1:

import numpy as np mu = X.mean(axis=0) sigma = X.std(axis=0) sigma[sigma == 0] = 1.0 # 常数列不能除零 X_std = (X - mu) / sigma

三个必须记住的点:第一,mu和sigma只能从训练集算,然后原封不动套到验证集和测试集上。用全量数据算统计量再划分,属于数据泄漏,线下指标会虚高,上线就掉。第二,标准化参数要保存下来,预测时用同一套,否则输入分布和训练时不一致。第三,常数列的除法要保护,标准差为零时直接置 1。

标准化之后还有个附带好处:L2 正则化的惩罚对各个特征是公平的。如果不标准化,量级小的特征本身权重就大,被正则压制的程度和量级大的特征完全不一样,正则强度变得没有物理意义。

3.2 核心训练循环:完整代码与逐行说明

下面是训练函数,包含防溢出的 sigmoid、L2 正则、损失记录和过程监控:

import numpy as np def sigmoid(z): """防溢出写法:正负分开算,避免 exp 大数溢出""" out = np.empty_like(z, dtype=float) pos = z >= 0 out[pos] = 1.0 / (1.0 + np.exp(-z[pos])) ez = np.exp(z[~pos]) out[~pos] = ez / (1.0 + ez) return out def train(X, y, lr=0.1, epochs=3000, l2=0.0, log_every=300): m, n = X.shape w = np.zeros(n) b = 0.0 history = [] for epoch in range(1, epochs + 1): z = X @ w + b p = sigmoid(z) err = p - y # 核心:预测减真实 grad_w = X.T @ err / m + l2 * w / m grad_b = err.mean() w -= lr * grad_w b -= lr * grad_b eps = 1e-12 # 防止 log(0) loss = -(y * np.log(p + eps) + (1 - y) * np.log(1 - p + eps)).mean() \ + l2 * (w @ w) / (2 * m) history.append(loss) if log_every and epoch % log_every == 0: acc = ((p >= 0.5).astype(int) == y).mean() print(f"epoch {epoch:5d} | loss {loss:.6f} | acc {acc:.4f}") return w, b, history

几个值得单独说的实现细节。sigmoid为什么要分开算?因为exp(-z)在z是一个很大的负数时会溢出成inf,1/(1+inf)得到 0,虽然结果对,但会抛警告;当z是很大的正数时exp(-z)接近 0,1/(1+0)=1,也没问题。真正麻烦的是np.exp(z)这种写法在z很大时直接inf。分开处理后正负两支都只算指数衰减,不会溢出。

eps那行是必须的。当模型预测p恰好等于 0 或 1(浮点下确实会发生),log(0)是负无穷,损失变成inf,后续梯度全是nan,训练直接崩掉。加一个极小的偏置就能规避。

acc的计算放在打印里而不是训练逻辑里,是为了让训练过程只关心损失。准确率是非光滑指标,不参与优化,只作为观察窗口。

3.3 mini-batch 版本:样本量大时的必备改写

全量梯度下降在样本量到百万级时,一轮迭代就要扫一遍全表,内存和时间都吃不消。这时候换成 mini-batch:每轮从数据里随机抽一批(比如 256 条)算梯度。代价是梯度有噪声,损失曲线会抖,但换来的是迭代速度大幅提升,而且噪声本身还有点正则化效果,泛化往往更好。

def train_minibatch(X, y, lr=0.1, epochs=50, batch_size=256, l2=0.0): m, n = X.shape w, b = np.zeros(n), 0.0 for epoch in range(1, epochs + 1): idx = np.random.permutation(m) X_shuf, y_shuf = X[idx], y[idx] for start in range(0, m, batch_size): xb = X_shuf[start:start + batch_size] yb = y_shuf[start:start + batch_size] p = sigmoid(xb @ w + b) err = p - yb w -= lr * (xb.T @ err / len(yb) + l2 * w / m) b -= lr * err.mean() return w, b

注意两件事。一是每一轮都要重新打乱,不能只打乱一次,否则每轮的分组完全一样,噪声模式固定,反而有害。二是 batch 大小和学习率要联动:batch 变大,梯度估计的方差变小,学习率可以相应放大;反过来 batch 很小的时候学习率必须调小,否则一批样本的噪声方向会把你带偏。经验上 batch 从 32 加到 256,学习率可以放大 2 到 3 倍,但这不是铁律,还是得看损失曲线。

3.4 观察训练过程:损失曲线怎么读

训练跑起来只是开始,会读曲线才是本事。我一般会把损失画出来,横轴轮次,纵轴损失。几种典型形态:

曲线形态说明处理方向
平滑下降后趋平正常收敛可以停,或加正则看泛化
前期下降后期缓慢上翘过拟合加 L2、减特征、加样本
反复震荡不降学习率过大降一个数量级重试
几乎一条水平线学习率过小或特征未标准化先标准化,再调学习率
突然跳到 nan数值溢出或标签含异常值查标签取值、sigmoid 写法、学习率

还有一个容易被忽略的检查:训练集损失降得很低但验证集损失不降,说明模型开始记数据了。这时候别急着加轮次,先看看特征里有没有“未来信息”——比如用本月的数据预测本月的流失,那是作弊。

4. 调参与踩坑:文档里不会写的那些事

4.1 学习率的三档试法之外,还有个梯度检验

写解析梯度的时候手滑写错一个符号,代码照样能跑,损失照样能降(因为某种意义上你在优化另一个函数),但结果就是不对,而且很难发现。这时候梯度检验是唯一靠谱的手段:用数值差分算一个近似梯度,和解析梯度比。

def grad_check(X, y, w, b, eps=1e-6): p = sigmoid(X @ w + b) err = p - y gw = X.T @ err / len(y) gb = err.mean() num_gw = np.zeros_like(w) for j in range(len(w)): w1, w2 = w.copy(), w.copy() w1[j] += eps w2[j] -= eps l1 = -(y * np.log(sigmoid(X @ w1 + b) + 1e-12) + (1 - y) * np.log(1 - sigmoid(X @ w1 + b) + 1e-12)).mean() l2_ = -(y * np.log(sigmoid(X @ w2 + b) + 1e-12) + (1 - y) * np.log(1 - sigmoid(X @ w2 + b) + 1e-12)).mean() num_gw[j] = (l1 - l2_) / (2 * eps) diff = np.linalg.norm(gw - num_gw) / (np.linalg.norm(gw) + np.linalg.norm(num_gw)) print(f"相对误差: {diff:.3e}") return diff

判断标准:相对误差在1e-7量级说明梯度实现正确;到1e-4就要警惕;超过1e-2基本可以确定写错了。检验要在小数据上做(比如 10 个样本、3 个特征),因为它是 O(n) 次前向计算,数据大了跑不动。还有一个关键设置:做梯度检验时必须关掉正则化,或者把正则项的梯度也一起验,否则两个梯度对不上,你会误判成主梯度写错。

注意:梯度检验只在开发阶段做,训练完就删掉或者注释掉,千万别留在线上代码里。

4.2 特征尺度、共线性与正则化强度

特征之间的共线性问题在业务数据里太常见了——比如“近三个月消费额”和“近六个月消费额”,两者相关系数可能到 0.95 以上。这不会让梯度下降崩掉,但会让权重变得极不稳定:这次训练w1=0.8, w2=0.1,换个随机种子可能是w1=0.2, w2=0.7,两者之和差不多。模型预测能力没受影响,但你没法解释“哪个特征更重要”,业务方一问就露馅。

处理办法有两个。简单粗暴的是保留一个、删掉另一个,依据是业务含义和缺失率。温和一点的是加 L2 正则,它会把共线特征组的权重往一起压,虽然还是分散,但幅度可控。L2 的强度参数我一般从1e-3开始,按 10 倍往上试1e-2、1e-1、1,看验证集指标。经验上,特征维度高、样本量少的时候正则要重一些;样本量几万以上、特征几十个的时候,正则往往只是锦上添花。

有个细节容易踩:正则项不要惩罚偏置 b。偏置反映的是正负样本的基础比例,惩罚它等于人为把决策边界往一边推,尤其在类别不平衡时后果明显。代码里grad_b那行不加正则,就是这个原因。

4.3 常见问题速查表

下面这张表是我这些年遇到过的、以及被问过最多的问题合集,按出现频率排序:

现象最可能的原因排查动作处理方式
损失打印 nan标签含 NaN、log(0)、学习率过大查y唯一值、加 eps、打印前几步 z 的范围清洗标签,改 sigmoid 写法,降学习率 10 倍
损失不降但也不涨学习率太小、特征未标准化看特征量级差异标准化后重跑,学习率放大
准确率恒定 0.5 附近特征无信号、标签编码错误、梯度符号写反单特征与标签的相关性修标签映射,检查更新是减号
训练集准但验证集差过拟合、数据泄漏对比两边损失加 L2、减特征、检查时间切分
权重突然巨大特征未标准化、共线性打印w的范数标准化,加正则或删冗余特征
每轮结果都不同且差异大样本量小、未固定随机种子固定种子重跑增大样本,或用多次平均
训练极慢用了逐样本循环计时看每轮耗时改矩阵运算,或换 mini-batch

我最想强调的一条是「准确率恒定 0.5 附近」。新手第一反应往往是“学习率没调好”,实际上八成是标签映射错了——比如原标签是 1 和 2,你直接当 0/1 用,那模型学到的是一堆矛盾的信号,最终只能输出一个常数。第二常见的是梯度符号写反,但因为学习率很小,损失涨得慢,看起来像“不收敛”。这两个问题都花不了五分钟就能查出来,前提是你要先把它们列进检查清单。

4.4 阈值选择与类别不平衡的应对

模型输出概率之后,默认用 0.5 切分。这个默认值在平衡数据上勉强能用,在不平衡数据上几乎一定不合适。假设正类只占 5%,模型全预测成负类也有 95% 的准确率,但这个模型毫无价值。这时候要做两件事。

第一,换指标。看精确率、召回率、F1,或者直接看 ROC 曲线下的面积。业务上的选择很清楚:如果漏掉一个正类代价很高(比如风险识别),就要拉高召回率,阈值往下调;如果误报代价高(比如给用户发提醒),就要拉高精确率,阈值往上调。

第二,做阈值扫描。遍历 0.05 到 0.95 之间的一批候选值,对每个值算一遍混淆矩阵和相关指标,画成表:

for t in np.arange(0.1, 0.91, 0.05): pred = (p_val >= t).astype(int) tp = ((pred == 1) & (y_val == 1)).sum() fp = ((pred == 1) & (y_val == 0)).sum() fn = ((pred == 0) & (y_val == 1)).sum() precision = tp / (tp + fp + 1e-12) recall = tp / (tp + fn + 1e-12) f1 = 2 * precision * recall / (precision + recall + 1e-12) print(f"阈值 {t:.2f} | P {precision:.3f} | R {recall:.3f} | F1 {f1:.3f}")

阈值必须在验证集上选,选定之后再到测试集上确认一次。我见过有人直接在全量数据上扫阈值,然后报最好的那个结果,这属于自欺欺人,线上一定打脸。如果要做样本层面的平衡,还有两个手段:给正样本更高的损失权重(相当于加权交叉熵),或者对负样本做欠采样。前者实现简单,后者要注意采样后概率校准会变,预测时最好还原回原始比例。

5. 收尾前的几道关:验证、对照与上线

5.1 数据划分与交叉验证怎么用

最简单的做法是按 7:1.5:1.5 划成训练、验证、测试三份。训练集学参数,验证集调学习率、正则强度和阈值,测试集只在最后用一次。这个顺序不能乱,一旦你根据测试集的结果回头改超参数,测试集就污染了,报出来的数字不再可信。

样本量小的时候(比如几千条),单次划分的方差太大,同一套参数换个种子可能差好几个百分点。这时候用 5 折交叉验证:把训练集分成 5 份,轮流用 4 份训练、1 份验证,取平均指标。代价是要训练 5 次,但换来的是对超参数更可靠的判断。这里有个容易忽略的点:标准化参数必须在每一折内部重新计算,如果先对全量数据标准化再分折,验证折的统计信息泄漏进了训练过程,交叉验证的结果会偏乐观。

时间序列数据不能随机划分,要按时间切。用后面发生的数据训练、前面发生的数据验证,是典型的方向性错误,指标会好得离谱。正确做法是用较早的数据训练、较晚的数据验证,模拟真实的上线场景。

5.2 用自己的实现和成熟库对照一次

我强烈建议每个人都做这件事:用同一份数据、同样的预处理,把自写的模型和一个成熟实现的分类器各跑一遍,对比系数、截距和预测概率。如果系数差距在合理范围内(考虑到求解方法和正则化定义的差异,绝对值不必完全相同,但符号和量级应该一致),说明你的实现是对的。如果差得离谱,那一定是某个环节出了问题——通常是正则项的定义不同(有的库惩罚项带了1/2,有的没有),或者默认带了截距正则。

这个对照最大的价值不是“证明我写对了”,而是让你明白库帮你做了多少事:自动的收敛判断、更好的优化器、特征缩放策略、默认正则强度。等你哪天线上模型效果不对,回头看这些细节,会有完全不同的体感。

5.3 一个容易被忽略的收尾动作

模型训完、指标达标,很多人就直接交付了。我自己的习惯是多做三件事。第一,把标准化参数、权重、偏置、阈值打包存成一个文件,附上训练时间、数据版本、特征顺序,缺一样后面都可能对不上。第二,写一个最简单的推理函数,输入原始特征、输出概率,并用几个手工构造的样本验证一遍——这一步经常能抓出特征顺序错位、标准化漏做的问题。第三,把训练时的损失曲线和验证集混淆矩阵截个图存档,过三个月再回头看,这是唯一能帮你复原当时判断依据的东西。

在线性模型上,还有一个实用的检查:把所有特征权重按绝对值排序,看看前几个是不是符合业务直觉。如果排第一的是个你想不出业务解释的特征,那多半是数据构造或者泄漏出了问题。这个动作花不了两分钟,但抓住过不少隐藏的坑。

最后分享一个小技巧。如果你发现模型在不同随机种子下结果差异明显,别急着怀疑算法,先把训练集按 8:2 反复切几次,看指标波动范围。如果波动本身就很大,那是样本量或者特征质量的问题,调学习率是治不好的。我自己的经验是,多变量二分类这类模型的性能天花板,八成由特征决定,两成由调参决定,而梯度下降的那几个参数,只要落在合理区间内,对最终效果的影响远没有想象中那么大。把精力放在特征和评估上,收益会高得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 14:45:53

GPTAILOR:大语言模型剪枝与轻量化部署新技术

1. GPTAILOR:大语言模型剪枝新范式在2026年ICLR会议上亮相的GPTAILOR技术,为大语言模型(LLM)的轻量化提供了突破性解决方案。这项技术通过创新的"层切割与缝合"(Layer Cutting and Stitching)方法…

作者头像 李华
网站建设 2026/10/27 14:45:48

行交叠HDR技术原理:单帧实现>100dB动态范围的硬件实现

简介:本资源为SmartSens(思特威)推出的SC850SL高性能CMOS图像传感器官方数据手册,面向安防监控、车载视觉及网络摄像机等领域的硬件工程师、嵌入式开发者与图像算法工程师,用于快速掌握芯片选型依据、接口配置逻辑与系…

作者头像 李华
网站建设 2026/10/27 14:44:35

自适应遗传算法在风光发电与电动汽车并网调度中的应用

1. 项目背景与核心价值风光发电与电动汽车的大规模并网是当前电力系统面临的重要挑战。传统调度方法难以应对风光出力的随机性和电动汽车充电的不确定性,这直接影响到电网的稳定运行和经济性。我们团队基于IEEE 33节点系统,开发了一套融合自适应遗传算法…

作者头像 李华
网站建设 2026/10/27 14:43:39

YOLOv26改进:压缩扩展模块提升小目标检测精度

1. 项目背景与核心价值 去年参与工业质检项目时,我们遇到一个棘手问题:在检测微小电子元件缺陷时,常规YOLO模型对0.5mm以下的焊点虚焊漏检率高达32%。经过三个月模型迭代,发现根本瓶颈在于特征提取阶段的信息损失。这促使我开始研…

作者头像 李华
网站建设 2026/10/27 14:42:04

EhLib 10.0.031 for Delphi 11:DBGrid增强与Excel导出乱码解决方案

简介:EhLib 10.0.031 是一款专为 Delphi 11(特别是 11.1.5 环境)深度适配的高性能第三方组件库,面向中高级 Delphi 桌面与数据库应用开发者,显著提升图形处理、数据可视化、报表生成及多数据库集成开发效率。资源包共 …

作者头像 李华
网站建设 2026/10/27 14:42:01

离线环境升级curl 8.4.0:依赖库处理与回滚方案

1. 为什么要在离线环境折腾 Curl 升级这件事手里有一批跑在内网的生产机器,系统是 Centos 7 或者 Anolis OS 8,业务侧最近做安全扫描,报出来系统自带的 curl 版本太低,存在若干已知问题,要求限期整改。麻烦的地方在于这…

作者头像 李华