scikit-learnによる機械学習実装 ~ digits編 ~

# 初めに
## 内容
scikit-learnを用いた機械学習を行います。
データセットは[scikit-learnから提供されるデータセット](https://pythondatascience.plavox.info/scikit-learn/scikit-learn%E3%81%AB%E4%BB%98%E5%B1%9E%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88)を使用します。

本記事では「**Digits**」（数字の手書き文字）を取り扱います。

[irisデータセットの記事はこちらです](https://qiita.com/RiRyCasals/items/8515739d8a3cf8cf7891)。
[bostonデータセットの記事はこちらです](https://qiita.com/RiRyCasals/items/bcf04638245a01fa25f6)。
[diabetsデータセットの記事はこちらです](https://qiita.com/RiRyCasals/items/2ea25de39610db0c78be)。
その他のデータセットは別の記事にて更新しようと思います。

## 対象
主に**初心者向け**です。が、入門向けではないです。

本記事は友人に向けて作成してますが、
**「機械学習したい」と思っている方**にも読んでもらえるように書いています。

実装に関しての解説は記載しますが、アルゴリズムなどの解説は（基本的に）しませんのでご理解のほどを。

## 環境
- Google Colaboratory
- Python 3.6.9
- sklearn 0.22.2
- pandas 1.1.5
- matplotlib 3.2.2

ローカルで環境をそろえるのは難しいので（Dockerやクラウドを使えばできますが）今回はColaboratoryを用います。
上記のバージョンに合わせるとローカル環境でも実行できます。

---
以降は実際にpythonを用いながらになるのでColaboratoryの用意をお願いします。

# Digitsデータセットについて
0~9の手書き数字が書かれた画像データセットです。
（正確には画像ではなく数値の配列）

一つの手書き数字は、１チャネルで表されます。（モノクロ画像）

一つの手書き数字は、8×8のサイズで構成され、１次元の配列に格納されています。
（横向きにスライスした画像を一列につなげるイメージで、64個の数値 の配列から構成される）

言葉だと少しわかりにくいので、可視化の項目で実際に見てみましょう。

## 説明変数
今回はpandas.DataFrameを使用しません。

```python:
from sklearn.datasets import load_digits
digits = load_digits()
data = digits.data
print("全体 :", data.shape) # => 全体 : (1797, 64)
print("1行目(1つ目のデータ) :", data[0].shape) # => 1行目(1つ目のデータ) : (64,)
```
レコード数は1797件ですね。
説明変数は64。
すなわち、1ピクセルが一つの説明変数になります。

### 可視化してみる
まず、一つ目のデータをそのまま見てみましょう

```python:
print(data[0])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/8c6819ec-1bec-6e90-0037-328b839fa13a.png)
1次元のベクトルになっているのがはっきりわかりますね。
可視化してみます。

```python:
import matplotlib.pyplot as plt
# plt.imshow(digits.images[0], interpolation='nearest')
plt.imshow(digits.images[0], cmap=plt.cm.gray_r, interpolation='nearest')
# plt.imshow(digits.images[0], cmap='plasma', interpolation='bicubic')
plt.show()
```
8×8ピクセルなので見ずらいですね...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/7eebb69a-e176-33d7-24b9-09d13b9c9535.png)
コメントアウトされている者もぜひ見てください。見やすくなりますよ！

## 目的変数
形を確認します。

```python:
target = digits.target
target.shape # => (1797,)
```
こちらは説明変数の一つのレコードが、何の数字を表しているか、というデータですね。
つまり今回は分類問題になります。

こんな感じのが格納されてます。（全部見たいならfor文とかで...）

```python:
print(target) # => [0 1 2 ... 8 9 8]
```

# 機械学習する
## 前処理
データセットの分割をします。おなじみの処理ですね。

```python:
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=123)
print('x_train :', x_train.shape) # => x_train : (1437, 64)
print('y_train :', y_train.shape) # => y_train : (1437,)
print('x_test :', x_test.shape) # => x_test : (360, 64)
print('y_test :', y_test.shape) # => y_test : (360,)
```

## 学習
今回はk近傍法を使用して分類します。
sklearn.neighborsのKNeighborsClassifierをインポートしましょう。
10クラスに分類したいのでハイパーパラメータとして n_neighbors=10 を指定しましょう。

```python:
from sklearn.neighbors import KNeighborsClassifier

model = KNeighborsClassifier(n_neighbors=10)
model.fit(x_train, y_train)
```

## 推論

```python:
from sklearn.metrics import accuracy_score, recall_score, precision_score

pred = model.predict(x_test)
accuracy_score(y_test, pred) # => 0.9861111111111112
```
98%を超えてるのでよしとしましょう。

CNNを利用すると簡単に100%の精度が出ると思います。
（[ぜひこちらの記事を...](https://qiita.com/RiRyCasals/items/c65524f2378b1ce04d4a)）
頑張ればscikit-learnでも100%が出るので暇な方はチャレンジを。（時間の無駄だと思います）

ちなみに、predの中身は次のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/db3682d7-5e0a-2592-0728-028d109b3079.png)
「64の説明変数から予測した、0~9のうち1つの目的変数」が入力したデータ分返っていますね。

# 最後に
データは予測して終わりではなく、「予測したデータを利用して何かを作る」ことのほうが重要です。
ぜひ作業時間を作って、アプリケーションにしてみてください。（colaboratory上で動く関数程度でもいいです）
将来の糧になるはずですので。