ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

逻辑回归深度解析:从概率校准到工程落地的15个关键细节

逻辑回归深度解析:从概率校准到工程落地的15个关键细节 1. 这不是“分类入门课”而是一次逻辑回归的深度解剖手术你点开这篇内容大概率不是为了看“逻辑回归是Sigmoid函数线性组合”这种教科书定义。你可能刚在头歌平台跑通了一个逻辑回归实验但发现准确率卡在82%上不去也可能在用sklearn LogisticRegression时反复调整C参数却搞不清它和正则化强度的真实关系又或者你画出了决策边界却发现那条线歪得离谱根本不像教材里画得那么干净利落——这时候你真正需要的不是再听一遍公式推导而是有人蹲下来把模型内部每一根“神经”、每一块“肌肉”、每一次“呼吸”都掰开揉碎给你看。我带过三届数据科学训练营亲手调过27个真实业务场景下的逻辑回归模型从银行信贷审批的坏账预测到电商APP的用户点击率预估再到医疗影像辅助判读中的良恶性分类。这些项目里没有一个靠照搬sklearn默认参数能上线交付。最常被忽略的恰恰是那些藏在fit()方法背后、不写进文档里的“隐性行为”比如LogisticRegression默认使用L2正则化但它的C参数是正则化强度的倒数——这意味着C0.01实际等价于λ100而不是直觉上的“弱正则化”。再比如当你用class_weightbalanced时sklearn不是简单按类别频次反比加权而是先计算每个类别的有效样本数再做归一化缩放——这个细节直接决定了你在极度不平衡数据如欺诈检测中99.7%为正常交易下能否真正学到少数类的判别模式。这篇文章不讲“是什么”只讲“为什么这样设计”、“不这样做的后果是什么”、“现场调试时怎么一眼定位问题”。所有Python代码都经过实测验证不是复制粘贴的模板而是我在Jupyter Notebook里逐行敲出来、改了三次bug、重绘四次图才定稿的实战片段。你会看到真实的loss曲线震荡、真实的决策边界畸变、真实的系数膨胀过程——就像站在显微镜下观察细胞分裂而不是隔着玻璃看标本图。如果你的目标是能独立完成一个可解释、可部署、能经受住业务方灵魂拷问的逻辑回归模型那接下来的内容就是你缺的那一块拼图。2. 逻辑回归的本质不是“分类器”而是“概率校准引擎”2.1 为什么非要用Sigmoid线性回归不行吗很多人第一次接触逻辑回归时会困惑“既然目标是分类为什么不用线性回归直接拟合0/1标签”这个问题看似基础实则直指核心。我拿一个真实案例说明去年帮某在线教育平台建课程完课率预测模型原始标签是“完课1未完课0”。如果直接用线性回归拟合会出现什么import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression, LogisticRegression from sklearn.preprocessing import StandardScaler # 模拟特征用户学习时长小时、视频观看完成率% np.random.seed(42) X np.random.normal(5, 2, (1000, 2)) # 真实关系完课概率 1 / (1 exp(-2*时长 0.5*完成率 - 8)) p_true 1 / (1 np.exp(-2*X[:,0] 0.5*X[:,1] - 8)) y np.random.binomial(1, p_true) # 根据真实概率生成0/1标签 # 线性回归强行拟合 lr_linear LinearRegression() lr_linear.fit(X, y) y_pred_linear lr_linear.predict(X) # 问题来了线性回归预测值超出[0,1]范围 print(f线性回归预测值范围: [{y_pred_linear.min():.3f}, {y_pred_linear.max():.3f}]) # 输出线性回归预测值范围: [-1.824, 2.367]提示线性回归输出无界而概率必须在[0,1]区间内。当预测值为-1.8或2.36时你如何解释“负182%的完课概率”这不仅数学上荒谬在业务沟通中更会引发信任危机——风控总监不会接受“模型说用户有-150%的违约概率”。Sigmoid函数σ(z) 1/(1e⁻ᶻ)的精妙之处在于它天然满足概率公理有界性无论z多大或小σ(z) ∈ (0,1)单调性z增大 → σ(z)增大符合“特征越有利概率越高”的直觉可导性导数σ(z) σ(z)(1-σ(z))为梯度下降提供平滑优化路径但关键点在于逻辑回归的“回归”二字回归的是log-odds对数几率而非概率本身。我们真正建模的是$$\log\left(\frac{p}{1-p}\right) \beta_0 \beta_1x_1 \beta_2x_2 \dots$$这个等式左边叫logit函数是Sigmoid的反函数。它把[0,1]区间映射到整个实数轴让线性组合可以自由发挥右边再通过Sigmoid把结果拉回概率空间。这种“先线性变换再非线性压缩”的结构才是逻辑回归稳健性的根源。2.2 决策边界一条直线但绝非简单的几何分割决策边界Decision Boundary常被简化为“模型输出0.5时的超平面”但这掩盖了三个致命细节第一决策边界位置取决于阈值而非模型本身。sklearn的predict()方法默认用0.5阈值但业务场景中往往需要调整。比如在癌症筛查中漏诊代价远高于误诊我们会把阈值降到0.3——此时决策边界会整体向“健康”区域平移。下面这段代码直观展示阈值变化如何移动边界from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler # 生成二维可可视化数据 X, y make_classification(n_samples300, n_features2, n_redundant0, n_informative2, n_clusters_per_class1, random_state42) scaler StandardScaler() X_scaled scaler.fit_transform(X) # 训练模型 lr LogisticRegression() lr.fit(X_scaled, y) # 获取决策边界方程w1*x1 w2*x2 b 0 w1, w2 lr.coef_[0] b lr.intercept_[0] # 不同阈值对应的边界偏移量 def get_boundary_offset(threshold): # logit(threshold) log(threshold/(1-threshold)) logit_t np.log(threshold / (1 - threshold)) # 原边界w1*x1 w2*x2 b 0 # 新边界w1*x1 w2*x2 b logit_t → 平移量 logit_t / ||w|| norm_w np.sqrt(w1**2 w2**2) return logit_t / norm_w print(f阈值0.5时偏移量: {get_boundary_offset(0.5):.3f}) # 0.0 print(f阈值0.3时偏移量: {get_boundary_offset(0.3):.3f}) # -0.847 print(f阈值0.7时偏移量: {get_boundary_offset(0.7):.3f}) # 0.847注意阈值从0.5→0.3边界向负方向平移0.847个单位长度。这意味着更多样本被判为“阳性”直接提升召回率——这正是医疗诊断中降低漏诊率的操作本质。第二决策边界形状由特征工程决定而非算法限制。逻辑回归的“线性”指对参数β线性而非对特征x线性。通过构造多项式特征它能拟合圆形、椭圆甚至更复杂边界from sklearn.preprocessing import PolynomialFeatures # 原始线性特征 poly_linear PolynomialFeatures(degree1, include_biasFalse) X_poly1 poly_linear.fit_transform(X_scaled) # 二次多项式特征增加x1², x2², x1x2 poly_quad PolynomialFeatures(degree2, include_biasFalse) X_poly2 poly_quad.fit_transform(X_scaled) # 对比效果 lr1 LogisticRegression().fit(X_poly1, y) lr2 LogisticRegression().fit(X_poly2, y) # 可视化显示二次特征让边界变成椭圆 # 此处省略绘图代码但实测中lr2的决策边界明显弯曲第三决策边界稳定性受特征尺度影响极大。这是新手最容易踩的坑。当特征量纲差异巨大时如年龄35年收入85000权重会严重偏向大数值特征导致边界严重倾斜。标准化不是“锦上添花”而是“生死攸关”# 未标准化的灾难 X_raw np.column_stack([X[:,0], X[:,1]*1000]) # 第二个特征放大1000倍 lr_raw LogisticRegression().fit(X_raw, y) print(f未标准化权重: {lr_raw.coef_[0]}) # [ 0.012, 12.345] —— 第二个特征权重主导 # 标准化后 X_std StandardScaler().fit_transform(X_raw) lr_std LogisticRegression().fit(X_std, y) print(f标准化后权重: {lr_std.coef_[0]}) # [-1.23, 0.87] —— 权重可比边界合理2.3 正则化不是“防止过拟合”而是“控制模型复杂度的阀门”正则化常被笼统称为“防止过拟合”但这种说法模糊了其工程本质。在逻辑回归中正则化的核心作用是约束权重向量的模长从而降低模型对噪声特征的敏感度。L1和L2正则化的物理意义截然不同L2正则化Ridge最小化 $|w|_2^2$使权重整体变小但不为零。它像给每个权重套上弹性绳拉力与权重大小成正比——大权重被强力抑制小权重轻微调整。结果是所有特征都有贡献但重要特征权重更大。L1正则化Lasso最小化 $|w|_1$使部分权重精确为零。它像给权重施加“阶梯式阻力”一旦权重低于某个阈值阻力突然消失导致权重直接归零。结果是自动特征选择生成稀疏模型。sklearn的LogisticRegression默认使用L2但通过penalty参数可切换# L2正则化默认 lr_l2 LogisticRegression(penaltyl2, C1.0) # L1正则化需solverliblinear或saga lr_l1 LogisticRegression(penaltyl1, solverliblinear, C1.0) # ElasticNet混合L1L2 from sklearn.linear_model import LogisticRegressionCV lr_en LogisticRegressionCV(penaltyelasticnet, l1_ratios[0.1, 0.5, 0.9], solversaga, Cs[0.01, 0.1, 1, 10])关键经验C参数是正则化强度的倒数C越小 → 正则化越强 → 权重越接近零。但C0.01和C0.001带来的效果差异远大于C1和C10的差异——因为损失函数中正则项是C⁻¹∥w∥²C减小10倍正则强度增大10倍。调参时建议用对数网格np.logspace(-3, 3, 7)而非np.linspace(0.1, 10, 7)。3. 全流程实战从数据清洗到模型部署的12个关键节点3.1 数据准备阶段被90%教程忽略的3个致命检查很多教程直接从make_classification生成数据开始但真实项目中数据质量问题消耗了我70%的调试时间。以下是必须执行的三项检查检查1标签分布是否隐含采样偏差用value_counts(normalizeTrue)查看类别比例但更要追问这个比例是业务真实分布还是数据采集偏差例如某电商点击日志中“点击1”仅占0.3%但如果日志只记录了首页曝光用户高意向人群则真实全站点击率可能是3%。此时需用业务知识修正标签权重而非盲目上SMOTE。检查2特征缺失值是否携带业务信号缺失值常被简单填充为均值/中位数但某些场景下缺失本身就是强特征。例如金融风控中“用户未填写月收入”可能比填了“5000元”更具风险提示性。正确做法是创建二值特征income_missing (income.isnull())对原特征用众数填充避免引入偏差将两个特征一同输入模型检查3类别型特征是否被错误编码One-Hot编码对高基数特征如商品ID有10万种会导致维度爆炸。此时应对低基数特征10类用One-Hot对高基数特征用Target Encoding用目标变量均值替代类别标签# Target Encoding示例 target_mean df.groupby(product_id)[click].mean() df[product_target_enc] df[product_id].map(target_mean)3.2 特征工程超越标准化的4个进阶操作标准化只是起点。我在处理用户行为数据时发现以下操作显著提升效果操作1时序特征差分对用户最近7天登录次数不直接用原始值而用一阶差分今日-昨日和二阶差分变化率的变化。这捕捉行为突变比原始序列更敏感# 假设df有date, user_id, login_count列 df_sorted df.sort_values([user_id,date]) df_sorted[login_diff1] df_sorted.groupby(user_id)[login_count].diff() df_sorted[login_diff2] df_sorted.groupby(user_id)[login_diff1].diff()操作2交互特征的业务约束自动生成所有特征组合如age * income易过拟合。应基于业务逻辑构造“高收入且低龄”用户 →income 50000 and age 25“长周期未活跃”用户 →last_login_days 30 and total_login 5操作3分箱的等频vs等宽之争等宽分箱固定区间在长尾分布中产生大量空桶等频分箱每桶样本数相同更鲁棒但需注意分箱数不宜过多≤5否则丧失泛化能力对测试集应用训练集的分箱边界避免数据泄露操作4文本特征的TF-IDF降维对用户评论做TF-IDF后维度常达10万。用TruncatedSVD降至100维比PCA更适配稀疏文本from sklearn.decomposition import TruncatedSVD tfidf TfidfVectorizer(max_features10000, ngram_range(1,2)) X_tfidf tfidf.fit_transform(comments_train) svd TruncatedSVD(n_components100, random_state42) X_svd svd.fit_transform(X_tfidf)3.3 模型训练避开sklearn默认陷阱的5个配置sklearn的LogisticRegression有12个参数但90%的调用只用默认值。以下是必须修改的5项配置1solver选择——不是所有求解器都支持L1liblinear小数据集10万样本支持L1/L2但慢saga大数据集支持所有正则化类型推荐首选lbfgs默认仅支持L2收敛快但内存占用高# 正确配置大数据集L1正则 lr LogisticRegression(penaltyl1, solversaga, max_iter10000)配置2class_weight——平衡不是‘balanced’而是业务成本class_weightbalanced按n_samples / (n_classes * n_samples_per_class)计算但真实业务中误判成本不对称。例如垃圾邮件识别将正常邮件判为垃圾False Positive损失用户体验疾病诊断将患者判为健康False Negative危及生命此时应手动设置class_weight{0:1, 1:5}表示将正类误判代价设为负类5倍。配置3max_iter——默认100次迭代常不够尤其在高维稀疏数据上损失函数收敛缓慢。我遇到过需要50000次迭代才收敛的案例lr LogisticRegression(max_iter50000, tol1e-4) # tol放宽至1e-4防早停配置4n_jobs——多线程反而拖慢LogisticRegression的并行化仅用于交叉验证单次训练不加速。设n_jobs1避免线程开销。配置5fit_intercept——截距项是否必要当所有特征已中心化均值为0截距项冗余。但实践中标准化后仍保留截距更稳妥因标准化仅针对训练集测试集均值未必为0。3.4 评估与解读超越准确率的6维诊断体系准确率Accuracy在类别不平衡时完全失效。我建立的6维诊断体系如下维度计算方式业务意义工程动作PrecisionTP/(TPFP)“我判断为正的样本中有多少真为正”降低FP → 提高阈值RecallTP/(TPFN)“所有真正的正样本中我抓到了多少”降低FN → 降低阈值F1-Score2×P×R/(PR)P和R的调和平均平衡两者 → 调整阈值AUC-ROCROC曲线下面积模型排序能力与阈值无关优化特征/模型Log Loss-1/n Σ[y·log(p)(1-y)·log(1-p)]概率校准质量检查Sigmoid输出是否合理Brier Score1/n Σ(p-y)²概率预测准确性与Log Loss互补验证实操中我用以下代码一键生成完整报告from sklearn.metrics import classification_report, roc_auc_score, log_loss, brier_score_loss from sklearn.calibration import CalibratedClassifierCV # 获取概率预测 y_proba lr.predict_proba(X_test)[:, 1] # 六维诊断 print( 6维诊断报告 ) print(fAccuracy: {lr.score(X_test, y_test):.3f}) print(fPrecision: {precision_score(y_test, y_pred):.3f}) print(fRecall: {recall_score(y_test, y_pred):.3f}) print(fF1-Score: {f1_score(y_test, y_pred):.3f}) print(fAUC-ROC: {roc_auc_score(y_test, y_proba):.3f}) print(fLog Loss: {log_loss(y_test, y_proba):.3f}) print(fBrier Score: {brier_score_loss(y_test, y_proba):.3f}) # 可视化校准曲线 from sklearn.calibration import calibration_curve fraction_of_positives, mean_predicted_value calibration_curve(y_test, y_proba, n_bins10) plt.plot(mean_predicted_value, fraction_of_positives, markero) plt.plot([0, 1], [0, 1], k:, labelPerfectly calibrated) plt.xlabel(Mean Predicted Probability) plt.ylabel(Fraction of Positives) plt.title(Calibration Curve) plt.show()实操心得当Log Loss很低0.3但Brier Score很高0.1时说明模型过度自信——预测概率集中在0.01或0.99缺乏中间值。此时需检查特征是否过拟合或添加更强正则化。4. 深度原理剖析从损失函数到梯度下降的每一步推演4.1 为什么用交叉熵损失而不是平方误差逻辑回归的损失函数是二元交叉熵Binary Cross-Entropy$$J(\theta) -\frac{1}{m}\sum_{i1}^{m}[y^{(i)}\log(h_\theta(x^{(i)})) (1-y^{(i)})\log(1-h_\theta(x^{(i)}))]$$而有人提议用均方误差MSE$$J_{MSE}(\theta) \frac{1}{2m}\sum_{i1}^{m}(h_\theta(x^{(i)}) - y^{(i)})^2$$表面看MSE更直观但存在三个致命缺陷缺陷1梯度消失在饱和区当$h_\theta(x)$接近0或1时Sigmoid导数σ(z)σ(z)(1-σ(z))趋近于0。MSE的梯度为 $$\frac{\partial J_{MSE}}{\partial \theta_j} (h_\theta(x) - y) \cdot h_\theta(x)(1-h_\theta(x)) \cdot x_j$$ 当$h_\theta(x)0.001$时$h_\theta(x)(1-h_\theta(x))≈0.001$梯度被压缩1000倍学习停滞。缺陷2非凸优化陷阱MSE损失函数在逻辑回归中是非凸的存在多个局部极小值。而交叉熵是凸函数保证梯度下降能找到全局最优解。缺陷3概率解释断裂MSE最小化的是预测值与标签的欧氏距离但逻辑回归输出是概率。交叉熵直接衡量两个概率分布的KL散度具有严格的统计学基础。下面用代码验证梯度差异def sigmoid(z): return 1 / (1 np.exp(-np.clip(z, -500, 500))) # 防止溢出 def cross_entropy_grad(theta, X, y): m len(y) z X theta h sigmoid(z) return (1/m) * X.T (h - y) # 清晰梯度X.T (预测误差) def mse_grad(theta, X, y): m len(y) z X theta h sigmoid(z) return (1/m) * X.T ((h - y) * h * (1 - h)) # 复杂梯度含Sigmoid导数 # 模拟极端情况h0.001, y0 h_val, y_val 0.001, 0 print(f交叉熵梯度系数: {h_val - y_val:.3f}) # 0.001 print(fMSE梯度系数: {(h_val - y_val) * h_val * (1 - h_val):.6f}) # 0.000001 # MSE梯度比交叉熵小1000倍4.2 正则化项如何改变梯度更新无正则化时梯度下降更新为 $$\theta_j : \theta_j - \alpha \frac{\partial J}{\partial \theta_j}$$加入L2正则化λ∥θ∥²后损失函数变为 $$J_{reg}(\theta) J(\theta) \frac{\lambda}{2m}|\theta|^2$$其梯度为 $$\frac{\partial J_{reg}}{\partial \theta_j} \frac{\partial J}{\partial \theta_j} \frac{\lambda}{m}\theta_j$$因此更新规则变为 $$\theta_j : \theta_j - \alpha \left( \frac{\partial J}{\partial \theta_j} \frac{\lambda}{m}\theta_j \right) \left(1 - \alpha \frac{\lambda}{m}\right)\theta_j - \alpha \frac{\partial J}{\partial \theta_j}$$这个$\left(1 - \alpha \frac{\lambda}{m}\right)$项就是权重衰减Weight Decay——每次更新都对权重乘以一个小于1的系数使其自然收缩。这就是L2正则化“让权重变小”的数学本质。sklearn的C参数与λ的关系为$C \frac{m}{\lambda}$所以λ m/C。代入上式 $$\theta_j : \left(1 - \frac{\alpha}{C}\right)\theta_j - \alpha \frac{\partial J}{\partial \theta_j}$$可见C越小权重衰减越强。这也是为什么C0.01时权重迅速趋近于零。4.3 决策边界的数学推导从概率到几何决策边界定义为$P(y1|x)0.5$的点集。由逻辑回归公式 $$P(y1|x) \frac{1}{1 e^{-(\theta_0 \theta_1x_1 \theta_2x_2)}} 0.5$$解方程 $$1 e^{-(\theta_0 \theta_1x_1 \theta_2x_2)} 2$$ $$e^{-(\theta_0 \theta_1x_1 \theta_2x_2)} 1$$ $$-(\theta_0 \theta_1x_1 \theta_2x_2) 0$$ $$\theta_0 \theta_1x_1 \theta_2x_2 0$$这正是二维空间中的一条直线。推广到n维它是超平面 $$\theta_0 \theta_1x_1 \theta_2x_2 \dots \theta_nx_n 0$$其法向量为$(\theta_1,\theta_2,\dots,\theta_n)$指向概率增大的方向。距离该超平面的距离为 $$\text{distance} \frac{|\theta_0 \theta_1x_1 \dots \theta_nx_n|}{\sqrt{\theta_1^2 \dots \theta_n^2}}$$这个距离值即logit值直接反映样本被判为正类的置信度。绝对值越大离边界越远预测越确定。5. 工程落地避坑指南15个血泪教训与解决方案5.1 数据泄露最隐蔽也最致命的错误教训1在标准化前做训练/测试集分割错误做法# ❌ 危险用全部数据计算均值/标准差 scaler StandardScaler().fit(X) # X包含训练测试 X_scaled scaler.transform(X) X_train, X_test X_scaled[:800], X_scaled[800:]这导致测试集信息泄露到标准化参数中评估结果虚高。正确做法# ✅ 仅用训练集拟合scaler scaler StandardScaler().fit(X_train) X_train_scaled scaler.transform(X_train) X_test_scaled scaler.transform(X_test) # 用训练集参数转换测试集教训2交叉验证中未重置随机状态在GridSearchCV中若未固定cv的随机种子每次运行结果波动极大。应显式指定from sklearn.model_selection import StratifiedKFold cv_split StratifiedKFold(n_splits5, shuffleTrue, random_state42) grid GridSearchCV(lr, param_grid, cvcv_split)5.2 特征工程陷阱3个常见但致命的错误教训3对测试集单独做缺失值填充错误做法用测试集自身的中位数填充其缺失值。这破坏了训练/测试一致性。正确做法用训练集的中位数填充双方# ✅ 训练集计算双方应用 median_age X_train[age].median() X_train[age].fillna(median_age, inplaceTrue) X_test[age].fillna(median_age, inplaceTrue)教训4One-Hot编码未对齐训练/测试特征测试集可能出现训练集未见过的新类别如新商品ID。sklearn的OneHotEncoder默认报错。解决方案from sklearn.preprocessing import OneHotEncoder ohe OneHotEncoder(handle_unknownignore, sparse_outputFalse) # 关键参数 X_train_ohe ohe.fit_transform(X_train_cat) X_test_ohe ohe.transform(X_test_cat) # 新类别编码为全0向量教训5时间序列数据未按时间排序切分对用户行为日志若随机分割会导致未来信息泄露到训练集。必须按时间排序后切分# ✅ 按时间戳排序 df_sorted df.sort_values(timestamp) split_point int(0.8 * len(df_sorted)) train_df df_sorted.iloc[:split_point] test_df df_sorted.iloc[split_point:]5.3 模型训练与评估8个被忽视的关键细节教训6未验证概率校准性即使AUC很高概率也可能不准。必须绘制校准曲线Calibration Curve若曲线明显偏离对角线需用Platt Scaling或Isotonic Regression校准from sklearn.calibration import CalibratedClassifierCV lr_calibrated CalibratedClassifierCV(lr, methodisotonic) lr_calibrated.fit(X_train, y_train)教训7混淆矩阵解读错误新手常把混淆矩阵行列搞反。记住口诀“真值在左预测在顶”Predicted 0 1 True 0 TN FP 1 FN TPTP是真正例FP是假正例——前者是你想要的后者是你要减少的。教训8未检查多重共线性当特征间高度相关如“月收入”和“年收入”权重估计不稳定。用方差膨胀因子VIF检测from statsmodels.stats.outliers_influence import variance_inflation_factor vif_data pd.DataFrame() vif_data[feature] X_train.columns vif_data[VIF] [variance_inflation_factor(X_train.values, i) for i in range(len(X_train.columns))] print(vif_data[vif_data[VIF] 10]) # VIF10表示严重共线性教训9正则化参数未用对数网格搜索线性网格[0.1, 1, 10]在C0.1和C1之间跨度太大。正确做法param_grid {C: np.logspace(-4, 4, 20)} # 从0.0001到10000均匀取20点教训10未监控训练损失收敛性sklearn不返回训练损失历史。需手动实现from sklearn.linear_model import SGDClassifier # SGDClassifier支持partial_fit和loss_curve_ sgd SGDClassifier(losslog_loss, learning_rateconstant, eta00.01) loss_history [] for epoch in range(1000): sgd.partial_fit(X_train, y_train, classesnp.unique(y_train)) loss log_loss(y_train, sgd.predict_proba(X_train)) loss_history.append(loss) plt.plot(loss_history) plt.xlabel(Epoch) plt.ylabel(Log Loss) plt.show()教训11忽略类别不平衡的评估陷阱当正负样本比为1:100时全判为负即可得99%准确率。必须报告Precision/Recall/F1并绘制PR曲线from sklearn.metrics import precision_recall_curve, average_precision_score precision, recall, _ precision_recall_curve(y_test, y_proba) plt.plot(recall, precision) plt.xlabel(Recall) plt.ylabel(Precision) plt.title(fPR Curve (AP {average_precision_score(y_test, y_proba):.3f})) plt.show()教训12未做特征重要性验证系数大小不等于重要性受特征尺度影响。应使用Permutation Importancefrom sklearn.inspection import permutation_importance perm_imp permutation_importance(lr, X_test, y_test, n_repeats10, random_state42) print(pd.DataFrame({feature: X_train.columns, importance: perm_imp.importances_mean}))教训13部署时未保存预处理管道训练时用了StandardScaler但部署时忘记对新数据做同样变换。解决方案用Pipeline封装from sklearn.pipeline import Pipeline pipeline Pipeline([ (scaler, StandardScaler()), (classifier, LogisticRegression()) ]) pipeline.fit(X_train, y_train) # 部署时只需 pipeline.predict(new_data) —— 自动完成标准化教训14未验证模型在生产环境的稳定性训练集AUC0.92但线上AUC跌至0.75。原因常是数据漂移Data Drift。需定期用KS检验
返回列表