ITPub博客

首页 > 应用开发 > IT综合 > Python实现多分类Logistic回归的原理与实战解析

Python实现多分类Logistic回归的原理与实战解析

投稿 IT综合 作者:bhluo 时间:2025-12-18 16:40:33 0 删除 编辑

# Python实现多分类Logistic回归的原理与实战解析


## 引言:从二分类到多分类的拓展


Logistic回归作为经典的分类算法,在二分类问题中表现出色。然而现实世界中的分类问题往往涉及多个类别,如新闻分类、疾病诊断、产品推荐等场景都需要将样本划分到多个类别中。多分类Logistic回归通过扩展二元模型,实现了对多类别问题的有效处理,成为机器学习领域的重要工具。


## 多分类Logistic回归理论基础


### Softmax函数与概率分布


多分类Logistic回归的核心是Softmax函数,它将线性模型的输出转换为概率分布。假设有K个类别,对于第i个样本,其属于第k个类别的概率为:


$$P(y_i=k|\mathbf{x}_i) = \frac{e^{\mathbf{w}_k^T\mathbf{x}_i}}{\sum_{j=1}^{K}e^{\mathbf{w}_j^T\mathbf{x}_i}}$$


其中$\mathbf{w}_k$是第k个类别的权重向量。


```python

import numpy as np

from typing import List, Tuple


def softmax(z: np.ndarray) -> np.ndarray:

    """

    Softmax函数实现

    

    参数:

    z: 输入向量或矩阵,形状为(n_samples, n_classes)或(n_classes,)

    

    返回:

    概率分布,形状与输入相同

    """

    # 数值稳定性优化:减去最大值避免指数溢出

    exp_z = np.exp(z - np.max(z, axis=-1, keepdims=True))

    return exp_z / np.sum(exp_z, axis=-1, keepdims=True)


# 测试Softmax函数

def test_softmax():

    # 单个样本

    z_single = np.array([2.0, 1.0, 0.1])

    probs_single = softmax(z_single)

    print(f"单个样本输入: {z_single}")

    print(f"Softmax输出: {probs_single}")

    print(f"概率和为: {np.sum(probs_single):.6f}")

    

    # 多个样本

    z_batch = np.array([[1, 2, 3],

                        [3, 2, 1],

                        [0, 0, 0]])

    probs_batch = softmax(z_batch)

    print(f"\n批量输入形状: {z_batch.shape}")

    print(f"批量Softmax输出形状: {probs_batch.shape}")

    print(f"每行概率和: {np.sum(probs_batch, axis=1)}")


if __name__ == "__main__":

    test_softmax()

```


### 交叉熵损失函数


多分类问题使用交叉熵损失函数:


$$J(\mathbf{W}) = -\frac{1}{N}\sum_{i=1}^{N}\sum_{k=1}^{K}y_{ik}\log(p_{ik})$$


其中$y_{ik}$是真实标签的one-hot编码,$p_{ik}$是预测概率。


```python

class CrossEntropyLoss:

    """交叉熵损失函数实现"""

    

    @staticmethod

    def compute(y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-12) -> float:

        """

        计算交叉熵损失

        

        参数:

        y_true: 真实标签,形状(n_samples, n_classes),one-hot编码

        y_pred: 预测概率,形状(n_samples, n_classes)

        epsilon: 小数值避免log(0)

        

        返回:

        平均交叉熵损失

        """

        # 限制预测值范围,避免数值问题

        y_pred = np.clip(y_pred, epsilon, 1. - epsilon)

        

        # 计算交叉熵

        loss = -np.mean(np.sum(y_true * np.log(y_pred), axis=1))

        return loss

    

    @staticmethod

    def gradient(y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:

        """

        计算损失函数的梯度

        

        返回:

        梯度,形状(n_samples, n_classes)

        """

        return y_pred - y_true


def test_cross_entropy():

    # 模拟数据

    n_samples = 100

    n_classes = 3

    

    # 真实标签(one-hot编码)

    y_true = np.zeros((n_samples, n_classes))

    true_labels = np.random.randint(0, n_classes, n_samples)

    y_true[np.arange(n_samples), true_labels] = 1

    

    # 预测概率

    y_pred = np.random.rand(n_samples, n_classes)

    y_pred = y_pred / y_pred.sum(axis=1, keepdims=True)

    

    # 计算损失

    loss = CrossEntropyLoss.compute(y_true, y_pred)

    print(f"交叉熵损失: {loss:.6f}")

    

    # 计算梯度

    grad = CrossEntropyLoss.gradient(y_true, y_pred)

    print(f"梯度形状: {grad.shape}")

    print(f"梯度均值: {np.mean(grad):.6f}")


if __name__ == "__main__":

    test_cross_entropy()

```


