colab-logo

2. 機械学習ライブラリの基礎

本章では,代表的な機械学習アルゴリズムの紹介とその使い方のポイントを数学的な背景と合わせて紹介していきます. 機械学習の考え方を身に着ける練習として,単回帰分析重回帰分析のアルゴリズムを数式と一緒に考えていきましょう.これらを学ぶことで微分と線形代数,統計の使い方が見えてくると思います.重回帰分析は次章で紹介するニューラルネットワークでもその考え方のベースになるところが多いため,しっかりと数式を理解しておきましょう.

2.1. 単回帰分析

まずはじめに,機械学習アルゴリズムの中でも最も基礎的な手法のひとつである,単回帰分析について説明します.機械学習アルゴリズムは,教師あり学習教師なし学習に大別され,単回帰分析は教師あり学習の一種です.教師あり学習の典型的な問題として,\(10\)\(0.1\)のように数値(厳密には連続値)を予測する回帰と,赤ワイン or 白ワインのようにカテゴリ値を予測する分類があります.単回帰分析はその名の通り,回帰を取り扱う手法で,ひとつの入力変数からひとつの出力変数を予測する機械学習アルゴリズムです.

2.1.1. 問題設定(単回帰分析)

機械学習では,データをもとに学習を行いますが,データに含まれる情報の中から何を利用し,何を予測させるかは人間が決める必要があります.

ここでは例として,家賃を予測する問題を考えることにします.従って,家賃が 出力変数 \(y\) となります.

次に, 入力変数 として何を採用するかを考えます.家賃の予測では,たとえば部屋の広さ,駅からの距離,犯罪発生率などを入力変数として検討することができます.ここでは部屋の広さを入力変数 \(x\) として採用することにしましょう.実際には,複数の入力変数候補があった際に,それらすべてを扱うことができるようなモデル化が一般的ですが,それは次の重回帰分析以降で紹介することにします.

機械学習のアルゴリズムでは,どの手法も大きく分けて次の3ステップで成り立っています.

  • Step1: モデルを決める
  • Step2: 目的関数を決める
  • Step3: 最適なパラメータを求める

上記の3ステップについて,順に説明していきます.

2.1.2. Step1. モデルを決める(単回帰分析)

まずStep1ではモデルを決めます.モデルとは,出力変数 \(y\) と入力変数 \(x\) の関係性を定式化したものです.どのように定式化すれば,家賃をうまく予測することができるのでしょうか.このモデル設計は現在は人手で行うのが一般的であり,機械が自動的に決めてくれるわけではありません(最近ではAutoMLなど,モデルも自動決定する研究も進展してきています).

例えば,与えられたデータセットにおいて,家賃と部屋の広さの関係性が次のようになっていたとします.

家賃と部屋の広さの関係

家賃と部屋の広さの関係

この場合,部屋が広くなるほど,家賃が高くなるという関係がみられ,直線を予測に用いるのが妥当にみえます.

直線式によるモデル化

直線式によるモデル化

そこで今回は直線をモデルとして採用して,Step1のモデルを以下のように定式化します.

\[y = wx + b\]

ここで\(w\)は傾き,\(b\)は切片とよばれるパラメータです(機械学習では,傾きを重み (weight) \(w\), 切片をバイアス (bias) \(b\) という記号で表現するのが一般的です).

単回帰分析では,このようにモデルを直線 \(y = wx + b\) と決めて,重み\(w\)とバイアス\(b\)をデータにうまくフィットするように調整していきます.

多くの機械学習ではこのようなパラメータで特徴付けられたモデルを使い,与えられたデータセットに適合するように最適なパラメータを求めることが目標となります.ここでデータセットは,入力変数である部屋の広さ \(x\) と,教師データとなる家賃 \(t\) の組からなるデータの集合です(本解説では,機械学習による予測値を \(y\) ,教師データとして与えるものを \(t\) と使い分けています).

データセットは \(\mathcal{D} = \{x_n, t_n\}_{n=1}^{N}\) として表されることもあります.ここで,添え字 \(n\) (\(n=1,2,\ldots,N\)) は \(n\) 番目の物件という意味であり,\(N\) は全体の物件数のことです.この \(N\)サンプル数とよばれています.

ここで,この後の計算を楽に進めるために,データの中心化というテクニックを紹介します.下図に示すように,部屋の広さと家賃は両方とも正の値であるため,左のグラフのような形になります.中心化では,平均を \(\boldsymbol{0}\)とした中央に配置するような変換の処理を施します.この中心化は多くのアルゴリズムで前処理として行うことが一般的です.厳密には前章で紹介した中心化込みのスケーリングがよく用いられます.

中心化処理

中心化処理

この処理を行う理由の一つとして,下図のように,データの中心化によってバイアス \(b\)\(0\)となり,\(y_{c} = wx_{c}\) のように,モデルをバイアス成分なしで表現することができるということが挙げられます,これによって,調整すべきパラメータを減らすことができます.

中心化後の直線式

中心化後の直線式

データの中心化は入出力の平均をデータの全体から引くことで実現されます.つまり,

\[\begin{split}\begin{aligned} x_{c} &= x - \bar{x} \\ t_{c} &= t - \bar{t} \end{aligned}\end{split}\]

となります.例えば,具体的な数値で見ると,下図の通りです. 中心化前後の数値比較

中心化後を示す添え字の \(c\) に関しては表現が冗長となるため,今後はこの添え字を省略し,データの中心化を事前に行っていることを前提とします.この時,モデルは

\[y = wx\]

となり,単回帰分析の目標は,データセット \(\mathcal{D} = \{x_n, t_n\}_{n=1}^{N}\) に基づいて,パラメータ \(w\)適切に調整することになります.

2.1.3. Step2. 目的関数を決める(単回帰分析)

1章で説明したように,教師あり学習では多くの場合,目的関数を設計し,その目的関数を最小化(または最大化)することでモデルの学習を行います.

今回は教師データと予測値が一致することが目標であり,それを表す目的関数として教師データと予測値の二乗誤差を使います.二乗誤差が\(0\)であるとき,またその時のみ\(t = y\) となり,完璧な予測を達成しているといえます.\(n\) 番目の物件に対する教師データ\(t_{n}\) と予測値\(y_{n}\)の二乗誤差は

\[(t_{n} - y_{n})^{2}\]

となります.これを全物件で考慮する必要があるため,最終的な目的関数はその総和をとり,

\[\begin{split}\begin{aligned} \mathcal{L}&=\left( t_{1}-y_{1}\right)^{2}+\left( t_{2}-y_{2}\right)^{2}+\ldots + (t_{N}-y_{N})^{2} \\ &=\sum^{N}_{n=1}\left( t_{n}-y_{n}\right)^{2}\\ \end{aligned}\end{split}\]

となります.また,Step1で決めたモデル

\[y_{n} = wx_{n}\]

を代入すると,目的関数は

\[\mathcal{L}=\sum^{N}_{n=1}\left( t_{n}-wx_{n}\right)^{2}\]

