找回密码
 立即注册
首页 业界区 业界 彩笔运维勇闯机器学习--逻辑回归

彩笔运维勇闯机器学习--逻辑回归

悯拄等 7 小时前
前言

从本节开始,我们的机器学习之旅进入了下一个篇章。之前讨论的是回归算法,回归算法主要用于预测数据。而本节讨论的是分类问题,简而言之就是按照规则将数据分类
而要讨论的逻辑回归,虽然名字叫做回归,它要解决的是分类问题
开始探索

scikit-learn

还是老规矩,先来个例子,再讨论原理
假设以下场景:一位老哥想要测试他老婆对于抽烟忍耐度,他进行了以下测试
星期一星期二星期三星期四星期五星期六星期日抽烟(单位:根)61814135108是否被老婆打否是是是否是否将以上情形带入模型
  1. from sklearn.linear_model import LogisticRegression
  2. import numpy as np
  3. X = np.array([6, 18, 14, 13, 5, 10, 8]).reshape(-1, 1)
  4. y = np.array([0, 1, 1, 1, 0, 1, 0])
  5. model = LogisticRegression()
  6. model.fit(X, y)
  7. print(f"系数: {model.coef_[0][0]:.4f}")
  8. print(f"截距: {model.intercept_[0]:.4f}")
  9. decision_boundary = -model.intercept_[0] / model.coef_[0][0]
  10. print(f"决策边界: {decision_boundary:.2f}")
复制代码
脚本!启动:
1.png

报告解读

单特征影响结果,这明显是一个线性模型,所以出现了熟悉的系数与截距,还有一个新的参数:决策边界,这意味着9.1就是分类阈值,>=9.1的结果分类为1,0.5为1,反之3个特征就已经不能画出来了
2个特征

继续刚才的问题,比如除了抽烟被打,再加上喝酒,2个特征
星期一星期二星期三星期四星期五星期六星期日抽烟(单位:根)61814135108喝酒(单位:两)8124330是否被老婆打是否否是否是是
  1. from sklearn.metrics import log_loss
  2. y_proba = model.predict_proba(X)[:, 1]
  3. loss_sklearn = log_loss(y, y_proba)
  4. print('=='*20)
  5. print(f"损失函数(Log Loss): {loss_sklearn:.4f}")
复制代码
2.png

决策边界:$$ y=\frac{0.127x-0.94}{0.26} $$
  1. from sklearn.metrics import accuracy_score
  2. y_pred = model.predict(X)
  3. accuracy = accuracy_score(y, y_pred)
  4. print('=='*20)
  5. print(f"准确率:{accuracy:.2f}")
复制代码
3.png

在边界以上的是1,边界以下的0
类别不平衡

比如以下代码,1000个样本中,只有14个1,986个0,属于严重的类别不平衡
  1. [[3 1]  # TN=3, FP=1
  2. [1 3]] # FN=1, TP=3
复制代码
4.png


  • precision:模型在识别少数类1上完全失败,虽然多数类0的准确率是99%,但是毫无意义,从未正确预测为1
  • recall:所有真正为0的样本都被找到了(100%);一个1类都没找到
  • f1-score:类别1的 F1 是 0,说明模型对少数类的预测能力完全崩溃
  • support:类别0有 296 个样本,类别1只有 4 个样本
  • accuracy:0.99,模型总共预测对了 296 个,错了 4 个
  • macro avg:每个类的指标的“简单平均”,不考虑样本数权重
  • weighted avg:各类指标的“加权平均”,考虑样本量
有位彦祖说了,你这分类只分了1次训练集和测试集,如果带上交叉验证,多分几次类,让其更有机会学习到少数类,情况能不能有所改善?
  1. from sklearn.metrics import confusion_matrix
  2. print('=='*20)
  3. print('混淆矩阵:')
  4. y_pred = model.predict(X)
  5. cm = confusion_matrix(y, y_pred)
  6. print(cm)
复制代码
5.png

情况并没有好转,模型依然无法区分少数类
权重调整
  1. from sklearn.metrics import classification_report
  2. print('=='*20)
  3. y_pred = model.predict(X)
  4. print("Logistic Regression 分类报告:\n", classification_report(y, y_pred))
复制代码
6.png