## 从零实现多分类Logistic回归


### 模型实现


```python

import numpy as np

from sklearn.base import BaseEstimator, ClassifierMixin

from sklearn.preprocessing import LabelBinarizer

from sklearn.utils.validation import check_X_y, check_array, check_is_fitted


class MultinomialLogisticRegression(BaseEstimator, ClassifierMixin):

    """多分类Logistic回归模型"""

    

    def __init__(self, learning_rate: float = 0.01, 

                 max_iter: int = 1000,

                 tol: float = 1e-4,

                 l2_lambda: float = 0.0,

                 verbose: bool = False):

        """

        初始化多分类Logistic回归模型

        

        参数:

        learning_rate: 学习率

        max_iter: 最大迭代次数

        tol: 收敛阈值

        l2_lambda: L2正则化系数

        verbose: 是否打印训练信息

        """

        self.learning_rate = learning_rate

        self.max_iter = max_iter

        self.tol = tol

        self.l2_lambda = l2_lambda

        self.verbose = verbose

        self.loss_history_ = []

        

    def _initialize_weights(self, n_features: int, n_classes: int):

        """初始化权重矩阵"""

        # 使用Xavier初始化

        scale = np.sqrt(2.0 / (n_features + n_classes))

        self.W_ = np.random.randn(n_features, n_classes) * scale

        self.b_ = np.zeros(n_classes)

        

    def _softmax(self, X: np.ndarray) -> np.ndarray:

        """计算Softmax概率"""

        # 线性部分

        scores = X @ self.W_ + self.b_

        

        # 数值稳定的Softmax

        exp_scores = np.exp(scores - np.max(scores, axis=1, keepdims=True))

        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)

        

        return probs

    

    def fit(self, X: np.ndarray, y: np.ndarray) -> 'MultinomialLogisticRegression':

        """

        训练多分类Logistic回归模型

        

        参数:

        X: 特征矩阵,形状(n_samples, n_features)

        y: 标签向量,形状(n_samples,)

        

        返回:

        训练好的模型实例

        """

        # 验证输入

        X, y = check_X_y(X, y)

        

        # 标签编码

        self.label_binarizer_ = LabelBinarizer()

        y_onehot = self.label_binarizer_.fit_transform(y)

        self.classes_ = self.label_binarizer_.classes_

        n_classes = len(self.classes_)

        

        # 添加偏置项的特征

        n_samples, n_features = X.shape

        

        # 初始化权重

        self._initialize_weights(n_features, n_classes)

        

        # 梯度下降优化

        for i in range(self.max_iter):

            # 前向传播

            probs = self._softmax(X)

            

            # 计算损失

            loss = self._compute_loss(probs, y_onehot, X)

            self.loss_history_.append(loss)

            

            # 计算梯度

            grad_W, grad_b = self._compute_gradients(X, y_onehot, probs)

            

            # 更新权重

            self.W_ -= self.learning_rate * grad_W

            self.b_ -= self.learning_rate * grad_b

            

            # 检查收敛

            if i > 0 and abs(self.loss_history_[-2] - loss) < self.tol:

                if self.verbose:

                    print(f"迭代 {i}: 提前收敛,损失 = {loss:.6f}")

                break

            

            # 打印进度

            if self.verbose and i % 100 == 0:

                print(f"迭代 {i}: 损失 = {loss:.6f}")

        

        # 标记模型已训练

        self.is_fitted_ = True

        return self

    

    def _compute_loss(self, probs: np.ndarray, 

                      y_onehot: np.ndarray,

                      X: np.ndarray) -> float:

        """计算总损失"""

        # 交叉熵损失

        epsilon = 1e-12

        probs_clipped = np.clip(probs, epsilon, 1. - epsilon)

        cross_entropy = -np.mean(np.sum(y_onehot * np.log(probs_clipped), axis=1))

        

        # L2正则化

        if self.l2_lambda > 0:

            l2_penalty = 0.5 * self.l2_lambda * np.sum(self.W_ ** 2)

            cross_entropy += l2_penalty

        

        return cross_entropy

    

    def _compute_gradients(self, X: np.ndarray,

                          y_onehot: np.ndarray,

                          probs: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:

        """计算权重梯度"""

        n_samples = X.shape[0]

        

        # 误差

        error = probs - y_onehot

        

        # 权重梯度

        grad_W = (X.T @ error) / n_samples

        

        # 偏置梯度

        grad_b = np.mean(error, axis=0)

        

        # L2正则化梯度

        if self.l2_lambda > 0:

            grad_W += self.l2_lambda * self.W_

        

        return grad_W, grad_b

    

    def predict_proba(self, X: np.ndarray) -> np.ndarray:

        """

        预测类别概率

        

        参数:

        X: 特征矩阵

        

        返回:

        概率矩阵,形状(n_samples, n_classes)

        """

        check_is_fitted(self, 'is_fitted_')

        X = check_array(X)

        

        return self._softmax(X)

    

    def predict(self, X: np.ndarray) -> np.ndarray:

        """

        预测类别标签

        

        参数:

        X: 特征矩阵

        

        返回:

        预测标签,形状(n_samples,)

        """

        probs = self.predict_proba(X)

        predictions = np.argmax(probs, axis=1)

        return self.classes_[predictions]

    

    def score(self, X: np.ndarray, y: np.ndarray) -> float:

        """

        计算模型准确率

        

        参数:

        X: 特征矩阵

        y: 真实标签

        

        返回:

        准确率

        """

        predictions = self.predict(X)

        return np.mean(predictions == y)


# 测试自定义模型

def test_custom_model():

    from sklearn.datasets import make_classification

    from sklearn.model_selection import train_test_split

    

    # 生成多分类数据

    X, y = make_classification(n_samples=1000, n_features=20, 

                               n_classes=3, n_informative=10,

                               random_state=42)

    

    # 划分训练测试集

    X_train, X_test, y_train, y_test = train_test_split(

        X, y, test_size=0.2, random_state=42

    )

    

    # 创建和训练模型

    model = MultinomialLogisticRegression(

        learning_rate=0.1,

        max_iter=500,

        l2_lambda=0.01,

        verbose=True

    )

    

    model.fit(X_train, y_train)

    

    # 评估模型

    train_acc = model.score(X_train, y_train)

    test_acc = model.score(X_test, y_test)

    

    print(f"训练准确率: {train_acc:.4f}")

    print(f"测试准确率: {test_acc:.4f}")

    

    return model


if __name__ == "__main__":

    model = test_custom_model()

```


