逻辑回归介绍
逻辑回归(Logistic Regression)是一种用于分类任务的算法。虽然名字里有“回归”,但它本质上是一个分类模型,不是拿来预测连续值的;它最擅长处理二分类任务,比如“是/不是”“通过/不通过”“会/不会点击”这类问题。它先对特征做线性组合,再通过 Sigmoid 函数把结果压到 0~1,最后把这个值解释成“属于某个类别的概率”。

扩展部分:几个常见概率背景/概念
概率基础
- 逻辑回归最后输出的不是“具体分数”,而是“属于某一类的概率”,所以先把几个常见概率概念分清。
- 边际概率
- 描述的是单个事件发生的可能性。
- 记作 $P(A)$。
- 例如:明天下雨的概率、一个用户会点击广告的概率。
- 联合概率
- 描述的是两个或多个事件同时发生的概率。
- 记作 $P(A \cap B)$。
- 例如:一个用户既点击了广告,又完成了下单。
- 条件概率
- 描述的是在事件 A 已经发生的前提下,事件 B 发生的概率。
- 记作 $P(B \mid A)$。
- 例如:用户已经点进商品页后,最终下单的概率。
一句话理解
- 边际概率:只看一件事会不会发生。
- 联合概率:看几件事会不会一起发生。
- 条件概率:先假设一件事已经发生,再看另一件事发生的可能性。
扩展部分:Sigmoid 函数不是归一化
它俩只是恰好都能把数值压缩到 0~1 区间,但作用对象、目的、使用阶段完全是两回事。
1. 归一化:是「输入特征的预处理操作」 我们常说的归一化(比如 Min-Max 归一化)、标准化,都属于数据预处理步骤——是在数据喂给模型之前,对「输入特征」做的处理。
- 作用对象:原始特征列(比如房屋面积、用户收入、年龄)
- 核心目的:消除不同特征的数值量级差异,让模型公平学习、训练收敛更快
- 典型计算(Min-Max 归一化):
(x - 最小值) / (最大值 - 最小值) - 特点:依赖当前这批数据的统计值(最值/均值),是对“一整列数据”做尺度统一
2. Sigmoid:是「模型内部的概率映射函数」
Sigmoid 是逻辑回归模型计算过程中的一环,它处理的不是输入特征,而是“特征线性组合后的结果 z = wx + b”。
- 作用对象:线性计算的输出值(可以是负无穷到正无穷的任意实数)
- 核心目的:把任意实数转换成 0~1 之间的数值,并赋予「概率」的含义,最终用来做二分类决策
- 计算公式:$f(z) = 1 / (1 + e^(-z))$
- 特点:是固定的数学函数,和数据分布无关;输入越大输出越接近1,输入越小输出越接近0,输入为0时刚好等于0.5
核心区别对照表
| 对比维度 | 归一化(Min-Max) | Sigmoid 函数 |
|---|---|---|
| 使用阶段 | 数据预处理(模型外部) | 模型计算内部 |
| 处理对象 | 输入特征 x | 线性加权结果 z = wx + b |
| 核心目的 | 统一特征量纲,辅助模型稳定训练 | 输出概率值,支撑分类判断 |
| 数值依赖 | 依赖数据的最值/均值等统计量 | 固定公式,和数据分布无关 |
通俗例子帮你分清
比如做“用户会不会点击广告”的分类模型:
- 归一化:是把“用户年龄(18 ~ 60岁)”“用户月收入(3千 ~ 5万)”这些尺度差很大的特征,都缩到 0~1 之间,避免模型误以为“收入”天然比“年龄”更重要。
- Sigmoid:是模型把年龄、收入等特征加权算完得到一个分数(比如 2.5 或者 -1.3)后,用 Sigmoid 把这个分数转成 0 ~ 1 的数,比如 0.92,解释成“这个用户有 92% 的概率会点击广告”。
一句话理清顺序
在逻辑回归的完整流程里,它俩是先后关系,互不冲突:
原始特征 → 做归一化/标准化 → 线性加权求和 → 过 Sigmoid → 输出分类概率
使用场景
逻辑回归是分类任务,线性回归就是回归任务

