逻辑回归:从Sigmoid函数到实战应用,掌握二分类核心算法 1. 从分类问题到逻辑回归为什么它不只是“回归”在数据分析和建模的实战中我们常常会遇到一个核心问题预测一个事件是否会发生。比如一封邮件是不是垃圾邮件一个客户明天会不会流失一个病人根据各项指标患某种疾病的概率有多大这些问题都有一个共同点我们想要预测的结果不是一个连续的数值比如房价、销售额而是一个“是”或“否”的类别标签或者更精确地说是“是”这个事件发生的概率。很多初学者一听到“回归”第一反应是线性回归——那条试图用一条直线去拟合数据点的经典方法。线性回归确实强大但它有一个致命的弱点当你用它去预测一个概率时它可能会给你一个小于0或者大于1的荒谬结果比如预测出“患病概率是-0.2或1.5”这显然不符合概率的定义概率必须在0到1之间。这就是分类问题直接套用线性回归的尴尬。于是逻辑回归Logistic Regression登场了。别看名字里带着“回归”它其实是解决二分类问题的王者级算法。它的核心思想非常巧妙我们不直接用线性方程去预测概率而是先预测一个“可能性”的度量称为对数几率再通过一个特殊的函数将这个度量“压缩”到0到1之间。这个函数就是大名鼎鼎的Sigmoid函数也叫Logistic函数。你可以把它想象成一个“概率转换器”。线性部分和线性回归一样是特征的加权和负责评估事件的倾向性数值越大倾向性越强。Sigmoid函数则像一个温和的裁判把任何倾向性分数无论正负、大小都温柔地映射到(0,1)这个概率区间内。这样一来模型的输出就天然是一个合理的概率值了。在数学建模竞赛和实际业务中逻辑回归几乎是处理二分类问题的首选基线模型。它模型清晰、可解释性强、计算效率高而且作为广义线性模型的一员其参数具有明确的统计意义。更重要的是理解了逻辑回归就为理解更复杂的神经网络尤其是分类任务的输出层打下了坚实的基础。接下来我们就深入这个“概率转换器”的内部看看它的数学心脏是如何跳动的。2. Sigmoid函数逻辑回归的“概率压缩”核心要理解逻辑回归必须彻底搞懂Sigmoid函数。它不仅是数学上的一个变换更是整个模型逻辑的基石。2.1 函数形式与直观理解Sigmoid函数的数学表达式如下[ \sigma(z) \frac{1}{1 e^{-z}} ]这里的 ( z ) 就是我们线性组合的结果即 ( z \theta_0 \theta_1 x_1 \theta_2 x_2 ... \theta_n x_n \theta^T x )。其中( \theta ) 是模型参数权重( x ) 是输入特征。这个函数有什么特性呢我们拆开来看输出范围在(0,1)无论输入 ( z ) 是正无穷大、负无穷大还是任何实数( e^{-z} ) 永远大于0。因此分母 ( 1e^{-z} 1 )整个分数的值就严格在0到1之间。完美符合概率的定义。单调递增函数是单调递增的。这意味着 ( z ) 越大( \sigma(z) ) 越接近1( z ) 越小( \sigma(z) ) 越接近0。这符合直觉特征加权和越大说明正向证据越强属于正类标签为1的概率就越高。以0.5为分界点当 ( z 0 ) 时( \sigma(z) 0.5 )。这是一个非常自然的决策边界。在实际预测时我们通常设定一个阈值默认为0.5当预测概率 ( \geq 0.5 ) 时我们判定为正类1否则为负类0。函数形状它的图像是一条平滑的“S”形曲线。在 ( z0 ) 附近曲线变化相对陡峭概率对 ( z ) 的变化很敏感而当 ( z ) 的绝对值很大时无论正负曲线变得非常平缓概率趋近于0或1对 ( z ) 的变化不再敏感。这个特性使得模型对极端值的预测不会过于“疯狂”。注意Sigmoid函数也叫Logistic函数但“Logistic”这个词更泛指整个模型和其背后的逻辑分布。在沟通时说“Sigmoid激活函数”或“Logistic函数”通常指同一个东西但在严谨的数学表述中需注意上下文。2.2 从几率Odds到对数几率Logit逻辑回归的“逻辑”二字来源于它和“几率”概念的紧密联系这是理解其参数意义的关键。首先定义几率Odds它是指事件发生的概率与不发生的概率的比值。即 ( Odds \frac{p}{1-p} )其中 ( p ) 是事件发生的概率。如果 ( p0.75 )那么 ( Odds 0.75 / 0.25 3 )意思是“发生的可能性是不发生的3倍”。逻辑回归模型做了一个关键的假设对数几率Logit是输入特征的线性组合。[ \log(\frac{p}{1-p}) \theta^T x z ]这个等式就是逻辑回归的核心模型。左边是事件发生概率 ( p ) 的对数几率右边是特征的线性组合。我们对这个等式进行简单的变换[ \frac{p}{1-p} e^{\theta^T x} ] [ p \frac{e^{\theta^T x}}{1 e^{\theta^T x}} \frac{1}{1 e^{-\theta^T x}} ]看我们就这样从“对数几率线性”的假设推导出了Sigmoid函数的形式。这解释了为什么逻辑回归属于广义线性模型它是对响应变量这里是概率 ( p ) 进行一个单调可微的链接函数Sigmoid函数变换后使得变换后的结果对数几率与特征呈线性关系。2.3 参数的解释一个实战案例这个线性关系赋予了逻辑回归参数极其直观的解释。假设我们在建立一个预测客户流失的模型其中一个特征是“上月客服投诉次数” ( x_1 )其对应的参数权重 ( \theta_1 ) 被训练为 0.8。参数 ( \theta_1 0.8 ) 意味着什么它意味着在保持其他所有特征不变的情况下“上月客服投诉次数”每增加1次客户流失的“对数几率”将增加0.8。更进一步我们可以解释为对“几率”的影响。因为 [ \log(Odds_{new}) - \log(Odds_{old}) \theta_1 * 1 0.8 ] 所以 [ \frac{Odds_{new}}{Odds_{old}} e^{0.8} \approx 2.225 ]这意味着投诉次数每增加一次客户流失的几率将变为原来的约2.225倍。这种解释在业务上非常有力可以直接告诉业务方“你看客户每多投诉一次他流失的风险就会翻一倍还多。” 这正是逻辑回归在需要模型可解释性的场景如金融风控、医疗诊断中备受青睐的原因。3. 模型训练如何找到那组最优参数模型的形式定了接下来的问题就是给定一堆已知标签的数据我们如何找到那组最优的参数 ( \theta ) 这个过程就是模型训练其本质是一个优化问题。3.1 损失函数交叉熵损失Cross-Entropy Loss线性回归用均方误差MSE作为损失函数但这对逻辑回归不适用因为MSE在用于概率输出时会导致损失函数非凸存在很多局部最优解不利于优化。逻辑回归使用的是交叉熵损失函数它源于信息论衡量的是真实概率分布与预测概率分布之间的差异。对于单个样本 ( (x^{(i)}, y^{(i)}) )其中 ( y^{(i)} ) 是真实标签0或1( h_\theta(x^{(i)}) \sigma(\theta^T x^{(i)}) ) 是模型预测为正类的概率其损失定义为[ Cost(h_\theta(x^{(i)}), y^{(i)}) -[y^{(i)} \log(h_\theta(x^{(i)})) (1 - y^{(i)}) \log(1 - h_\theta(x^{(i)}))] ]这个公式设计得非常巧妙当真实标签 ( y^{(i)}1 ) 时损失变为 ( -\log(h_\theta(x^{(i)})) )。如果模型预测概率 ( h_\theta(x^{(i)}) ) 接近1预测正确那么 ( \log(1) 0 )损失接近0如果预测概率接近0预测错误那么 ( -\log(0) ) 会趋向无穷大给予模型极大的惩罚。当真实标签 ( y^{(i)}0 ) 时损失变为 ( -\log(1 - h_\theta(x^{(i)})) )。逻辑同理。整个训练集上的损失函数成本函数 ( J(\theta) ) 就是所有样本损失的平均 [ J(\theta) -\frac{1}{m} \sum_{i1}^{m} [y^{(i)} \log(h_\theta(x^{(i)})) (1 - y^{(i)}) \log(1 - h_\theta(x^{(i)}))] ]我们的目标就是找到一组参数 ( \theta )使得 ( J(\theta) ) 最小化。3.2 优化算法梯度下降的实战细节交叉熵损失函数是凸函数这意味着只要学习率设置得当梯度下降法一定能找到全局最优解或接近最优的解。参数更新的核心公式如下[ \theta_j : \theta_j - \alpha \frac{\partial J(\theta)}{\partial \theta_j} ]其中 ( \alpha ) 是学习率。关键在于求梯度。经过推导这是一个经典的推导建议手动做一遍对于单个样本损失函数对参数 ( \theta_j ) 的偏导数为 [ \frac{\partial Cost}{\partial \theta_j} (h_\theta(x^{(i)}) - y^{(i)}) x_j^{(i)} ]这个结果非常简洁优美预测值与真实值的误差乘以对应的特征值。对于整个训练集梯度就是所有样本梯度的平均 [ \frac{\partial J(\theta)}{\partial \theta_j} \frac{1}{m} \sum_{i1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) x_j^{(i)} ]在实际编程实现如Python中使用NumPy或调用库如scikit-learn时算法就是基于这个梯度进行迭代更新。这里有几个至关重要的实战要点特征缩放Feature Scaling虽然逻辑回归的损失函数不受特征尺度影响最终收敛但特征缩放能极大加快梯度下降的收敛速度。如果特征尺度差异巨大如“年龄”范围0-100“收入”范围0-1000000梯度下降的路径会非常曲折需要很多次迭代。通常使用标准化Standardization( x \frac{x - \mu}{\sigma} )让每个特征均值为0标准差为1。这是建模前几乎必做的预处理步骤。学习率 ( \alpha ) 的选择学习率太小收敛慢训练时间长学习率太大可能会在最小值附近震荡甚至发散无法收敛。一个实用的方法是绘制损失函数值随迭代次数变化的曲线学习曲线。如果曲线下降平滑说明学习率合适如果曲线震荡或上升说明学习率太大如果曲线下降极其缓慢说明学习率可能太小。通常可以从0.01、0.03、0.1、0.3等值开始尝试。迭代终止条件通常有两种(1) 设置最大迭代次数。(2) 设定一个阈值当两次迭代之间损失函数值的下降小于这个阈值时认为已经收敛。在实际中常结合两者使用。实操心得在数学建模或自己实现时不要急于一开始就写复杂的优化算法。先用小批量数据把梯度计算、参数更新的代码写对并手动计算前几次迭代的结果进行验证。确保基础单元正确再扩展到全数据集和更复杂的优化器如带动量的梯度下降。4. 从理论到代码手把手实现与sklearn应用理解了原理我们来看看如何用代码实现。这里会展示两种方式纯NumPy实现有助于彻底理解和scikit-learn应用实战首选。4.1 纯NumPy实现深入每一步import numpy as np class LogisticRegression_FromScratch: def __init__(self, learning_rate0.01, n_iters1000): self.lr learning_rate self.n_iters n_iters self.weights None self.bias None def _sigmoid(self, z): Sigmoid函数防止溢出 # 对z进行裁剪防止exp(-z)过大导致溢出 z np.clip(z, -500, 500) return 1 / (1 np.exp(-z)) def fit(self, X, y): 训练模型 n_samples, n_features X.shape # 初始化参数 self.weights np.zeros(n_features) self.bias 0 # 梯度下降 for _ in range(self.n_iters): # 线性模型输出 linear_model np.dot(X, self.weights) self.bias # 通过sigmoid得到预测概率 y_predicted self._sigmoid(linear_model) # 计算梯度 (推导出的简洁形式) dw (1 / n_samples) * np.dot(X.T, (y_predicted - y)) db (1 / n_samples) * np.sum(y_predicted - y) # 更新参数 self.weights - self.lr * dw self.bias - self.lr * db def predict_proba(self, X): 预测概率 linear_model np.dot(X, self.weights) self.bias return self._sigmoid(linear_model) def predict(self, X, threshold0.5): 预测类别 probabilities self.predict_proba(X) return (probabilities threshold).astype(int)代码关键点解析_sigmoid函数中的np.clip这是一个非常重要的防溢出技巧。当z是一个非常小的负数时np.exp(-z)会变成一个极大的数可能导致计算溢出得到inf。裁剪到[-500, 500]是一个经验值能保证计算稳定。梯度计算dw和db直接对应我们之前推导的公式(1/m) * X^T * (h - y)。这里利用NumPy的广播和点乘一次性计算所有权重的梯度非常高效。predict方法中的threshold展示了如何将概率转换为类别。0.5是默认阈值但可以根据业务需求调整例如在疾病筛查中为了不漏诊可能降低阈值以提高召回率。4.2 scikit-learn实战高效与全面在实际项目和数学建模中我们几乎总是使用成熟的库如scikit-learn。它高效、稳定且功能全面。import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_auc_score # 1. 加载数据示例使用鸢尾花数据集二分类 from sklearn.datasets import load_breast_cancer data load_breast_cancer() X pd.DataFrame(data.data, columnsdata.feature_names) y data.target # 0: malignant, 1: benign # 2. 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 3. 特征标准化非常重要 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意使用训练集的均值和标准差来转换测试集 # 4. 创建并训练逻辑回归模型 # 参数说明 # penalty: 正则化类型l2是默认防止过拟合。 # C: 正则化强度的倒数C越小正则化越强。默认1.0。 # solver: 优化算法对于小数据集或L1正则liblinear是好的选择对于大数据集sag或saga更快。 # max_iter: 最大迭代次数如果数据复杂或特征多需要调大。 model LogisticRegression(penaltyl2, C1.0, solverliblinear, max_iter1000, random_state42) model.fit(X_train_scaled, y_train) # 5. 预测与评估 y_pred model.predict(X_test_scaled) y_pred_proba model.predict_proba(X_test_scaled)[:, 1] # 取正类1的概率 print(准确率:, accuracy_score(y_test, y_pred)) print(\n混淆矩阵:\n, confusion_matrix(y_test, y_pred)) print(\n分类报告:\n, classification_report(y_test, y_pred)) print(ROC-AUC分数:, roc_auc_score(y_test, y_pred_proba)) # 6. 查看模型参数可解释性 print(f\n模型截距 (bias): {model.intercept_[0]:.4f}) # 可以将权重与特征名对应查看 feature_importance pd.DataFrame({ feature: data.feature_names, coefficient: model.coef_[0] }).sort_values(bycoefficient, keyabs, ascendingFalse) print(\n特征权重绝对值排序:\n, feature_importance.head(10))scikit-learn使用精髓solver选择这是新手最容易困惑的参数之一。liblinear适用于小数据集支持L1和L2正则化lbfgs是默认选项对大多数情况表现良好且稳定sag和saga在样本量很大数万以上时速度更快。如果用了L1正则化必须选择liblinear或saga。C参数这是正则化强度的倒数。C越小正则化惩罚越重模型越简单越可能欠拟合C越大惩罚越轻模型越复杂越可能过拟合。通常需要通过交叉验证在一个范围如[0.001, 0.01, 0.1, 1, 10, 100]内进行网格搜索来寻找最佳值。fit_transform与transform务必记住标准化器StandardScaler要在训练集上fit计算均值和标准差然后在训练集和测试集上分别transform。绝对不能用测试集的数据去fit标准化器这会引入数据泄露导致模型评估结果虚高。5. 模型评估准确率远远不够模型训练好了预测也做了但怎么知道它好不好对于分类模型尤其是像逻辑回归这样输出概率的模型评估需要多维度进行不能只看一个准确率。5.1 混淆矩阵与衍生指标混淆矩阵是评估分类模型的基础它统计了真实类别和预测类别的所有组合情况。预测为负 (0)预测为正 (1)真实为负 (0)TN (真阴性)FP (假阳性)真实为正 (1)FN (假阴性)TP (真阳性)从混淆矩阵可以计算出多个关键指标准确率 (Accuracy)(TPTN) / (TPTNFPFN)。最直观但在不平衡数据集中具有欺骗性。例如如果99%的客户都不流失那么一个永远预测“不流失”的模型准确率也有99%但这毫无用处。精确率 (Precision)TP / (TPFP)。在所有被预测为正的样本中真正为正的比例。它关注的是预测的“准度”。在垃圾邮件过滤中我们追求高精确率因为把正常邮件误判为垃圾邮件FP的代价很高。召回率 (Recall)TP / (TPFN)。在所有真实为正的样本中被成功预测出来的比例。它关注的是模型的“查全率”。在疾病筛查中我们追求高召回率因为漏诊FN的代价很高。F1分数 (F1-Score)2 * Precision * Recall / (Precision Recall)。精确率和召回率的调和平均数在两者需要权衡时是一个综合指标。实操心得永远不要只依赖准确率。第一步永远是看混淆矩阵了解错误的具体类型是FP多还是FN多。然后根据业务目标选择侧重精确率还是召回率。例如在金融反欺诈中初期可能更看重召回率宁可错杀不可放过随着模型成熟再优化精确率减少误伤。5.2 ROC曲线与AUC衡量模型排序能力逻辑回归输出的是概率我们通过设定阈值如0.5将其转换为类别。但阈值是可以调整的。调整阈值会改变混淆矩阵进而改变精确率和召回率。ROC曲线描绘了当阈值从1到0变化时真正例率 (TPR Recall)和假正例率 (FPR FP / (FPTN))的变化关系。一个完美的模型其ROC曲线会紧贴左上角TPR1 FPR0。而一条对角线从(0,0)到(1,1)代表一个随机猜测的模型。AUC (Area Under Curve)就是ROC曲线下的面积。AUC的取值范围在0.5到1之间。AUC 0.5模型没有区分能力和随机猜测一样。0.5 AUC 0.7模型有较弱的区分能力。0.7 ≤ AUC 0.8模型有一定的区分能力可以接受。0.8 ≤ AUC 0.9模型有良好的区分能力。AUC ≥ 0.9模型有非常优秀的区分能力。AUC有一个更直观的概率解释随机选取一个正样本和一个负样本模型对正样本给出的预测概率高于负样本的概率就是AUC值。因此AUC衡量的是模型对样本的排序能力而不依赖于具体的分类阈值这对于评估概率输出模型非常关键。5.3 概率校准你的概率可信吗逻辑回归理论上输出的是校准过的概率即预测概率为0.7的样本中应该有70%确实是正类。但在实际中特别是样本不平衡或使用了强正则化时模型的概率输出可能会“失真”变得过于自信或过于保守。检查概率校准性的常用工具是校准曲线Calibration Curve也叫可靠性曲线。其方法是将预测概率区间[0,1]分成若干个桶如10个。计算每个桶内所有样本的平均预测概率。计算每个桶内所有样本的真实正例比例。以平均预测概率为横坐标真实正例比例为纵坐标作图。一条完美的校准曲线应该是一条对角线yx。如果曲线在对角线之上说明模型预测概率偏低过于保守如果在对角线之下说明预测概率偏高过于自信。在scikit-learn中可以使用CalibrationDisplay.from_estimator来绘制校准曲线。如果发现概率校准性不好可以考虑使用CalibratedClassifierCV对模型进行事后校准这对于需要精确概率输出的场景如风险定价至关重要。6. 进阶话题与实战避坑指南掌握了基础我们来看看逻辑回归在实际应用中那些容易踩坑和需要深入思考的地方。6.1 过拟合与正则化给模型加上“紧箍咒”当模型参数过多或训练数据不足时逻辑回归很容易记住训练数据中的噪声导致在训练集上表现极好但在未知数据测试集上表现糟糕这就是过拟合。对抗过拟合最有效的手段之一就是正则化。它在损失函数中增加一个惩罚项用于约束参数的大小迫使模型变得“简单”。L1正则化 (Lasso)在损失函数中加入参数绝对值之和的惩罚项 ( \lambda \sum_{j1}^{n} |\theta_j| )。L1正则化有一个神奇的特性它倾向于将一些不重要的特征的权重精确地压缩到0从而实现特征选择。如果你的特征维度很高且怀疑很多特征无关L1正则化是首选。L2正则化 (Ridge)在损失函数中加入参数平方和的惩罚项 ( \frac{\lambda}{2} \sum_{j1}^{n} \theta_j^2 )。L2正则化会让所有参数的权重都均匀地缩小但不会完全为0。它更擅长处理特征间存在多重共线性的情况使模型更稳定。在scikit-learn的LogisticRegression中通过penalty参数选择l1或l2注意solver的兼容性通过C参数控制正则化强度C 1 / \lambdaC越小惩罚越重。如何选择一个实用的流程是先使用L2正则化因为它更稳定。如果模型特征非常多例如成百上千并且你需要一个稀疏模型即只有少数特征起作用以便解释或部署那么可以尝试L1正则化。也可以使用ElasticNetpenaltyelasticnet它是L1和L2的混合通过l1_ratio参数控制混合比例。6.2 特征工程模型性能的上限逻辑回归是一个线性模型它的表达能力直接受限于特征。可以说特征工程的质量决定了逻辑回归性能的上限。以下是一些针对逻辑回归的特征工程技巧数值特征处理除了标准化还可以考虑分箱Binning。将连续年龄分为“青年”、“中年”、“老年”然后进行独热编码。这可以捕捉非线性关系因为逻辑回归在分箱后的每个区间内是线性的但整体上变成了分段线性模型。类别特征编码必须将文字型类别如“城市”转化为数字。独热编码One-Hot Encoding是最常用且安全的方法为每个类别创建一个新的二值特征。避免使用简单的标签编码如北京1上海2深圳3因为这会给模型强加一个不存在的顺序关系。交叉特征这是提升逻辑回归能力的关键。既然模型本身是线性的我们可以手动创建非线性特征。例如预测贷款违约不仅有“收入”和“负债”两个特征还可以加入“负债收入比”负债/收入这个交叉特征。更复杂的可以用多项式特征如PolynomialFeatures但需警惕维度爆炸。处理缺失值逻辑回归本身不能处理缺失值。常用方法包括删除缺失样本如果缺失很少、用中位数/众数/均值填充、或者使用“是否缺失”作为一个新的二值特征。踩坑实录我曾在一个用户流失预测项目中直接使用了用户的“最近一次登录时间戳”Unix时间戳作为特征。模型效果很差。后来意识到时间戳的绝对值对模型来说没有意义。将其转化为“距离今天的天数”并进一步分箱为“近1天”、“近3天”、“近7天”、“7天以上”模型效果显著提升。这个教训是对于特征要始终思考其业务意义并将其转化为模型能理解的、有信息量的形式。6.3 多分类问题从“一对多”到“多项式”逻辑回归本质是二分类器。那如何处理多分类问题如鸢尾花分类有3个品种有两种主流策略一对多One-vs-Rest, OvR这是最常用的方法。假设有K个类别我们就训练K个独立的二分类逻辑回归模型。对于第i个模型将类别i的样本作为正例其他所有类别的样本作为负例。预测时让K个模型都给出样本属于其正类的概率然后选择概率最高的那个类别作为最终预测。优点简单训练K个模型即可每个模型只用到部分数据。缺点当类别不平衡时每个二分类器的正负样本比例可能严重失调影响性能。而且可能存在“灰色区域”即多个模型给出的概率都较高或都较低。多项式逻辑回归Multinomial Logistic Regression这是一种更“原生”的方法。它直接修改模型使其输出一个K维向量向量每个元素代表样本属于对应类别的概率通过Softmax函数保证和为1。它一次性考虑所有类别之间的关系。优点理论更优雅通常能获得比OvR略好一点的效果特别是当类别互斥且特征空间有重叠时。缺点计算复杂度稍高。在scikit-learn中设置LogisticRegression的multi_class参数即可选择。对于大多数情况multi_classovr默认已经足够好且更快。如果类别不多且你追求最优性能可以尝试multi_classmultinomial并配合合适的solver如lbfgs或saga。6.4 样本不平衡当99%都是负样本这是实际中最常见也最棘手的问题之一。例如在欺诈检测中正常交易占99.9%欺诈交易只占0.1%。如果直接用原始数据训练模型会倾向于把所有样本都预测为“正常”因为这样就能获得99.9%的准确率但对欺诈的召回率为0。解决方法调整类别权重这是最简单有效的方法。在LogisticRegression中设置class_weightbalanced。算法会自动根据类别频率调整损失函数中每个类别的权重让模型更关注少数类。其权重通常与类别频率成反比。重采样过采样增加少数类样本的副本。最简单的方法是随机复制但容易导致过拟合。更高级的方法是SMOTESynthetic Minority Over-sampling Technique它通过插值在少数类样本之间生成新的合成样本。欠采样随机丢弃一部分多数类样本。这会损失信息只适用于数据量非常大的情况。通常建议先尝试class_weight如果效果不佳再考虑复杂的重采样技术。调整决策阈值如前所述默认0.5的阈值是基于类别平衡假设的。在不平衡时可以降低阈值如降到0.1让模型更“敏感”地预测正类从而提高召回率。但这会降低精确率需要在两者间权衡。可以通过P-R曲线或ROC曲线来帮助选择最佳阈值。一个黄金法则在处理不平衡数据时永远不要用准确率作为评估指标必须使用混淆矩阵、精确率、召回率、F1分数特别是ROC-AUC。AUC对类别不平衡相对不敏感是一个更可靠的模型排序能力指标。同时在测试集上评估时务必确保测试集的类别分布与真实业务场景一致。