とパラメータを含んだ形式で表現することができます.このような関数を損失関数とよぶことを思い出してください.

2.1.4. Step3. 最適なパラメータを求める(単回帰分析)

最後は目的関数を最小化するようなパラメータを求めます.ここで,ある関数を最小化する点を求める方法として,微分が使えることを既に学んでいます.今回のような差の二乗の場合,微分して「傾き0」となる点が損失が\(0\)となる点です.目的関数の微分を求めると,次のようになります.

\[\begin{split}\begin{aligned} \dfrac{\partial }{\partial w} \mathcal{L} &= \dfrac{\partial}{\partial w} { \sum^{N}_{n=1} ( t_{n}-wx_{n})^{2} }\\ \end{aligned}\end{split}\]

ここで,微分は線形性の性質を持っており,特に和の微分は,微分の和であることを利用して次を得ます.

\[\dfrac{\partial}{\partial w} \mathcal{L}=\sum^{N}_{n=1}\dfrac {\partial }{\partial w}\left( t_{n}-wx_{n}\right)^{2}\]

ここで微分と総和 \(\sum\) の記号が入れ替わっています.次に,和の各項をみると,

\[\dfrac {\partial }{\partial w}\left( t_{n}-wx_{n}\right)^{2}\]

の部分は\(t_n - wx_n\)とその二乗の合成関数になっていることがわかります.\(u_{n} = t_{n} - wx_{n}\), \(f(u_{n}) = u_{n}^{2}\) とおくと,

\[\begin{split}\begin{aligned} \dfrac {\partial }{\partial w}\left( t_{n}-wx_{n}\right)^{2} &= \dfrac {\partial }{\partial w} f(u_{n}) \\ &= \dfrac {\partial u_{n}}{\partial w} \dfrac{\partial f(u_{n})}{\partial u_{n}} \\ &=-x_{n} \times 2 u_{n} \\ &= -2x_{n}( t_{n}-wx_{n} ) \end{aligned}\end{split}\]

が得られます.これより,

\[\begin{split}\begin{aligned} \dfrac{\partial }{\partial w} \mathcal{L} &=\sum^{N}_{n=1}\dfrac {\partial }{\partial w}\left( t_{n}-wx_{n}\right)^{2} \\&=-\sum^{N}_{n=1}2x_{n}\left( t_{n}-wx_{n}\right) \end{aligned}\end{split}\]

となります.この微分の値が0となるように\(w\)を求めていくと,

\[\begin{split}\begin{aligned} \dfrac {\partial }{\partial w} \mathcal{L} &=0\\ -2\sum^{N}_{n=1}x_{n}\left( t_{n}-wx_{n}\right) &=0\\ -2 \sum^{N}_{n=1}x_{n}t_{n} + 2\sum^{N}_{n=1}wx^{2}_{n}&=0\\ -2\sum^{N}_{n=1}x_{n}t_{n}+2w\sum^{N}_{n=1}x^{2}_{n}&=0\\ w\sum^{N}_{n=1}x^{2}_{n}&=\sum^{N}_{n=1}x_{n}t_{n}\\ \end{aligned}\end{split}\]

より,

\[\begin{aligned} w&=\dfrac {\displaystyle \sum^{N}_{n=1}x_{n}t_{n}}{\displaystyle \sum^{N}_{n=1}x^{2}_{n}} \end{aligned}\]

と求まりました.この求まったパラメータ \(w\) を確認すると,与えられたデータセット \(\mathcal{D} = \{x_n, t_n\}_{n=1}^{N}\) のみから決定できていることがわかります.

次に,例題にあげていた数値例でパラメータ \(w\) を求めてみましょう.まずは,データの中心化を行うために,

\[\begin{split}\begin{aligned} \bar{x} &= \dfrac{1}{3} (1 + 2 + 3) = 2 \\ \bar{t} &= \dfrac{1}{3}(2 + 3.9 + 6.1) = 4 \end{aligned}\end{split}\]

とそれぞれの平均を求め,各変数に対して前処理として,中心化の処理を施すと,

\[\begin{split}\begin{aligned} x_{1} &= 1 - 2 = -1 \\ x_{2} &= 2 -2 = 0 \\ x_{3} &= 3- 2 = 1\\ t_{1} &= 2 - 4 = -2\\ t_{2} &= 3.9 - 4 = -0.1\\ t_{3} &= 6.1 - 4 = 2.1 \end{aligned}\end{split}\]

となります.そして,中心化後の値を用いて,最適なパラメータ\(w\)を導出すると,

\[\begin{split}\begin{aligned} w &= \dfrac{\displaystyle \sum_{n=1}^{N}x_{n}t_{n}}{\displaystyle \sum_{n=1}^{N}x_{n}^{2}} \\ &= \dfrac{x_{1}t_{1} + x_{2}t_{2} + x_{3}t_{3}}{x_{1}^{2} + x_{2}^{2} + x_{3}^{2}} \\ &= \dfrac{-1 \times (-2) + 0 \times 0.1 + 1 \times 2.1}{(-1)^{2} + 0^2 + 1^2} \\ &= 2.05 \end{aligned}\end{split}\]

と求まりました.これで単回帰分析の学習が完了しました.この求まったパラメータを使用したモデルが学習済みモデルとなります.

続いて,このモデルを使って新しいサンプルに対する予測をしてみましょう.学習したモデルを使って新たな入力データについて予測値を計算する処理を 推論 とよびます.例えば,新しいサンプル \(x_{q}=1.5\) に対する予測値は次のように求まります,

\[\begin{split}\begin{aligned} y_{c} &= wx_{c} \\ y_{q} - \bar{t} &= w(x_{q}-\bar{x}) \\ \Rightarrow y_{q} &= w(x_{q}-\bar{x}) + \bar{t} \\ &= 2.05 \times (1.5 - 2) + 4 \\ &= 2.975 \end{aligned}\end{split}\]

モデルは中心化データを用いて学習を行ったので,実際の予測値は中心化したデータを元に戻すことを忘れないようにしましょう.

以上が,単回帰分析の一連の手順となります.

2.2. 重回帰分析

次に,多変数の入力変数を扱う重回帰分析を扱います.この重回帰分析を学ぶことで線形代数に関する知識が深まります.

重回帰分析は単回帰分析と同様に教師あり学習の一種であり,回帰を取り扱う手法です.問題設定は,ほとんど単回帰分析と同じですが,重回帰分析では入力変数の数が複数となります.つまり,複数の入力変数から出力変数を予測できるような機械学習アルゴリズムです.

2.2.1. 問題設定(重回帰分析)

ここでは単回帰分析の場合と同様に家賃を予測する問題を考え,家賃を出力変数 \(y\) とします.入力変数としては,単回帰分析では考慮しきれていなかった駅からの距離や犯罪発生率なども考慮していきます.例えば,部屋の広さ \(x_{1}\), 駅からの距離 \(x_{2}\), …, 犯罪発生率 \(x_{M}\) のように \(M\) 個の入力変数があるとします(\(M=1\)の場合,単回帰分析の問題に帰着されます).

