機械学習入門(回帰モデル)

schedule 2021/04/01  refresh 2023/11/09

 

 

機械学習が一般的な会話にものぼるようになってきた昨今、機械学習って何となく意味はわかるけど実際どんなことやってるんだろう?って人は多いのではないでしょうか。そこで、AI碁や画像認識などのAIの最先端技術のベースとなるような概念を1から説明してみます。

今回は一番ベーシックな線形回帰。

 

 

回帰モデル作成のステップ

  • 数値予測の関数を仮定する。
  • 関数を学習データに最適化し関数の係数を決定する。
  • 関数に予測したいデータを代入して予測結果を得る。
    1.  

1. 数値予測の関数を仮定する。

    1.  
  1. ではまずデータは概念の理解のためなので仮定式の妥当性などを考えずに適当に身長、体重を想定したものを作ります。
  2.  
身長(x) 体重(y)
172.5  70.2
171 62.1
165.5 58.1
169.9 60.4
170.2 64.1

 

yp = x * w1 + w2

 

予測体重をypとした体重予測関数をこのように仮定します。


 

2. 関数を学習データに最適化し関数の係数を決定する。

2.1. 損失関数

yp – yt

実際の体重をytとすると以下が誤差になります。

これが最少になるようなw1, w2を決定するという計算処理に落とし込めます。

 

L(w1, w2) = (yp0 – yt0)**2 + (yp1 -yt1)**2 + …
 
  1. ただ、このままではプラスになったりマイナスになったりで計算しにくいので2乗したものをすべてのデータについて計算し足し合わせたものを指標とします。
  2. このような誤差を計算し関数を評価する関数を損失関数といいます。

 


2.2. 最適化

  1. このまま計算してもいいのですが以後の計算を楽にするために平均値を0とした学習データに変換します。平均それぞれ169.82, 62.98なので最初のデータから引いて以下の通り。
  2.  
身長– 平均身長(X) 体重 – 平均体重(Y)
2.68 7.22
1.18 0.88
-4.32 -4.88
0.08 -2.58
0.38 1.12

 

Yp = X * w1
 
  1. データ変換により予測関数が簡単になりました。身長から平均身長を引いたものから体重から平均体重を引いたものを予想する関数に変わっていることは忘れないでください。

 

L2(w1) = (Yp0 – Yt0)**2 + (Yp1 – Yt1)**2 + …
 
  1. 予測式が原点を通るようになったので損失関数はこのように変えられます。

 

L2(w1) = (X0 * w1 – Yt0)**2 + (X1 * w1 – Yt1)**2 + …
 
  1. Yp = X * w1を代入します。

 

=a(w1 – b)**2 + c
 
  1. X, Ytはデータの値で実数値なので代入するとw1の2次方程式になり、このように変換できるはずです。見ればわかる通りw1がbのときに損失関数が最小つまりデータに基づく予測誤差が最小になるといえます。

 

x = X + 169.82
y = Y + 62.98

y – 62.98 = (x – 169.82) * b
y = b * x + d

 

  1. 最適化されたX, Yの関係式は Y = X * bであり、X, Yはそれぞれx, yから平均を引いたものなので代入するとこのようになります。

 


3. 関数に予測したいデータを代入して予測結果を得る。

2で最適化された予測式 y = b * x + d のxに身長をいれれば体重が予測できます。(計算すればb, dは実数値で決定されてるので結果がでます。)こうして出た予測値をそのまま使ったり、予測値と実際の測定値を比較することで異常検知に使ったりなどいろいろ応用できます。

 
 

Pythonで実装

 

  1. じゃ実際プログラムでどうやって計算するの?ってことでPythonで実装してみます。

 

1. データX, Yを生成

D = np.array(((172.5,70.2),(171,62.1),(165.5,58.1),(169.9,60.4),(170.2,64.1)))
d_avg = np.average(D, axis=0)
x = D - d_avg
  1. 身長、体重データでDを初期化してそれぞれの平均d_avgで引いて、上の説明でいうX, Yのデータ配列xを生成します。

 

2. 損失関数

  1. def lose_score(X, w):
        l = X[:,0] * w - X[:,1]
        lose = np.sum(l**2)
        return lose
 
  1. この関数はデータ配列xと予測関数のウェイトwを与えると実数値と予測値の残差平方和を出します。(上で説明した損失関数)

 

l = X[:,0] * w - X[:,1]
 
  1. この部分は誤差 l = yp - yt に yp = x * w を代入しただけで l = x * w - y を表しています。

 

lose = np.sum(l**2)
 
  1. この部分はそのまま残差配列lを2乗したものを足しただけです。
  2.  

3. 勾配関数

  1. def grad(X, w):
        x = X[:,0]
        y = X[:,1]
        g = x**2 * 2 * w - 2 * x * y
        grad = np.sum(g)
        return grad
 
  1. ここから新しい概念です。
  2. 最小値を求めるのにプログラムでできて計算量はなるべく少なくなるべく正確にできるように色々な手法があります。データによって正解が違うのでここではとりあえず一番一般的な勾配降下法で最小値を求めてみます。
  3. 損失関数はU字型のwの2次関数なので最小値に向かって傾きが0に近づき、傾きのマイナス方向に最小値がある性質を利用し、傾きに学習効率をかけた値をウェイトから引くことで誤差が最小になるようなウェイトに近づけていきます。
  4. この関数はその傾きを返します

     

    x = X[:,0]
    y = X[:,1]

 

  1. ここは、みやすいようにX, Yのデータ配列を身長から平均身長を引いたデータ配列xと体重から平均体重を引いたデータ配列yに切り出しているだけです。

     

    g = x**2 * 2 * w - 2 * x * y
 
  1. gは損失関数で示したl**2をwで偏微分したものです。

     

    l**2 = (x * w - y)**2 = x**2 * w**2 - 2 * x * y * w + y**2
 
  1. l**2
x**2 * 2 * w - 2 * x * y
 
  1. l**2をwで偏微分、これが傾きになります。

 

4. 学習

  1.  

    def train(X, l_rate=0.01, max_run=50):
        w = 1
        for _ in range(max_run):
            g = grad(X, w)
            if g == 0:
                break
            else:
                w -= l_rate * g
        return w

    1. この関数は最小値のときのウェイト(前の説明でいうb)を返す関数です。
      l_rateは学習効率、max_runは最大調整回数、wはウェイト(初期値1にしてます)、傾きgが0になったときは終了します。
    2. *l_rateの設定によっては収束しなかったりしますのでデータによって調整します
    3. 実行するとbは1.446326858478165であることがわかります。

 

5. 予想式

 

y – 62.98 = (x – 169.82) * b
y – 62.98 = (x – 169.82) * 1.446
y = 1.446 * x - 182.57972
 
これが与えられたデータから導き出した予測式になります。

 

6. 全体

      1. import numpy as np

        def lose_score(X, w):
            l = X[:,0] * w - X[:,1]
            lose = np.sum(l**2)
            return lose

        def grad(X, w):
            x = X[:,0]
            y = X[:,1]
            g = x**2 * 2 * w - 2 * x * y
            grad = np.sum(g)
            return grad

        def train(X, l_rate=0.01, max_run=50):
            w = 1
            for _ in range(max_run):
                g = grad(X, w)
                if g == 0:
                    break
                else:
                    w -= l_rate * g
            return w

        D = np.array(((172.5,70.2),(171,62.1),(165.5,58.1),(169.9,60.4),(170.2,64.1)))
        d_avg = np.average(D, axis=0)
        x = D - d_avg
        b = train(x)
        s = lose_score(x, b)
        print(b)
        print(s)