04. 逻辑回归

一、逻辑回归

  逻辑回归(Logistic Regression)是一种用于解决分类问题的统计方法, 尤其适用于二分类问题。逻辑回归通过将线性回归的输出映射到 [0, 1] 区间,来表示某个类别的概率。

  最常用的映射函数是 migmoid() 函数:\(f(x) = \frac{1}{1 + e^{-x}}\),其导数如下:

\[\begin{align} f'(x) & = -\frac{1}{(1 + e^{-x})^{2}}e^{-x} * (-1) \\ & = \frac{e^{-x}}{(1+e^{-x})^{2}} \\ & = \frac{1 + e^{-x} - 1}{(1+e^{-x})^{2}} \\ & = \frac{1}{1 + e^{-x}}(1 - \frac{1}{1 + e^{-x}}) \\ & = f(x)(1 - f(x)) \end{align} \]

  逻辑回归结果可表示为:

\[P(y=1 | x) = \frac{1}{1 + e^{-(\beta_{0} + \beta_{1}x_{1} + \beta_{2}x_{2} + ... + \beta_{n}x_{n})}} \]

  其中,\(\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 发生的概率。

\[P(y=1|\vec{x};\vec{\beta}) = \frac{1}{1 + e^{-(\beta^{T}x)}} \\ P(y=0|\vec{x};\vec{\beta}) = 1 - P(y=1|\vec{x};\vec{\beta}) = 1 -\frac{1}{1 + e^{-(\beta^{T}x)}} \\ \]

  我们整理两个公式可得:

\[\begin{align} P(y|\vec{x};\vec{\beta}) & = P(y=1|\vec{x};\vec{\beta})^{y}( 1 - P(y=1|\vec{x};\vec{\beta}))^{1-y} \\ & = (\frac{1}{1 + e^{-(\beta^{T}x)}})^{y}(1 - \frac{1}{1 + e^{-(\beta^{T}x)}})^{1-y} \end{align} \]

  似然函数 \(L(\vec{\beta})\) 表示已知 y 的结果,必须模型参数为 \(\vec{\beta}\) 的概率。

  对于 1 个样本:

\[L(\vec{\beta}) = P(y | \vec{x}; \vec{\beta}) = P(jy=1|\vec{x};\vec{\beta})^{y}(1 - P(y=1 | \vec{x};\vec{\beta}))^{1-y} \]

  对于 n 个样本:

\[L(\vec{\beta}) = \prod\limits_{i = 1}^{n}{P(y_{i} | x_{i};\vec{\beta})} = \prod\limits_{i = 1}^{n}{P(y_{i} = 1 | x_{i};\vec{\beta})}^{y}(1 - {P(y_{i} = 1 | x_{i};\vec{\beta})}^{1-y_{i}} \]

  取对数似然函数可得:

\[logL(\vec{\beta}) = \prod\limits_{i = 1}^{n}{y_{1}logP(y_{i} = 1 | x_{i};\vec{\beta} + (1 - y_{i})log(1 - P(y_{i} | \vec{x}; \vec{\beta}))} \]

  拟合的过程就是求解似然函数的最大值,为了方便优化,令损失函数:

\[\begin{align} Loss & = -\frac{1}{n}logL(\vec{\beta}) \\ & = -\frac{1}{n}\sum_{y_{i}}^{n}{logP(y_{i} = 1 | \vec{x_{i}}; \vec{\beta}) + (1 - y_{i})log(1 - P(y_{i} = 1 | \vec{x_{i}}; \vec{\beta}))} \\ & = -\frac{1}{n}(\vec{y}^{T}log\vec{p} + (1 - \vec{y})^{T}log(1 - \vec{p}) \end{align} \]

  其中:

\[\begin{align} \vec{y} & = \begin{bmatrix} y_{1} & y_{2} & ... & y_{n} \end{bmatrix}^{T} \\ \vec{p} & = \begin{bmatrix} P(y_{1} | x_{1}; \vec{\beta}) & P(y_{2} | x_{2}; \vec{\beta}) & ... & P(y_{n} | x_{n}; \vec{\beta})\end{bmatrix}^{T} \\ & = \begin{bmatrix} \frac{1}{1 + e^{-(\vec{\beta}^{T}x_{1})}} & \frac{1}{1 + e^{-(\vec{\beta}^{T}x_{2})}} & ... & \frac{1}{1 + e^{-(\vec{\beta}^{T}x_{n})}} \end{bmatrix}^{T} \\ & = \frac{1}{1 + e^{-X\beta}} \end{align} \]

  则梯度:

\[\begin{align} \nabla Loss & = \frac{\partial Loss}{\partial\vec{p}}.\frac{\partial \vec{p}}{\partial \vec{\beta}} \\ & = \frac{\partial(-\frac{1}{n}(\vec{y}^{T}logP + (1 - \vec{y})^{T}log(1 - \vec{p}))}{}.\frac{\partial{\frac{1}{1 + e^(-X\beta)}}}{\partial\vec{\beta}} \\ & = -\frac{1}{n}(\frac{\vec{y}}{\vec{p}} - \frac{1 - \vec{y}}{1 - \vec{p}}).(P\odot(1 - \vec{p}).X^{T}) \\ & = \frac{1}{n}X^{T}(\vec{p} - \vec{y}) \\ & = \frac{1}{n}{X^{T}}(\frac{1}{1 + e^{-(X\beta)}} - \vec{y}) \end{align} \]

三、二分类任务

  我们可以在终端中使用 pip 安装 sklearn 机器学习库。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install scikit-learn -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

  在 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:

\[P(y=c|\vec{x};\vec{\beta}) = \frac{1}{1 + e^{-(\beta^{T}\vec{x})}} \\ Loss = -\frac{1}{n}\sum_{i=1}^{n}{(y_{i}logP(y_{i}=c|\vec{x};\vec{\beta}) + (1 - y_{i})logP( 1 - P(y_{i}=c|\vec{x};\vec{\beta})))} \]

from sklearn.linear_model import LogisticRegression
from sklearn.multiclass import OneVsRestClassifier

model = OneVsRestClassifier(LogisticRegression())                               # 逻辑回归模型(多分类)

每个类别训练 1 个分类器,当类别数量较多时,训练时间较长。

4.2、多项逻辑回归(Softmax)

  我们可以使用 Softmax() 函数将模型输出转换为概率分布。这时,我们只需要训练 1 个逻辑回归模型,预测时使用 1 个模型计算所有类别的概率,选择最大值。若有 C 个类别,模型将输出 C 个分类。对于类别 c:

\[P(y=c|\vec{x}) = \frac{e^{\beta_{c}^{T}x}}{\sum_{j=1}^{c}{e^{\beta_{j}^{T}x}}} \\ Loss = -\frac{1}{n}\sum_{i=1}^{n}{\sum_{c=1}^{c}I(y_{i}=c)logP(y_{i}=c|\vec{x_{i}})} \]

对于多分类任务默认就是使用 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()                                                                      # 显示图像
posted @ 2026-01-10 22:17  星光映梦  阅读(5)  评论(0)    收藏  举报