Kaggle Courses 学習メモ(機械学習イントロダクション編 1/2) 

前回([Python講座編](https://qiita.com/mossycat/items/e173f46d933707239866))の続きです．

今回は[機械学習基礎編](https://www.kaggle.com/learn/intro-to-machine-learning)ということで，

 - 主要ライブラリ(pandas,scikit-learnの基礎)
 - バリデーションのやり方
 - アンダーフィッティング・オーバーフィッティングについて

 など，機械学習の基礎の基礎から学べる内容になっております．

それでは進めていきましょう．

#モデルがどのようにして動くのか
ある物件について，間取りや築年数からその売値を予測する機械学習モデルを構築することにします．
ここでは，条件分岐を繰り返すことで予測を行うシンプルな機械学習モデルである決定木DecisionTreeを考えてみましょう

寝室が2つ以上...
&nbsp;&nbsp;ある→1880万円の家
&nbsp;&nbsp;ない→1780万円の家

この決定木は家を2つのカテゴリに分け，その価格を，過去の物件の平均と予想します．
この，データからパターンを認識するこの過程は，**フィッティングFitting** または **トレーニングTraining** と呼ばれています．このフィッティング（トレーニング）に用いるデータを訓練データとします．このフィッティングの詳細については後述します．
モデルへのフィッティング後は，新規データの予想に用いることが出来ます．

##決定木の性能向上
①寝室が2つ以上...
&nbsp;&nbsp;ある→1880万円の家
&nbsp;&nbsp;ない→1780万円の家

②寝室が2つ以上...
&nbsp;&nbsp;ある→1780万円の家
&nbsp;&nbsp;ない→1880万円の家

どちらの木のほうが理にかなっているでしょうか？と言われると，これは前者でしょう．
「寝室の数が多いほうが売値も高い傾向がある」という事実をしっかり捉えているからです．

しかし，この決定木は寝室の数しか見ていません．
寝室だけでなく，風呂の数，敷地面積，立地など，もっと多くの要素を掴むことができれば，もっと理にかなったモデルができそうですね．

例えば敷地面積を加味したこんな決定木が考えられます．

寝室が2つ以上...
&nbsp;&nbsp;ある→敷地面積が11500平方フィートより...
&nbsp;&nbsp;&nbsp;&nbsp;大きい→2330万円の家
&nbsp;&nbsp;&nbsp;&nbsp;小さい →1700万円の家
&nbsp;&nbsp;ない→敷地面積が8500平方フィートより...
&nbsp;&nbsp;&nbsp;&nbsp;大きい →1880万円の家
&nbsp;&nbsp;&nbsp;&nbsp;小さい →1460万円の家

このように，条件分岐を増やすことで決定木はどんどん深くなります．
最終的に〇〇〇〇万円と予測する到着点の部分は葉leafとよびます．

#データ読み込み・整形
モデルをフィッティングするため，まずはデータを読み込み，特徴量・予測値に用いる値を抽出しておく必要があります．
そのため，データ整形に特化したライブラリであるpandasを用いていきます．「機械学習エンジニア名乗っておきながらpandas使えないってマジ？」と言われないよう，しっかり勉強していきましょう．

##基本の読み込み
```Python
# データへのパス
melbourne_file_path = '../input/melbourne-housing-snapshot/melb_data.csv'
# データ読み込み・DataFrame形式へ
melbourne_data = pd.read_csv(melbourne_file_path) 
#お試し表示
melbourne_data.describe()
```
![スクリーンショット 2021-03-02 15.27.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269213/1b357800-7f9c-86a9-1901-5184fec0f509.png)

 - count...値がNA(Not Available)/nullでないデータの数
 - mean ... 平均値
 - std..標準偏差，Standard Deviation
 - max min ... 最大最小
 - 25%,50%,75% ... 四分位数
 
カラムへは配列のようにアクセスできる
上記の代表値は同名のメソッドを用いて取得可能

```Python
import datetime

#敷地面積の平均値
avg_lot_size = round(home_data['LotArea'].mean())
#最も新築な家の築年数
#newest_home_age = 2021-home_data['YearBuilt'].max()
newest_home_age = datetime.date.today().year-home_data['YearBuilt'].max()

#columnsでカラムを取得可能
melbourne_data.columns
#Index(['Suburb', 'Address', 'Rooms', 'Type', 'Price', ... ,'Lattitude',
#       'Longtitude', 'Regionname', 'Propertycount'],
      dtype='object')
```

データセットのサブセットを取る方法はいくつかあるが，代表的なのは以下の2つ．

- ドット表記．1カラムを指定して取得する．「予測ターゲット」を得る時に有用．
- カラムリストによる選択．複数カラムをDataFrame形式で取得する．モデルへの入力となる特徴量を得る時に有用．

```Python
#ドット表記で予測ターゲットを得る
y = melbourne_data.Price

#カラムのリストによって特徴量を得る
melbourne_features = ['Rooms', 'Bathroom', 'Landsize', 'Lattitude', 'Longtitude']
X = melbourne_data[melbourne_features]
```
複数カラムをまとめて得られるデータはDataFrame形式なので，`describe()`メソッドや`head()`メソッドが使える.


```Python
X.describe()
```


![スクリーンショット 2021-03-02 16.17.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269213/2dc3d0ca-a118-7e17-ba75-f561b34fcef2.png)

```Python
X.head()
```
![スクリーンショット 2021-03-02 16.18.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269213/59795cfa-38e8-d3eb-c3f0-033a1eb1d19f.png)


#モデルの構築
特徴量$ \mathbf{X} $と予測ターゲット$y$をデータから切り出すことが出来たので，Xを用いてyを予測する機械学習モデルを構築していきます．
本コースでは著名な機械学習手法をかんたんに試せるscikit-learnを用いていきます．

##scikit-learn
```Python
from sklearn.tree import DecisionTreeRegressor

# 決定木モデル，同じ結果が得たい場合はrandom_stateを同じ値にする
melbourne_model = DecisionTreeRegressor(random_state=1)
# フィッティング
melbourne_model.fit(X, y)
# 予測
melbourne_model.predict(X)
#[1200, 1100, 800,...]
```
簡単に作れちゃいすぎてこわいですね．

#モデルの評価
それっぽいモデルが出来ました．
でもこのモデルの性能って実際どうなんでしょう．

性能を評価してみましょう．

一般には予測精度が高いと「いいモデル」ということになりますが，大きな間違いとして，評価指標に「訓練データに対する予測精度」を用いることがあげられます．
何が問題なのかと，どう対処するかはとりあえず置いておくとして，まずはこの「訓練データに対する予測」を試してみましょう．

各データに対する予測は，データごとに良かったり悪かったりするわけで，これらを個別に見ていっても仕方ありません．
予測結果を要約する方法として，ここでは**平均絶対誤差，MeanAbsoluteError， MAE**を試しましょう．
```Python
#誤差 = 実際の値 - 予測値
from sklearn.metrics import mean_absolute_error

predicted_home_prices = melbourne_model.predict(X)
mean_absolute_error(y, predicted_home_prices)
```
MAEではこの誤差の絶対値の平均を取ります．名前通りですわね．

##サンプル内で誤差を取ると何がマズイのか
訓練データとして用いたサンプルは結局，現実のマーケットのごくごく一部でしかないのが問題です．

例えば，現実には「ドアの色」と「家の価格」には（流行を考慮しなければ）あんまり関係がないと考えられます．
しかしサンプル内で，たまたま「緑色のドアの家」がめちゃくちゃ高額だったとしましょう．モデルは家の価格を予測するためのパターンを見つけるため，高額の家は緑色のドアがあるというパターンを抽出してしまいます．

このモデルに対し，訓練データのみを用いて評価をしてしまうと「いい感じ」という評価になりますが...
いざ実際のデータで予測をさせてみるとダメダメじゃんということになるわけで．

というわけで，モデル構築時に一部のデータを学習から除外して，そのデータを用いてモデルのデータを「テスト」することを考えます．このデータは検証(Validation)データとよばれます．

scikit-learnではもうこれを実現するメソッドがあって，`train_test_split`メソッドで簡単に訓練データと検証データを分割できます．

```Python
from sklearn.model_selection import train_test_split

#訓練(train)データと検証(val)データに分ける
train_X, val_X, train_y, val_y = train_test_split(X, y, random_state = 0)
#モデル定義
melbourne_model = DecisionTreeRegressor()
#学習
melbourne_model.fit(train_X, train_y)
#予測
val_predictions = melbourne_model.predict(val_X)
print(mean_absolute_error(val_y, val_predictions))
#263007.8766946417
```
訓練データに対しては\$500-程度の誤差で済んだモデルも，実際には\$250,000程度の誤差になってしまいました．現実で使えないダメダメモデルじゃないか...ということが検証できるわけですね．

なので，より良い特徴を探すために試行錯誤したり，モデルを変えるなどして，このモデルを改善してみましょう...というところで今日はここまで．

[第2回](https://qiita.com/mossycat/items/b6840ed43609721a6e54)ではこのモデルによる新規データの予測がうまくいかない原因について考察していきます．