ロジスティック回帰

入門

2.2.1

ロジスティック回帰

最終更新 2020-01-29 読了時間 4 分
まとめ
  • ロジスティック回帰は入力の線形結合をシグモイド関数に通し、クラス1に属する確率を直接推定する二値分類モデルです。
  • 出力が \([0, 1]\) に収まるため、意思決定のしきい値を柔軟に調整でき、係数は対数オッズ比として解釈できます。
  • 学習はクロスエントロピー損失(対数尤度の最大化)で行い、L1/L2 正則化を組み合わせると過学習を抑えられます。
  • scikit-learn の LogisticRegression を使えば、前処理から決定境界の可視化まで数行で実装できます。

直感 #

線形回帰の出力は実数全域に広がりますが、分類では「クラス1である確率」が欲しい場面が多くあります。ロジスティック回帰は線形結合 \(z = \mathbf{w}^\top \mathbf{x} + b\) をシグモイド関数 \(\sigma(z) = 1 / (1 + e^{-z})\) に通し、確率として解釈できる値を得ます。得られた確率に対して「0.5 を超えたらクラス1と予測する」といった単純なしきい値規則を決めるだけで分類できます。

flowchart LR A["入力x"] --> B["線形結合\nz = w·x + b"] B --> C["シグモイド\nσ(z)"] C --> D["確率\nP(y=1|x)"] D --> E{"閾値判定\nθ=0.5"} E -->|"≥ θ"| F["クラス1"] E -->|"< θ"| G["クラス0"] style A fill:#2563eb,color:#fff style C fill:#1e40af,color:#fff style F fill:#10b981,color:#fff style G fill:#ef4444,color:#fff

数式で見る #

入力 \(\mathbf{x}\) に対するクラス1の確率は

$$ P(y=1 \mid \mathbf{x}) = \sigma(\mathbf{w}^\top \mathbf{x} + b) = \frac{1}{1 + \exp\left(-(\mathbf{w}^\top \mathbf{x} + b)\right)}. $$

学習は対数尤度

$$ \ell(\mathbf{w}, b) = \sum_{i=1}^{n} \Bigl[ y_i \log p_i + (1 - y_i) \log (1 - p_i) \Bigr], \quad p_i = \sigma(\mathbf{w}^\top \mathbf{x}_i + b), $$

の最大化(すなわち負のクロスエントロピー損失の最小化)として表現できます。L2 正則化を加えると係数の暴走を抑え、L1 正則化を組み合わせると不要な特徴量の重みを 0 にできます。

アルゴリズムの詳細 #

最尤推定とクロスエントロピー #

ロジスティック回帰のパラメーター推定は**最尤推定(MLE)**に基づいています。各サンプル\((\mathbf{x}_i, y_i)\)の尤度は

$$ P(y_i \mid \mathbf{x}_i) = p_i^{y_i}(1 - p_i)^{1 - y_i}, \quad p_i = \sigma(\mathbf{w}^\top \mathbf{x}_i + b) $$

で表されます。全サンプルの独立性を仮定すると、対数尤度は

$$ \ell(\mathbf{w}, b) = \sum_{i=1}^{n} \bigl[ y_i \log p_i + (1 - y_i) \log(1 - p_i) \bigr] $$

となります。これの符号を反転させたものがクロスエントロピー損失\(\mathcal{L} = -\ell\)です。つまり、対数尤度の最大化とクロスエントロピー損失の最小化は等価です。

勾配の導出 #

損失関数\(\mathcal{L}\)の\(\mathbf{w}\)に関する勾配を求めます。シグモイド関数の微分の性質\(\sigma’(z) = \sigma(z)(1 - \sigma(z))\)を利用すると、