情况有所好转

  • 1的recall从0-->0.5,2 个正类样本中至少预测中了 1 个
  • 1的Precision从0-->0.01,模型预测为正类的样本大多数是错的,这是 class_weight 造成的:宁愿错也要猜一猜正类
  • 0的recall从1-->0.7,同样是class_weight造成的,把一部分原本是负类的样本错判为正类了
  • accuracy从99%-->70%,模型开始尝试预测少数类,虽然整体正确率下降,但变得更愿意去预测少数类了
过采样

增加少数类样本,复制或生成新样本,通过 SMOTE(Synthetic Minority Over-sampling Technique)进行过采样
  1. from sklearn.metrics import roc_curve, roc_auc_score
  2. y_proba = model.predict_proba(X)[:, 1]
  3. auc_score = roc_auc_score(y, y_proba)
  4. print('=='*20)
  5. print(f"AUC = {auc_score:.4f}")
复制代码
7.png


  • recall提升到了0.64,模型识别了少数类的概率提升了
  • Precision=0.04,精确率依旧不佳
  • accuracy=0.75,由于少数类的识别概率提升,所以整体的准确率有所提升
欠采样

减少多数类样本(随机删除或聚类),通过RandomUnderSampler进行欠采样
  1. import matplotlib.pyplot as plt
  2. fpr, tpr, thresholds = roc_curve(y, y_proba)
  3. plt.figure(figsize=(6, 5))
  4. plt.plot(fpr, tpr, color='blue', label=f'ROC curve (AUC = {auc_score:.4f})')
  5. plt.plot([0, 1], [0, 1], color='gray', linestyle='--')
  6. plt.xlabel('False Positive Rate')
  7. plt.ylabel('True Positive Rate')
  8. plt.title('ROC Curve')
  9. plt.legend()
  10. plt.grid(True)
  11. plt.tight_layout()
  12. plt.show()
复制代码
8.png

与过采样大同小异,效果还不如过采样
正则化

lasso与Ridge在这里依然可以使用
  1. from sklearn.linear_model import LogisticRegression
  2. from sklearn.metrics import classification_report
  3. import numpy as np
  4. X = np.array([
  5.     [6,8],
  6.     [18,1],
  7.     [14,2],
  8.     [13,4],
  9.     [5,3],
  10.     [10,3],
  11.     [8,0],
  12. ])
  13. y = np.array([1, 0, 0, 1, 0, 1, 1])
  14. model = LogisticRegression()
  15. model.fit(X, y)
  16. coef = model.coef_[0]
  17. intercept = model.intercept_[0]
  18. print(f"系数: {coef}")
  19. print(f"截距: {intercept}")
复制代码
9.png

代价敏感学习

这其实也是其中调整的一种,只不过针对于class_weight这个超参数,进行了更精细化得调整
  1. import matplotlib.pyplot as plt
  2. x_vals = np.linspace(X[:, 0].min() - 1, X[:, 0].max() + 1, 100)
  3. decision_boundary = -(coef[0] * x_vals + intercept) / coef[1]
  4. plt.figure(figsize=(8, 6))
  5. colors = ['red' if label == 0 else 'blue' for label in y]
  6. plt.scatter(X[:, 0], X[:, 1], c=colors, s=80, edgecolor='k')
  7. plt.plot(x_vals, decision_boundary, 'k--', label='Decision Boundary')
  8. plt.legend()
  9. plt.grid(True)
  10. plt.tight_layout()
  11. plt.show()
复制代码
class_weight={0: 1, 1: 50} 的含义:

  • 类别 0(多数类)的权重为 1(标准惩罚)
  • 类别 1(少数类)的权重为 50(错误预测时惩罚更严重)
10.png

这是一种牺牲准确率为代价,尽量不要漏掉任何一个少数类,所以表现就是少数类1的precision很低,但是recall是非常高的。这就是所谓的宁可错杀一千,也绝不放过一个
小结

在逻辑回归中,针对类别不平衡的问题,往往有两种决策

  • 一种是宁可误报,也不能漏报。先把少数类找出来,再对少数类进行进一步的校验。比如预测入侵筛查、代码漏洞检测等
  • 另外一种则是需要更关注多数类,有少数类被误报,也是可以接受。比如垃圾邮件分类、推荐系统的准确率等
联系我


  • 联系我,做深入的交流
    11.bmp

至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册