機械学習

1.機械学習について

1.機械学習の分類

機械学習は、「教師あり学習」「教師なし学習」「強化学習」の3つに分類される。
教師があるかないかで、正しい答えがあるかの違い。だから、教師なしの場合は、単純なデータのグループ化(=クラスタリング)くらいしかできない。
❶教師あり
教師とは、特徴データ(または特徴量、説明変数)に関するラベル(または目的変数)がある場合。このあとのirisデータでは、 classとして、Iris-Setosa、Iris-Versicolour、 Iris-Virginicaの3つがあるが、これが教師としてのラベルである。データをもとに、新しいデータに関しても、どのラベルになるかを予測する。
❷教師なし
 たとえば、「データ同士の類似度を定義し,その定義した類似度に従って似たもの同士は同じグループに入るようにデータをグループ化するクラスタリング(R1秋高度共通PM1より)」がある
❸強化学習
 たとえば、「数式で解を求めることが難しい場合に,乱数を使って疑似データを作り,数値計算をすることによって解を推定するモンテカルロ法(R1秋高度共通PM1より)」がある。

項目 特徴 具体例
教師あり学習 説明変数に対して、目的変数が存在する 回帰、分類、ニューラルネットワーク、ディープラーニング
教師なし学習 教師というか、答えとなる目的変数が存在しない。 クラスタリング、次元削減
強化学習 ・・ 

■memo R1秋高度共通PM1
幾つかのグループに分かれている既存データ間に分離境界を定め,新たなデータがどのグループに属するかはその分離境界によって判別するパターン認識手法

■memo 過去問(H29秋2SC午前2問21)
問21 ビッグデータの解析に利用されるニューラルネットワークに関する記述のうち,適切なものはどれか。
ア 誤差逆伝旛法(バックプロパゲーション)は,ニューラルネットワーク全体の重みを調整する手法であり,調整作業は入力層から出力層に向かって行われる。
イ サポートベクタマシンは機械学習に必要な機能を実現する装置のことであり,ニn.―ラルネットワークで大量計算する際に利用される。
ウ 深層学習(ディープラーニング)に用いられるニューラルネットワークは,入力層と出力層の間に複数の中間層をもつモデルが利用される。
エ 中間層を増やしたニューラルネットワークによる訓練データを用いた学習は,訓練データ以外の未知のデータに対しても高精度な正解が導け,これを過学習(オーバフイッテイング)という。

2.教師あり学習の詳細

(1)前提条件

・概要は上で述べた通りである。
・例として、部屋の特徴というインプットデータから、部屋の値段(=アプトプット)を予測するとする
・訓練データとして、部屋の特徴と実際の部屋の値段を学習させる。このとき、実際の値段が何かという正解が分かっているので、これが教師である。
・ここがちょっと怪しいが、結果(ラベル)の値の分類

(2)連続値→回帰、離散値→分類

ラベル値は連続値の場合と離散値(値が離れている)場合があり、連続値の場合は回帰、離散値の場合は分類
❶連続値→回帰
・(ちょっと怪しいが)連続値、つまり、10万円とか20万円とかの普通の数字の場合は回帰になる。
・たとえば、「プロットされた時系列データに対して,曲線の当てはめを行い,得られた近似曲線によってデータの補完や未来予測を行う回帰分析(R1秋高度共通PM1より)」がある。
・回帰とは、y=4x+2などと、1つの変数が決まると、もう一つの変数が決まること(その様子)。線形(=線の形。おそらく直線のみ。曲線は非線形だと思う)、つまり1次式だとシンプルである。たとえば、部屋の数をx、家賃をyとして、部屋の数が1つの場合は、家賃が6万円になる、などである。実際にはもっと複雑な計算式になると思う。
・ロジスティック回帰 (LogisticRegression) は、もう少し複雑な式になると考えればいい。ただ、大きく違うのは、線形関数は、ズバリ家賃を出したが、ロジスティック回帰の場合は、その家賃になる確率を予測する。

❷離散値→分類
 離れている数字の場合、高いか安いかなどのに分離する。
・次に、未知のデータに対して予測をする

(3)学習モデルの評価

・決定係数?というか計算式を使う。これが1に近いほど、精度が高い学習が行われていることになる。
・pythonの場合、accuracy_score関数を使って、学習モデルの予測精度を評価する。
❶accuracy_score
・以下は、何かしらの分類結果をxとして、正解yと、どれくらいの精度かを判断する。10個中6個が正解なので、正解率は0.6である。
https://stackoverflow.com/questions/32664717/got-continuous-is-not-supported-error-in-randomforestregressor

