04. 逻辑回归
一、逻辑回归
逻辑回归(Logistic Regression)是一种用于解决分类问题的统计方法, 尤其适用于二分类问题。逻辑回归通过将线性回归的输出映射到 [0, 1] 区间,来表示某个类别的概率。
最常用的映射函数是 migmoid() 函数:\(f(x) = \frac{1}{1 + e^{-x}}\),其导数如下:
逻辑回归结果可表示为:
其中,\(\beta_{0} + \beta_{1}x_{1} + \beta_{2}x_{2} + ... + \beta_{n}x_{n}\) 为线性回归输出结果,\(P(y = 1 | x)\) 表示输出为 1 类的概率。根据逻辑回归结果和阈值来确定最终预测结果,如逻辑回归结果大于阈值则输出为 1 类,反之输出为 0 类。
二、损失函数
逻辑回归的损失函数通常使用对数损失,也称为 二元交叉熵损失,用于衡量模型输出的概率分布于真实值之间的差距。逻辑回归的损失函数来源于最大似然估计。
\(P(Y|X;\beta)\) 表示给定输入特征 \(\vec{x}\) 和模型参数 \(\vec{\beta}\) 时,因变量 y 发生的概率。
我们整理两个公式可得:
似然函数 \(L(\vec{\beta})\) 表示已知 y 的结果,必须模型参数为 \(\vec{\beta}\) 的概率。
对于 1 个样本:
对于 n 个样本:
取对数似然函数可得:
拟合的过程就是求解似然函数的最大值,为了方便优化,令损失函数:
其中:
则梯度:
三、二分类任务
我们可以在终端中使用 pip 安装 sklearn 机器学习库。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载。
pip install scikit-learn -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
在 scikit-learn 库中提供 LogisticRegression 类实现逻辑回归,它支持二分类和多分类任务,并提供了多种优化算法和正则化选项。
LogisticRegression(
penalty="deprecated",
C=1.0, # 正则化系数,C越小,正则化越强
l1_ratio=0.0, # l1正则化比例,0表示只使用l2正则化
dual=False, # 是否使用对偶形式,默认值为False
tol=1e-4, # 停止迭代的阈值,当损失函数变化小于tol时,停止迭代
fit_intercept=True, # 是否拟合截距项
intercept_scaling=1, # 截距项的缩放系数,默认值为1
class_weight=None, # 类别权重,balanced表示自动平衡类别权重
random_state=None, # 随机种子,用于复现结果
solver="lbfgs", # 优化算法,lbfgs表示拟牛顿法
max_iter=100, # 最大迭代次数,默认值为100
verbose=0, # 是否打印迭代过程中的信息,默认值为0
warm_start=False, # 是否使用前一次迭代的结果作为初始化,默认值为False
n_jobs=None, # 并行计算的进程数,默认值为None,即不使用并行计算
)
我们可以通过参数 solver 指定使用的优化算法,它的取值如下:
lbfgs:拟牛顿法(默认),仅支持 L2 正则化。newton-cg:牛顿法,仅支持 L2 正则化。liblinera:坐标下降法,适用于小数据集,支持 L1 和 L2 正则化。sag:随机平均梯度下降,适用于大规模数据集,仅支持 L2 正则化。
Heart Disease 数据集:https://www.kaggle.com/datasets/johnsmith88/heart-disease-dataset。
| 字段名 | 值 |
|---|---|
| 年龄 | 连续值 |
| 性别 | 0:女;1:男 |
| 胸疼类型 | 0:典型心绞痛;1:非典型心绞痛;2:非心绞痛;3:无症状 |
| 静息血压 | 连续值,单位 mmHg |
| 胆固醇 | 连续值,单位 mg/dl |
| 空腹血糖 | 0:小于等于 120mg/dl;1:大于120mg/dl |
| 静息心电图结果 | 0:正常;1:ST-T 异常;2:可能左心室肥大 |
| 最大心率 | 连续值 |
| 运动性心绞疼 | 0:无;1:有 |
| 运动后的 ST 下降 | 连续值 |
| 峰值 ST 段的斜率 | 0:向上;1:水平;2:向下 |
| 主血管数量 | 0 到 3 |
| 地中海贫血 | 0:正常;1:固定缺陷;2:可逆缺陷 |
| 是否患有心脏病(标签) | 0:否;1:是 |
我们在终端中通过 pip 安装 pandas 模块。
pip install scikit-learn -i https://mirrors.aliyun.com/pypi/simple
import pandas as pd
import sklearn.model_selection
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
# 加载数据集
dataset = pd.read_csv("assets/data/heart_disease.csv") # 加载数据集
dataset.dropna(inplace=True) # 删除缺失值
# 划分数据集
X = dataset.drop(columns=["是否患有心脏病"], axis=1) # 特征矩阵
y = dataset["是否患有心脏病"] # 目标向量
x_train, x_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=42)
# 特征工程
numeric_features = ["年龄", "静息血压", "胆固醇", "最大心率", "运动后的ST下降", "主血管数量"] # 数值型特征
categorical_features = ["胸痛类型", "静息心电图结果", "峰值ST段的斜率", "地中海贫血"] # 类别型特征
binary_features = ["性别", "空腹血糖", "运动性心绞痛"] # 二值型特征
columnTransformer = ColumnTransformer(
transformers=[
("num", StandardScaler(), numeric_features),
("cat", OneHotEncoder(), categorical_features),
("bin", "passthrough", binary_features),
]
)
x_train = columnTransformer.fit_transform(x_train) # 对训练集进行特征工程
x_test = columnTransformer.transform(x_test) # 对测试集进行特征工程
model = LogisticRegression() # 逻辑回归模型
model.fit(x_train, y_train) # 训练模型
# 模型评估
print(f"模型准确率: {model.score(x_test, y_test):.4f}")
四、多分类任务
4.1、一对多(OVR)
若有 C 个类别,则训练 C 个二分类逻辑回归分类器。每个分类器将一个类别作为正例,所有其它类别作为反例。预测时,计算 C 个分类器的输出概率,选取概率最高的类别。对于类别 c:
from sklearn.linear_model import LogisticRegression
from sklearn.multiclass import OneVsRestClassifier
model = OneVsRestClassifier(LogisticRegression()) # 逻辑回归模型(多分类)
每个类别训练 1 个分类器,当类别数量较多时,训练时间较长。
4.2、多项逻辑回归(Softmax)
我们可以使用 Softmax() 函数将模型输出转换为概率分布。这时,我们只需要训练 1 个逻辑回归模型,预测时使用 1 个模型计算所有类别的概率,选择最大值。若有 C 个类别,模型将输出 C 个分类。对于类别 c:
对于多分类任务默认就是使用 Softmax。
Digit Recognizer 数据集:https://www.kaggle.com/competitions/digit-recognizer。
文件 train.csv 中包含手绘数字(从 0 到 9)的灰度图像,每张图像为 28 * 28 像素,共 784 像素,每个像素有一个从 0 到 255 的值表示该像素的亮度。文件第 1 列为标签,之后 784 列分别为像素的亮度值。如果我们要获取第 x 行第 y 列的像素值,可以通过公式 \(x * 128 + y\) 获得。
import pandas as pd
import matplotlib.pyplot as plt
import sklearn.model_selection
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LogisticRegression
# 加载数据集
dataset = pd.read_csv("assets/data/train.csv") # 加载数据集
dataset.dropna(inplace=True) # 删除缺失值
# 划分数据集
X = dataset.drop(columns=["label"], axis=1) # 特征矩阵
y = dataset["label"] # 目标向量
x_train, x_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=42)
# 特征工程
scaler = MinMaxScaler() # 归一化缩放器
x_train = scaler.fit_transform(x_train) # 对训练集进行归一化缩放
x_test = scaler.transform(x_test) # 对测试集进行归一化缩放
# 模型训练
model = LogisticRegression(max_iter=1000) # 逻辑回归模型
model.fit(x_train, y_train) # 训练模型
# 模型评估
accuracy = model.score(x_test, y_test) # 计算模型在测试集上的准确率
print("模型准确率:", accuracy) # 打印模型准确率
# 预测某个样本
sample = x_test[124].reshape(1, -1) # 取测试集中的第一个样本作为预测样本
prediction = model.predict(sample) # 对样本进行预测
print("预测结果:", prediction) # 打印预测结果
print("真实标签:", y_test.iloc[124]) # 打印真实标签
print("概率:", model.predict_proba(sample)) # 打印样本的预测概率
plt.imshow(sample.reshape(28, 28), cmap="gray") # 显示样本图像
plt.title("Predicted Label: {}".format(prediction[0])) # 显示预测标签
plt.show() # 显示图像

浙公网安备 33010602011771号