単回帰分析と同様,以下の3ステップで学習していきます.

  • モデルを決める
  • 目的関数を決める
  • 最適なパラメータを求める

2.2.2. Step1. モデルを決める(重回帰分析)

単回帰分析のモデルは,

\[y = wx + b\]

であり,\(w\) を重み(weight),\(b\) をバイアス (bias) とよびました.重回帰分析では,この式を複数の入力変数へと拡張し,

\[y=w_{1}x_{1}+w_{2}x_{2}+\ldots +w_{M}x_{M}+b\]

のような線形結合の形で表します.この場合,各入力変数は線形に出力変数に影響を与えることを仮定しており,かなり単純なモデル化といえます.実際には,入力変数間に非線形な依存関係が存在する場合には,そのことを考慮してモデル化を行う必要があります.それについては今後説明していきます.

重回帰分析のモデルは総和の記号を使って整理すると,

\[y = \sum_{m=1}^{M} w_{m} x_{m} + b\]

のように書くことができます.さらにここで,\(x_0 = 1\)\(w_0 = b\)とおくと,

\[\begin{split}\begin{aligned} y&=w_{1}x_{1}+w_{2}x_{2}+\ldots +w_{M}x_{M}+b\\ &=w_{1}x_{1}+w_{2}x_{2}+\ldots +w_{M}x_{M}+w_{0} x_{0}\\ &=w_{0}x_{0}+w_{1}x_{1}+\ldots +w_{M}x_{M}\\ \end{aligned}\end{split}\]

のようにバイアス \(b\) を総和に包含することができます.そして,この式を整理していくと,

\[\begin{split}\begin{aligned} y&=w_{0}x_{0}+w_{1}x_{1}+\ldots +w_{M}x_{M}\\ &=\begin{bmatrix} w_{0} & w_{1} & \ldots & w_{M} \end{bmatrix}\begin{bmatrix} x_{0} \\ x_{1} \\ \vdots \\ x_{M} \end{bmatrix}\\ &=\boldsymbol{w}^{T}\boldsymbol{x} \end{aligned}\end{split}\]

のように,ベクトルの内積で表現することができます.また,今後取り扱う際には,\(\boldsymbol{x}\) が前に来ているほうが計算上便利であるため,

\[\begin{split}\begin{aligned} y&=w_{0}x_{0}+w_{1}x_{1}+\ldots +w_{M}x_{M}\\ &=\begin{bmatrix} x_{0} & x_{1} & \ldots & x_{M} \end{bmatrix}\begin{bmatrix} w_{0} \\ w_{1} \\ \vdots \\ w_{M} \end{bmatrix}\\ &=\boldsymbol{x}^{T}\boldsymbol{w} \end{aligned}\end{split}\]

として表現します.これが重回帰分析のモデルです.今回はパラメータとして \(M+1\) 個の重み \(\boldsymbol{w}\) を求めていきます.

2.2.3. Step2. 目的関数を決める(重回帰分析)

単回帰分析では,教師データ\(t\)と予測値\(y\)の二乗誤差が小さいほど良い予測であるとし,その総和を目的関数として定めました.重回帰分析でも,予測値\(y\)を求めるということは同じであるため,次のような同じ目的関数を使います.

\[\begin{aligned} \mathcal{L}&=\left( t_{1}-y_{1}\right)^{2}+\left( t_{2}-y_{2}\right)^{2}+\ldots + \left( t_{N}-y_{N}\right)^{2} \end{aligned}\]

このように,二乗誤差の総和を単回帰分析同様,目的関数として採用します.単回帰分析では,これを

\[\mathcal{L}=\sum^{N}_{n=1} ( t_{n} - y_{n})^{2}\]

のように,総和の記号を使ってまとめていましたが,

\[\begin{split}\begin{aligned} \mathcal{L}&=\left( t_{1}-y_{1}\right)^{2}+\left( t_{2}-y_{2}\right)^{2}+\ldots + \left( t_{N}-y_{N}\right)^{2}\\ &=\begin{bmatrix} t_{1} - y_{1} & t_{2}-y_{2} & \ldots & t_{N}-y_{N} \end{bmatrix} \begin{bmatrix} t_{1}-y_{1} \\ t_{2}-y_{2} \\ \vdots \\ t_{N}-y_{N} \end{bmatrix}\\ &=\left( \boldsymbol{t}-\boldsymbol{y}\right)^{T}\left( \boldsymbol{t}-\boldsymbol{y}\right) \end{aligned}\end{split}\]

のようにベクトルを使って表現することもできます.また,\(\boldsymbol{y}\) に関して,

\[\begin{split}\begin{aligned} \boldsymbol{y}=\begin{bmatrix} y_{1} \\ y_{2} \\ \vdots \\ y_{N} \end{bmatrix}=\begin{bmatrix} \boldsymbol{x}_{1}^{T}\boldsymbol{w} \\ \boldsymbol{x}_{2}^{T}\boldsymbol{w} \\ \vdots \\ \boldsymbol{x}_{N}^{T}\boldsymbol{w} \end{bmatrix} =\begin{bmatrix} \boldsymbol{x}_{1}^{T} \\ \boldsymbol{x}_{2}^{T} \\ \vdots \\ \boldsymbol{x}_{N}^{T} \end{bmatrix} \boldsymbol{w} \end{aligned}\end{split}\]

のように書くことができます.これを整理すると,

\[\begin{split}\begin{aligned} \boldsymbol{y}&= \begin{bmatrix} x_{10} & x_{11} & x_{12} & \ldots & x_{1M} \\ x_{20} & x_{21} & x_{22} & \ldots & x_{2M} \\ \vdots & \vdots & \vdots & \ddots \\ x_{N0} & x_{N1} & x_{N{2}} & \ldots & x_{NM} \end{bmatrix}\begin{bmatrix} w_{0} \\ w_{1} \\ w_{2} \\ \vdots \\ w_{M} \end{bmatrix}\\ &=\boldsymbol{X}\boldsymbol{w} \end{aligned}\end{split}\]

と表記できます.ここで,行(横)方向がサンプルを表しており,例えば各物件に対応します.列(縦)方向が入力変数を表しており,例えば,部屋の広さや駅からの距離などに対応します.もう少し具体的な数値で考えると,部屋の広さ \(= 50m^{2}\) ,駅からの距離 \(= 600 m\) ,犯罪発生率 \(= 2\)% のような \(n\) 番目の物件の場合,入力変数の数\(M=3\)であり,

\[\boldsymbol{x}_{n}^{T} = \begin{bmatrix} 1 & 50 & 600 & 0.02 \end{bmatrix}\]

のようにデータが行方向に格納されているイメージです.先頭の \(1\) はバイアスを包含する際に使用している \(x_{0}\) であることに注意してください.

2.2.4. Step3. パラメータを最適化する(重回帰分析)