## 使用Scikit-learn实现


### 基础应用


```python

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from sklearn.linear_model import LogisticRegression

from sklearn.datasets import load_iris

from sklearn.model_selection import train_test_split, cross_val_score

from sklearn.preprocessing import StandardScaler

from sklearn.metrics import classification_report, confusion_matrix

import seaborn as sns


def basic_multiclass_logistic():

    """使用Scikit-learn实现多分类Logistic回归"""

    

    # 加载经典数据集

    iris = load_iris()

    X = iris.data

    y = iris.target

    feature_names = iris.feature_names

    target_names = iris.target_names

    

    print("数据集信息:")

    print(f"样本数: {X.shape[0]}, 特征数: {X.shape[1]}")

    print(f"类别数: {len(np.unique(y))}")

    print(f"类别名称: {target_names}")

    

    # 数据标准化

    scaler = StandardScaler()

    X_scaled = scaler.fit_transform(X)

    

    # 划分训练测试集

    X_train, X_test, y_train, y_test = train_test_split(

        X_scaled, y, test_size=0.3, random_state=42, stratify=y

    )www.pt.wandaihu.cn>www.vs.wandaihu.cn>

    

    # 创建多分类Logistic回归模型

    # multi_class='multinomial'表示使用Softmax回归

    # solver='lbfgs'支持多分类的优化算法

    model = LogisticRegression(

        multi_class='multinomial',

        solver='lbfgs',

        max_iter=1000,

        C=1.0,  # 正则化强度的倒数,越小正则化越强

        random_state=42

    )

    

    # 训练模型

    model.fit(X_train, y_train)

    

    # 模型评估

    train_score = model.score(X_train, y_train)

    test_score = model.score(X_test, y_test)

    

    print(f"\n模型性能:")

    print(f"训练准确率: {train_score:.4f}")

    print(f"测试准确率: {test_score:.4f}")

    

    # 预测概率

    y_pred_proba = model.predict_proba(X_test)

    y_pred = model.predict(X_test)

    

    # 详细分类报告

    print("\n分类报告:")

    print(classification_report(y_test, y_pred, target_names=target_names))

    

    # 混淆矩阵

    cm = confusion_matrix(y_test, y_pred)

    

    plt.figure(figsize=(8, 6))

    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',

                xticklabels=target_names,

                yticklabels=target_names)

    plt.title('混淆矩阵')

    plt.xlabel('预测标签')

    plt.ylabel('真实标签')

    plt.tight_layout()

    plt.show()

    

    # 特征重要性分析

    feature_importance = pd.DataFrame({

        'feature': feature_names,

        'coefficient': np.mean(np.abs(model.coef_), axis=0)

    }).sort_values('coefficient', ascending=False)

    

    print("\n特征重要性:")

    print(feature_importance)

    

    return model, X_test, y_test, target_names


if __name__ == "__main__":

    model, X_test, y_test, target_names = basic_multiclass_logistic()

```


