K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

ロジスティック回帰

入門

02.2.1

ロジスティック回帰

最終更新 2020-01-29 読了時間 5 分
まとめ
ロジスティック回帰は、入力の線形結合 \(z = \mathbf{w}^\top \mathbf{x} + b\) をシグモイド関数 \(\sigma(z) = 1/(1+e^{-z})\) に通し、「クラス 1 である確率」を 0〜1 の値で返す二値分類モデルです。確率が 0.5 になる場所、つまり \(z = 0\) の直線が決定境界で、しきい値を 0.3 や 0.7 に動かせば Precision と Recall のバランスを後から調整できます。学習は対数尤度の最大化(クロスエントロピー損失の最小化)で行い、解析解がないので勾配法や Newton-Raphson 法(IRLS)で反復します。読み終えると、LogisticRegression の coef_ を「1 増えるとオッズが \(e^{w}\) 倍」と読み、C を変えたときに境界がどう動くかを説明できるようになります。

直感 #

勉強時間と睡眠時間から「小テストに合格するか」を予測する場面を考えます。線形回帰で点数を出すと \(-3\) や \(+12\) のような値になり、確率としては読めません。ロジスティック回帰は、この点数 \(z = \mathbf{w}^\top \mathbf{x} + b\) をシグモイド関数 \(\sigma(z) = 1 / (1 + e^{-z})\) に通し、\(z = 0\) で 0.5、\(z = 3\) で 0.95、\(z = -3\) で 0.05 という確率に変えます。得られた確率に「0.5 を超えたらクラス 1」というしきい値規則を当てるだけで分類が完成し、しきい値を変えても学習し直す必要はありません。

何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:間違えた点ほど線を強く押す。最初の 2〜3 回で大きく動き、その後は微調整になる。

式で書くと確率と損失はどうなるか #

「点数を確率に変える」部分を式にすると、入力 \(\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)}. $$

です。学習は、正解が 1 の点では \(p_i\) を、正解が 0 の点では \(1 - p_i\) を大きくする、つまり対数尤度

$$ \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 正則化を加えると係数 \(\mathbf{w}\) の暴走を抑え、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) $$

で表されます。\(y_i = 1\) なら \(p_i\)、\(y_i = 0\) なら \(1 - p_i\) だけが残る書き方です。全サンプルの独立性を仮定すると、対数尤度は

$$ \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\)です。つまり、対数尤度の最大化とクロスエントロピー損失の最小化は等価で、LogisticRegression が内部で最小化しているのはこの \(\mathcal{L}\) です。

勾配の導出 #

損失関数\(\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) $$

勾配は「予測確率と正解ラベルの差」に入力を掛けた形です。確率 0.9 で正解が 1 の点は差が \(-0.1\) でほとんど境界を動かさず、確率 0.1 で正解が 1 の点は差が \(-0.9\) で強く動かします。上のステップ再生で「間違えた点ほど線を強く押す」と見えたのは、この式の \(\sigma(\cdot) - y_i\) の大きさの違いです。

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法は大域的最適解に収束します。scikit-learn の既定ソルバー lbfgs は、この \(\mathbf{H}^{-1}\) を直接計算せずに近似する準ニュートン法です。

正則化の幾何学的解釈 #

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正則化によるスパース性の幾何学的な理由です。C は罰則の逆数なので、C=0.01 は強い正則化、C=100 はほぼ正則化なしを意味します。

Pythonで確率面と境界を描く #

次のコードは make_classification で作った 2 次元 300 点に LogisticRegression を当て、平面の各点の確率 \(P(y=1 \mid \mathbf{x})\) を色の濃さで、確率 0.5 の等高線を黒い直線で描きます。黒い線が決定境界で、線から離れるほど確率は 0 か 1 に近づきます。

 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}")

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

実行結果の読み方 #

黒い直線が確率 0.5 の決定境界で、オレンジ側がクラス 1、青側がクラス 0 と予測される領域です。色の濃さは確率で、直線から遠い点ほど 0 や 1 に近く、直線のすぐそばは 0.5 前後の「どちらとも言えない」点です。print される 2 つの係数のうち絶対値が大きいほうが境界の向きを強く決めており、その係数の指数 \(e^{w}\) がオッズ比です。

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

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

罰則 C と決定境界 #

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

使いどころと落とし穴 #

ロジスティック回帰が向くのは、確率そのものを使いたい場面(審査の通過確率、離反の確率)と、係数で「どの特徴量が効いたか」を説明したい場面です。苦手なのは三日月形や同心円のように境界が曲がったデータで、SVM の RBF カーネルか k近傍法 に替えるか、\(x_1^2\) や \(x_1 x_2\) を特徴量に足します。間違えやすい設定は C の向きで、C は罰則の逆数なので小さいほど正則化が強くなります。クラス比が 98 対 2 のような不均衡では確率 0.5 のしきい値だと少数クラスをほぼ見逃すので、class_weight="balanced" を付けるか、しきい値を 0.2 前後まで下げて Recall を確認します。

まとめ #

ロジスティック回帰は、線形結合 \(z = \mathbf{w}^\top \mathbf{x} + b\) をシグモイド関数に通して「クラス 1 の確率」を返し、確率 0.5 の直線 \(z = 0\) を決定境界にします。学習は対数尤度の最大化で、勾配 \(\sum (\sigma(z_i) - y_i)\mathbf{x}_i\) が示すとおり「大きく間違えた点ほど境界を強く動かす」反復です。出力が確率なので、しきい値を 0.3 に下げて Recall を取るか 0.7 に上げて Precision を取るかを学習後に決められます。C で L2 正則化の強さを、penalty="l1" で不要な係数を 0 にする選択を制御し、境界が曲がるデータには別の手法を当ててください。

参考文献 #

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