それでは,Step2の目的関数を最小化する,モデルのパラメータ\(\boldsymbol{w}\)を求めていきましょう.

※ここでは,式変形を駆使しながら最適パラメータの解析的な解を求めていきますが,導出過程が少々複雑であり,導出結果は次節(§2.3)で示されているので,興味のある方以外はスキップして次節に進んでいただいて構いません.

まずは目的関数に関して,パラメータ \(\boldsymbol{w}\) で表現できるように式変形を行うと,

\[\begin{split}\begin{aligned} \mathcal{L}&=\left( \boldsymbol{t}-\boldsymbol{y}\right)^{T}\left( \boldsymbol{t}-\boldsymbol{y}\right) \\ &=\left( \boldsymbol{t}-\boldsymbol{X}\boldsymbol{w}\right)^{T}\left( \boldsymbol{t}-\boldsymbol{X}\boldsymbol{w}\right) \\ &= \left\{ \boldsymbol{t}^{T}-(\boldsymbol{X}\boldsymbol{w})^{T}\right\}\left( \boldsymbol{t}-\boldsymbol{X}\boldsymbol{w}\right) \\ &=\left( \boldsymbol{t}^{T}-\boldsymbol{w}^{T}\boldsymbol{X}^{T}\right)\left( \boldsymbol{t}-\boldsymbol{X}\boldsymbol{w}\right) \end{aligned}\end{split}\]

となります.ここで,転置の公式 \((\boldsymbol{A}\boldsymbol{B})^{T} = \boldsymbol{B}^{T}\boldsymbol{A}^{T}\) を使っていることに注意しましょう.さらに分配法則を使って展開を進めていくと,

\[\begin{split}\begin{aligned} \mathcal{L}&=\boldsymbol{t}^{T}\boldsymbol{t}-\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w}-\boldsymbol{w}^{T}\boldsymbol{X}^{T}\boldsymbol{t} + \boldsymbol{w}^{T}\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}\\ \end{aligned}\end{split}\]

となります.この目的関数に対しパラメータの\(w\)について偏微分をとりたいわけですが,その前にこの式はもう少し整理することができます.はじめに,

\[(1)^T = 1\]

というように,スカラーは転置しても変化しません.上式の中で出てくる \(\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w}\) はスカラーなので,

\[(\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w})^{T} = \boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w}\]

が成り立ちます.さらに,転置の公式 \((\boldsymbol{A}\boldsymbol{B}\boldsymbol{C})^T = \boldsymbol{C}^T\boldsymbol{B}^T\boldsymbol{A}^T\) より,

\[(\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w})^T = \boldsymbol{w}^{T} \boldsymbol{X}^{T} \boldsymbol{t}\]

も成り立ちます.これより,

\[(\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w})^{T} = \boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w} = \boldsymbol{w}^{T} \boldsymbol{X}^{T} \boldsymbol{t}\]

を導くことができます.目的関数を \(\mathcal{L}\) とおくと,上の式を利用して,

\[\begin{split}\begin{aligned} \mathcal{L}=\boldsymbol{t}^{T}\boldsymbol{t}-2\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w} + \boldsymbol{w}^{T}\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}\\ \end{aligned}\end{split}\]

とまとめることができます.ここで, \(\boldsymbol{w}\) に関する偏微分を行っていくため, \(\boldsymbol{w}\) 以外の定数項をまとめていくと,

\[\begin{split}\begin{aligned} \mathcal{L}&=\boldsymbol{t}^{T}\boldsymbol{t}-2\boldsymbol{t}^{T}\boldsymbol{X}\boldsymbol{w}+\boldsymbol{w}^{T}\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}\\ &=\boldsymbol{t}^{T}\boldsymbol{t}-2\left( \boldsymbol{X}^{T}\boldsymbol{t}\right)^{T} \boldsymbol{w}+\boldsymbol{w}^{T}\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w} \\ &= \gamma + \boldsymbol{\beta}^{T}\boldsymbol{w} + \boldsymbol{w}^{T}\boldsymbol{A}\boldsymbol{w} \end{aligned}\end{split}\]

のように,線形代数で学んだ \(\boldsymbol{w}\) に関する二次形式(二次関数)で表現することができました.ここで,\(\boldsymbol{A}= \boldsymbol{X}^{T}\boldsymbol{X}, \ \boldsymbol{\beta} =-2 \boldsymbol{X}^{T}\boldsymbol{t}, \ \gamma = \boldsymbol{t}^{T}\boldsymbol{t}\) であり,\(\boldsymbol{\beta}\) を転置の形式にした理由は,線形代数で学んだベクトルで微分の公式の形式に合わせるための工夫です.

それでは,目的関数を最小化することができるパラメータ \(\boldsymbol{w}\) の求め方を考えましょう.先述の通り,目的関数はパラメータ \(\boldsymbol{w}\)に関して二次関数です.例えば,

\[\begin{split}\begin{aligned} \boldsymbol{w} = \begin{bmatrix} w_{1} \\ w_{2} \end{bmatrix}, \boldsymbol{A}=\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix},\boldsymbol{\beta}=\begin{bmatrix} 1 \\ 2 \end{bmatrix}, \gamma = 1 \end{aligned}\end{split}\]

のように具体的な数値例で考えてみると,

\[\begin{split}\begin{aligned} \mathcal{L} & = \boldsymbol{w}^{T}\boldsymbol{A}\boldsymbol{w} + \boldsymbol{\beta}^{T}\boldsymbol{w} + \gamma \\ &= \begin{bmatrix} w_{1} & w_{2} \end{bmatrix}\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}\begin{bmatrix} w_{1} \\ w_{2} \end{bmatrix} +\begin{bmatrix} 1 & 2 \end{bmatrix} \begin{bmatrix} w_{1} \\ w_{2} \end{bmatrix} + 1 \\ &= \begin{bmatrix} w_{1} & w_{2} \end{bmatrix} \begin{bmatrix} w_{1} + 2w_{2} \\ 3w_{1} + 4w_{2} \end{bmatrix} + w_{1} + 2w_{2} + 1 \\ &=w_{1}\left( w_{1} + 2w_{2}\right) + w_{2}\left( 3w_{1} + 4w_{2}\right) + w_{1} + 2w_{2} + 1 \\ &=w^{2}_{1} + 5w_{1}w_{2} + 4w^{2}_{2} + w_{1} + 2w_{2}+1 \\ \end{aligned}\end{split}\]

となり,\(w_{1}, w_{2}\)に関してそれぞれまとめると,

\[\begin{split}\begin{aligned} \mathcal{L} &= w^{2}_{1} + \left( 5w_{2} + 1\right) w_{1} + \left( 4w^{2}_{2}+2w_{2}+1\right) \\ &=4w^{2}_{2} + \left(5w_{1} + 2\right) w_{2} + \left( w^{2}_{1} + w_{1} + 1\right) \end{aligned}\end{split}\]

のようにそれぞれの二次関数であることがわかります.

そして,二次関数であれば,下図のような形となります.