### 模型优化与调参


```python

from sklearn.model_selection import GridSearchCV, StratifiedKFold

from sklearn.pipeline import Pipeline


def optimize_logistic_regression():

    """Logistic回归模型优化"""

    

    # 加载数据

    from sklearn.datasets import load_wine

    wine = load_wine()

    X, y = wine.data, wine.target

    

    # 创建管道

    pipeline = Pipeline([

        ('scaler', StandardScaler()),

        ('classifier', LogisticRegression(

            multi_class='multinomial',

            solver='lbfgs',

            random_state=42

        ))www.rb.wandaihu.cn>www.mo.wandaihu.cn>

    ])

    

    # 定义参数网格

    param_grid = {

        'classifier__C': [0.001, 0.01, 0.1, 1, 10, 100],

        'classifier__max_iter': [500, 1000, 2000],

        'classifier__penalty': ['l2'],  # 'multinomial'只支持l2

    }

    

    # 交叉验证设置

    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

    

    # 网格搜索

    grid_search = GridSearchCV(

        pipeline,

        param_grid,

        cv=cv,

        scoring='accuracy',

        n_jobs=-1,

        verbose=1

    )

    

    # 执行搜索

    grid_search.fit(X, y)

    

    # 输出最佳参数

    print("最佳参数:")

    for param, value in grid_search.best_params_.items():

        print(f"{param}: {value}")

    

    print(f"\n最佳交叉验证准确率: {grid_search.best_score_:.4f}")

    

    # 各参数组合的结果

    results_df = pd.DataFrame(grid_search.cv_results_)

    print("\n参数搜索详情:")

    print(results_df[['mean_test_score', 'std_test_score', 

                      'param_classifier__C', 'param_classifier__max_iter']].head())

    

    return grid_search


def visualize_decision_boundary():

    """可视化决策边界(适用于二维特征)"""

    

    from sklearn.datasets import make_classification

    

    # 生成二维数据以便可视化

    X, y = make_classification(

        n_samples=300,

        n_features=2,

        n_classes=3,

        n_informative=2,

        n_redundant=0,

        n_clusters_per_class=1,

        random_state=42

    )

    

    # 训练模型

    model = LogisticRegression(

        multi_class='multinomial',

        solver='lbfgs',

        C=1.0,

        random_state=42

    )

    model.fit(X, y)

    

    # 创建网格点

    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1

    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1

    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),

                         np.arange(y_min, y_max, 0.1))

    

    # 预测网格点

    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])

    Z = Z.reshape(xx.shape)

    

    # 绘制决策边界

    plt.figure(figsize=(10, 8))

    plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Set3)

    

    # 绘制数据点

    scatter = plt.scatter(X[:, 0], X[:, 1], c=y, 

                          edgecolors='k', cmap=plt.cm.Set3)

    

    plt.xlabel('特征 1')

    plt.ylabel('特征 2')

    plt.title('多分类Logistic回归决策边界')

    plt.legend(*scatter.legend_elements(), title='类别')

    plt.tight_layout()

    plt.show()


if __name__ == "__main__":

    # 执行优化

    grid_search = optimize_logistic_regression()

    

    # 可视化决策边界

    visualize_decision_boundary()

```


