尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

逻辑回归:从原理到实践,掌握分类任务的经典算法

逻辑回归:从原理到实践,掌握分类任务的经典算法 1. 项目概述从“分类”的直觉到逻辑回归的落地聊到机器学习尤其是入门逻辑回归Logistic Regression几乎是所有人的第一站。但别被它的名字骗了它虽然叫“回归”干的却是“分类”的活儿。我第一次接触它时也纳闷后来才明白这个名字源于它内部使用了广义线性回归的框架但最终输出的是一个概率值用于判断类别。简单来说它就像一个经验老道的分拣员面对一堆混杂的物件数据通过学习历史分拣记录训练数据总结出一套打分规则模型参数然后给新物件打分分数超过某个阈值如0.5就归到A类否则归到B类。这东西能干嘛场景太广了。比如金融领域的信用评分判断一笔贷款申请是“通过”还是“拒绝”医疗诊断中根据一些生理指标判断肿瘤是“良性”还是“恶性”互联网广告里预测用户点击某个广告的“可能性”。它的核心价值在于不仅给出一个非黑即白的分类结果还能给出一个属于某个类别的“概率”这个概率值往往比单纯的“是/否”更有业务指导意义。比如银行可以给信用评分0.8的客户优先放款而对0.6的客户进行更严格的审核。无论你是刚接触机器学习的学生正在为“山东大学机器学习期末”这样的考试头疼还是业务部门的数据分析师想用“机器学习预测模型”做个“瀑布图”来分析影响因素或者工程师需要快速搭建一个如“tf-idf和逻辑回归做分类”的文本分类基线模型逻辑回归都是一个绕不开的、坚实可靠的起点。它模型简单、可解释性强、计算效率高在特征工程到位的情况下其性能常常不输于一些更复杂的模型是实践中名副其实的“瑞士军刀”。2. 核心原理从线性到非线性的“概率跃迁”要搞懂逻辑回归不能只停留在调包调用sklearn.linear_model.LogisticRegression得看看它肚子里的“墨水”。它的核心思想是把一个线性回归的结果“挤压”到0和1之间使其成为一个合法的概率值。2.1 线性回归的局限与Sigmoid函数的登场我们先回想一下最简单的线性回归z w^T * x b。这里z是预测值可以是任意实数从负无穷到正无穷。但概率值必须在[0, 1]区间内。直接拿z当概率显然不行。逻辑回归的妙处就是引入了一个“连接函数”把z映射到(0,1)区间。这个函数就是大名鼎鼎的Sigmoid 函数也叫逻辑函数Logistic Function。它的公式长这样σ(z) 1 / (1 e^{-z})你可以把它想象成一个“压扁器”或“饱和电路”。当z趋向于正无穷时e^{-z}趋近于0σ(z)趋近于1当z趋向于负无穷时e^{-z}趋近于正无穷σ(z)趋近于0当z0时σ(z) 0.5。这个函数的图像是一个优美的S型曲线完美地将整个实数轴“压缩”到了(0,1)的开区间内。于是逻辑回归的预测过程就变成了两步计算线性加权和z w1*x1 w2*x2 ... wn*xn b通过Sigmoid函数得到概率p σ(z) 1 / (1 e^{-z})这个p就代表了样本属于正类通常标记为1的概率。P(y1|x) p 自然P(y0|x) 1 - p。2.2 决策边界那条看不见的“分界线”模型输出了概率我们怎么最终分类呢通常设定一个阈值比如0.5。如果p 0.5我们预测为正类1如果p 0.5预测为负类0。由于p 0.5对应着σ(z) 0.5而σ(z)0.5时解得z 0。所以这个决策规则等价于如果z w^T*x b 0预测为正类。如果z w^T*x b 0预测为负类。这里的z 0即w^T*x b 0在二维特征空间里是一条直线在三维空间里是一个平面在高维空间里是一个超平面。这条线或面就是逻辑回归的“决策边界”。模型学习的过程本质上就是在寻找能最好地区分两类数据的那个决策边界的位置和方向。注意虽然逻辑回归的决策边界是线性的因为z是特征的线性组合但这并不意味着它只能处理线性可分的数据。通过特征工程比如引入多项式特征x1^2,x1*x2等逻辑回归可以拟合出非线性的决策边界。这是很多人初学时容易混淆的点。2.3 损失函数交叉熵损失为何是“最佳拍档”模型有了怎么让它学得好呢这就需要定义一个损失函数Loss Function来衡量模型预测概率p和真实标签y之间的差距。对于分类问题最常用且理论上非常优雅的损失函数是交叉熵损失Cross-Entropy Loss。对于单个样本损失函数定义为L - [y * log(p) (1-y) * log(1-p)]为什么是它我们可以直观理解如果真实标签y1损失变为-log(p)。预测概率p越接近1-log(p)越接近0损失小p越接近0-log(p)会变得非常大损失大惩罚很重。如果真实标签y0损失变为-log(1-p)。预测概率p越接近0即1-p越接近1损失越小p越接近1损失越大。这个函数完美地表达了我们的诉求对于正确分类我们希望模型对自己的预测非常确信概率接近1或0对于错误分类或者即使分类正确但信心不足概率接近0.5都会产生较大的损失。它比传统的均方误差MSE更适合概率输出因为MSE在概率问题上会导致优化目标非凸容易陷入局部最优而交叉熵损失是凸函数能保证找到全局最优解在逻辑回归的线性假设下。对于整个训练集m个样本我们计算平均损失即代价函数Cost FunctionJ(w, b) -(1/m) * Σ [y_i * log(p_i) (1-y_i) * log(1-p_i)]模型训练参数学习的目标就是找到一组参数(w, b)使得这个代价函数J的值最小。2.4 参数求解梯度下降的“寻宝之旅”如何最小化J(w, b)最常用的方法是梯度下降Gradient Descent。你可以把它想象成蒙眼下山我们站在参数空间的一个随机点山腰想要走到山谷最低点损失最小处。我们每走一步都沿着当前所在位置最陡的下坡方向前进。这个“方向”就是损失函数对各个参数的梯度偏导数。对于逻辑回归其梯度形式非常简洁优美这也是它受欢迎的原因之一。经过求导推导过程略我们可以得到∂J/∂w_j (1/m) * Σ (p_i - y_i) * x_j_i∂J/∂b (1/m) * Σ (p_i - y_i)这个结果非常直观参数更新的方向与“预测误差”(p_i - y_i)和对应特征值x_j_i的乘积之和有关。误差大更新幅度就大某个特征对误差的贡献大其对应权重的更新幅度也大。然后我们按照以下公式迭代更新参数直到收敛w_j : w_j - α * ∂J/∂w_jb : b - α * ∂J/∂b其中α是学习率控制着每一步的步长。实操心得在实际使用sklearn时我们通常不需要手动实现梯度下降。LogisticRegression的solver参数提供了多种优化算法如liblinear适用于小数据集、lbfgs默认适用于中等数据集、saga支持弹性网络正则化的大数据集。但对于理解原理亲手用NumPy实现一遍梯度下降是打通任督二脉的关键一步。3. 从理论到实践构建一个完整的逻辑回归模型理解了原理我们来看看如何一步步构建并运用一个逻辑回归模型。这里我以一个经典的“鸢尾花数据集”二分类简化版只取两类为例但流程完全通用。3.1 环境准备与数据洞察首先你需要一个Python环境安装好numpy,pandas,matplotlib和scikit-learn。数据科学三板斧。pip install numpy pandas matplotlib scikit-learn我们加载数据并快速查看一下。这里我用sklearn自带的鸢尾花数据集但只保留山鸢尾0和变色鸢尾1把问题转化为二分类。import numpy as np import pandas as pd from sklearn import datasets from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt # 加载数据 iris datasets.load_iris() X iris.data[iris.target ! 2] # 只取前两类 y iris.target[iris.target ! 2] # 为了方便可视化我们只取两个特征花瓣长度和花瓣宽度 X X[:, [2, 3]] # 花瓣长度特征2花瓣宽度特征3 # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) print(f训练集形状: {X_train.shape}) print(f测试集形状: {X_test.shape})先画个散点图看看数据分布这是建模前的好习惯。plt.figure(figsize(10, 6)) plt.scatter(X_train[y_train0, 0], X_train[y_train0, 1], colorblue, label山鸢尾 (0), alpha0.7) plt.scatter(X_train[y_train1, 0], X_train[y_train1, 1], colorred, label变色鸢尾 (1), alpha0.7) plt.xlabel(花瓣长度 (cm)) plt.ylabel(花瓣宽度 (cm)) plt.title(训练数据分布) plt.legend() plt.grid(True, linestyle--, alpha0.5) plt.show()从图上你应该能直观看到两类花在花瓣长度和宽度上是有区别的大致可以用一条斜线分开。这暗示线性决策边界可能是有效的。3.2 特征工程模型的“燃料”原始数据直接喂给模型往往效果不好。特征工程就是为模型准备高质量“燃料”的过程。对于逻辑回归以下几项尤为重要特征缩放Feature Scaling逻辑回归虽然不像KNN或SVM那样对尺度极度敏感但进行缩放如标准化能加速梯度下降的收敛过程。因为特征尺度差异大时损失函数的等高线会是扁长的椭圆梯度下降会走“之字形”收敛慢。使用StandardScaler是不错的选择。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意用训练集的均值和方差来转换测试集处理缺失值逻辑回归本身不能处理缺失值。需要根据情况填充如用均值、中位数或删除缺失样本。特征构造如果问题非线性可以手动构造多项式特征、交互项等。例如在“储能EMS”或“变压器需量控制”这类复杂工业场景中原始信号如电流、电压、温度可能不够需要构造出如“负载率变化斜率”、“相邻时段温差”等具有物理或业务意义的衍生特征。文本特征处理对于“tf-idf和逻辑回归做分类”这种文本场景核心就是将文本转换成数值特征。TF-IDF词频-逆文档频率是最常用的方法之一。TfidfVectorizer可以帮你自动完成。from sklearn.feature_extraction.text import TfidfVectorizer corpus [这是一个好产品, 这个产品很糟糕, 好与坏的标准] vectorizer TfidfVectorizer() X_text vectorizer.fit_transform(corpus) print(X_text.shape) # (3, 特征词数量)TF-IDF值反映了单词在文档中的重要程度构成了逻辑回归的输入特征矩阵。3.3 模型训练、预测与评估现在让我们用处理好的数据来训练模型。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, roc_auc_score # 创建模型实例 # penaltyl2 表示使用L2正则化默认C是正则化强度的倒数C越小正则化越强 model LogisticRegression(penaltyl2, C1.0, solverlbfgs, random_state42) # 训练模型 model.fit(X_train_scaled, y_train) # 在训练集和测试集上进行预测 y_train_pred model.predict(X_train_scaled) y_test_pred model.predict(X_test_scaled) y_test_pred_proba model.predict_proba(X_test_scaled)[:, 1] # 获取属于正类的概率 # 评估模型 print(训练集准确率, accuracy_score(y_train, y_train_pred)) print(测试集准确率, accuracy_score(y_test, y_test_pred)) print(\n测试集分类报告) print(classification_report(y_test, y_test_pred)) print(\n测试集混淆矩阵) print(confusion_matrix(y_test, y_test_pred)) # 计算AUC-ROC (需要概率值) print(测试集AUC-ROC, roc_auc_score(y_test, y_test_pred_proba))关键参数解析penalty: 正则化类型。l1Lasso可以产生稀疏解即让一些特征的系数变为0实现特征选择l2Ridge让系数整体变小防止过拟合。根据需求选择l2更常用。C: 正则化强度的倒数。C值越小正则化越强模型越简单越可能欠拟合C值越大正则化越弱模型越复杂越可能过拟合。通常通过交叉验证网格搜索来寻找最佳C值。solver: 优化算法。对于小数据集或l1正则化liblinear不错对于大数据集saga效率高lbfgs是默认的稳健选择。max_iter: 最大迭代次数。如果看到收敛警告可以适当增大这个值。3.4 模型解释与决策可视化逻辑回归的一大优势是可解释性强。我们可以查看学到的系数。print(模型截距 (b):, model.intercept_) print(模型系数 (w):, model.coef_) # 对应特征花瓣长度 花瓣宽度 feature_names [花瓣长度(scaled), 花瓣宽度(scaled)] for name, coef in zip(feature_names, model.coef_[0]): print(f{name}: {coef:.4f})系数的大小和符号非常有意义。正系数意味着该特征值增大会增加样本被预测为正类的概率负系数则相反。系数的绝对值大小反映了该特征对预测结果的影响力。我们可以将决策边界可视化直观感受模型是如何划分世界的。# 创建一个网格来覆盖整个特征空间 x_min, x_max X_train_scaled[:, 0].min() - 0.5, X_train_scaled[:, 0].max() 0.5 y_min, y_max X_train_scaled[:, 1].min() - 0.5, X_train_scaled[:, 1].max() 0.5 xx, yy np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02)) # 用模型预测网格上每一个点的类别 Z model.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) # 绘制决策区域和边界 plt.figure(figsize(12, 8)) plt.contourf(xx, yy, Z, alpha0.3, cmapplt.cm.coolwarm) # 决策区域 plt.scatter(X_train_scaled[y_train0, 0], X_train_scaled[y_train0, 1], colorblue, label山鸢尾 (0), edgecolorsk) plt.scatter(X_train_scaled[y_train1, 0], X_train_scaled[y_train1, 1], colorred, label变色鸢尾 (1), edgecolorsk) plt.xlabel(花瓣长度 (标准化后)) plt.ylabel(花瓣宽度 (标准化后)) plt.title(逻辑回归决策边界) plt.legend() plt.show()这张图会清晰地显示出一条分界线以及两侧被染成不同颜色的决策区域。你可以看到模型学习到的这条线性边界是如何尽可能好地将两类点分开的。4. 高级话题与实战避坑指南掌握了基础流程我们再来深入几个实战中必然会遇到的关键问题。4.1 多分类问题从“一对一”到“一对多”逻辑回归本质是二分类器。那遇到像完整鸢尾花数据集3类这样的多分类问题怎么办有两种主流策略OvROne-vs-Rest 一对多为每个类别训练一个二分类器。训练“类别0 vs 非类别0”的模型再训练“类别1 vs 非类别1”的模型以此类推。预测时哪个分类器给出的“正类”概率最高就属于哪个类别。sklearn的LogisticRegression在multi_classovr时采用此策略。OvOOne-vs-One 一对一为每两个类别训练一个二分类器。对于K个类别需要训练 K*(K-1)/2 个模型。预测时相当于让所有类别“两两对决”统计每个类别的获胜次数胜出最多的就是最终类别。当类别很多时模型数量会爆炸但每个模型只用两类数据训练可能更精确。sklearn的SVC默认使用OvO。对于逻辑回归sklearn默认使用multi_classauto对于二分类或solver为liblinear时用OvR其他情况使用multinomial多项逻辑回归即Softmax回归这是一种更自然的多分类扩展直接输出每个类别的概率。# 多分类示例使用完整鸢尾花数据 X_multi iris.data y_multi iris.target X_train_m, X_test_m, y_train_m, y_test_m train_test_split(X_multi, y_multi, test_size0.2, random_state42) scaler_m StandardScaler() X_train_m_scaled scaler_m.fit_transform(X_train_m) X_test_m_scaled scaler_m.transform(X_test_m) model_multi LogisticRegression(multi_classmultinomial, solverlbfgs, max_iter200) model_multi.fit(X_train_m_scaled, y_train_m) print(多分类测试集准确率, model_multi.score(X_test_m_scaled, y_test_m))4.2 样本不均衡当“1”比“0”少得多在真实世界中正负样本比例常常悬殊比如欺诈检测欺诈交易远少于正常交易、疾病诊断患者远少于健康人。这时如果直接使用原始数据训练模型会倾向于预测多数类因为这样整体准确率看起来很高但对少数类的识别能力召回率会极差。应对策略调整类别权重class_weight这是最简单有效的方法。在LogisticRegression中设置class_weightbalanced算法会自动根据类别频率调整损失函数中每个类别的权重让模型更关注少数类。model_balanced LogisticRegression(class_weightbalanced, random_state42)重采样过采样增加少数类样本的副本或合成新样本如SMOTE算法。欠采样随机减少多数类样本。注意过采样可能导致过拟合欠采样可能丢失重要信息。通常优先尝试调整class_weight。改变评估指标不要只看准确率Accuracy。要关注精确率Precision、召回率Recall、F1-Score特别是少数类的召回率以及AUC-ROC曲线下的面积。ROC曲线不依赖于阈值能更好地评估模型在不同阈值下的整体性能。4.3 过拟合与正则化给模型戴上“紧箍咒”当模型在训练集上表现很好在测试集上却很差时很可能发生了过拟合模型太复杂记住了训练数据的噪声。逻辑回归对抗过拟合的主要武器是正则化。正则化在损失函数中增加了一个惩罚项限制模型参数w的大小。L1正则化Lasso惩罚项是λ * Σ|w_j|。它倾向于产生稀疏解即将一些不重要的特征的系数直接压缩为0兼具特征选择功能。适用于特征维度很高且你认为只有少量特征真正起作用的情况。L2正则化Ridge惩罚项是(λ/2) * Σ(w_j^2)。它让所有系数都整体变小但不会变成0。更通用更稳定。在sklearn中通过penalty参数选择l1或l2通过C参数控制正则化强度C 1 / λ。C越小λ越大正则化越强。寻找最佳C值通常使用网格搜索交叉验证from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid {C: [0.001, 0.01, 0.1, 1, 10, 100], penalty: [l1, l2]} # 注意l1正则化需要特定的solver支持如liblinear或saga model_for_grid LogisticRegression(solverliblinear, max_iter1000) grid_search GridSearchCV(model_for_grid, param_grid, cv5, scoringaccuracy) grid_search.fit(X_train_scaled, y_train) print(最佳参数, grid_search.best_params_) print(最佳交叉验证分数, grid_search.best_score_) best_model grid_search.best_estimator_4.4 特征重要性分析与业务解释逻辑回归的系数可以直接用于解释。但要注意如果特征经过了标准化比较系数的绝对值大小才有意义。我们可以将其可视化# 假设我们有一个包含多个特征的数据集例如一个信贷评分数据集 # 这里用鸢尾花多特征示例 X_multi iris.data feature_names iris.feature_names model_multi.fit(StandardScaler().fit_transform(X_multi), iris.target) # 对于多分类每个类别有一组系数 coef_df pd.DataFrame(model_multi.coef_, columnsfeature_names, indexiris.target_names) print(各类别对应的特征系数) print(coef_df) # 绘制特征重要性以类别0为例 plt.figure(figsize(10, 5)) sorted_idx np.argsort(np.abs(coef_df.iloc[0]))[::-1] # 按绝对值排序 plt.barh(range(len(sorted_idx)), coef_df.iloc[0][sorted_idx]) plt.yticks(range(len(sorted_idx)), np.array(feature_names)[sorted_idx]) plt.xlabel(系数值) plt.title(逻辑回归特征系数类别setosa) plt.tight_layout() plt.show()在业务汇报中你可以这样说“我们的模型显示在判断是否为欺诈交易时‘交易金额’的系数最大且为正意味着单笔交易金额越大是欺诈的风险越高而‘用户历史交易次数’的系数为负说明历史交易频繁的老用户风险较低。” 这种解释能力是很多复杂“黑箱”模型如深度神经网络所不具备的。5. 常见问题排查与调优技巧实录在实际项目中你肯定会遇到各种问题。下面是我踩过的一些坑和总结的技巧。5.1 模型不收敛或警告问题运行模型时控制台输出ConvergenceWarning: lbfgs failed to converge (status1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.原因与解决迭代次数不足这是最常见的原因。增加max_iter参数比如从默认的100增加到500或1000。model LogisticRegression(max_iter1000)学习率或优化器问题sklearn的LogisticRegression封装了优化过程我们通常不直接设置学习率。但可以尝试换一个solver。对于小数据集试试liblinear对于大数据集或需要l1正则化试试saga。数据未标准化特征尺度差异巨大导致优化路径曲折。务必进行特征缩放。正则化太强C值设得太小如0.0001正则化项主导了损失函数可能导致模型学不到有效模式。尝试增大C。数据本身问题特征之间可能存在严格的线性关系多重共线性或者标签与特征之间几乎没有线性关系。检查数据进行特征选择或构造更有意义的特征。5.2 预测概率全是0.5左右模型很“犹豫”问题调用predict_proba发现很多样本预测的概率都在0.5附近模型区分能力很弱。原因与解决特征与目标关系弱你选的特征可能根本不足以区分两类。需要做更深入的特征工程或者寻找新的数据源。正则化过强同上C值太小模型权重被压缩得太厉害导致所有特征的贡献都很小z值集中在0附近经过Sigmoid后概率就集中在0.5。调大C。数据噪声太大数据中存在大量错误标签或噪声干扰了模型学习。需要清洗数据。尝试非线性如果问题本质是非线性的线性逻辑回归的决策边界能力有限。可以尝试使用sklearn.preprocessing.PolynomialFeatures生成多项式特征。使用核逻辑回归但sklearn未直接提供可通过SVC配合特定核函数近似。考虑使用能处理非线性关系的模型如决策树、随机森林或神经网络。5.3 AUC不错但业务效果差问题模型评估指标如AUC-ROC看起来很好但上线后实际业务效果如抓到的欺诈案数量不理想。原因与解决阈值选择不当AUC-ROC衡量的是模型整体排序能力但最终分类需要设定阈值。默认0.5不一定是最优的。你需要根据业务代价来调整阈值。精确率优先场景如垃圾邮件过滤宁可漏杀不可错杀提高阈值如0.7, 0.8只有模型非常确信时才判为正类。召回率优先场景如癌症筛查宁可误诊不可漏诊降低阈值如0.3, 0.2。 可以使用sklearn.metrics.precision_recall_curve绘制P-R曲线找到满足业务需求的最佳阈值。from sklearn.metrics import precision_recall_curve precisions, recalls, thresholds precision_recall_curve(y_test, y_test_pred_proba) # 假设我们要求召回率至少达到90% target_recall 0.9 idx np.argmin(np.abs(recalls[:-1] - target_recall)) # precisions和recalls比thresholds长1 optimal_threshold thresholds[idx] print(f达到召回率{target_recall}所需的最佳阈值约为{optimal_threshold:.3f})数据分布偏移训练数据与线上实时数据分布不一致。例如训练数据是三个月前的用户行为而现在的用户行为模式已经变了。需要建立持续监控和模型更新的机制。评估指标与业务目标未对齐AUC高不代表赚钱多。在金融风控中可能需要优化的是“利润-损失”曲线。需要与业务方紧密沟通定义更贴合业务目标的评估指标。5.4 与其它模型的对比与选型思考逻辑回归不是万能的。在项目开始时的模型选型会上我常会画下面这个简单的决策图来辅助思考考虑维度逻辑回归优势其他模型可能更优的情况可解释性极强系数直接对应特征影响。可解释性要求低更追求极致性能。数据量中小规模数据表现稳定训练快。数据量极大百万级以上深度学习或梯度提升树可能挖掘更深层模式。特征关系假设特征与log-odds是线性关系。特征间存在复杂交互、高度非线性关系如图像、音频。计算资源需求极低可在普通CPU上快速训练预测。资源充足愿意用计算换性能。需求场景需要概率输出、快速基线、线上高并发低延迟预测。复杂的推荐、自然语言处理、计算机视觉任务。一个实用的建议永远从逻辑回归开始。它快速、可解释、能提供一个坚实的性能基线。如果逻辑回归表现已经很好项目可能就成功了80%。如果不好通过分析它的错误案例和系数你能获得关于数据和问题的宝贵洞见这些洞见会指导你下一步是进行更复杂的特征工程还是转向更强大的模型。它不仅仅是一个模型更是一个强大的数据分析工具。
返回列表