パラメータと目的関数の関係(2次元)

パラメータと目的関数の関係(2次元)

これを3次元でイメージすると,下図のようになります.

パラメータと目的関数の関係(3次元)

パラメータと目的関数の関係(3次元)

そして,目的関数である二乗誤差の総和が最小となる点では各変数で微分した時の傾きが0となります.

目的関数が最小となる点

目的関数が最小となる点

この例では,\(w_{1}\)\(w_{2}\) の2つのパラメータの場合で考えましたが,これは \(w_{0}\), \(w_{1}\), \(w_{2}\), \(\ldots\), \(w_{M}\) の場合でも同様に考えることができ,目的関数が最小となる点は

\[\begin{split}\begin{cases} \dfrac {\partial }{\partial w_{0}}\mathcal{L}=0\\ \dfrac {\partial }{\partial w_{1}}\mathcal{L}=0\\ \ \ \ \ \ \vdots \\ \dfrac {\partial }{\partial w_{M}}\mathcal{L}=0\\ \end{cases}\end{split}\]

となり,これをまとめると,

\[\begin{split}\begin{aligned} \begin{bmatrix} \dfrac {\partial}{\partial w_{0}} \mathcal{L} \\ \dfrac {\partial}{\partial w_{1}} \mathcal{L} \\ \vdots \\ \dfrac {\partial}{\partial w_{M}} \mathcal{L} \\ \end{bmatrix}&=\begin{bmatrix} 0 \\ 0 \\ \vdots \\ 0 \\ \end{bmatrix} \\ \Rightarrow \dfrac {\partial}{\partial \boldsymbol{w}} \mathcal{L} &= \boldsymbol{0} \\ \end{aligned}\end{split}\]

のようにベクトルでの微分として表されます.あとは,上式を満たすように \(\boldsymbol{w}\) を決めていきます.まずは\(\boldsymbol{w}\)を求めやすくなるように,代入と式変形を行います.(下記の計算にはベクトルでの微分をはじめとして,線形代数で学んだ内容を活用しているため,計算途中がわからなくなった場合には,線形代数のパートを確認しながら進めてみてください.)

\[\begin{split}\begin{aligned} \dfrac {\partial }{\partial \boldsymbol{w}}\mathcal{L} =\dfrac {\partial }{\partial \boldsymbol{w}}\left( \gamma + \boldsymbol{\beta}^{T}\boldsymbol{w} + \boldsymbol{w}^{T}\boldsymbol{A}\boldsymbol{w}\right) = \boldsymbol{0}\\ \dfrac {\partial }{\partial \boldsymbol{w}}\left( \gamma\right) +\dfrac {\partial }{\partial \boldsymbol{w}}\left( \boldsymbol{\beta}^{T}\boldsymbol{w}\right) +\dfrac {\partial }{\partial \boldsymbol{w}}\left( \boldsymbol{w}^{T}\boldsymbol{A}\boldsymbol{w}\right) =\boldsymbol{0}\\ \boldsymbol{0}+\boldsymbol{\beta}+\left( \boldsymbol{A}+\boldsymbol{A}^{T}\right) \boldsymbol{w} =\boldsymbol{0}\\ -2\boldsymbol{X}^{T}\boldsymbol{t}+\left\{ \boldsymbol{X}^{T}\boldsymbol{X} + \left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{T}\right\} \boldsymbol{w} =\boldsymbol{0}\\ -2\boldsymbol{X}^{T}\boldsymbol{t}+2\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}=\boldsymbol{0}\\ \boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}=\boldsymbol{X}^{T}\boldsymbol{t}\\ \end{aligned}\end{split}\]

ここで,\(\boldsymbol{X}^{T} \boldsymbol{X}\)に逆行列が存在すると仮定して,両辺に左側から \(\left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\) をかけると,

\[\begin{split}\begin{aligned} \left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{X} \boldsymbol{w} =\left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t} \\ \boldsymbol{I}\boldsymbol{w}=\left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t} \\ \boldsymbol{w}=\left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t} \end{aligned}\end{split}\]

となり,与えられたデータセット \(\boldsymbol{X}, \boldsymbol{t}\) から,最適なパラメータ \(\boldsymbol{w}\) が求まりました.ここで,\(\boldsymbol{I}\) は単位行列です.また,式変形の際には,

\[\boldsymbol{w} = \dfrac{\boldsymbol{X}^{T}\boldsymbol{t}}{\boldsymbol{X}^{T}\boldsymbol{X}}\]

のような分数が表れないように注意してください.これは行列の計算には割り算がないためです.そのため,逆行列を使って行列積のみで計算しています.

また,もうひとつよくある間違いとして,\(\boldsymbol{w}\)を求めるために以下のような式変形をしてしまう例が挙げられます.

\[\begin{split}\begin{aligned} \boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}&=\boldsymbol{X}^{T}\boldsymbol{t}\\ \left( \boldsymbol{X}^{T}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{X}\boldsymbol{w}&=\left( \boldsymbol{X}^{T}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t}\\ \boldsymbol{X}\boldsymbol{w}&=\boldsymbol{t}\\ \boldsymbol{X}^{-1}\boldsymbol{X}\boldsymbol{w}&=\boldsymbol{X}^{-1}\boldsymbol{t}\\ \boldsymbol{w}&=\boldsymbol{X}^{-1}\boldsymbol{t} \end{aligned}\end{split}\]

しかし,これは一般的には成立しません.その理由は,逆行列を持つための条件として,正方行列であることを満たしていないためです.一般的に,サンプル数 \(N\) と入力変数の数 \(M+1\) は等しくないため,\(\boldsymbol{X} \in \mathcal{R}^{N \times (M+1)}\) は正方行列ではなく,逆行列をもちません.それに対し, \(\boldsymbol{X}^{T} \boldsymbol{X}\)\(\boldsymbol{X}^{T}\boldsymbol{X} \in \mathcal{R}^{(M+1) \times (M+1)}\) となり,サンプル数 \(N\) に依存することなく,常に正方行列となります.(逆行列が求まるためにはもう少し厳密な条件がありますが,ここでは説明しません.)

推論の際は学習で得られたパラメータ \(\boldsymbol{w}\) を用いて,

\[y_{q} = \boldsymbol{w}^{T}\boldsymbol{x}_{q}\]

のように計算することで予測値が得られます.

2.3. NumPyによる実装

それでは重回帰分析を例に,Pythonで線形代数を用いた実装を行っていきましょう.PythonにはNumPyとよばれる線形代数を簡単に扱えるライブラリが存在し,広く利用されています.次の章で紹介するChainerの中でもNumPyは多用されており,ディープラーニングを学ぶための第一歩として,まずはNumPyの使い方を習得することが重要です.

Pythonの文法に関しては把握していることを前提に進めています.具体的には,変数(数値・文字列,リスト,タプル,辞書),制御構文(for,if),関数,クラスを理解している必要があります.

重回帰分析では,最終的に最適なパラメータ \(\boldsymbol{w}\)