## 实战案例:手写数字识别


```python

import numpy as np

import matplotlib.pyplot as plt

from sklearn.datasets import load_digits

from sklearn.linear_model import LogisticRegression

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import StandardScaler

from sklearn.metrics import classification_report, confusion_matrix

import seaborn as sns


def handwritten_digits_recognition():

    """手写数字识别实战案例"""

    

    # 加载手写数字数据集

    digits = load_digits()

    X = digits.data

    y = digits.target

    

    print("手写数字数据集信息:")

    print(f"样本数: {X.shape[0]}")

    print(f"特征数: {X.shape[1]} (8x8像素图像展开)")

    print(f"类别数: {len(np.unique(y))} (数字0-9)")

    

    # 可视化部分样本

    fig, axes = plt.subplots(2, 5, figsize=(10, 5))

    for i, ax in enumerate(axes.flat):

        ax.imshow(digits.images[i], cmap='binary')

        ax.set_title(f"标签: {digits.target[i]}")

        ax.axis('off')

    plt.suptitle('手写数字样本示例')

    plt.tight_layout()

    plt.show()

    

    # 数据预处理

    scaler = StandardScaler()

    X_scaled = scaler.fit_transform(X)

    

    # 划分数据集

    X_train, X_test, y_train, y_test = train_test_split(

        X_scaled, y, test_size=0.2, random_state=42, stratify=y

    )

    

    # 训练多分类Logistic回归

    model = LogisticRegression(

        multi_class='multinomial',

        solver='lbfgs',

        max_iter=5000,

        C=0.1,

        random_state=42,

        n_jobs=-1

    )

    

    print("\n训练模型中...")

    model.fit(X_train, y_train)

    

    # 评估模型

    train_acc = model.score(X_train, y_train)

    test_acc = model.score(X_test, y_test)

    

    print(f"训练准确率: {train_acc:.4f}")

    print(f"测试准确率: {test_acc:.4f}")

    

    # 预测

    y_pred = model.predict(X_test)

    

    # 分类报告

    print("\n分类报告:")

    print(classification_report(y_test, y_pred))

    

    # 混淆矩阵

    cm = confusion_matrix(y_test, y_pred)

    

    plt.figure(figsize=(10, 8))

    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',

                square=True,

                xticklabels=range(10),

                yticklabels=range(10))

    plt.title('手写数字识别混淆矩阵')

    plt.xlabel('预测数字')

    plt.ylabel('真实数字')

    plt.tight_layout()

    plt.show()

    

    # 可视化错误分类样本

    errors = X_test[y_pred != y_test]

    error_preds = y_pred[y_pred != y_test]

    error_true = y_test[y_pred != y_test]

    

    if len(errors) > 0:

        print(f"\n错误分类样本数: {len(errors)}")

        

        # 反标准化以恢复原始像素值

        errors_original = scaler.inverse_transform(errors)

        

        n_errors = min(10, len(errors))

        fig, axes = plt.subplots(2, 5, figsize=(12, 6))

        fig.suptitle('错误分类样本示例')

        

        for i, ax in enumerate(axes.flat[:n_errors]):

            img = errors_original[i].reshape(8, 8)

            ax.imshow(img, cmap='binary')

            ax.set_title(f"预测: {error_preds[i]}\n真实: {error_true[i]}")

            ax.axis('off')

        

        plt.tight_layout()

        plt.show()

    

    # 特征重要性分析(像素重要性)

    feature_importance = np.mean(np.abs(model.coef_), axis=0).reshape(8, 8)

    

    plt.figure(figsize=(8, 6))

    plt.imshow(feature_importance, cmap='hot', interpolation='nearest')

    plt.colorbar(label='平均系数绝对值')

    plt.title('像素重要性热力图')

    plt.xlabel('X像素')

    plt.ylabel('Y像素')

    plt.tight_layout()

    plt.show()

    

    return model, X_test, y_test, y_pred

www.af.wandaihu.cn>www.bs.wandaihu.cn>www.ly.wandaihu.cn


def probability_analysis():

    """概率分析示例"""

    

    model, X_test, y_test, y_pred = handwritten_digits_recognition()

    

    # 获取预测概率

    y_proba = model.predict_proba(X_test)

    

    # 分析预测确定性

    confidence = np.max(y_proba, axis=1)

    

    plt.figure(figsize=(10, 6))

    plt.hist(confidence[y_pred == y_test], bins=30, alpha=0.7, 

             label='正确分类', density=True)

    plt.hist(confidence[y_pred != y_test], bins=30, alpha=0.7, 

             label='错误分类', density=True)

    plt.xlabel('预测置信度')

    plt.ylabel('密度')

    plt.title('正确与错误分类的置信度分布')

    plt.legend()

    plt.grid(True, alpha=0.3)

    plt.tight_layout()

    plt.show()

    

    # 展示不确定样本

    low_confidence_idx = np.where(confidence < 0.7)[0]

    if len(low_confidence_idx) > 0:

        print(f"\n低置信度样本数: {len(low_confidence_idx)}")

        

        n_samples = min(5, len(low_confidence_idx))

        fig, axes = plt.subplots(1, n_samples, figsize=(15, 3))

        

        for i, idx in enumerate(low_confidence_idx[:n_samples]):

            ax = axes[i] if n_samples > 1 else axes

            img = X_test[idx].reshape(8, 8)

            ax.imshow(img, cmap='binary')

            

            # 获取前3个最可能类别

            top3_idx = np.argsort(y_proba[idx])[-3:][::-1]

            top3_proba = y_proba[idx][top3_idx]

            

            title = f"真实: {y_test[idx]}\n预测: {y_pred[idx]}"

            for j, (cls, prob) in enumerate(zip(top3_idx, top3_proba)):

                title += f"\n第{j+1}: {cls} ({prob:.3f})"

            

            ax.set_title(title)

            ax.axis('off')

        

        plt.suptitle('低置信度预测样本')

        plt.tight_layout()

        plt.show()


if __name__ == "__main__":

    probability_analysis()

```