from sklearn.metrics import accuracy_score
x = [0, 0, 1, 0, 1, 1, 1, 0, 1, 0]  #分類結果
y = [0, 1, 1, 0, 1, 1, 1, 1, 0, 1]  #正解
print(accuracy_score(x, y))   # ==>  0.6 正答率を表示

・ただし、使えるときと使えないときがあって、回帰(線形関数を意味していると思う)では使えない。以下のように、誤差を求める場合には使えない。エラーになる。mean_squared_errorを使う。

from sklearn.metrics import accuracy_score
x = [0, 0.9, 1.1, 0.2, 1, 1.2, 0.9, 0, 1, 0]  #データ1
y = [0, 1, 1, 0, 1, 1, 1, 0, 1, 0]  #データ2
print(accuracy_score(x, y))   # ==>  エラー

❷mean_squared_error
・以下は、誤差を判断するmean_squared_errorを使っている。平均二乗誤差を計算している。

x = [0, 0.9, 1.1, 0.2, 1, 1.2, 0.9, 0, 1, 0]  #分類結果
y = [0, 1, 1, 0, 1, 1, 1, 0, 1, 0]  #正解
from sklearn.metrics import mean_squared_error
print(mean_squared_error(x,y)) # ==> 0.011  これがデータの誤差   
(4)ロジスティック回帰分析

・予測する変数、たとえばタイタニックで死亡したのかどうかが目的変数、その基になる年齢や性別などの変数を説明変数と言う。
また、年齢などの説明変数はリアルな数字であるが、目的変数はグループ分けとしての数字になる。
・ロジスティック回帰による関数は、y=1/(1+eのA乗) ※Aはxの多次元方程式 
これをみてもらうとわかるように、結果は0<y<1の値を取る。つまり、確率が出るのである。
・ロジスティック回帰では、判別結果の(予想が正しいかの)確率を求める。

2. irisデータで試してみる

1.使用するライブラリ

scikit-learn(サイキット・ラーン)ライブラリを使う。
Linuxでインストールには、以下のコマンド。pip3かも。

pip install scikit-learn

2.irisデータについて

・機械学習について学ぶために、irisデータ(「あやめ」の花のデータ)というのが150行×4列、用意されている。
・品種は、Setosa, Versicolour, Virginicaの3つ
・項目として、ガクの長さ(sepal_length)、ガクの幅(sepal_width)、花びらの長さ(petal_length)、花びらの幅( petal_width)の4つがある。
・それをインポートするだけで、インプットの情報が使える。
・データを見るには .data 、ラベルを見るには .target 
❶まずはインポートして、先頭10行を見る

from sklearn.datasets import load_iris
x = load_iris()
print(type(x))     # ==> <class 'sklearn.utils.Bunch'> 
print(x.data[:10])   #先頭の10行だけを表示

先頭10行だけの結果は、以下になる。

[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
 [5.  3.6 1.4 0.2]
 [5.4 3.9 1.7 0.4]
 [4.6 3.4 1.4 0.3]
 [5.  3.4 1.5 0.2]
 [4.4 2.9 1.4 0.2]
 [4.9 3.1 1.5 0.1]]

❷データの概要
このデータについての記述を見てみよう。

print(x['DESCR'])

以下がその結果である。 classとして、Iris-Setosa、Iris-Versicolour、 Iris-Virginicaの3つがあることが分かる。これが、ラベルであり、教師になる。

.. _iris_dataset:

Iris plants dataset
--------------------

**Data Set Characteristics:**

    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica
                
    :Summary Statistics:

    ============== ==== ==== ======= ===== ====================
                    Min  Max   Mean    SD   Class Correlation
    ============== ==== ==== ======= ===== ====================
    sepal length:   4.3  7.9   5.84   0.83    0.7826
    sepal width:    2.0  4.4   3.05   0.43   -0.4194
    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)
    petal width:    0.1  2.5   1.20   0.76    0.9565  (high!)
    ============== ==== ==== ======= ===== ====================

    :Missing Attribute Values: None
    :Class Distribution: 33.3% for each of 3 classes.
    :Creator: R.A. Fisher
    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
    :Date: July, 1988

The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken
from Fisher's paper. Note that it's the same as in R, but not as in the UCI
Machine Learning Repository, which has two wrong data points.