\[\boldsymbol{w}=\left( \boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t}\]

で表されることが分かりました.この最適なパラメータを計算するために,以下の5つを扱います.

  • ベクトルの定義
  • 行列の定義
  • 転置
  • 行列積
  • 逆行列

具体的に,以下のようなデータセットが与えられているケースを想定してみましょう.この例では,データのサンプル数\(N\)\(4\)であり,入力データ\(X\)の変数の数は\(2\)です.そして\(t\)は教師データとなります.

\[\begin{split}\boldsymbol{X} = \begin{bmatrix} 1 & 2 & 3 \\ 1 & 2 & 5 \\ 1 & 3 & 4 \\ 1 & 5 & 9 \end{bmatrix}, \ \boldsymbol{t} = \begin{bmatrix} 1 \\ 5 \\ 6 \\ 8 \end{bmatrix}\end{split}\]

ここで\(\boldsymbol{X}\)パラメータ \(\boldsymbol{w}\) がバイアス \(\boldsymbol{b}\) を包含する 形式を想定しており,従って入力データ\(\boldsymbol{X}\)の1列目には\(1\)が格納されています.

それでは実装方法について見ていきましょう.まずは,NumPyの読み込みから始めます.

In [ ]:
import numpy as np

ベクトルの定義は以下のように行います.

In [ ]:
t = np.array([1, 5, 6, 8])

ベクトルを表示してみましょう.

In [ ]:
print(t)
[1 5 6 8]

行列の定義も行い,表示してみましょう.

In [ ]:
X = np.array([
    [1, 2, 3],
    [1, 2, 5],
    [1, 3, 4],
    [1, 5, 9]
])
In [ ]:
print(X)
[[1 2 3]
 [1 2 5]
 [1 3 4]
 [1 5 9]]

ここではnp.arrayという関数を用いて,PythonのリストからNumPyの多次元配列の形式(np.ndarray)への変換を行っています.

次に,Xの転置を行ってみましょう.np.ndarrayで定義されている場合,.Tをつけるだけで転置することができます.

In [ ]:
print(X.T)
[[1 1 1 1]
 [2 2 3 5]
 [3 5 4 9]]

縦と横が入れ替わっていることを確認できます.

行列積は以下のように np.dot によって実現できます.行列積を行う際には,一番目の行列の列数と,二番目の行列の行数が同じであることに注意して下さい.

In [ ]:
XX = np.dot(X.T, X)
In [ ]:
print(XX)
[[  4  12  21]
 [ 12  42  73]
 [ 21  73 131]]