## 性能优化与最佳实践


### 数据预处理建议


```python

from sklearn.preprocessing import PolynomialFeatures

from sklearn.decomposition import PCA


def data_preprocessing_tips():

    """数据预处理技巧"""

    

    from sklearn.datasets import load_breast_cancer

    

    # 加载数据

    data = load_breast_cancer()

    X, y = data.data, data.target

    

    # 1. 特征标准化

    scaler = StandardScaler()

    X_scaled = scaler.fit_transform(X)

    

    # 2. 特征选择(基于模型)

    from sklearn.feature_selection import SelectFromModel

    

    lr = LogisticRegression(penalty='l1', solver='liblinear', C=0.1)

    selector = SelectFromModel(lr, threshold='median')

    X_selected = selector.fit_transform(X_scaled, y)

    

    print(f"原始特征数: {X.shape[1]}")

    print(f"选择后特征数: {X_selected.shape[1]}")

    

    # 3. 降维处理

    pca = PCA(n_components=0.95)  # 保留95%方差

    X_pca = pca.fit_transform(X_scaled)

    

    print(f"PCA降维后特征数: {X_pca.shape[1]}")

    

    # 4. 多项式特征扩展

    poly = PolynomialFeatures(degree=2, interaction_only=True)

    X_poly = poly.fit_transform(X_scaled[:, :5])  # 只对前5个特征

    

    print(f"多项式特征扩展后: {X_poly.shape[1]} 个特征")

    

    return X_scaled, X_selected, X_pca, X_poly


def handling_imbalanced_data():

    """处理类别不平衡"""

    

    from sklearn.datasets import make_classification

    from imblearn.over_sampling import SMOTE

    from imblearn.under_sampling import RandomUnderSampler

    

    # 生成不平衡数据

    X, y = make_classification(

        n_samples=1000, n_features=20, n_classes=3,

        n_informative=10, weights=[0.1, 0.3, 0.6],

        random_state=42

    )

    

    # 查看类别分布

    unique, counts = np.unique(y, return_counts=True)

    print("原始类别分布:")

    for cls, count in zip(unique, counts):

        print(f"类别 {cls}: {count} 样本 ({count/len(y)*100:.1f}%)")

    

    # 1. 过采样(SMOTE)

    smote = SMOTE(random_state=42)

    X_smote, y_smote = smote.fit_resample(X, y)

    

    unique_s, counts_s = np.unique(y_smote, return_counts=True)

    print("\nSMOTE后类别分布:")

    for cls, count in zip(unique_s, counts_s):

        print(f"类别 {cls}: {count} 样本")

    

    # 2. 欠采样

    under = RandomUnderSampler(random_state=42)

    X_under, y_under = under.fit_resample(X, y)

    

    unique_u, counts_u = np.unique(y_under, return_counts=True)

    print("\n欠采样后类别分布:")

    for cls, count in zip(unique_u, counts_u):

        print(f"类别 {cls}: {count} 样本")

    

    # 3. 类权重调整

    model_weighted = LogisticRegression(

        multi_class='multinomial',

        solver='lbfgs',

        class_weight='balanced',  # 自动调整类权重

        max_iter=1000,

        random_state=42

    )

    

    return model_weighted

```