逻辑回归原理
逻辑回归主要拿来做分类。更准确地说,它可以分成两条主线:预测流程和训练流程。
主线流程:
- 预测流程:线性组合 -> Sigmoid -> 概率 -> 阈值分类
- 训练流程:线性组合 -> Sigmoid -> 交叉熵损失 -> 梯度下降更新参数
展开看逻辑回归的预测流程和训练流程
1. 预测流程:模型怎么做分类
先做线性组合:$z = w^T x + b$
- 这个 $z$ 可以理解成模型先算出来的一个“原始分数”,范围不固定,不能直接当概率用。
再过 Sigmoid 函数:$\sigma(z) = \frac{1}{1 + e^{-z}}$
- Sigmoid 会把任意实数映射到
0到1之间。公式:$\sigma(z) = \frac{1}{1 + e^{-z}}$ - 所以逻辑回归真正输出的是:$P(y=1 \mid x) = \sigma(w^T x + b)$
- 这个值就可以理解成:给定输入特征 $x$ 时,样本属于正类的概率。
最后按阈值分类
- 如果概率大于某个阈值,就判成正类。
- 如果概率小于某个阈值,就判成负类。
- 最常见的阈值是
0.5,但实际业务里也可以按需求调整。
2. 训练流程:模型怎么学会这条分类线
- 训练时,模型不会先急着按阈值分正负类,而是先看“预测概率和真实标签到底差多少”。
- 这个“差多少”的衡量标准,常用的就是交叉熵损失。
- 再通过梯度下降不断调整参数 $w$ 和 $b$,让损失越来越小。
- 参数调好之后,模型本质上就是学到了一条更合适的分类边界。
一句话理解
逻辑回归本质上是:预测时先算分数、再转概率、最后做分类;训练时用交叉熵衡量误差,再用梯度下降调整参数。
为什么转概率值?
- 因为线性计算出来的原始分数没有固定范围,直接拿它做分类不直观,也不方便统一设阈值。
- 转成
0~1的概率后,就更容易解释成“属于某一类的可能性有多大”,也更方便做二分类判断。
扩展:底层原理 交叉熵损失与极大似然估计等
这部分属于更偏原理的扩展内容,知道它和交叉熵有关就够了
伯努利分布与似然函数
伯努利分布
- 逻辑回归最常见的是二分类,而二分类的结果只有两种:
0或1。 - 这种“只有两种结果”的随机变量,就很适合用伯努利分布来描述。
- 你可以把它理解成一次抛硬币:
- 结果要么是正面,要么是反面;在模型里,就是要么属于这一类,要么不属于这一类。
伯努利分布函数
- 如果一个样本的标签 $y \in \{0, 1\}$,模型预测它属于正类的概率是 $p$,那么伯努利分布可以写成:
- 当 $y=1$ 时,公式会变成 $P(y \mid x)=p$。
- 当 $y=0$ 时,公式会变成 $P(y \mid x)=1-p$。
- 在逻辑回归里,这里的 $p$ 就是:
- 也就是说,逻辑回归先用 Sigmoid 算出概率,再拿这个概率去描述二分类结果。
似然函数
- 似然函数可以理解成:假设当前这组参数是对的,那训练数据出现的可能性有多大。
- 如果模型参数选得好,真实标签就应该更容易被“解释出来”,也就是似然更大。
- 对单个样本来说,似然就是:
- 对整个训练集来说,就是把所有样本的概率乘起来:
- 逻辑回归训练时,本质上就是希望这整个似然函数尽量大。
交叉熵损失与极大似然估计
为什么又会变成交叉熵损失
- 直接把很多概率连乘,数值会非常小,计算不方便。
- 所以通常会先取对数,把“连乘”变成“连加”:
- 训练时我们一般习惯写成“损失越小越好”,所以再取一个负号,就得到常见的对数损失,也就是二分类交叉熵损失:
怎么理解交叉熵损失
- 如果真实标签是
1,模型给出的 $p$ 越接近1,损失越小。 - 如果真实标签是
0,模型给出的 $p$ 越接近0,损失越小。 - 如果模型明明很有把握,却判断错了,交叉熵会罚得很重。
- 所以它特别适合分类任务,能逼着模型把“正确类别的概率”尽量拉高。
交叉熵和似然函数是什么关系
- 这两个东西本质上是在说同一件事,只是站的角度不一样。
- 似然函数站在“概率越大越好”的角度,希望训练数据在当前参数下出现的可能性尽量大。
- 交叉熵损失站在“损失越小越好”的角度,希望模型犯错更少、而且别错得太自信。
- 数学上,交叉熵损失其实就是负对数似然再取平均:
- 所以:
- 最大化似然 = 最大化对数似然
- 最大化对数似然 = 最小化负对数似然
- 最小化负对数似然 = 最小化交叉熵损失
极大似然估计
- 极大似然估计(MLE,Maximum Likelihood Estimation)可以理解成:
- 在一堆参数里,挑出那组最能解释训练数据的参数。
- 也就是找到一组 $w$ 和 $b$,让训练集出现的概率最大。
- 放到逻辑回归里,就是让真实标签在模型预测下“尽可能合理”。
- 因为直接最大化连乘形式的似然不方便,所以实际训练时通常改成:
- 最大化对数似然,或者等价地最小化交叉熵损失。
一句话理解极大似然
- 极大似然估计就是:哪组参数最像是“生成”当前这批训练数据的,就选哪组参数。
一句话串起来
- 逻辑回归先输出一个概率,这个概率符合二分类的伯努利分布;训练时通过最大化似然,最后就等价成最小化交叉熵损失。
逻辑回归API应用
LogisticRegression 基础用法
sklearn.linear_model.LogisticRegression():逻辑回归分类模型,最常见的是拿来做二分类fit(X, y):用训练数据拟合模型,学出权重和偏置predict_proba(X):输出每个类别的概率predict(X):根据概率和阈值输出最终类别coef_:查看模型学到的权重intercept_:查看模型学到的偏置
代码示例
# 导包
import pandas as pd
import numpy as np
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
# 1.读取文件获取数据
data = pd.read_csv('data/breast-cancer-wisconsin.csv', sep=',')
print(data.shape, data.ndim) # 形状:(699, 11) 维度:2
# 2.数据预处理
# 2.0 注意: 数据中有"?"无效字符,需要先转换为numpy中的nan,然后使用dropna()删除或者fillna()填充
new_data = data.replace('?', np.nan).dropna()
print(new_data.shape, new_data.ndim) # 形状:(683, 11) 维度:2
# 2.1 分别获取特征和标签
# 拓展: iloc格式 : 数据.iloc[行索引,列索引]
# 所有行,第1列到最后一列,不包含最后一列,不包含第0列索引
x = new_data.iloc[:, 1:-1]
# -1 最后一列
y = new_data.iloc[:, -1]
print(x.shape, x.ndim) # 形状:(683, 9) 维度:2
print(y.shape, y.ndim) # 形状:(683,) 维度:1
# 2.2 使用train_test_split()按比例切割成4部分
# 同一个种子,同一份随机数据
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2,random_state=666)
# 3.特征处理(标准化)
ss = StandardScaler() # 标准化模型对特征数据处理
new_x_train = ss.fit_transform(X_train)
new_x_test = ss.transform(X_test)
# 4.创建模型
lr_model = LogisticRegression() # 逻辑回归模型真正预测结果
# 5.模型训练
lr_model.fit(new_x_train, y_train)
print('=======================================================')
# 6.模型预测和评估: 准确率
y_pred = lr_model.predict(new_x_test) # 1.先预测
print(y_pred)
print(y_test.tolist())
print(f"准确率:{accuracy_score(y_test, y_pred)}") # 2.再计算
print('---------------------------------------------------')
# 把上面两步合并一步,底层也是先预测再计算
print(f"准确率:{lr_model.score(new_x_test, y_test)}")
print('=======================================================')打印:
(699, 11) 2
(683, 11) 2
(683, 9) 2
(683,) 1
=======================================================
[2 2 2 2 4 2 2 2 2 2 4 4 4 4 2 2 2 4 4 2 2 2 2 2 4 2 4 2 2 4 2 4 4 2 2 2 4
2 2 2 4 2 2 2 4 2 2 4 2 2 2 2 4 2 2 4 4 2 2 2 2 4 4 4 2 4 4 4 2 4 2 2 4 4
2 2 2 2 2 2 2 4 4 4 4 2 2 2 4 2 4 2 2 4 4 2 2 2 2 2 2 4 4 2 2 2 2 4 4 4 4
2 2 2 2 2 2 4 4 2 2 4 4 2 4 2 4 4 2 2 4 4 4 2 2 4 2]
[2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 4, 2, 4, 4, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 4, 2, 4, 2, 2, 4, 2, 4, 4, 2, 2, 2, 4, 2, 2, 4, 4, 2, 2, 2, 4, 2, 2, 4, 2, 2, 2, 2, 4, 2, 2, 4, 4, 2, 2, 2, 2, 4, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 2, 4, 2, 4, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 4, 4, 2, 4, 2, 4, 4, 2, 2, 4, 4, 4, 2, 2, 4, 2]
准确率:0.9854014598540146
---------------------------------------------------
准确率:0.9854014598540146
=======================================================
扩展:numpy中随机种子的应用
train_test_split(x, y, test_size=0.2, random_state=666)里的random_state=666,本质上就是在固定这次数据切分时的随机种子。- 这样做的好处是:同一份数据、同一段代码,多次运行后训练集和测试集的划分结果都一样。
- 如果不固定种子,每次切分出来的训练集、测试集都可能不同,最终准确率也可能跟着波动,不方便复现和对比实验结果。
可以先把“随机种子”理解成一个固定的起点:
- 种子一样,随机结果就会一样。
- 种子不一样,随机结果通常也会不一样。
所以在机器学习里,固定随机种子最常见的作用就是:保证实验可复现。
这里再区分一下:
np.random.seed(666):更偏向直接控制 Numpy 自己生成随机数的结果。random_state=666:更偏向给 sklearn 这类工具传一个固定种子,让它内部涉及随机的步骤保持可复现。
一句话理解:上面的代码已经用 random_state=666 把“随机切分数据”这件事固定住了,本质上和设置随机种子的目的相同。
随机种子示例
import numpy as np
# 设置随机种子为固定值
np.random.seed(true)
# 生成随机数组
arr1 = np.random.randint(0, 10, size=5)
print("第一次生成:", arr1)
print('============================================')
# 设置随机种子为固定值
np.random.seed(2)
# 生成随机数组
arr2 = np.random.randint(0, 10, size=5)
print("第二次生成:", arr2)
分类模型评估方法
- 准确率(Accuracy):预测对了多少,最直观。
- 精确率(Precision):预测成正类的样本里,有多少是真的正类。
- 召回率(Recall):真实正类里,有多少被模型找出来了。
- F1-score:精确率和召回率的折中指标。
展开查看更多
好嘞,这个我来!用一个"癌症筛查"的例子把这几个概念一次串清楚——
先说场景
假设医院有个测试,能判断一个人有没有癌症。我们拿 10000 个人去测,结果如下:
| 真的没癌(实际阴性) | 真的有癌(实际阳性) | |
|---|---|---|
| 测试说没癌 | 9700(正确放行) | 30(漏诊) |
| 测试说有癌 | 70(误诊) | 200(正确抓出) |
横着看是测试的结果,竖着是真实情况。这张 2×2 的表,就是混淆矩阵——名字挺玄乎,其实就是把"猜对猜错"摊开给你看。
四个格子都有专门名字:
- TP(True Positive):真有,测出有 → 200
- FN(False Negative):真有,测出没有 → 30(漏诊,最危险)
- FP(False Positive):真没有,测出有 → 70(误诊,吓你一跳)
- TN(True Negative):真没有,测出没有 → 9700
然后四个指标
1. 准确率 Accuracy = 猜对的比例
$$(TP + TN) / 总数 = (200 + 9700) / 10000 = 99\%$$问题在哪? 如果癌症发病率只有 0.1%,那我让模型对所有人说"没癌",准确率 99.9%——但它一个真病人都抓不出来。所以光看准确率会被骗。
2. 精确率 Precision = 我说有癌的人里,真的有的比例
$$TP / (TP + FP) = 200 / (200 + 70) = 74\%$$大白话:模型不轻易报警,但一旦报警,74% 真的有事。关心的是"我喊狼来了,喊得准不准"。代价是宁可漏报也不乱报(你也不想没事被拉去化疗吧)。
3. 召回率 Recall = 真有病的人里,我抓出多少
$$TP / (TP + FN) = 200 / (200 + 30) = 87\%$$大白话:100 个真病人我抓到 87 个。关心的是"真的狼,一只都不能漏"。代价是宁可多误报也不能漏(漏诊的代价远大于误诊)。
4. F1 = 精确率和召回率的"调和平均"
$$2 \times P \times R / (P + R) = 2 \times 0.74 \times 0.87 / (0.74 + 0.87) \approx 0.80$$大白话:你又想准又怕漏,两个都想要又不能偏科太严重,F1 就是那个"不偏科分数"。如果一个高一个低,F1 会狠狠惩罚你。
一张表记住区别
| 指标 | 关心的问题 | 适用场景 |
|---|---|---|
| 准确率 | 总共猜对了多少 | 数据均衡、四类差不多重要 |
| 精确率 | 说我阳性的里,真的阳性占多少 | 误报代价大(垃圾邮件误判会丢重要邮件) |
| 召回率 | 真阳性里,我抓出了多少 | 漏报代价大(癌症、欺诈、安全事故) |
| F1 | 精确率和召回率的综合 | 两个都重要又数据不均衡 |
一句话记忆法 🎯
- 精确率:抓得准不准
- 召回率:抓得全不全
- F1:又准又全的均衡分
- 准确率:总体答对率(数据偏的时候别太信它)
记住一个直觉:精确率和召回率是跷跷板——阈值调高,报警更保守,精确率上去、召回率下来;阈值调低,反过来。F1 就是帮你找那个最舒服的平衡点。
讲清楚了吗?还有具体的场景想套一下,或者想看代码实现(比如 sklearn 里 classification_report 怎么用),随时说~
acc = accuracy_score(y_test, y_pred)
# 这个乳腺癌数据集里通常是 2=良性、4=恶性,这里显式指定 4 为正类
pre = precision_score(y_test, y_pred, pos_label=4)
rec = recall_score(y_test, y_pred, pos_label=4)
f1 = f1_score(y_test, y_pred, pos_label=4)
混淆矩阵
混淆矩阵就是一张分类结果的“对账统计表”,清清楚楚告诉你:你的分类模型「哪些判对了、哪些判错了,以及是怎么错的」,是评估分类算法好坏的基础工具。
混淆矩阵是带错题详情的成绩单,而且可以用来计算准确率、精确率、F1 分数,分数只能告诉你考得好不好,错题本才能告诉你哪里错了、该怎么改进。
基础版本:二分类混淆矩阵