ここからさらに,\(\boldsymbol{X}^{T}\boldsymbol{X}\)に対する逆行列,\(\left(\boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\)を計算します.逆行列を求めるには,np.linalg.inv を用います.

In [ ]:
XX_inv = np.linalg.inv(XX)
In [ ]:
print(XX_inv)
[[ 1.76530612 -0.39795918 -0.06122449]
 [-0.39795918  0.84693878 -0.40816327]
 [-0.06122449 -0.40816327  0.24489796]]

これで重回帰分析のために必要な演算が揃いました.

最適なパラメータ\(\left(\boldsymbol{X}^{T}\boldsymbol{X}\right)^{-1}\boldsymbol{X}^{T}\boldsymbol{t}\)を求めると,

In [ ]:
Xt = np.dot(X.T, t)
In [ ]:
print(Xt)
[ 20  70 124]
In [ ]:
w = np.dot(XX_inv, Xt)
In [ ]:
print(w)
[-0.14285714  0.71428571  0.57142857]

このようにパラメータ \(\boldsymbol{w}\) が求まりました.NumPyを使うことで,数式をそのままプログラム上で書くことができます.

2.4. Scikit-learnによる機械学習アルゴリズムの実行

重回帰分析であればNumPyで比較的容易に実装することができましたが,実践的に使用する機械学習アルゴリズムの多くは複雑であり,初学者が一から書くには難しい場合も少なくありません.そこで,PythonではScikit-learnとよばれる機械学習用のフレームワークが公開されており,初学者でも簡単に様々な機械学習アルゴリズムを扱うことができます.

ここでは重回帰分析の,Scikit-learnを用いた実装方法を紹介します.データセットは先程と同様に\(\boldsymbol{X}\)\(\boldsymbol{t}\)を使用しますが,Scikit-learnにおいては, パラメータ \(\boldsymbol{w}\) がバイアス \(\boldsymbol{b}\) を包含しない 形式を想定しており,入力データ\(\boldsymbol{X}\)の1列目から\(1\)を取り除くのが一般的です.従って,

\[\begin{split}\boldsymbol{X} = \begin{bmatrix} 2 & 3 \\ 2 & 5 \\ 3 & 4 \\ 5 & 9 \end{bmatrix}, \ \boldsymbol{t} = \begin{bmatrix} 1 \\ 5 \\ 6 \\ 8 \end{bmatrix}\end{split}\]

が与えられているとします.

2.4.1. Scikit-learn 基礎編

Scikit-learnはsklearnという名前で呼び出すことができます.

In [ ]:
import sklearn

重回帰分析を使用する場合は以下のように呼び出します.

In [ ]:
from sklearn.linear_model import LinearRegression

なお,使い方を調べる際には,公式のリファレンスに加えて,実際のコード例を見るのも有用です(例えば検索エンジンで「重回帰分析 Scikit-learn」のようなキーワードで検索すればたくさんのコード例が見つかります).

重回帰分析のアルゴリズムはクラスとして定義されており,実際のモデルを利用するにはインスタンス化する必要があります. インスタンス化はクラス名の後に()をつければ行えます.

In [ ]:
model = LinearRegression()

これだけで,重回帰分析を使用するための準備が完了です.このモデルを使って,パラメータの学習は以下のように行います.

In [ ]:
X = np.array([
    [2, 3],
    [2, 5],
    [3, 4],
    [5, 9]
])
t = np.array([1, 5, 6, 8])

model.fit(X, t)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

結果の検証は,次のように行います.

In [ ]:
model.score(X, t)
0.6923076923076923

回帰の場合は,以下の式で示される,決定係数とよばれる指標が自動的に計算されるようになっています.

\[R^{2} = 1 - \dfrac{\sum_{i}\left( t_{i} - y_{i} \right)^{2}}{\sum_{i}\left( t_{i} - \bar{t} \right)^{2}}\]

このように,Scikit-learnでは,簡単なインターフェースでやり取りができるようになっています.Scikit-learnの良い点は,最初にアルゴリズムを決めてしまえば,どのアルゴリズムでも,.fit()で学習,.score()で検証が行える点です.

また,アルゴリズムによって内容は多少異なりますが,パラメータもインスタンス変数として格納されているため,学習後に確認することができます.

In [ ]:
# パラメータw
model.coef_
array([0.71428571, 0.57142857])
In [ ]:
# バイアスb
model.intercept_
-0.14285714285714501

2.4.2. Scikit-learn 応用編

Scikit-learnは機械学習の実装を支援する多くの機能を兼ね備えています.本節では,サンプルデータセットの使用方法,及びデータセットの分割方法について紹介していきます.

2.4.2.1. サンプルデータセットの使用

まずはじめにサンプルデータセットの取り扱いを紹介します.Scikit-learnでは,幾つかのデータセットが提供されています.今回はその中から,米国ボストン市郊外における地域別の物件価格のデータセットを使用することにします.

このデータセットには\(506\)件のデータが登録されており,各サンプルには対象地域の平均物件価格と,それに紐づく情報として対象地域の平均的な物件情報(一戸あたりの部屋数,築年数,雇用施設からの距離など),人口統計情報(低所得者の割合,教師あたりの生徒数など),生活環境に関する情報(犯罪発生率など)などが含まれています.このデータセットを利用する目的は,物件や人口統計などの情報を入力変数として,出力変数である平均物件価格を予測するモデルを構築することです. 入力変数は全部で13種類あり,詳細は以下の通りです.

  • CRIM : 人口\(1\)人あたりの犯罪発生率
  • ZN : \(25,000\)平方フィート以上の住宅区画が占める割合
  • INDUS : 非小売業が占める面積の割合
  • CHAS : チャールズ川に関するダミー変数 (1 : 川沿い,0 : それ以外)
  • NOX : 窒素酸化物の濃度
  • RM : 住居あたりの平均部屋数
  • AGE : 1940年以前に建てられた物件の割合
  • DIS : 5つのボストン雇用施設からの重み付き距離
  • RAD : 都心部の幹線道路へのアクセス指数
  • TAX : $ \(10,000\)あたりの固定資産税の割合
  • PTRATIO : 教師1人あたりの生徒数
  • B : 黒人の比率を表す指数
  • LSTAT : 低所得者の割合

それでは, load_boston() 関数を実行して,データセットを読み込んでみましょう.

In [ ]:
from sklearn.datasets import load_boston
In [ ]:
boston = load_boston()

変数のbostonには辞書型で格納されており,変数の中身を見ながら入力データと出力データに対応するものを見つけていきます.今回はdataが入力であり,targetが出力に対応します.

In [ ]:
X = boston['data']
t = boston['target']
In [ ]:
print(X)
[[6.3200e-03 1.8000e+01 2.3100e+00 ... 1.5300e+01 3.9690e+02 4.9800e+00]
 [2.7310e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 3.9690e+02 9.1400e+00]
 [2.7290e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 3.9283e+02 4.0300e+00]
 ...
 [6.0760e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9690e+02 5.6400e+00]
 [1.0959e-01 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9345e+02 6.4800e+00]
 [4.7410e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9690e+02 7.8800e+00]]
In [ ]:
print(t)
[24.  21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 15.  18.9 21.7 20.4
 18.2 19.9 23.1 17.5 20.2 18.2 13.6 19.6 15.2 14.5 15.6 13.9 16.6 14.8
 18.4 21.  12.7 14.5 13.2 13.1 13.5 18.9 20.  21.  24.7 30.8 34.9 26.6
 25.3 24.7 21.2 19.3 20.  16.6 14.4 19.4 19.7 20.5 25.  23.4 18.9 35.4
 24.7 31.6 23.3 19.6 18.7 16.  22.2 25.  33.  23.5 19.4 22.  17.4 20.9
 24.2 21.7 22.8 23.4 24.1 21.4 20.  20.8 21.2 20.3 28.  23.9 24.8 22.9
 23.9 26.6 22.5 22.2 23.6 28.7 22.6 22.  22.9 25.  20.6 28.4 21.4 38.7
 43.8 33.2 27.5 26.5 18.6 19.3 20.1 19.5 19.5 20.4 19.8 19.4 21.7 22.8
 18.8 18.7 18.5 18.3 21.2 19.2 20.4 19.3 22.  20.3 20.5 17.3 18.8 21.4
 15.7 16.2 18.  14.3 19.2 19.6 23.  18.4 15.6 18.1 17.4 17.1 13.3 17.8
 14.  14.4 13.4 15.6 11.8 13.8 15.6 14.6 17.8 15.4 21.5 19.6 15.3 19.4
 17.  15.6 13.1 41.3 24.3 23.3 27.  50.  50.  50.  22.7 25.  50.  23.8
 23.8 22.3 17.4 19.1 23.1 23.6 22.6 29.4 23.2 24.6 29.9 37.2 39.8 36.2
 37.9 32.5 26.4 29.6 50.  32.  29.8 34.9 37.  30.5 36.4 31.1 29.1 50.
 33.3 30.3 34.6 34.9 32.9 24.1 42.3 48.5 50.  22.6 24.4 22.5 24.4 20.
 21.7 19.3 22.4 28.1 23.7 25.  23.3 28.7 21.5 23.  26.7 21.7 27.5 30.1
 44.8 50.  37.6 31.6 46.7 31.5 24.3 31.7 41.7 48.3 29.  24.  25.1 31.5
 23.7 23.3 22.  20.1 22.2 23.7 17.6 18.5 24.3 20.5 24.5 26.2 24.4 24.8
 29.6 42.8 21.9 20.9 44.  50.  36.  30.1 33.8 43.1 48.8 31.  36.5 22.8
 30.7 50.  43.5 20.7 21.1 25.2 24.4 35.2 32.4 32.  33.2 33.1 29.1 35.1
 45.4 35.4 46.  50.  32.2 22.  20.1 23.2 22.3 24.8 28.5 37.3 27.9 23.9
 21.7 28.6 27.1 20.3 22.5 29.  24.8 22.  26.4 33.1 36.1 28.4 33.4 28.2
 22.8 20.3 16.1 22.1 19.4 21.6 23.8 16.2 17.8 19.8 23.1 21.  23.8 23.1
 20.4 18.5 25.  24.6 23.  22.2 19.3 22.6 19.8 17.1 19.4 22.2 20.7 21.1
 19.5 18.5 20.6 19.  18.7 32.7 16.5 23.9 31.2 17.5 17.2 23.1 24.5 26.6
 22.9 24.1 18.6 30.1 18.2 20.6 17.8 21.7 22.7 22.6 25.  19.9 20.8 16.8
 21.9 27.5 21.9 23.1 50.  50.  50.  50.  50.  13.8 13.8 15.  13.9 13.3
 13.1 10.2 10.4 10.9 11.3 12.3  8.8  7.2 10.5  7.4 10.2 11.5 15.1 23.2
  9.7 13.8 12.7 13.1 12.5  8.5  5.   6.3  5.6  7.2 12.1  8.3  8.5  5.
 11.9 27.9 17.2 27.5 15.  17.2 17.9 16.3  7.   7.2  7.5 10.4  8.8  8.4
 16.7 14.2 20.8 13.4 11.7  8.3 10.2 10.9 11.   9.5 14.5 14.1 16.1 14.3
 11.7 13.4  9.6  8.7  8.4 12.8 10.5 17.1 18.4 15.4 10.8 11.8 14.9 12.6
 14.1 13.  13.4 15.2 16.1 17.8 14.9 14.1 12.7 13.5 14.9 20.  16.4 17.7
 19.5 20.2 21.4 19.9 19.  19.1 19.1 20.1 19.9 19.6 23.2 29.8 13.8 13.3
 16.7 12.  14.6 21.4 23.  23.7 25.  21.8 20.6 21.2 19.1 20.6 15.2  7.
  8.1 13.6 20.1 21.8 24.5 23.1 19.7 18.3 21.2 17.5 16.8 22.4 20.6 23.9
 22.  11.9]

NumPyの形式で入力データと教師データが格納されており,.shapeを使うことで行と列の数を確認できます.

In [ ]:
X.shape
(506, 13)
In [ ]:
t.shape
(506,)

入力データの配列\(X\)には,\(506\)件分のデータが格納されています.各サンプルは\(13\)次元ベクトルとして表現されており,これはそれぞれ\(13\)種類の入力変数を表しています.教師データ\(t\)には,入力変数に対応する出力変数として,平均物件価格のスカラー値が格納されています.

2.4.2.2. データセットの分割

つぎに,この学習データを 訓練データテストデータ に分割する方法をご紹介します.もし,学習の時に使ったデータを使ってモデルの性能を評価した場合,学習データの性能が高くても学習中に見たことが無い(同じような分布からとられた)未知のデータはうまくいかない場合があります.これを 過学習 とよびます.機械学習ではこれを防ぐために学習データと別に性能を評価するテストデータを分けて評価します.このように分割して検証することを ホールドアウト法 とよびます.

Scikit-learnでは訓練用とテスト用を分割する機能が用意されています.

In [ ]:
from sklearn.model_selection import train_test_split
In [ ]:
X_train, X_test, t_train, t_test = train_test_split(X, t, test_size=0.3, random_state=0)
In [ ]:
X_train.shape
(354, 13)
In [ ]:
X_test.shape
(152, 13)

train_test_split() 関数の引数test_sizeは検証用に使うデータの比率であり,\(0.3\)と指定すると全体の\(30\)%がテストデータとなります.また,random_stateは乱数のシードであり,固定のシード値を与えると,分割の再現性を確保することができます.なぜ乱数が登場するかというと,前から\(70\)%を訓練用,残りをテスト用とするのではなく,全体からランダムに選択した\(70\)%を訓練用,残り\(30\)%をテスト用と選択しているためです.

それでは,訓練データを用いて学習を行います.

In [ ]:
model = LinearRegression()
In [ ]:
model.fit(X_train, t_train)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

検証を行う場合は,訓練データとテストデータの両方に対してチェックしておくと良いでしょう.

In [ ]:
# 訓練データ
model.score(X_train, t_train)
0.7644563391821222
In [ ]:
# テストデータ
model.score(X_test, t_test)
0.673528086534723

テストデータだけでなく,訓練データでも検証することで学習に失敗している場合の問題を切り分けることができます.

モデルが訓練データに対して良い精度で予測できない状態をアンダーフィッティングといいます.アンダーフィッティングが起きている場合,現状の機械学習アルゴリズムでうまくデータの特徴を捉えられていないと考えられ,アルゴリズムを変更したり,入力データの特徴をより適切に表現できるような変換を考えたりするなどして改善を試みます.逆にオーバーフィッティング(過学習)の場合,アルゴリズムでデータの特徴をある程度捉えられていることは確認できているので,モデルが過学習しないように対策していきます.代表的な方法として,ハイパーパラメータとよばれる各アルゴリズムのパラメータ学習に使われるパラメータ値を調整していくことで解決できる場合があります.このように,望ましい結果が得られない中にも,それぞれの状況を把握することで次に打つべき対策が変わってくるため,訓練データとテストデータの両方に対する検証を行うことは重要であることが分かります.

また,Scikit-learnでは,スケーリングも行うことができます.例えば,平均0,標準偏差1に変換するデータ正規化を行う場合の手順は以下の通りです.

In [ ]:
from sklearn.preprocessing import StandardScaler
In [ ]:
# インスタンス化
scaler = StandardScaler()

訓練データを用いて,平均と分散を計算します.

In [ ]:
# 平均と分散を計算
scaler.fit(X_train)
StandardScaler(copy=True, with_mean=True, with_std=True)

計算された平均,分散を用いて,訓練データ及びテストデータをスケーリングします.

In [ ]:
# 変換
X_train_s = scaler.transform(X_train)
X_test_s  = scaler.transform(X_test)

テストデータをスケーリングする際にも,訓練データの平均・分散を利用していることに注意しましょう.テストデータはモデルにとっては未知のデータセットであるため,訓練データとテストデータを合わせた全データの平均・分散を利用すると,それは本来知りえないテストデータの情報をモデルに与えてしまうことになります.そこで,モデルが利用可能な訓練データのみを用いてスケーリングが行われます.

訓練データとテストデータでは平均・分散が異なるため,訓練データの平均・分散でスケーリングされたテストデータについては,その平均が\(0\),分散が\(1\)になるとは限らないことに注意してください.

In [ ]:
print(X_train_s)
[[-0.20416267 -0.49997924  1.54801583 ...  1.2272573   0.42454294
   3.10807269]
 [-0.38584317  0.34677427 -0.58974728 ...  0.05696346  0.40185312
  -0.66643035]
 [-0.33266283 -0.49997924  1.54801583 ...  1.2272573   0.39846135
   0.63936662]
 ...
 [-0.38147768 -0.49997924 -0.15303077 ... -0.30312696  0.39659002
  -0.30284441]
 [-0.3720831  -0.49997924 -0.59690657 ... -0.25811566  0.37588849
   0.89967717]
 [-0.38289844 -0.49997924 -1.00641779 ... -0.84326258  0.42454294
   0.31822262]]
In [ ]:
print(X_test_s)
[[-0.39152624 -0.49997924 -1.12239824 ... -0.70822867  0.17086147
  -0.72160487]
 [ 0.70825498 -0.49997924  1.00534187 ...  0.77714428  0.0648977
  -0.41177872]
 [-0.38588517 -0.49997924  0.4025299  ... -0.93328518  0.38758427
  -0.27454978]
 ...
 [ 1.6177735  -0.49997924  1.00534187 ...  0.77714428  0.42454294
   2.59876943]
 [-0.34043865 -0.49997924 -0.1687812  ... -0.03305915  0.42454294
  -1.11772962]
 [-0.39601293 -0.49997924 -1.27417512 ...  0.10197476  0.39202867
  -1.02294263]]

この他,Scikit-learnはロジスティック回帰やサポートベクターマシン,ランダムフォレストなど様々な機械学習アルゴリズムをサポートしています.

これらについても,重回帰分析と同様にモデルをインスタンス化し,学習データを引数として.fit()関数で学習し,.score()関数で評価できるようになっています.

より詳しく知りたい方はScikit-learnサイトや解説サイトなどを参照してください.