機械学習勉強記録 Scikit-learnで重回帰分析を実装

# Scikit-learnとは

Scikit-learnは、Pythonの機械学習ライブラリです。「サイキット・ラーン」と読みます。
これを使って重回帰分析を行ってみます。

#下準備
まず、使うライブラリをインポートする必要があります。

```python
# Scikit-learnを使う時に一緒によく使うライブラリをインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
```
それぞれが何かは、今までの記事で解説してきました。

# データセットのインポート
Scikit-learnの中にデータセットがあらかじめ色々と用意してあります。
今回はボストン近郊の住宅データセットを使います。

```python
from sklearn.datasets import load_boston
```
これでボストン近郊の住宅データセットを読み込みました。実際にどんなデータが入っているのが、みてみましょう。

```python
print(load_boston())
"""
出力結果 : 
{'data': array([[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]]), 'target': array([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]), 'feature_names': array(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD',
       'TAX', 'PTRATIO', 'B', 'LSTAT'], dtype='<U7'), 'DESCR': ".. _boston_dataset:\n\nBoston house prices dataset\n---------------------------\n\n**Data Set Characteristics:**  \n\n    :Number of Instances: 506 \n\n    :Number of Attributes: 13 numeric/categorical predictive. Median Value (attribute 14) is usually the target.\n\n    :Attribute Information (in order):\n        - CRIM     per capita crime rate by town\n        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.\n        - INDUS    proportion of non-retail business acres per town\n        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)\n        - NOX      nitric oxides concentration (parts per 10 million)\n        - RM       average number of rooms per dwelling\n        - AGE      proportion of owner-occupied units built prior to 1940\n        - DIS      weighted distances to five Boston employment centres\n        - RAD      index of accessibility to radial highways\n        - TAX      full-value property-tax rate per $10,000\n        - PTRATIO  pupil-teacher ratio by town\n        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town\n        - LSTAT    % lower status of the population\n        - MEDV     Median value of owner-occupied homes in $1000's\n\n    :Missing Attribute Values: None\n\n    :Creator: Harrison, D. and Rubinfeld, D.L.\n\nThis is a copy of UCI ML housing dataset.\nhttps://archive.ics.uci.edu/ml/machine-learning-databases/housing/\n\n\nThis dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.\n\nThe Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic\nprices and the demand for clean air', J. Environ. Economics & Management,\nvol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics\n...', Wiley, 1980.   N.B. Various transformations are used in the table on\npages 244-261 of the latter.\n\nThe Boston house-price data has been used in many machine learning papers that address regression\nproblems.   \n     \n.. topic:: References\n\n   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.\n   - Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.\n", 'filename': '/usr/local/lib/python3.7/dist-packages/sklearn/datasets/data/boston_house_prices.csv'}
"""
```
このままでは非常に見辛いですね。
なので、これをpandasのデータフレーム型で表示しましょう。
まずは、x（入力変数）とt（目標値）を定義します。

```python
dataset = load_boston()
# dataset の中の data を x, target を t に代入
x,t = dataset.data, dataset.target
# dataset の中の feature_names を columns に代入
columns = dataset.feature_names
```
`feature_names`というのは、`CRIM`,`ZN`などが入っているリストですね。
そしてこれをpandasのデータフレーム型にして表示すると、

```python
df = pd.DataFrame(x, columns=columns)
df.head(3)
"""
出力結果 : 

        CRIM    	ZN	    INDUS	CHAS NOX	RM	    AGE	    DIS	    RAD	 TAX	 PTRATIO B	 
LSTAT
0	    0.00632	    18.0	2.31	0.0	 0.538	6.575	65.2	4.0900	1.0  296.0	 15.3	396.90	4.98
1	    0.02731	    0.0     7.07	0.0	 0.469	6.421	78.9	4.9671	2.0	 242.0	 17.8	396.90	9.14
2	    0.02729	    0.0   	7.07	0.0	 0.469	7.185	61.1	4.9671	2.0	 242.0	 17.8	392.83	4.03
"""
```
このように見やすくなりました。
`columns=columns`というのは、データフレーム内の`columns`（＝列の名前）に、先ほど定義した`columns`を代入するという感じです。名前が同じでややこしいですね。
僕はここを理解するのに時間がかかりましたが、一度`load_boston()`の中身を見れば、意味がわかりやすいと思います。

上のデータフレームには、`target`の列がありませんので、追加します。

```python
# tのデータを、'Target'という名前の列を追加してそこに代入
df['Target'] = t
```
これで、`Target`という列がデータフレームに追加されます。