This is perhaps the best known database to be found in the
pattern recognition literature.  

後半は省略した。
❸ラベル
このデータのラベルは、print(x.target)で確認できる。出力結果は、以下のように0,1,2の3つである。

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

❹その他の情報
もう少し色々とみる

from sklearn.datasets import load_iris
x = load_iris()
#データ数と項目数
print(x.data.shape)  # ==> (150, 4)
#花の種類
print(x.target_names)  # ==> ['setosa' 'versicolor' 'virginica']
print(iris.feature_names) # ==> ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

❺【参考】GitHubにあるCSVを取り込む場合は以下 ※これ以降でもこれは使わない。あくまでも参考として。

import pandas as pd
x = pd.read_csv('https://raw.githubusercontent.com/UTokyo-IPP/utpython/master/7/iris.csv')
print(x.head(5))

結果は以下。importするよりもデータが見やすい。

   sepal_length  sepal_width  petal_length  petal_width species
0           5.1          3.5           1.4          0.2  setosa
1           4.9          3.0           1.4          0.2  setosa
2           4.7          3.2           1.3          0.2  setosa
3           4.6          3.1           1.5          0.2  setosa
4           5.0          3.6           1.4          0.2  setosa

❻データをグラフ化する
以下のURLのコードをそのまま活用し、Google Colabで実行すると、グラフが表示される。
[機械学習] iris データセットを用いて scikit-learn の様々な分類アルゴリズムを試してみた - Qiita

from sklearn.datasets import load_iris
iris = load_iris()

import pandas as pd
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target
df.loc[df['target'] == 0, 'target'] = "setosa"
df.loc[df['target'] == 1, 'target'] = "versicolor"
df.loc[df['target'] == 2, 'target'] = "virginica"

df.describe()
import seaborn as sns
# Colabで絵がかけるようにする。
%matplotlib inline

sns.pairplot(df, hue="target")

結果は以下
f:id:seeeko:20201101173444p:plain

3.訓練データとテストデータ

(1)概要

・機械学習をするには、まず、学習させなければいけない、そのために使うのが「訓練データ」である。
そして、それが正しいかをテストするのが「テストデータ」である。

(2)訓練データとテストデータの分割

すでにあるデータを訓練データとテストデータに分割する。その方法には以下の方法がある。
❶ホールドアウト法
irisデータの場合、150のデータがあるが、それを一定の割合(たとえば、7:3)などにデータを分割する。
❷K-分割交差検証
クロスバリデーション法とも言われる。データをK個に分割し、K-1個のデータを訓練用、1個のデータをテスト用として、検証をK回行う。

(3)ホールドアウト法の実践

以下は、ホールドアウト法により訓練データとテストデータを7:3に分割する。実行結果も含めて確認しておこう。

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

iris = load_iris()
t = iris.data   # tに特徴量をセット
r = iris.target # rにラベルをセット

# 訓練データとテストデータに分割
t1,t2,r1,r2 = train_test_split(t,r, test_size=0.3, random_state=1, stratify=r)

# 結果を確認しておこう
print(t1[:5])   #先頭の5行だけを表示
print(t1.shape)  # ==> (105, 4) 150*0.7=105 
print(t2.shape)  # ==> (45, 4)
print(r1.shape)  # ==> (105,)
print(r2.shape)  # ==> (45,)

4.予測とその精度を確認

では、実際に訓練データを学習させて、テストデータのラベルを予想してみよう。
その前にいくつか説明する。
・fit(訓練データ、ラベル):訓練データと正解であるラベルを使ってデータの学習
・predict(テストデータ):(テストデータなどを使って)ラベルを予測

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score             #学習モデルの予測精度を評価
from sklearn.datasets import load_iris

iris = load_iris()
t = iris.data   # tに特徴量をセット
label = iris.target # rにラベルをセット

# 訓練データとテストデータに分割 train_data=訓練データ、test_dataがテストデータ、train_labelが訓練データのラベル、test_labelがテストデータのラベル
train_data,test_data,train_label,test_label = train_test_split(t,r, test_size=0.3, random_state=1, stratify=label)

x = LogisticRegression() # ロジスティック回帰のインスタンス作成
#参考までに、LogisticRegression(solver='lbfgs',  multi_class='auto')などと、回帰分析の手法を指定することも可能
x.fit(train_data,train_label)        #訓練データ(train_data)と正解であるラベル(train_label)を使ってデータの学習
y=x.predict(test_data)              #(test_dataを使って)ラベルを予測
print(accuracy_score(test_label,y)) #==> 0.9777777777777777  正答率を表示 