- 图里一共
10个样本,其中6个是恶性、4个是良性。 - 这里约定:恶性 = 正类,良性 = 负类。
矩阵的行代表真实情况,列代表模型预测结果,最终分出 4 个核心格子:
| 真实\预测 | 预测“恶性”(阳性) | 预测“良性”(阴性) |
|---|---|---|
| 真恶性 | ✅ 真阳性(TP) | ❌ 假阴性(FN) |
| 真良性 | ❌ 假阳性(FP) | ✅ 真阴性(TN) |
四个格子一秒记牢
- 第一个字(真/假)看判得对不对,第二个字(阳/阴)看模型给出的结论。
- 所以:
TP= 真恶性判成恶性,FN= 真恶性判成良性,FP= 真良性判成恶性,TN= 真良性判成良性。
它的核心价值:揭穿“准确率”的骗局
很多人一上来只看准确率,但在分类任务里,只看准确率很容易把模型看错。
- 因为两个模型就算准确率一样,错法也可能完全不同。
- 混淆矩阵的价值就在这里:它不只告诉你“对了多少”,还告诉你“错在了哪里”。
- 比如在癌症筛查里,通常更怕 FN,也就是把真的恶性漏成良性。
- 所以这里不能只盯着准确率,还得继续看精确率、召回率、F1。
同样的准确率,可能是完全不同的模型
下面把指标公式和实际代入分开看。
计算公式
-
准确率(Accuracy):
(TP + TN) / (TP + TN + FP + FN) -
精确率(Precision):
TP / (TP + FP) -
召回率(Recall):
TP / (TP + FN) -
F1-score:
2 * Precision * Recall / (Precision + Recall) -
精确率:你报成正类的那堆里,有多少是真的。
-
召回率:所有真的正类里,你找出来多少。
-
F1:看精确率和召回率有没有明显偏科。
再看实际值 按图里的两个模型代入。
| 模型 | TP | FN | FP | TN |
|---|---|---|---|---|
| 模型 A | 3 | 3 | 0 | 4 |
| 模型 B | 6 | 0 | 3 | 1 |
模型 A
- 准确率:
(3 + 4) / 10 = 70% - 精确率:
3 / (3 + 0) = 100% - 召回率:
3 / (3 + 3) = 50% - F1:
2 * 1.0 * 0.5 / (1.0 + 0.5) = 0.667
模型 B
- 准确率:
(6 + 1) / 10 = 70% - 精确率:
6 / (6 + 3) = 66.7% - 召回率:
6 / (6 + 0) = 100% - F1:
2 * 0.667 * 1.0 / (0.667 + 1.0) ≈ 0.8
怎么理解这两个结果
- 两个模型准确率一样,都是
70%,所以只看准确率分不出谁更适合业务。 - 模型 A 更“保守”:精确率高,但漏掉了
3个恶性,召回率低。 - 模型 B 更“激进”:召回率满分,但多错报了
3个良性,精确率下降。 - 从
F1看,模型 B 的综合表现更好。
这就是为什么只看准确率很危险:
- 如果这是机场安检,通常会更偏向模型 B,因为宁可多复检几个人,也别把真正危险的东西漏过去。
- 如果这是垃圾邮件过滤,你可能反而不想要一个误报特别多的模型。
混淆矩阵代码应用
# 导包
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
# 1.读取文件获取数据
data = pd.read_csv('data/breast-cancer-wisconsin.csv', sep=',')
print(data.shape, data.ndim) # 形状:(699, 11) 维度:2
# 2.数据预处理
# 2.0 注意: 数据中有"?"无效字符,需要先转换为numpy中的nan,然后使用dropna()删除或者fillna()填充
new_data = data.replace('?', np.nan).dropna()
print(new_data.shape, new_data.ndim) # 形状:(683, 11) 维度:2
# 2.1 分别获取特征和标签
# 拓展: iloc格式 : 数据.iloc[行索引,列索引]
x = new_data.iloc[:, 1:-1]
y = new_data.iloc[:, -1]
print(x.shape, x.ndim) # 形状:(683, 9) 维度:2
print(y.shape, y.ndim) # 形状:(683,) 维度:1
print(pd.DataFrame(y).value_counts()) # 2:444 3:239
# 2.2 使用train_test_split()按比例切割成4部分
# 同一个种子,同一份随机数据
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666)
# 3.特征处理(标准化)
ss = StandardScaler() # 标准化模型对特征数据处理
new_x_train = ss.fit_transform(X_train)
new_x_test = ss.transform(X_test)
# 4.创建模型
lr_model = LogisticRegression() # 逻辑回归模型真正预测结果
# 5.模型训练
lr_model.fit(new_x_train, y_train)
print('=======================================================')
# TODO 导包: 依次导入混淆矩阵,准确率,精确率,召回率,f1分数,分类报告/报表
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, \
classification_report
# TODO 模型预测和评估: 准确率,精确率,召回率,f1分数
y_pred = lr_model.predict(new_x_test)
print(f"准确率:{accuracy_score(y_test, y_pred)}")
print(f"精确率:{precision_score(y_test, y_pred, pos_label=4)}")
print(f"召回率:{recall_score(y_test, y_pred, pos_label=4)}")
print(f"F1分数:{f1_score(y_test, y_pred, pos_label=4)}")
# 混淆矩阵
print(f"混淆矩阵:\n{confusion_matrix(y_test, y_pred)}")
# 分类报告
print(f"分类报告:\n{classification_report(y_test, y_pred)}")