2. 機械学習ライブラリの基礎¶
本章では,基礎的な機械学習手法として代表的な単回帰分析と重回帰分析の仕組みを、数式を用いて説明します. ここで単回帰分析と重回帰分析を紹介することには 2 つの理由があります. 1 つ目は,回帰分析と重回帰分析の数学がニューラルネットワーク含めたディープラーニングの数学の基礎となるためです. 2 つ目は,単回帰分析のアルゴリズムを通して微分,重回帰分析のアルゴリズムを通して線形代数に関する理解を深めることができるためです.
2.1. 単回帰分析¶
まずはじめに,単回帰分析について説明します. 機械学習手法は,教師あり学習 (supervised learning),教師なし学習 (unsupervised learning),強化学習 (reinforcement learning)に大別され,単回帰分析は教師あり学習に含まれます.
教師あり学習の中でも典型的な問題設定は 2 つに大別されます. 与えられた入力変数から,\(10\) や \(0.1\) といった実数値を予測する回帰 (regression)と、「赤ワイン」,「白ワイン」といったカテゴリを予測する分類 (classification)の 2 つです.
単回帰分析は回帰を行うための手法であり,1 つの入力変数から 1 つの出力変数を予測します. それに対し,重回帰分析は,複数の入力変数から 1 つの出力変数を予測します. この両手法は教師あり学習であるため,訓練の際には、入力変数 \(x\) と目的変数 \(t\) がペアで準備されている必要があります.
2.1.1. 問題設定(単回帰分析)¶
まず,データに含まれる情報の中から何を利用し,何を予測させるかを決めます.
ここでは例として,家賃を予測する問題を考えることにします. 従って,家賃が 出力変数 \(y\) となります.
次に, 入力変数 として何を採用するかを考えます. 家賃の予測には,部屋の広さ,駅からの距離,犯罪発生率などを考慮する必要があると思われます. ここでは部屋の広さを入力変数 \(x\) として採用することにします. 複数の入力変数の候補がある場合に,それらを同時に扱う方法は,次の重回帰分析の説明の際に紹介します.
多くの機械学習手法は,大きく分けて次の3ステップで構成されています.
- Step1: モデルを決める
- Step2: 目的関数を決める
- Step3: 最適なパラメータを求める
上記の3ステップについて,順に説明していきます.
2.1.2. Step1. モデルを決める(単回帰分析)¶
まずはじめに、入力変数 x と出力変数 y との関係をどのように定式化するかを決定します. この定式化したものをモデルもしくは数理モデルと呼びます.
どのように定式化すれば,家賃をうまく予測することができるのでしょうか. このモデル設計は現在は人手で行うのが一般的であり,機械が自動的に決めてくれるわけではありません(ただし最近ではAutoMLなどモデルを自動決定するための研究も進展してきています).
例えば,家賃と部屋の広さの組で表されるデータを 3 つ集め,「家賃」を y 軸に,「部屋の広さ」を x 軸にとってそれらをプロットしたとき,次のようになっていたとします.
この場合,部屋が広くなるほど,家賃が高くなるという関係が予想されます. また,この 2 変数間の関係性は直線によって表現を行うことができそうだと考えられます. そこで、2 つのパラメータ \(w\) と \(b\) によって特徴づけられる直線の方程式
によって,部屋の広さと家賃の関係を表すことを考えます. ここで,\(w\) は重み (weight),\(b\) はバイアス (bias)の頭文字を採用しています.
単回帰分析では,このようにモデルとして直線 \(f(x; w, b) = wx + b\) を用います. そして,2 つのパラメータ \(w\) と \(b\) を,直線がデータによくフィットするように調整します.
パラメータで特徴づけられたモデルを用いる場合,与えられたデータセットに適合するように最適なパラメータを求めることが目標となります. 今回はデータセットとして部屋の広さ \(x\) と家賃 \(t\) の組からなるデータの集合を用います. 全部で \(N\) 個のデータがあり,\(n\) 番目のデータが \((x^{(n)}, t^{(n)})\) と表されるとき,データセットは
と表すことができます. これを用いて、新しい \(x\) を入力すると,それに対応する \(t\) を予測するモデルを訓練します.
ここで,データセット中の入力値とのことをデータ点(datum)ということがあることに注意してください. データ点とは,具体的には上の説明で登場した \(\mathcal{D}\) 中の各 \((x^{(1)},t^{(1)})\) などのことです.
ここで,この後の計算を楽に進めるために,データの中心化というテクニックを紹介します. 部屋の広さと家賃は両方とも正の値であるため,各データ点をいくつかプロットすると,下図の左のグラフのようになります. 中心化では,各次元の平均が \(\boldsymbol{0}\) となるよう全てのデータを同量平行移動します. 中心化はしばしば前処理として採用されます. 厳密には前章で紹介したスケーリング方法の一つである標準化(正規化)がよく用いられます.
この処理を行うと,下図のように,バイアス \(b\) を \(0\) とおけるため,\(f_c(x; w) = wx_{c}\) のように,モデルをバイアス成分なしで表現することができるようになります. これによって,調整すべきパラメータを減らすことができます.
データの中心化は入出力の平均をデータの全体から引くことで実現されます. つまり,
という変換を全ての \(n\) について行います.
例えば,具体的な数値で見ると,下の表のようになります.
n | \(x^{(n)}\) | \(\bar{x}\) | \(x^{(n)}_c\) |
---|---|---|---|
1 | 1 | 3 | -2 |
2 | 3 | 3 | 0 |
3 | 5 | 3 | 2 |
中心化後を示す添え字の \(c\) に関しては表現が冗長となるため,今後はこの添え字を省略し,データの中心化を事前に行っていることを前提とします. この時,モデルは
となり,単回帰分析の目標は,データセット \(\mathcal{D} = \{ x^{(n)}, t^{(n)} \}_{n=1}^{N}\) に基づいて,パラメータ \(w\) を適切に調整することになります.
2.1.3. Step2. 目的関数を決める(単回帰分析)¶
1章で説明したように,教師あり学習では多くの場合,目的関数を設計し,その目的関数を最小化(または最大化)することでモデルの訓練を行います.
今回は教師データと予測値が一致することが目標であり,乖離度を表す最小化すべき目的関数として教師データと予測値の二乗誤差を使います. ここで,モデルの出力が予測値となります.すなわち,\(y = f(x; w)\) です. 二乗誤差が \(0\) であれば \(t = y\) となり予測が完全に教師データと一致していることを意味します. \(n\) 番目の物件に対する教師データ \(t^{(n)}\) と予測値 \(y^{(n)} = f(x^{(n)}; w)\) の二乗誤差は
です. 特定の物件についてだけ考慮するのではなく,データセット中の全ての物件の情報を考慮してモデルの訓練を行うために,上式で計算される各データ点における二乗誤差を全物件に対してそれぞれ計算して和をとったものを目的関数とします. すなわち,
です. また,Step1で決めたモデル
をこれに代入すると,目的関数は
とパラメータを含んだ形式で表現することができます.このような関数を損失関数とよぶことを思い出してください.
2.1.4. Step3. 最適なパラメータを求める(単回帰分析)¶
次に目的関数を最小にするようなパラメータを求めます. 今回用いた目的関数は二次関数であるため,微分して求まる導関数を 0 とおいてそのときの x を求めれば,最小値を取る時の x が分かります. すなわち,目的関数の「傾きが0」となる点が値 \(0\) をとる点です.
それでは,目的関数を微分しましょう.
微分という操作は線形性を持っているため,和の微分は,微分の和と等しくなります. これを利用して次を得ます.
パラメータ \(w\) による微分を表す \(\dfrac {\partial }{\partial w}\) と総和を表す \(\sum\) が入れ替わっています. 次に和の各項を見ます.
\(t^{(n)} - wx^{(n)}\) と \((\cdot)^2\) の合成関数になっています.
\(u^{(n)} = t^{(n)} - wx^{(n)}\), \(g(u^{(n)}; w) = (u^{(n)})^2\) とおくと,
が得られます.これより,
となります.この微分の値を 0 とおいて \(w\) について解くと,
より,
と求まります. この右辺に着目すると,与えられたデータセット \(\mathcal{D} = \{x^{(n)}, t^{(n)}\}_{n=1}^{N}\) のみから決定されていることがわかります.
それでは,以下の数値例を使って実際にパラメータ \(w\) を求めてみましょう.
n | \(x^{(n)}\) | \(t^{(n)}\) |
---|---|---|
1 | 1 | 2 |
2 | 2 | 3.9 |
3 | 3 | 6.1 |
まずは,データの中心化を行うために,平均を求めます.
各データ点からそれぞれの値を引きます.
それでは,中心化後の値を用いて,最適なパラメータ\(w\) を計算します.
これで単回帰分析の学習が完了しました. この求まったパラメータを \(\hat{w}\) とすると,これを使用したモデル \(f(x; \hat{w})\) が学習済みモデルとなります.
続いて,このモデルを使って新しいサンプルに対する予測をしてみましょう. 学習したモデルを使って新たな入力データについて予測値を計算する処理を推論 (inference)とよびます. 例えば,新しいサンプル \(x^{(q)}=1.5\) に対する予測値は次のように求まります,
モデルは中心化データを用いて学習を行ったので,推論の際にも入力値・予測値それぞれに同様の操作を行う必要があることに注意しましょう.
以上が,単回帰分析の一連の手順となります.
2.2. 重回帰分析¶
次に,多変数の入力変数を扱う重回帰分析を扱います. この重回帰分析を学ぶことで線形代数に関する知識が深まります.
重回帰分析は単回帰分析と同様に教師あり学習の一種であり,回帰を行う手法です. 問題設定はほとんど単回帰分析と同じですが,重回帰分析では入力変数の数が複数となります. つまり,複数の入力変数から出力変数を予測する機械学習手法の一つです.
2.2.1. 問題設定(重回帰分析)¶
ここでは単回帰分析の場合と同様に家賃を予測する問題を考え,家賃を出力変数 \(y\) とします. 入力変数としては,単回帰分析では考慮しきれていなかった駅からの距離や犯罪発生率なども同時に考慮します. 例えば,部屋の広さ \(x_{1}\), 駅からの距離 \(x_{2}\), ..., 犯罪発生率 \(x_{M}\) のように \(M\) 個の入力変数があるとします(\(M=1\)の場合,単回帰分析の問題に帰着されます).
単回帰分析と同様,以下の3ステップに従います.
- Step1: モデルを決める
- Step2: 目的関数を決める
- Step3: 最適なパラメータを求める
2.2.2. Step1. モデルを決める(重回帰分析)¶
単回帰分析のモデルは,
であり,\(w\) を重み(weight),\(b\) をバイアス (bias) とよびました. 重回帰分析では,この式を複数の入力変数へと拡張し,
のような線形結合の形で表します. ここで,太字の \({\bf x}, {\bf w}\) はそれぞれベクトルを表し,それぞれの \(m\) 番目の要素が \(w_m, x_m\) で表されています.
ここでは,各入力変数は出力変数に線形に影響を与えることが仮定されています. 一方,もし入力変数間に非線形な依存関係が想定される場合には,そのことを考慮したモデル化を行う必要があります. それについては後述します.
重回帰分析のモデルは総和の記号を使って整理すると,
のように書くことができます.さらにここで,\(x_0 = 1\),\(w_0 = b\)とおくと,
のようにバイアス \(b\) を総和記号の中に含めることができます. \(w_0\) を含む \({\bf w}\) を改めて \({\bf w}\),最初の要素 \(x_0\) として \(1\) を付け加えた入力ベクトルを改めて \({\bf x}\) と定義しなおすと,このモデルはベクトルを用いて
と書けます. これで,ベクトルの内積を用いて表現することができました.
これで重回帰分析を行うためのモデルが決定できました. このモデルはパラメータとして \(M+1\) 個の重み \({\bf w}\) を持っています.
2.2.3. Step2. 目的関数を決める(重回帰分析)¶
単回帰分析では,目標値 \(t\) と予測値 \(y\) の二乗誤差が小さいほど良い予測であるとし,その総和を目的関数にしました. 重回帰分析でも,そのような予測値 \(y\) を求めるというのは同じであるため,同じ目的関数を使います.
ここで,\(n\) 個目の入力値 \({\bf x}^{(n)}\) に対する予測値 \(f({\bf x}^{(n)}; {\bf w})\) を \(y^{(n)}\),目標値を \(t^{(n)}\) とおくと,目的関数は
となります. 単回帰分析では,これを総和記号 \(\sum\) を用いて表すことができましたが,これは以下のようにベクトル同士の内積を用いて表すこともできます.
また,\({\bf y}\) は
のように書くことができます. ここで,\({\bf x}^{(n)}\) の \(m\) 番目の要素を \(x_{nm}\) と書くことにすると,上式はさらに
と表せます. \({\bf X}\) の各行が各サンプルを表しており,各列が入力変数を表しています. つまり,各サンプルごとに \(M\) 個の入力変数(\(x_{n0}\) は常に \(1\))を持ちます.
具体例を挙げてみます. 例えば,\(m=1\) が部屋の広さ,\(m=2\) が駅からの距離,\(m=3\) が犯罪発生率に対応する入力変数だとして,\(n\) 個目のデータ点が部屋の広さ \(= 50m^{2}\) ,駅からの距離 \(= 600 m\) ,犯罪発生率 \(= 2\)% であるような物件を表している場合,これは
というベクトルになります. 今,3 つの入力変数を考えているので,\(M=3\) ですが,入力ベクトル \({\bf x}_n\) の1つ目の要素 \(x_{n1}\) はバイアス \(w_0\) に対応する要素で常に値は \(1\) となることに注意してください.
2.2.4. Step3. パラメータを最適化する(重回帰分析)¶
それでは,目的関数を最小化するパラメータ \({\bf w}\)を求めてみましょう.
※ここでは,式変形を駆使しながら最適パラメータの解析的な解を求めていきますが,導出過程が少々複雑になります.導出結果は次節(§2.3)で示されているので,導出に興味のある方以外は本節はスキップしていただいて構いません.
まずは目的関数をパラメータ \({\bf w}\) を用いて表し直すと
となります. ここで,転置の公式 \(({\bf A}{\bf B})^{\rm T} = {\bf B}^{\rm T}{\bf A}^{\rm T}\) を使っていることに注意しましょう. さらに分配法則を使って展開すると,
となります. この目的関数に対しパラメータの \(w\) についての偏微分を計算します. その前にこの式はもう少し整理することができます. はじめに,
のようにスカラは転置しても変化しません. 上式の中で出てくる \({\bf t}^{\rm T}{\bf X}{\bf w}\) はスカラなので,
が成り立ちます. さらに,転置の公式 \(({\bf A}{\bf B}{\bf C})^{\rm T} = {\bf C}^{\rm T}{\bf B}^{\rm T}{\bf A}^{\rm T}\) より,
も成り立ちます.これより,
を導くことができます. 目的関数を \(\mathcal{L}\) とおくと,上の式を利用して,
とまとめることができます.ここで, \({\bf w}\) について偏微分を行いやすくするため, \({\bf w}\) 以外の定数項をまとめると,
となります. 線形代数の章で学んだような \({\bf w}\) に関する二次形式(二次関数)で表現することができました. ここで,\({\bf A}= {\bf X}^{\rm T}{\bf X}, \ \boldsymbol{\beta} =-2 {\bf X}^{\rm T}{\bf t}, \ \gamma = {\bf t}^{\rm T}{\bf t}\) です. また,\(\boldsymbol{\beta}\) を転置の形式にした理由は,線形代数の章で学んだベクトルで微分するための公式集にある形式に合わせるためです.
それでは,この目的関数を最小化するパラメータ \({\bf w}\) の求め方を考えましょう. 前述の通り,目的関数はパラメータ \({\bf w}\) に関しては二次関数になっています. 例えば,
のとき,
となります. さらに \(w_{1}, w_{2}\) に関してそれぞれまとめると,
となり,それぞれの二次関数であることがわかります.
そして,二次関数であれば,下図のような形となります.
これを3次元でイメージすると,下図のようになります.
そして,目的関数である二乗誤差の総和が最小となる点では各変数で微分した時の傾きが0となります.
この例では,\(w_{1}\) と \(w_{2}\) の2つのパラメータの場合で考えましたが,これは \(w_{0}\), \(w_{1}\), \(w_{2}\), \(\ldots\), \(w_{M}\) の場合でも同様に考えることができ,目的関数が最小となる点は
となり,これをまとめると,
のようにベクトルでの微分として表されます. あとは,上式を満たす \({\bf w}\) を決めます. まずは \({\bf w}\) が求めやすくなるように,代入と式変形を行います. (下記の計算ではベクトルでの微分をはじめとして線形代数の章で学んだ内容を活用しているため,計算途中がわからなくなった場合には,線形代数の章を再度確認しながら進めてください.)
ここで,\({\bf X}^{\rm T} {\bf X}\)に逆行列が存在すると仮定して,両辺に左側から \(\left( {\bf X}^{\rm T}{\bf X}\right)^{-1}\) をかけると,
となり,与えられたデータセット \({\bf X}, {\bf t}\) から,最適なパラメータ \({\bf w}\) が求まりました.ここで,\({\bf I}\) は単位行列です.また,式変形の際には,
のような分数が表れないように注意してください.これは行列の計算には割り算が定義されていないためです. そのため,逆行列を使って行列積のみで計算しています.
また,もうひとつよくある間違いとして,\({\bf w}\) を求めるために以下のような式変形をしてしまう例が挙げられます.
しかし,これは一般には成立しません. その理由は,逆行列を持つための条件の一つである正方行列であることを \({\bf X}\) が常に満たすとは限らないためです. サンプル数 \(N\) と入力変数の数 \(M+1\) が等しくない場合,\({\bf X} \in \mathcal{R}^{N \times (M+1)}\) は正方行列ではなく,逆行列をもちません. 一方,\({\bf X}^{\rm T} {\bf X}\) は \({\bf X}^{\rm T}{\bf X} \in \mathcal{R}^{(M+1) \times (M+1)}\) であり,サンプル数 \(N\) に依存することなく,常に正方行列となることに注目してください.(逆行列が求まるためにはもう少し厳密な条件がありますが,ここでは説明しません.)
推論の際は学習で得られたパラメータを \(\hat{\bf w}\) として
のように計算することで予測値が得られます.
で表されることが分かりました.この最適なパラメータを計算するために,以下の5つを扱います.
- ベクトルの定義
- 行列の定義
- 転置
- 行列積
- 逆行列
具体的に,以下のようなデータセットが与えられているケースを想定してみましょう.この例では,データのサンプル数\(N\)は\(4\)であり,入力データ\(X\)の変数の数は\(2\)です.そして\(t\)は教師データとなります.
ここで\(\boldsymbol{X}\)は パラメータ \(\boldsymbol{w}\) がバイアス \(\boldsymbol{b}\) を包含する 形式を想定しており,従って入力データ\(\boldsymbol{X}\)の1列目には\(1\)が格納されています.
それでは実装方法について見ていきましょう.まずは,NumPyの読み込みから始めます.
2.3. NumPyによる実装¶
それでは重回帰分析で行われる計算をPythonを用いてコンピュータに実行させてみましょう. PythonにはNumPyとよばれる線形代数を簡単に扱えるライブラリが存在し,広く利用されています. 次の章で紹介するChainerの中でもNumPyは用いられており,様々なデータ解析・科学計算のライブラリで広く採用されているため,NumPyの使い方にある程度慣れ親しんでおくことは後々役立ちます.
以下では,Pythonの基本的な文法はすでに理解していることを前提としています. 例えば,変数(数値・文字列,リスト,タプル,辞書),制御構文(for,if),関数,クラスを理解している必要があります.
重回帰分析では,最終的に最適なパラメータ \({\bf w}\) が
と閉じた形で求まりました. この計算を行うために,これから以下の5つをNumPyを使って行います.
- ベクトルの定義
- 行列の定義
- 転置
- 行列積
- 逆行列
以下のようなデータセットが与えられているとします.
データ数 \(N = 4\) であり,入力変数の数 \(M = 2\) です. それではこのデータセットにフィットするモデル \(f({\bf x}; {\bf w}) = {\bf x}^{\rm T}{\bf w}\) のパラメータ \({\bf w}\) をNumPyを使って計算してみましょう.
まずNumPyを読み込みます.
[1]:
import numpy as np
ベクトルの定義は以下のように行います.
[2]:
t = np.array([1, 5, 6, 8])
ベクトルを表示してみましょう.
[3]:
print(t)
[1 5 6 8]
行列の定義も行い,表示してみましょう.
[4]:
X = np.array([
[1, 2, 3],
[1, 2, 5],
[1, 3, 4],
[1, 5, 9]
])
[5]:
print(X)
[[1 2 3]
[1 2 5]
[1 3 4]
[1 5 9]]
ここではnp.array
という関数を用いて,PythonのリストからNumPyの多次元配列の形式(np.ndarray
)への変換を行っています.
次に,Xの転置を行ってみましょう.np.ndarray
で定義されている場合,.T
をつけるだけで転置することができます.
[6]:
print(X.T)
[[1 1 1 1]
[2 2 3 5]
[3 5 4 9]]
縦と横が入れ替わっていることを確認できます.
行列積は以下のように np.dot
によって実現できます.行列積を行う際には,一番目の行列の列数と,二番目の行列の行数が同じであることに注意して下さい.
[7]:
XX = np.dot(X.T, X)
[8]:
print(XX)
[[ 4 12 21]
[ 12 42 73]
[ 21 73 131]]
ここからさらに,\({\bf X}^{\rm T}{\bf X}\) に対する逆行列,\(\left( {\bf X}^{\rm T}{\bf X} \right)^{-1}\) を計算します.逆行列を求めるには,np.linalg.inv
を用います.
[9]:
XX_inv = np.linalg.inv(XX)
[10]:
print(XX_inv)
[[ 1.76530612 -0.39795918 -0.06122449]
[-0.39795918 0.84693878 -0.40816327]
[-0.06122449 -0.40816327 0.24489796]]
これで重回帰分析のために必要な演算が揃いました.
最適なパラメータ \(\left({\bf X}^{\rm T}{\bf X} \right)^{-1} {\bf X}^{\rm T}{\bf t}\) を求めると,
[11]:
Xt = np.dot(X.T, t)
[12]:
print(Xt)
[ 20 70 124]
[13]:
w = np.dot(XX_inv, Xt)
[14]:
print(w)
[-0.14285714 0.71428571 0.57142857]
このように求まりました. NumPyを使って数式と同じ計算をプログラムに書き下せば,コンピュータに具体的な数値を使った計算を高速に行わせることができます.
2.4. Scikit-learnによる機械学習アルゴリズムの実行¶
重回帰分析であればNumPyで比較的容易に実装することができましたが,実践的に使用する機械学習手法のアルゴリズムの多くは複雑であり,初学者が一から書くのは難しい場合も少なくありません. PythonにはScikit-learnと呼ばれる様々な機械学習手法の実装が含められたライブラリが公開されており,初学者でも簡単に扱うことができます.
ここでは重回帰分析をScikit-learnを用いて行う方法を紹介します. データセットは先程と同様に \({\bf X}\) と \({\bf t}\) を使用しますが,Scikit-learnにおいては,パラメータ \({\bf w}\) がバイアス \({\bf b}\) を包含しない 形式を想定しており,入力データ \({\bf X}\) の1列目から \(1\) を取り除く必要があることに注意してください. 従って,
をデータセットとして準備します.
2.4.1. Scikit-learn 基礎編¶
Scikit-learnはsklearn
という名前で読み込みます.
[15]:
import sklearn
重回帰分析を使用する場合は以下の LinearRegression
というクラスを読み込みます.
[16]:
from sklearn.linear_model import LinearRegression
なお,使い方を調べる際には,公式のリファレンスに加えて,使用例を見るのも有用です(例えば検索エンジンで「重回帰分析 Scikit-learn」のようなキーワードで検索すればたくさんの使用例が見つかります).
LinearRegression
クラスを利用するために,インスタンス化を行い,model
と名付けます.
[17]:
model = LinearRegression()
これで重回帰分析を行う準備が完了しました. この model
を使って,最適なパラメータを求めるには以下のようにします.
[18]:
# データセットの定義
X = np.array([
[2, 3],
[2, 5],
[3, 4],
[5, 9]
])
t = np.array([1, 5, 6, 8])
# 最適なパラメータの計算
model.fit(X, t)
[18]:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
求まったパラメータの検証結果を以下のようにして確認することができます.
[19]:
model.score(X, t)
[19]:
0.6923076923076923
LinearRegression
クラスのオブジェクトが持つ score()
メソッドを呼ぶと,以下の式で示される,決定係数とよばれる指標が計算され,結果が返されます.
Scikit-learnは,よく使う機械学習手法を簡単に切り替えて使えるよう,統一されたインターフェースで様々な機械学習手法が実装されています. Scikit-learnが好まれる理由の一つには,様々なアルゴリズムが「.fit()
で学習,.score()
で検証」という同じインターフェースから利用できることが挙げられるでしょう.
また,アルゴリズムによって内容は多少異なりますが,パラメータも model
オブジェクトに属性として格納されているため,学習後に確認することができます.
[20]:
# パラメータw
model.coef_
[20]:
array([0.71428571, 0.57142857])
[21]:
# バイアスb
model.intercept_
[21]:
-0.14285714285714235
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()
関数を実行して,データセットを読み込みましょう.
[22]:
from sklearn.datasets import load_boston
[23]:
boston = load_boston()
boston
はPythonの辞書と同じインターフェースが備わっており,'data'
キーに入力値が,'target'
キーに目標値が格納されています.
[24]:
X = boston['data']
t = boston['target']
[25]:
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]]
[26]:
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
を使うことで行と列の数を確認できます.
[27]:
X.shape
[27]:
(506, 13)
[28]:
t.shape
[28]:
(506,)
入力値 X
には,506 件分のデータが格納されていることが分かりました. 各サンプルは 13 次元のベクトルとなっており,これはそれぞれが 13 種類の入力変数を持っていることを表しています. 目標値 t
には,入力変数に対応する出力変数の値として,平均物件価格のスカラー値が 506 件分格納されています.
2.4.2.2. データセットの分割¶
つぎに,このデータセットを 訓練データセット と テストデータセット に分割する方法をご紹介します. モデルの性能の評価を訓練の時に使ったデータを使って行っても,訓練中に見たことが無い未知のデータに対してもうまく働くかどうかが分かりません. 多くの場合機械学習手法の目標は既知のデータから一般的な法則やパターンを見つけ出し,未知のデータに対しても正しく予測等が行えるようになることであるので,訓練済みモデルの評価は訓練には用いていない(モデルにとって)未知のデータ,テストデータを使って行う必要があります. 訓練データとテストデータは,用意したデータセットを分割して作成することができます. この方法を ホールドアウト法 と呼びます.
Scikit-learnではデータセットを訓練用とテスト用に分割するための機能が用意されています. これを用いて上で読み込んだデータセットを2つに分割してみましょう.
[29]:
from sklearn.model_selection import train_test_split
[30]:
X_train, X_test, t_train, t_test = train_test_split(X, t, test_size=0.3, random_state=0)
[31]:
X_train.shape
[31]:
(354, 13)
[32]:
X_test.shape
[32]:
(152, 13)
train_test_split()
関数の引数 test_size
にはテスト用に使うデータ数のデータセット全体のデータ数に対する比率を指定します. \(0.3\) と指定すると全体の \(30\)% がテストデータセットが持つデータの数となります. また,random_state
は乱数のシードであり,これに与える値を固定すると,データセット中からどのデータを訓練用に割り振り,どのデータをテスト用に割り振るか,という選択が固定されます.
この選択はランダムに行われますが,乱数シードを固定すればそのランダムな選択を再現することができるためです.
それでは,訓練データを用いて学習を行います.
[33]:
model = LinearRegression()
[34]:
model.fit(X_train, t_train)
[34]:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
訓練が終了しました. score()
を用いたモデルの検証を訓練データとテストデータの両方に対して行ってみましょう.
[35]:
# 訓練データ
model.score(X_train, t_train)
[35]:
0.7645451026942549
[36]:
# テストデータ
model.score(X_test, t_test)
[36]:
0.6733825506400171
テストデータだけでなく,訓練データでもモデルの検証を行うことで良いモデルの獲得に失敗している場合はその原因について考察するのに役立つ情報を得ることができることがあります.
2.4.2.3. アンダーフィッティングとオーバーフィッティング¶
モデルが訓練データに対してすら良い精度で予測できないという状態は,アンダーフィッティングと呼ばれます. アンダーフィッティングが起きている場合,現状の機械学習手法もしくはモデルではうまくデータの特徴を捉えられていないと考えられ,アルゴリズムを変更したり,入力データの特徴をより適切に表現できるような変換をモデルの訓練の前に施すことを考えるなど,新たな工夫を加える必要があります. 一方,訓練データに対しては良い精度で予測が行えるモデルになっているにも関わらず,テストデータに対してのパフォーマンスが良くない,という場合は,モデルが訓練データセットにオーバーフィッティングしていると言います. この場合,訓練データにあまりにも特化したモデルになってしまっている可能性があります. この問題を避ける方法が現在も活発に研究されていますが,よく行われる対策としては正則化(パラメータが取りうる値に制約をもたせるなど)やモデルの自由度(パラメータ数など)の調整があります. また,ハイパーパラメータとよばれる各アルゴリズムの動作を制御するのに使われるパラメータ値を調整していくことも行われます. このように,一口に望ましい結果が得られないといっても,原因としては様々なものがあり得るため,訓練データセットとテストデータセットの両方に対して訓練済みモデルの性能評価を行っておくことは,その原因を探るにあたって重要になります.
2.4.2.4. Scikit-learnを使った前処理¶
Scikit-learnを用いると,データに対する前処理も簡単に行うことができます. 例えば,訓練データセットの入力値の集合が,平均0,標準偏差1となるように変換を行う場合は,以下のようにします.
[37]:
from sklearn.preprocessing import StandardScaler
[38]:
# インスタンス化
scaler = StandardScaler()
訓練データを用いて,平均と分散を計算します.
[39]:
# 平均と分散を計算
scaler.fit(X_train)
[39]:
StandardScaler(copy=True, with_mean=True, with_std=True)
計算された平均,分散を用いて,訓練データ及びテストデータをスケーリングします.
[40]:
# 変換
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test)
テストデータをスケーリングする際にも,訓練データの平均・分散を利用していることに注意しましょう. テストデータはモデルにとっては未知のデータセットでなければならないにも関わらず,訓練データとテストデータを合わせた全データの平均・分散を利用して訓練データのスケーリングを行ってしまうと,本来知りえないはずのテストデータの情報をモデルに間接的に与えてしまうことになります. それを避けるために,訓練データのみを用いて統計値の計算を行い,これを用いてスケーリングを行っています.
訓練データとテストデータでは平均・分散が異なるため,訓練データの平均・分散でスケーリングされたテストデータについては,その平均が\(0\),分散が\(1\)になるとは限らないことに注意してください.
[41]:
print(X_train_s)
[[-0.20735619 -0.49997924 1.54801583 ... 1.2272573 0.42454294
3.10807269]
[-0.38886492 0.34677427 -0.58974728 ... 0.05696346 0.40185312
-0.66643035]
[-0.33573486 -0.49997924 1.54801583 ... 1.2272573 0.39846135
0.63936662]
...
[-0.38450355 -0.49997924 -0.15303077 ... -0.30312696 0.39659002
-0.30284441]
[-0.37511786 -0.49997924 -0.59690657 ... -0.25811566 0.37588849
0.89967717]
[-0.38592298 -0.49997924 -1.00641779 ... -0.84326258 0.42454294
0.31822262]]
[42]:
print(X_test_s)
[[-0.39454262 -0.49997924 -1.12239824 ... -0.70822867 0.17086147
-0.72160487]
[ 0.70419882 -0.49997924 1.00534187 ... 0.77714428 0.0648977
-0.41177872]
[-0.38890688 -0.49997924 0.4025299 ... -0.93328518 0.38758427
-0.27454978]
...
[ 1.61285743 -0.49997924 1.00534187 ... 0.77714428 0.42454294
2.59876943]
[-0.34350332 -0.49997924 -0.1687812 ... -0.03305915 0.42454294
-1.11772962]
[-0.39902507 -0.49997924 -1.27417512 ... 0.10197476 0.39202867
-1.02294263]]
この他,Scikit-learnはロジスティック回帰やサポートベクターマシン,ランダムフォレストなど様々な機械学習手法をサポートしています.
それらについても,前節までで紹介した重回帰分析を行う場合と同様に,モデルをインスタンス化し,学習データを引数に与えて .fit()
関数で訓練を行い,.score()
関数で結果を評価できるようになっています.
より詳しく知りたい方はScikit-learnサイトや解説サイトなどを参照してください.