Now Loading...

Now Loading...

今回は機械学習、中でも教師あり学習の一つである線形回帰について学びます。線形回帰は機械学習において最も基本的なタスクの一つなので、ぜひ理解しましょう。

線形回帰とは?

次のグラフのような「30人の身長と体重のデータ」が与えられているとします。

ここで、新しい人が入ってきてその人の身長だけがわかっている時、その人の体重を予測するにはどうすればいいでしょうか?下図のようにデータの分布を表すような直線を引けば、その直線に当てはめることで身長からだいたいの体重を予測できそうです。

このように、データの分布に合う直線を求めることを「線形回帰」といい、この直線を仮説と呼びます。

入力値(今回は身長)をX、予測値(今回は体重)をhとすると、直線は「h=aX+b」で表されます。線形回帰はこの直線を求めることなので、すなわち最適なaとbを見つけることになります。
※Xに対応する正解の値をY、予測値をhで区別します。

では、これから具体例に沿って線形回帰の方法を説明していきます。まずは使用するデータの準備から始めていきましょう。

データの準備

まずは線形回帰の実践で使用するデータを用意します。今回は、「30人の身長と体重のデータ」をNumPy配列で作成します。NumPyやグラフ表示のためのmatplotlibなど、外部ライブラリを使用する際は、初めに以下のようにインポートすることが必要です。

import numpy as np
import matplotlib.pyplot as plt

さっそくデータをNumPy配列で定義し、それがどのような分布になっているか、グラフで表示して見てみましょう。

 

30人分の身長と体重のデータを用意できました。 今回はこのデータに対して線形回帰を行います。

データの前処理 – 標準化

実は、このデータのまま学習すると上手くいかない可能性があります。それはデータのスケール(値の範囲)が違うためです。先ほど用意したデータを見ると、身長は約130~190、体重は約30~90と桁が違うことがわかります。このようにデータのスケールに偏りがあると上手く学習できないため、事前にスケールを揃えておくことが必要です。

データのスケールを揃えることを一般的に「正規化」といい、今回はその中でもよく使われる「標準化」を行います。

以下に関数「stand」(標準化の意味であるstandardizationの略)として標準化処理を実装し、さらに標準化したデータをグラフで表示してみます。

 

データXとYの範囲がどちらもだいたい(-2~2)になったと思います。これでデータのスケールを揃えることができたので、いよいよ線形回帰に入っていきましょう。

線形回帰の実装

線形回帰のゴールはデータに合う仮説「h=aX+b」を求めることでしたが、では一体何をもって「良い仮説」と判断できるでしょうか。それは次のようなイメージになります。

すなわち、仮説と実際の値との誤差を見るのです。2つの図を見ればわかる通り、誤差の合計が一番小さいものが良い仮説と言えます。

線形回帰では、まず初めは仮説を適当に設定します(例えばa=0,b=0とすると左側のような仮説になる)。ここから、誤差が小さくなるように少しずつaとbの値を変えていくのです。

この誤差を計算する関数を「目的関数」といい、誤差(目的関数の値)が小さくなるようにa,bを変更することを「パラメータを更新する」といいます。また、このパラメータ更新を何度も繰り返すことが「学習」です。

以上をまとめると、線形回帰の全体的な流れはこうなります。

この流れに沿って、
1.仮説 → 2.目的関数 → 3.パラメータ更新
の順で線形回帰を実装していきます。

1. 仮説

仮説の式は「h=aX+b」でした。aとbを定義したら、これをそのまま書くだけです。

Pythonでは以下のように計算されるため、Xを配列で渡せば仮説も配列のまま扱えます。

仮説を定義したら、実際にデータに重ねてみます。

 

これで仮説が定義できました。パラメータの初期値を1としましたが、まったくデータに重なっていません。ここから、学習によって適切なaとbを求めていきます。

2. 目的関数

「仮説による予測値hと正解値Yとの誤差」を計算するのが目的関数であり、仮説がどれくらい良いかを表す指標でした。

ではどのように誤差を計算すればいいでしょうか。
単純に考えると予測値と正解値の差をとることを思いつくと思います。ただ、次の図のように、単純に差をとるだけだと、どちらが大きいかによって+の誤差と-の誤差が出てきます。

