一次関数の答えを「確率」に変える
分類の最初のモデルとして、ロジスティック回帰 を学びます。前の章で学んだ一次関数を、ほぼそのまま使えるモデルです。
まず、合格・不合格を一次関数で予測しようとすると、何が困るかを考えてみましょう。z = a x + b z = ax + b z = a x + b の値は、入力によって − 3 -3 − 3 にも 150 150 150 にもなります。しかし分類で本当に知りたいのは「合格する確率は何%か 」という、0から1の間の値です。
そこで、一次関数の結果 z z z を、どんな値でも0から1の間に押し込める関数に通します。これが シグモイド関数 です。
σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ ( z ) = 1 + e − z 1
e e e は約2.718という決まった数(ネイピア数)です。e − z e^{-z} e − z は見慣れない書き方ですが、意味は次の通りです。
z = 0 z = 0 z = 0 のときは1(ここが出発点)
z z z が1増えるごとに、2.718で割っていく(だんだん0に近づく)
z z z が1減るごとに、2.718を掛けていく(どんどん大きくなる)
なぜ2.718という半端な数なのか、気になった人もいるでしょう。実は、2.718の代わりに2や3を使っても、同じようなS字の曲線になります。e e e が使われるのは、学習のときに必要な計算(勾配を求める計算)が一番すっきりした形になるからです。その理由は高校・大学の数学で学びます。ここでは「S字を作るために使う、決まった数」と考えて大丈夫です。
ここで大事なのは、e − z e^{-z} e − z はどんな z z z でも必ず正の数になる ということです。すると、シグモイド関数は「1 ÷ ( 1 + 正の数 ) 1 \div (1 + \text{正の数}) 1 ÷ ( 1 + 正の数 ) 」なので、分母は必ず1より大きくなり、答えは 必ず0より大きく1より小さい 値になります。これが、どんな z z z でも確率として使える値に変換できる理由です。例えば z = 0 z = 0 z = 0 なら、1 1 + 1 = 0.5 \frac{1}{1 + 1} = 0.5 1 + 1 1 = 0.5 です。
その結果、シグモイド関数は次のような性質を持ちます。
グラフにすると、左側では0に張り付き、真ん中で急に立ち上がり、右側では1に張り付く、なめらかなS字の形になります。
z σ(z) -6 -4 -2 0 2 4 6 0 0.5 1 横軸をz、縦軸をσ(z)にしたグラフ。zが大きなマイナスでは0に近く、z=0でちょうど0.5を通り、zが大きなプラスでは1に近づくS字の曲線です。
つまりロジスティック回帰は、一次関数でスコアを計算し、それをシグモイド関数で「クラス1である確率」に変換するモデル です。
確率からクラスを決める:しきい値
確率が分かっても、最終的には「合格」か「不合格」かを決める必要があります。そのために使う区切りの値を しきい値 と呼びます。一般的には0.5が使われ、「クラス1である確率が0.5以上ならクラス1、未満ならクラス0」と判定します。
σ ( z ) = 0.5 \sigma(z) = 0.5 σ ( z ) = 0.5 になるのは z = 0 z = 0 z = 0 のときでした。つまり、一次関数 a x + b ax + b a x + b の値がちょうど0になる場所が、前のレッスンで見た 境界線 にあたります。
scikit-learnでは、線形回帰とまったく同じ流れで使えます。
from sklearn.linear_model import LogisticRegression
import numpy as np
# 勉強時間 → 合格(1) / 不合格(0)
X = np.array([[1], [2], [3], [4], [6], [7], [8], [9]])
y = np.array([0, 0, 0, 0, 1, 1, 1, 1])
model = LogisticRegression()
model.fit(X, y) # 学習
model.predict([[8]]) # クラスを予測 → array([1])
model.predict_proba([[8]]) # 確率を予測 → [[クラス0の確率, クラス1の確率]]
predictはしきい値で判定した後の クラス を、predict_probaは判定する前の 確率 を返します。predict_probaの結果は「クラス0の確率」と「クラス1の確率」の組で、2つを足すと必ず1になります。クラス1の確率だけを取り出したいときは、model.predict_proba([[8]])[0][1]のように書きます。
ロジスティック回帰はどうやって学習するのか
fitで学習するとき、ロジスティック回帰は何をしているのでしょうか。基本の考え方は、前の章の線形回帰とまったく同じです。
今の a a a と b b b で、各データの確率を予測する
予測がどれくらい悪いかを、損失 (罰の合計)として1つの数値にする
損失が小さくなる向きに、a a a と b b b を少しずつ調整する(勾配降下法)
違うのは2つ目の「罰の付け方」です。分類では、各データについて 「正解のクラスに、何%の確率を付けたか」 を見ます。正解に高い確率を付けていれば良い予測、低い確率しか付けていなければ悪い予測です。ロジスティック回帰では、次のような罰を使います。
この罰には、覚えやすい規則があります。正解に付けた確率が半分になるたびに、罰が約0.69ずつ増えます (0.5 → 0.25で、0.69 → 1.39)。確率が0に近づくほど、罰はいくらでも大きくなっていきます。この罰は 交差エントロピー (またはログ損失)と呼ばれ、正体は高校数学で習う「対数」を使った式ですが、ここでは性質を押さえれば十分です。
なぜ二乗誤差ではだめなのか
回帰と同じ二乗誤差を使うとどうなるでしょうか。正解が1のときに確率0.01と予測しても、二乗誤差は ( 1 − 0.01 ) 2 ≈ 0.98 (1 - 0.01)^2 \approx 0.98 ( 1 − 0.01 ) 2 ≈ 0.98 で、どんなに外しても1を超えません。確率0.4と予測した「惜しい」場合の0.36と比べても、差は3倍程度です。一方、上の罰なら0.01のときは4.61、0.4のときは0.92と、「自信満々で大外れ」を5倍もの重さで罰します。この強い罰があるからこそ、モデルは大外れを優先して直すように学習します。
公式ではなく、勾配で少しずつ解く
線形回帰は、損失が最小になる a a a と b b b を公式で一度に計算できました。ロジスティック回帰にはそのような公式がないため、前の章で自分で動かした勾配降下法の考え方で、損失を少しずつ小さくしていきます。scikit-learnのLogisticRegressionは、勾配降下法をより速く進められるように工夫した仲間の方法を使って、a a a と b b b を求めています。
よくある誤解と、確率を見る意味
誤解1:「回帰」という名前だから、数値を予測するモデル?
ロジスティック回帰は、名前に「回帰」と付いていますが 分類のモデル です。内部で一次関数(回帰と同じ形の式)を使っていることから、この名前が付いています。名前に惑わされないようにしましょう。
誤解2:予測結果が同じ「1」なら、どれも同じくらい確か?
確率0.51で「合格」と判定された人と、確率0.99で「合格」と判定された人は、predictの結果はどちらも同じ1です。しかし、モデルの確信の度合いはまったく違います。
実務では、この確率の情報がとても役に立ちます。例えば医療の診断支援では、「確率が0.4〜0.6のような判断が微妙なケースだけは、必ず医師が詳しく確認する」という使い方ができます。predictの結果だけを見ていると、こうした判断の余地が見えなくなってしまいます。
振り返り
ロジスティック回帰は「一次関数 → シグモイド関数 → 確率」という流れで分類する
確率がしきい値(一般的に0.5)以上かどうかでクラスを決める
predictはクラスを、predict_probaは確率を返す
学習では、「正解に付けた確率が低いほど大きくなる罰(交差エントロピー)」の合計を、勾配降下法の考え方で小さくしていく
次のレッスンでは、まったく違う考え方で分類する「決定木」を学びます。