★最後の行であるが、2つの引数を持つが、test_labelが正解で、yが予測値。逆にしても結果は同じになる。ようは、1以下の値になる。

3.乱数から作ってみる

(1)データの作成

❶一つ目のデータ
numpyのrandomで、1~2までの乱数を作成する。これを実験データとする。

#1~2の間の乱数のペアを100個作る
import numpy as np
x = np.random.rand(100,2) + 1

#処理しやすいのでpndasに入れる。また、小数点の桁数を2に。
import pandas as pd
x_pd=pd.DataFrame(x)
pd.options.display.precision = 2   #小数点の桁数を2にする。
print(x_pd[:5])     #==> どんなデータが入っているか、5行目まで表示

#散布図を書く
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(x_pd[0],x_pd[1])

f:id:seeeko:20201108141220p:plain

y=np.random.rand(100,2) +1.8
#print(y[:10])

❷2つ目のデータも作成
・1.8~2.8までの乱数を作成する

#1~2の間の乱数のペアを100個作る
import numpy as np
x = np.random.rand(100,2) + 1
y=np.random.rand(100,2) +1.8
#処理しやすいのでpndasに入れる。
import pandas as pd
x_pd=pd.DataFrame(x)
y_pd=pd.DataFrame(y)
df= pd.concat([x_pd,y_pd])
print(x_pd[:5])     #==> どんなデータが入っているか、5行目まで表示

#散布図を書く
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df[0],df[1])
(2)テストデータを作って評価

irisデータと同じやり方で、実施する。
❶精度の確認まで
これで、値の正確性までが出力された。

#1~2の間の乱数のペアを100個作る
import numpy as np
x = np.random.rand(100,2) + 1
y=np.random.rand(100,2) +1.8
#処理しやすいのでpndasに入れる。
import pandas as pd
x_pd=pd.DataFrame(x)
x_pd[2] = 'one'
y_pd=pd.DataFrame(y)
y_pd[2] = 'two'
df= pd.concat([x_pd,y_pd])

from sklearn.model_selection import train_test_split
t=df.loc[:,[0,1]]    #0列目と1列目をデータ
r=df[2]            # 2列目がラベル
t1,t2,r1,r2 = train_test_split(t,r, test_size=0.3) # 訓練データとテストデータに分割

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

x = LogisticRegression() # ロジスティック回帰のインスタンス作成
x.fit(t1,r1)        #訓練データ(t1)と正解であるラベル(r1)を使ってデータの学習
y=x.predict(t2)              #(t2を使って)ラベルを予測
print(accuracy_score(r2,y)) #==> 0.9833333333333333  正答率を表示 

❷散布図を描く
こちらは既に説明済

%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df[0],df[1])

❸境界曲線
ロジスティック回帰の場合、訓練結果の境界線の1次式は自動で作成され、属性 intercept_、coef_ に入れられている。
式を以下とする。
w0 + w1 * x + w2 * y = 0

w2 = x.coef_[0,1]
w1 = x.coef_[0,0]
w0 = x.intercept_[0]

ここで、先ほどの散布図と同時に境界曲線を描く。直線の書き方は色々あるが、linspaceを使うとする。

#散布図を書く
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df[0],df[1])
# ==> 色を付ける場合はこのように書くといい。 plt.scatter(df[0],df[1],c=(df[2]=='one').astype(np.int)) 

#境界曲線を描く
w2 = x.coef_[0,1]
w1 = x.coef_[0,0]
w0 = x.intercept_[0]
line=np.linspace(0.5,3)   #x軸を、0.5から3の間のグラフを描く
plt.plot(line, -(w1*line+w0)/w2)

f:id:seeeko:20201108163031p:plain

❹まとめとして、全てのコードを記載

#1~2の間の乱数のペアを100個作る
import numpy as np
x = np.random.rand(100,2) + 1
y=np.random.rand(100,2) +1.8
#処理しやすいのでpndasに入れる。
import pandas as pd
x_pd=pd.DataFrame(x)
x_pd[2] = 'one'
y_pd=pd.DataFrame(y)
y_pd[2] = 'two'
df= pd.concat([x_pd,y_pd])