$$ \frac{\partial \mathcal{L}}{\partial \mathbf{w}} = \sum_{i=1}^{n} \bigl(\sigma(\mathbf{w}^\top \mathbf{x}_i + b) - y_i\bigr)\, \mathbf{x}_i $$$$ \frac{\partial \mathcal{L}}{\partial b} = \sum_{i=1}^{n} \bigl(\sigma(\mathbf{w}^\top \mathbf{x}_i + b) - y_i\bigr) $$

勾配は「予測確率と正解ラベルの差」に入力を掛けた形をしており、線形回帰の正規方程式と類似した構造を持ちます。

Newton-Raphson法(IRLS) #

勾配降下法よりも高速に収束するために、2次の情報(ヘッセ行列)を使うNewton-Raphson法が使われます。ロジスティック回帰の文脈では**IRLS(Iteratively Reweighted Least Squares)**とも呼ばれます。

ヘッセ行列は以下で与えられます。

$$ \mathbf{H} = \frac{\partial^2 \mathcal{L}}{\partial \mathbf{w} \partial \mathbf{w}^\top} = \sum_{i=1}^{n} p_i(1 - p_i)\, \mathbf{x}_i \mathbf{x}_i^\top = \mathbf{X}^\top \mathbf{S} \mathbf{X} $$

ここで\(\mathbf{S} = \text{diag}(p_i(1 - p_i))\)は対角重み行列です。更新則は

$$ \mathbf{w}^{(t+1)} = \mathbf{w}^{(t)} - \mathbf{H}^{-1} \nabla \mathcal{L} $$

です。\(\mathbf{H}\)は半正定値であるため、\(\mathcal{L}\)は凸関数であり、Newton-Raphson法は大域的最適解に収束します。

正則化の幾何学的解釈 #

L1正則化とL2正則化は、パラメーター空間での制約領域の形状が異なります。

$$ \text{L2}: \quad \min_{\mathbf{w}} \; \mathcal{L}(\mathbf{w}) + \frac{1}{2C} \lVert \mathbf{w} \rVert_2^2 $$$$ \text{L1}: \quad \min_{\mathbf{w}} \; \mathcal{L}(\mathbf{w}) + \frac{1}{C} \lVert \mathbf{w} \rVert_1 $$
L2正則化L1正則化
制約領域の形状超球(滑らかな境界)超菱形(角がある境界)
係数への影響全体的に小さくなる(縮小)一部が厳密に0になる(スパース)
特徴量選択行わない自動で行われる
scikit-learnpenalty="l2"(デフォルト)penalty="l1", solver="saga"

L1正則化の制約領域は角を持つため、等高線との接点が座標軸上に来やすく、結果として一部の係数が厳密に0になります。これがL1正則化によるスパース性の幾何学的な理由です。

flowchart TD A["学習データ (X, y)"] --> B["対数尤度 ℓ(w,b)\n= Σ[y log p + (1−y) log(1−p)]"] B --> C["勾配 ∇ℓ を計算\n(σ(w·x+b) − y)·x"] C --> D{"求解法の選択"} D -->|"勾配降下法"| E["w ← w − η∇ℓ"] D -->|"Newton-Raphson\n(IRLS)"| F["ヘッセ行列 H = X'SX\nw ← w − H⁻¹∇ℓ"] E --> G{"収束?"} F --> G G -->|No| C G -->|Yes| H["最適パラメーター w*, b*"] style A fill:#2563eb,color:#fff style C fill:#1e40af,color:#fff style H fill:#10b981,color:#fff

Pythonによる実験 #

次のコードは人工的に生成した2次元データにロジスティック回帰を適用し、決定境界を可視化した例です。LogisticRegression を利用するだけで学習から予測・境界の描画まで完結します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from __future__ import annotations

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