これでは指標として不便なので、差を二乗して全て+にすればよさそうです。
さらにそれを全てのデータで平均をとって、一般的に線形回帰の目的関数は次のように定義されます。

hとYの右上の(i)は、i番目のデータであることを表しています。
つまりこの目的関数は、「データ点ごとに予測値と正解値の差の二乗を計算し、データ数mで平均をとる」ことを示しています。
※式を見るとさらに2で割っていますが、これは微分をする時に都合がいいためです。

ではこの目的関数を実装してみましょう。

 

以上で目的関数が定義できました。最初のtheta(値が1)を入れて計算した結果、値が0.53くらいになったと思います。つまりこれが現在の予測値と正解値の誤差です。ここから、この値が小さくなるようにパラメータaとbを更新していきます。

3. パラメータ更新(最急降下法)

​仮説と目的関数を定義できたので、ここからはいよいよ仮説を調整していく「パラメータ更新」です。
目的関数の値をもとにどうやってaとbを更新すればよいか考えてみましょう。

今、以下のようにaが変化すると値が変わる目的関数があるとします。

目的関数の値が小さくなればよいので、この場合はaを小さくすればよいとわかります。
逆にaと目的関数の関係が以下のようになっていたらどうでしょう。

この場合はaを大きくすればよいとすぐにわかります。

この二つのパターンは数学的にはどのように分けられるでしょうか?

それは、目的関数の勾配、すなわち微分です。つまり、現在のaにおいて、勾配が正の時はaを小さくし、勾配が負の時はaを大きくすればよいのです。すなわち、次の式でパラメータを更新をすればよさそうです。

ここで、勾配にかかっているαは学習率といい、「αが大きければ一度に大きく更新し、小さければ一度に少しずつ更新する」ことを意味します。

この方法を最急降下法(勾配降下法)といい、パラメータ更新の代表的な方法です。まとめると以下のようなイメージになります。

bについてもまったく同じで、パラメータごとにこの更新を行います。

この最急降下法の式を実装するためにまず勾配部分(目的関数の微分)を計算してみましょう。パラメータaとbのそれぞれについて考えると

となります(微分については、実際に目的関数にhの式を代入して計算してみてください)。

これを使うと、Pythonでは以下のようにパラメータ更新を実装できます。

a = a - (alpha / m)*((h - y)*X).sum()
b = b - (alpha / m)*(h - y).sum()

学習ではこれを何回も繰り返します。繰り返し回数(イテレーションといいます)と学習率αを指定して、for文を使って実装しましょう。

 

学習率を0.01、繰り返し回数を1000回として学習してみました。目的関数の値の推移を見ると、パラメータを更新するたびに目的関数の値が下がっていっていることがわかると思います。
さらに、グラフを見ると仮説がしっかりとデータの分布を表しています。
これで線形回帰を行う事ができました。

未知データの予測

最後に、得られた回帰直線によって未知のデータに対する予測を行ってみましょう。機械学習は、このようにして、学習データをもとに得られたパラメータを使って未知のデータに対する予測を行うことがゴールになります。

※今回はデータを標準化しているので、予測時にも標準化を行う必要があります。さらに、標準化したデータを入れて得られた予測値も標準化された値になるので、元のスケールに戻す必要があります。こちらも詳しくは別のセクションで説明するので、ここでは流していただいてかまいません。

 

まとめ

今回は「身長から体重を予測する」というタスクを例に、線形回帰を学びました。

ここで行った一連の流れは、線形回帰に関わらず機械学習の基本的な考え方になります。そのため、今回説明したことをしっかり理解すれば、発展的な手法も勉強しやすいでしょう。

線形回帰は直線で仮説を作りましたが、実際には直線で表せるようなデータばかりではありません。体重は身長だけでなく別の要因によって変わるかもしれませんし、実際には完全に比例関係にはないかもしれません。

そのような場合も線形回帰の考え方を拡張した非線形回帰や重回帰といった手法で対応することができるので、ぜひそちらにも挑戦してみましょう。

\ シェア /

Related article

関連する記事はありません