#データの分割
from sklearn.model_selection import train_test_split
t=df.loc[:,[0,1]]    #0列目と1列目をデータ
r=df[2]            # 2列目がラベル
t1,t2,r1,r2 = train_test_split(t,r, test_size=0.3) # 訓練データとテストデータに分割

#正答率の表示
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
x = LogisticRegression() # ロジスティック回帰のインスタンス作成
x.fit(t1,r1)        #訓練データ(t1)と正解であるラベル(r1)を使ってデータの学習
y=x.predict(t2)              #(t2を使って)ラベルを予測
print(accuracy_score(r2,y)) #==> 0.9833333333333333  正答率を表示 

#散布図を書く
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df[0],df[1],c=(df[2]=='one').astype(np.int)) #df[2]がラベルになっているので、oneのものに色を付ける、というような意味だと思う。

#境界曲線を描く
w2 = x.coef_[0,1]
w1 = x.coef_[0,0]
w0 = x.intercept_[0]
line=np.linspace(0.5,3)
plt.plot(line, -(w1*line+w0)/w2)

4.教師なし学習

・クラスタリングには、非階層的クラスタ分析と、階層的クラスタ分析がある。

1.非階層的クラスタ分析とKMeans

(1)概要

・非階層クラスタ分析の代表がK-Meansである。
・KMeans クラスをインポートする。
・n_clustersでクラスタ数を指定する。これにより、データをいくつのクラスに分類するかを指定できる。
・fit()メソッドを使うのは、教師有りと同じ。
教師有りの場合は、fit(訓練データ、ラベル)という形で、訓練データと正解であるラベルを使ってデータの学習をした。
ところが、教師なしの場合は正解のラベルが無いので、fit(訓練データ)という形になる。
・データの予想にpredictを使うのも同じ(predictは「予想する」という意)。predictによって、予想されるクラスタグループの値がセットされる。
・最後に、pandasの列に、予想されたクラスタを追加する。もちろん、クラスで色分けをすると、散布図がわかりやすい。

from sklearn.cluster import KMeans
(2)実際にやってみる

・numpyのrandomで、1~2までの乱数と、1.8~2.8までの乱数を作成する
・それを教師なし学習で、2つのクラスに分ける。

#1~2の間の乱数のペアを100個作る
import numpy as np
x = np.random.rand(100,2) + 1
y=np.random.rand(100,2) +1.8
#処理しやすいのでpndasに入れる。
import pandas as pd
x_pd=pd.DataFrame(x)
y_pd=pd.DataFrame(y)
df= pd.concat([x_pd,y_pd])

#散布図を書く
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df[0],df[1])

from sklearn.cluster import KMeans
z = KMeans(n_clusters=2) # k-meansモデル
z.fit(df) # モデルをデータに適合
z_cluster=z.predict(df) # クラスタを予測

df['cluster']=z_cluster #データフレームのclusterという属性を作成し、そこに予測されたクラスタデータを入れる。
df.plot.scatter(x=0,y=1, c='cluster', colormap='viridis') #色は、clusterという列で分けていて、色の種類はviridisというGoogle Colabのを採用していると思う。

f:id:seeeko:20201108194026p:plain

2.次元削減とPCA

(1)概要

・言葉通り、データセットの次元を削減する。
・たとえば、irisデータの場合、4つの項目がある。これをグラフ化すると2次元では表せない。
・そこで、4次元を2次元にする。このとき、全く新しい項目というか軸が作成される。
・2次元になれば、平面の図にできるので、可視化はしやすい。
・そのときに使うのが、主成分分析(PCA)で、PCAのクラスをインポートする。

from sklearn.decomposition import PCA

・n_componentsに削減後の次元数をセットする。

(2)実際にやってみよう

irisデータは4次元だが、それを2次元に次元削減する。

from sklearn.datasets import load_iris
iris = load_iris()

import pandas as pd
df = pd.DataFrame(iris.data, columns=iris.feature_names)
print(df[:5])

x = PCA(n_components=2) # PCAで2次元に
x.fit(df) # まずは、fit()でデータをセットする
df2=x.transform(df) # 次元削減をし、それをdf2とする。
print(df2[:5])

出力結果である。上が4次元のデータで、下が次元削減をした2次元データ。

   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
3                4.6               3.1                1.5               0.2
4                5.0               3.6                1.4               0.2
[[-2.68  0.32]
 [-2.71 -0.18]
 [-2.89 -0.14]
 [-2.75 -0.32]
 [-2.73  0.33]]