# データを`ndarray`型に変換

機械学習アルゴリズムの実装を行う際には、numpyの`ndarray`型にする必要があります。
それは、`.values`の属性で取り出すことができます。
そして、同時にデータフレーム内の入力変数と出力変数の切り分けを行います。
データフレーム内の'Target'が出力変数、それ以外が入力変数にあたります。
切り分ける時は、`.drop`メソッドを使います。

```python
# 'Target'という列名の列の値をtに代入
t = df['Target'].values
# dfの中の'Target'という列名の列の値以外の値をxに代入
x = df.drop(labels=['Target'], axis=1).values
```

# 学習用データセットとテスト用データセットへ分割
ボストン近郊の住宅データセットを学習用データセットとテスト用データセットに分けます。
学習用データセットで、適切な重みなどを求めて、それが本当に使えるかどうかをテスト用データセットでテストするためです。

```python
from sklearn.model_selection import train_test_split
x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0)
```
`train_test_split`を使うことで、データセットを
`x_train`学習用入力変数
`x_test`テスト用入力変数
`t_train`学習用出力変数
`t_test`テスト用出力変数
と、ランダムに抽出して4つに分けます。
`test_size=0.3`というのは、データ全体の3割をテスト用データセットにするという意味です。
`random_state=0`は、ランダムに抽出する際のランダム性を指定することができます。同じ数字にすることで再現性を確保できます。

# モデルの学習・検証
機械学習において、入力値を受け取り、何かしらの評価・判定をして出力値を出すものをモデルと言います。
例えば家賃を予測するモデルであれば、賃貸物件の面積と駅からの距離という入力値を受け取り、重回帰分析をして家賃という出力値を出すという働きをします。

モデルを定義し、学習させ、検証する。
この3ステップで、モデルを作っていきます。

## 1.モデルの定義
Scikit-learnで重回帰分析を行う時は、`LinearRegression`を使います。

```python
from sklearn.linear_model import LinearRegression
# モデルの定義
model = LinearRegression()
```
これで、`model`は、重回帰分析をするモデルだと定義します。

## 2.モデルの学習
今回学習で使用するのは、先ほど分割したデータセットのうち、学習用のデータセットです。

```python
# モデルの学習
model.fit(x_train, t_train)
```
この`.fit`を使うだけで、モデルの学習が完了します。簡単ですね。
ここでパラメータ（重みwとバイアスbのこと）を確かめてみましょう。

```python
# 重み
print(model.coef_)
"""
出力結果 : 
[-1.21310401e-01  4.44664254e-02  1.13416945e-02  2.51124642e+00
 -1.62312529e+01  3.85906801e+00 -9.98516565e-03 -1.50026956e+00
  2.42143466e-01 -1.10716124e-02 -1.01775264e+00  6.81446545e-03
 -4.86738066e-01]
"""

# バイアス
print(model.intercept_)
# 出力結果 : 37.93710774183309
```
重みについて、棒グラフで可視化することもできます。（出力結果は割愛）

```python
plt.figure(figsize=(10, 7))
plt.bar(x=columns, height=model.coef_)
```

## 3.モデルの検証
モデルの検証は以下で行えます。

```python
# 学習済みモデルを用いて、学習用データセットで計算した係数
print('train score : ', model.score(x_train, t_train))
# 出力結果 : train score :  0.7645451026942549


# 学習済みモデルを用いて、テスト用データセットで計算した係数
print('test score : ', model.score(x_test, t_test))
# 出力結果 : test score :  0.6733825506400171
```
出力された係数が、1に近ければ近いほど良いモデルです。

学習用データに対してのみ数値が高く、テスト用データに対して数値が低い状態のことを「過学習」といいます。
同じことばっかり勉強しちゃって、他の問題への応用がききにくくなってしまった、違う傾向を持つ問題について対応しにくくなった、そんな状態です。
過学習を防いで、テスト用データで計算した係数を1に近づけるようにしましょう。

# 推論
学習が終わったモデルに新たな入力値を加えて、予測値を計算させることもできます。それを「推論」と言います。
テスト用データからサンプルを一つ取り出し、推論してみます。

```python
# x_testに対して、モデルで推論する
y = model.predict(x_test)

# 推論結果の1番目の予測値を表示
print('予測値 : ', y[0])
# 目標値の1番目はどうだったか確認
print('目標値 : ', t_test[0])
'''
出力結果 : 
予測値 :  24.935707898576915
目標値 :  22.6
'''
```
まぁまぁ近い数字が推論されていることがわかりました。