## 模型评估与选择


```python

from sklearn.model_selection import learning_curve, validation_curve


def evaluate_model_performance():

    """模型性能评估"""

    

    from sklearn.datasets import load_digits

    

    digits = load_digits()

    X, y = digits.data, digits.target

    

    # 划分数据集

    X_train, X_test, y_train, y_test = train_test_split(

        X, y, test_size=0.2, random_state=42

    )

    

    # 创建模型

    model = LogisticRegression(

        multi_class='multinomial',

        solver='lbfgs',

        max_iter=1000,

        random_state=42

    )

    

    # 学习曲线

    train_sizes, train_scores, test_scores = learning_curve(

        model, X_train, y_train,

        cv=5, n_jobs=-1,

        train_sizes=np.linspace(0.1, 1.0, 10),

        scoring='accuracy'

    )

    

    plt.figure(figsize=(10, 6))

    plt.plot(train_sizes, np.mean(train_scores, axis=1), 

             'o-', label='训练准确率')

    plt.plot(train_sizes, np.mean(test_scores, axis=1),

             's-', label='验证准确率')

    plt.fill_between(train_sizes,

                     np.mean(train_scores, axis=1) - np.std(train_scores, axis=1),

                     np.mean(train_scores, axis=1) + np.std(train_scores, axis=1),

                     alpha=0.1)

    plt.fill_between(train_sizes,

                     np.mean(test_scores, axis=1) - np.std(test_scores, axis=1),

                     np.mean(test_scores, axis=1) + np.std(test_scores, axis=1),

                     alpha=0.1)

    plt.xlabel('训练样本数')

    plt.ylabel('准确率')

    plt.title('学习曲线')

    plt.legend()

    plt.grid(True, alpha=0.3)

    plt.tight_layout()

    plt.show()

    

    # 验证曲线(正则化强度)

    param_range = np.logspace(-4, 4, 10)

    train_scores, test_scores = validation_curve(

        model, X_train, y_train,

        param_name='C',

        param_range=param_range,

        cv=5, n_jobs=-1,

        scoring='accuracy'

    )

    

    plt.figure(figsize=(10, 6))

    plt.semilogx(param_range, np.mean(train_scores, axis=1),

                 'o-', label='训练准确率')

    plt.semilogx(param_range, np.mean(test_scores, axis=1),

                 's-', label='验证准确率')

    plt.fill_between(param_range,

                     np.mean(train_scores, axis=1) - np.std(train_scores, axis=1),

                     np.mean(train_scores, axis=1) + np.std(train_scores, axis=1),

                     alpha=0.1)

    plt.fill_between(param_range,

                     np.mean(test_scores, axis=1) - np.std(test_scores, axis=1),

                     np.mean(test_scores, axis=1) + np.std(test_scores, axis=1),

                     alpha=0.1)

    plt.xlabel('正则化强度 (C)')

    plt.ylabel('准确率')

    plt.title('验证曲线(正则化参数调优)')

    plt.legend()

    plt.grid(True, alpha=0.3)

    plt.tight_layout()

    plt.show()


if __name__ == "__main__":

    evaluate_model_performance()

```


## 总结


多分类Logistic回归通过Softmax函数将二元分类扩展到多类别场景,成为处理多分类问题的基础且有效的方法。本文从理论原理、手动实现、Scikit-learn应用、到实战案例,全面介绍了多分类Logistic回归的各个方面。在实际应用中,需要注意数据预处理、特征工程、模型调参和评估等环节,同时根据具体问题选择合适的优化策略和处理方法。通过合理运用多分类Logistic回归,可以在保持模型简单可解释的同时,获得不错的分类性能。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3105615/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933968