56日目 機械学習、アヤメの分類ができました。

今日も、[Pythonによるスクレイピング＆機械学習 開発テクニック](https://www.socym.co.jp/book/1079)の第４章機械学習の続きです。

#アヤメデータの入手
機械学習でよく使われるデータだそう。
さっそくダウンロード。リンク先の[RAW]ボタンをクリックするとCSVが表示されるので、ダウンロードしました。
https://github.com/pandas-dev/pandas/blob/master/pandas/tests/data/iris.csv

さて、ざくざく書いていきます。
わからない所は調べます。

###lambda
無名関数。使いこなすと簡単になるらしい。
[Pythonのlambdaって分かりやすい](https://qiita.com/nagataaaas/items/531b1fc5ce42a791c7df)

###`re.match( r’^[0-9\.]+$', n )`
正規表現を使っています。
[re \-\-\- 正規表現操作](https://docs.python.org/ja/3/library/re.html)

最初の`r`　・・・バックスラッシュを特別あつかいしないモード、raw。
次の`^`　・・・文字列の先頭
`[]`・・・文字列の集合
`0-9` ・・・0から9までの数字
`\` ・・・バックスラッシュ。正規表現では特殊文字をそのまま使う、Pythonでは続く一文字の無視。何行かにわけて描きたいときに使ったりする。Pythonぽく使いたい時は文字列先頭に`r`をつける。Macで入力するには、Option+¥。
`+`直前の正規表現を１回以上繰り返したらマッチ。
`$`文字列の末尾にマッチ。

###`if`を一行で書く
`fn = lambda n : float(n) ` `if re.match( r'^[0-9\.]+$', n )`  `else n`
これはこれと同じ。

`if re.match( r'^[0-9\.]+$', n )`  :
	`fn = lambda n : float(n) `
`else n`

英語の人には一行書きの方がわかりやすいらしい。

つまり・・・

バックスラッシュはPythonモード。
先頭が数字、２文字以上、数字で終わる。
この条件にマッチしたら、floatにする。そうでなければnのまま。
これでいいのかな？

###map
Listの全てに対して同じ処理をする。
`cols = list(map( fn, cols))` の場合、
Colsの項目ひとつづつに対し、fnを実行する。
→読み込んだCSVのセル１つづつに対し、数字ならfloat、それ以外はそのままとしてデータセットを返す。

###スライス
`data = csv[i][0:4] ` 先頭から４文字とってくる。
[【Python】スライス操作についてまとめ](https://qiita.com/tanuk1647/items/276d2be36f5abb8ea52e)

#さて実行！


###１回目
```
$ python ayame.py 
Traceback (most recent call last):
  File "ayame.py", line 30, in <module>
    train_data:append(data)
NameError: name 'append' is not defined
```

train_data`:`appendではなく、train_data`.`append でした。

###２回目
```
$ python ayame.py 
Traceback (most recent call last):
  File "ayame.py", line 35, in <module>
    pre = clf.predict(test_data)
  File "/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py", line 574, in predict
    y = super().predict(X)
  File "/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py", line 322, in predict
    X = self._validate_for_predict(X)
  File "/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py", line 454, in _validate_for_predict
    accept_large_sparse=False)
  File "/anaconda3/lib/python3.7/site-packages/sklearn/utils/validation.py", line 521, in check_array
    "if it contains a single sample.".format(array))
ValueError: Expected 2D array, got 1D array instead:
array=[].
Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.
```

###`ValueError: Expected 2D array, got 1D array instead:`
２次元配列を読もうとしたら、１次元でしたよ？？？って何だろう。
試しに`print(train_data)`してみます。

```
[[6.5, 3.0, 5.8, 2.2], [5.0, 3.3, 1.4, 0.2], [5.1, 3.7, 1.5, 0.4], …[5.7, 2.5, 5.0, 2.0]]
```

二次元なんだけど・・・
ふと気がついたのですが、CSVを読み込んでデータセットを作るところで`else:`を忘れていました。

```
for i in range(total_len):
	data = csv[i][0:4] #先頭から４文字
	label = csv[i][4]  #５列目のIris-xxxx
	if i < train_len:
		train_data.append(data)
		train_label.append(label)
	#このelseを追加
	else:
		test_data.append(data)
		test_label.append(label)
print(train_data)
```

###あっっ！！！
データの３分の２を学習用、３分の１を予測用にしたので、else以下で作られるテストデータがなくって空のデータを渡していました。よく見たら`ValueError `の次の行に`array=[].`が出力されていました。

`print(test_data)`したら`[]`。これですね。さっそく`else` 以下、テストデータを追記、これで実行すると、

```
$ python ayame.py 
正解率＝  0.96
```

###できました！

```ayame.py
from sklearn import svm, metrics
import random ,re

csv=[]
#CSVをutf-8で開く
with open('iris.csv', 'r', encoding='utf-8') as fp:
	
	for line in fp:			#一行づづ読む
		line = line.strip() #空白を削除
		cols = line.split(',')	#カンマで区切る
		fn = lambda n : float(n) if re.match( r'^[0-9\.]+$', n ) else n
		cols = list(map(fn,cols))
		csv.append(cols)

#一行目はタイトルなので削除
del csv[0]

random.shuffle(csv)

total_len = len(csv)
train_len = int(total_len * 2 / 3)
train_data = []
train_label = []
test_data = []
test_label = []

for i in range(total_len):
	data = csv[i][0:4] #先頭から４文字
	label = csv[i][4]  #５列目のIris-xxxx
	if i < train_len:  #学習用のデータ
		train_data.append(data)
		train_label.append(label)
	else:				#テスト用のデータ
		test_data.append(data)
		test_label.append(label)

#学習開始！
clf = svm.SVC(gamma="scale")
clf.fit(train_data, train_label)

#学習をもとに予測。
pre = clf.predict(test_data)

#結果
ac_score = metrics.accuracy_score(test_label, pre)
print("正解率＝ ", ac_score)

```
テストデータを忘れていたのに気がつくのに２時間かかってしまいました。いやはや。

それにしても機械学習、、、こんなちょっとの事でこんなに大変とは。。。ゾウリムシむちゃくちゃ賢いんですね。

（所要時間 4時間）


#訓練用のデータと学習用データを分けるツール
アヤメデータの分類、訓練用のデータと学習用データを分けるツールを使ってみました。

```
import pandas as pandas
from sklearn import svm, metrics, cross_validation

csv=pd.read_csv('iris.csv')
csv_data = csv[["SepalLength","SepalWidth","PetalLength","PetalWidth"]]
csv_label= csv["Name"]

train_data, test_data, train_label, test_label = \
cross_validation.train_test_test_split(csv_data, csv_lavel)

clf=svm.SVC()
clf.fit(train_data, train_label)
pre=clf.predict(test_data)

ac_score=metrics.accuracy_score(test_label, pre)
print("正解率=", ac_score)
```

結果。
###`ImportError: cannot import name 'cross_validation' from 'sklearn'`

```
$ python ayame2.py 
Traceback (most recent call last):
  File "ayame2.py", line 2, in <module>
    from sklearn import svm, metrics, cross_validation
ImportError: cannot import name 'cross_validation' from 'sklearn' (/anaconda3/lib/python3.7/site-packages/sklearn/__init__.py)
```
`cross_validation` がimportできなかったとね？
綴りを確認→大丈夫。そこで、この本のサイトからお手本をDLして実行したら、、、同じエラーになりました。

`ImportError: cannot import name 'cross_validation' from 'sklearn'` で調べると、最近の`sklearn`では`model_selection`になったのだそう。

[ImportError: cannot import name 'cross\_validation' from 'sklearn' \[duplicate\]](https://stackoverflow.com/questions/53978901/importerror-cannot-import-name-cross-validation-from-sklearn)

では修正して実行！

###１回目

```
$ python ayame2.py
Traceback (most recent call last):
  File "ayame2.py", line 4, in <module>
    csv=pd.read_csv('iris.csv')
NameError: name 'pd' is not defined
[21:47:21 9 ch4]$ python ayame2.py
Traceback (most recent call last):
  File "ayame2.py", line 9, in <module>
    model_selection.train_test_split(csv_data, csv_lavel)
NameError: name 'csv_lavel' is not defined
```
`pd`と`pandas`を間違っていました。
`csv_lavel`のスペルをミスってました。

###２回目
```
$ python ayame2.py
/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:193: FutureWarning: The default value of gamma will change from 'auto' to 'scale' in version 0.22 to account better for unscaled features. Set gamma explicitly to 'auto' or 'scale' to avoid this warning.
  "avoid this warning.", FutureWarning)
正解率= 0.9736842105263158
```

SVCに`gamma="scale"`をセットでOK！
そして出ました、正解率！
一発目で97%ってすごいですね！
（所要時間 1時間）