def run_logistic_regression_demo(
    n_samples: int = 300,
    random_state: int = 2,
    label_class0: str = "クラス0",
    label_class1: str = "クラス1",
    label_boundary: str = "決定境界",
    title: str = "ロジスティック回帰による決定境界",
) -> dict[str, float]:
    """Train logistic regression on a synthetic 2D dataset and visualise the boundary.

    Args:
        n_samples: Number of samples to generate.
        random_state: Seed for reproducible sampling.
        label_class0: Legend label for class 0.
        label_class1: Legend label for class 1.
        label_boundary: Legend label for the separating line.
        title: Title for the plot.

    Returns:
        Dictionary containing training accuracy and coefficients.
    """
    X, y = make_classification(
        n_samples=n_samples,
        n_features=2,
        n_redundant=0,
        n_informative=2,
        random_state=random_state,
        n_clusters_per_class=1,
    )

    clf = LogisticRegression()
    clf.fit(X, y)

    accuracy = float(accuracy_score(y, clf.predict(X)))
    coef = clf.coef_[0]
    intercept = float(clf.intercept_[0])

    x1, x2 = X[:, 0], X[:, 1]
    grid_x1, grid_x2 = np.meshgrid(
        np.linspace(x1.min() - 1.0, x1.max() + 1.0, 200),
        np.linspace(x2.min() - 1.0, x2.max() + 1.0, 200),
    )
    grid = np.c_[grid_x1.ravel(), grid_x2.ravel()]
    probs = clf.predict_proba(grid)[:, 1].reshape(grid_x1.shape)

    cmap = ListedColormap(["#aec7e8", "#ffbb78"])
    fig, ax = plt.subplots(figsize=(7, 6))
    contour = ax.contourf(grid_x1, grid_x2, probs, levels=20, cmap=cmap, alpha=0.4)
    ax.contour(grid_x1, grid_x2, probs, levels=[0.5], colors="k", linewidths=1.5)
    ax.scatter(x1[y == 0], x2[y == 0], marker="o", edgecolor="k", label=label_class0)
    ax.scatter(x1[y == 1], x2[y == 1], marker="x", color="k", label=label_class1)
    ax.set_xlabel("特徴量1")
    ax.set_ylabel("特徴量2")
    ax.set_title(title)
    ax.legend(loc="best")
    fig.colorbar(contour, ax=ax, label="P(class = 1)")
    fig.tight_layout()
    plt.show()

    return {
        "accuracy": accuracy,
        "coef_0": float(coef[0]),
        "coef_1": float(coef[1]),
        "intercept": intercept,
    }

metrics = run_logistic_regression_demo(
    label_class0="クラス0",
    label_class1="クラス1",
    label_boundary="決定境界",
    title="ロジスティック回帰による決定境界",
)
print(f"訓練精度: {metrics['accuracy']:.3f}")
print(f"特徴量1の係数: {metrics['coef_0']:.3f}")
print(f"特徴量2の係数: {metrics['coef_1']:.3f}")
print(f"切片: {metrics['intercept']:.3f}")

ロジスティック回帰による決定境界

ロジスティック回帰は決定境界が線形であるため、非線形な分布のデータにはそのまま適用できません。多項式特徴量やカーネル近似を組み合わせるか、SVMやニューラルネットワークなどの非線形モデルを検討してください。

係数の絶対値が大きい特徴量ほど予測への影響が大きいですが、スケールが揃っていないと比較できません。StandardScalerで標準化したうえで係数を比較してください。多クラス分類ではソフトマックス回帰に拡張できます。

罰則 C と決定境界 #

罰則係数 C を変えるとロジスティック回帰の決定境界がどう変化するか確認できます。

まとめ #

  • ロジスティック回帰はシグモイド関数を用いて、入力に対するクラス1の確率を直接推定します。
  • 出力が確率として解釈できるため、しきい値を調整してPrecision/Recallのトレードオフを制御できます。
  • L1/L2正則化を組み合わせることで、過学習の抑制と不要な特徴量の除去が可能です。
  • 決定境界が線形であるため、解釈性が高い反面、非線形な分布には対応できない点に注意が必要です。

参考文献 #

  • Agresti, A. (2015). Foundations of Linear and Generalized Linear Models. Wiley.
  • Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning. Springer.