Upload
y-uti
View
3.137
Download
0
Embed Size (px)
Citation preview
Python 機械学習プログラミング 第 3 章
分類問題― 機械学習ライブラリ scikit-learnの活用
内山 雄司 (@y__uti)
2017-05-18 社内勉強会
自己紹介内山雄司 (@y__uti)
◦ http://y-uti.hatenablog.jp/ (phpusers-ja)
仕事◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています
興味◦ プログラミング言語処理系
◦ 機械学習
2017-05-18 社内勉強会 2
この章で学ぶこと1. scikit-learn を使ってみる
2. さまざまな分類アルゴリズムを試してみる◦ パーセプトロン
◦ ロジスティック回帰
◦ サポートベクトルマシン
◦ 決定木
◦ k 近傍法
2017-05-18 社内勉強会 3
データの準備3.2 sc ik it- learn を使ったパーセプトロンのトレーニング
2017-05-18 社内勉強会 4
Iris データセット
2017-05-18 社内勉強会 5
「あやめ」の 3 品種 150 サンプルからなるデータセット◦ データベースでいうところのテーブルだと思えばよい
◦ 150 行のレコードがあるということ
各サンプルは以下の情報 (カラムだと思えばよい) を持つ◦ がく片の長さ sepal length
◦ がく片の幅 sepal width
◦ 花びらの長さ petal length
◦ 花びらの幅 petal width
◦ 品種 class
◦ Iris-Setosa
◦ Iris-Versicolor
◦ Iris-Virginica
我々の目的あやめの品種を判別する関数を作りたい
引数◦ 花びらの長さ
◦ 花びらの幅
戻り値◦ 品種
2017-05-18 社内勉強会 6
def predict(petal_length, petal_width):...return iris_class
データを睨みながら職人芸で実装するのではなく
データから自動的にルールを見つけ出したい
データセットの分割何のために?◦ あやめの品種を判別する関数が得られたとしましょう
◦ その関数を使って「当たった!」とか「外れた」とか遊びたい
◦ ところが手持ちのデータは全部で 150 個しかない
◦ 遊ぶためのデータを 150 個のサンプルから最初に分けておく
なぜ「分けておく」必要が? (同じデータでは駄目?)
◦ 関数を作るときに使ったデータを判別に使うと「ズル」ができる
◦ たとえば丸暗記
2017-05-18 社内勉強会 7
層化抽出法手持ちのデータは 3 品種それぞれ 50 サンプル
教科書の方法で分割すると各品種の比率は維持されない
2017-05-18 社内勉強会 8
>>> from collections import Counter>>> sorted(Counter(y_train).items())[(0, 34), (1, 32), (2, 39)]
層化抽出法:母集団の比率を維持してサンプリングする◦ train_test_split 関数の stratify オプション
>>> X_train, ... = train_test_split(X, y, ..., stratify=y)
>>> sorted(Counter(y_train).items())[(0, 35), (1, 35), (2, 35)]
特徴量のスケーリング何のために?◦ アルゴリズムそのものに由来する問題
◦ コンピュータでの計算上の都合
具体的には・・・1. 特徴量が不揃いだと良い結果を得られないアルゴリズムがある
◦ たとえば k 近傍法 (距離の定義による)
2. 特徴量が不揃いだと遅くなるアルゴリズムがある◦ たとえばパーセプトロン (パーセプトロンの収束定理)
3. 特徴量が不揃いだと数値計算上の問題を招くアルゴリズムがある◦ たとえばサポートベクトルマシン
◦ see sec. 2.2 in "A Practical Guide to Support Vector Classification"
◦ https://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf
2017-05-18 社内勉強会 9
標準化データを次の操作で変換する1. 平均が 0 になるように平行移動する
2. 標準偏差が 1 になるように定数倍する
2017-05-18 社内勉強会 10
>>> X_train_tmp = X_train - np.mean(X_train, 0)>>> X_train_std = X_train_tmp / np.std(X_train_tmp, 0)
テストデータの標準化トレーニングデータと「同じ値だけ」動かす
2017-05-18 社内勉強会 11
>>> X_test_tmp = X_test - np.mean(X_train, 0)>>> X_test_std = X_test_tmp / np.std(X_train, 0)
パーセプトロン3.2 sc ik i t - learn を使ったパーセプトロンのトレーニング
2017-05-18 社内勉強会 12
パーセプトロンアルゴリズムの説明は第 2 章で既出なので省略
実装と実行手順も教科書のとおりなので省略
2017-05-18 社内勉強会 13
One vs Rest classifier二クラス分類器を使って多クラス分類を実現する方法の一つ
例:Iris データセットの場合
次の 3 個の二クラス分類器を学習する1. Setosa か「それ以外」か⇒判別関数の値が正なら Setosa
2. Versicolor か「それ以外」か⇒判別関数の値が正なら Versicolor
3. Virginica か「それ以外」か⇒判別関数の値が正なら Virginica
多クラス分類の方法◦ 学習した 3 個の分類器でそれぞれ判別関数の値を求める
◦ 判別関数の値が最大となったクラスを採用する◦ 個々の値を見れば「全て正」や「全て負」になっているかもしれないが気にしない
2017-05-18 社内勉強会 14
パーセプトロンの学習率パーセプトロンでは学習率を調整する必要はない◦ この点に関する教科書の記述は誤り
◦ eta0 の既定値は 1.0 であり既定値のまま使えばよい
参考 (他の教科書など)
◦ 『パターン認識と機械学習』4.1.7 パーセプトロンアルゴリズム◦ "一般性を失うことなく学習率パラメータ τ を 1 に設定することができる" (p. 192)
◦ 『オンライン機械学習』アルゴリズム 3.1 (p. 24)
◦ アルゴリズムを説明した擬似コードに学習率は表れない
◦ Perceptron - Wikipedia
◦ https://en.wikipedia.org/wiki/Perceptron
◦ "Learning Algorithm" の記述に学習率は表れない
◦ scikit-learn の API ドキュメント
◦ http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Perceptron.html
◦ eta0 の既定値が 1.0 であることを確認できる
2017-05-18 社内勉強会 15
ロジスティック回帰3.3 ロジスティック回帰を使ったクラスの確率のモデリング
2017-05-18 社内勉強会 16
ロジスティック回帰の結果教科書のコード例と同じ設定での実行結果
◦ C = 1000
◦ 一対他の手法で多クラス分類を実現 (パーセプトロンと同じ)
2017-05-18 社内勉強会 17
ロジスティック回帰 [1/4]例:virginica と「それ以外」を分類
まずは特徴量の重み付け和を考える (パーセプトロンと同じ)
2017-05-18 社内勉強会 18
z = 2.0 * petal_length - 1.5 * petal_width - 0.2
ロジスティック回帰 [2/4]例:virginica と「それ以外」を分類
シグモイド関数を用いて 0 ~ 1 の範囲に押し込める
2017-05-18 社内勉強会 19
phi = 1.0 / (1.0 + np.exp(-z))
ロジスティック回帰 [3/4]例:virginica と「それ以外」を分類
コスト関数 (= 分類のはずれ具合の総和) を考える
2017-05-18 社内勉強会 20
J = np.sum(-y * np.log(phi(z)) - (1-y) * np.log(1-phi(z)))
J = 59.78
ロジスティック回帰 [4/4]例:virginica と「それ以外」を分類
コスト関数を小さくする方向に重みベクトルを更新する
2017-05-18 社内勉強会 21
z = 1.0 * petal_length + 1.0 * petal_width - 1.0
J = 27.94
正則化正則化とは?◦ モデルの複雑さを抑えるテクニック
◦ 具体的には「重みベクトルの大きさに応じたペナルティ」を足す
何のために?◦ パラメータ数が多いと過学習が発生する
◦ そういうモデルはテストデータを予測できない
(たぶん) よく使われる「ペナルティ」の種類◦ L2 正則化 重みベクトルの大きさの二乗に比例
◦ L1 正則化 重みベクトルの大きさに比例
◦ elasticnet L2 + L1
2017-05-18 社内勉強会 22
コストパラメータ [1/2]コスト関数 (= 分類のはずれ具合の総和) に掛ける係数◦ 小さな値に設定⇒重みベクトルを小さくすること (正則化項) を重視
◦ 大きな値に設定⇒学習データを分類することを重視
2017-05-18 社内勉強会 23
コストパラメータ [2/2]ロジスティック回帰の場合◦ 下図は petal length = petal width に沿って動かしたときの様子
◦ Versicolor (橙) は殆ど変化しない。他の分類器の影響で結果が変わる
2017-05-18 社内勉強会 24
線形 SVM3.4 サポートベクトルマシンによる最大マージン分類
2017-05-18 社内勉強会 25
SVM の概要決定境界のなかで一番「幅を広く」できるものを求める◦ どうやってそんなことが!
◦ 難しいので説明しないがとにかくできる
下図は Setosaと「それ以外」を二値分類した様子◦ 中央が SVM で得られる結果
2017-05-18 社内勉強会 26
SVM のコスト関数(導出過程はさておき) 結果として SVM は◦ 「ヒンジ損失 + L2 正則化」をコスト関数として最適化する
いろいろな損失関数 (右図)
◦ 青:パーセプトロン
◦ 橙:ロジスティック回帰
◦ 緑:ヒンジ損失
2017-05-18 社内勉強会 27
線形 SVM の実行結果教科書のコード例と同じ設定での実行結果
2017-05-18 社内勉強会 28
>>> svm = SVC(kernel='linear', C=1.0, random_state=0)
◦ scikit-learn の SVC は libsvm を利用する
◦ https://www.csie.ntu.edu.tw/~cjlin/libsvm/
LinearSVCでの実行結果線形 SVM での分類には LinearSVCも利用できる
2017-05-18 社内勉強会 29
>>> svm = LinearSVC(random_state=0)
◦ LinearSVCは liblinear を利用する
◦ https://www.csie.ntu.edu.tw/~cjlin/liblinear/
SGDClassifier一括学習 (batch learning)
◦ すべての学習データを読み込んでから学習する
◦ LogisticRegression, SVC, LinearSVCはこちら◦ 内部で利用している libsvm や liblinear が一括学習のため
◦ LogisticRegression は solver を選べる。solver='liblinear' がデフォルト
逐次学習 (online learning)
◦ 学習データを一つずつ使って学習する
◦ Perceptron はこちら (教科書の記述は誤り)
◦ 確率的勾配降下法 (Stochastic Gradient Descent)
◦ http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html
◦ データ量が多い場合やストリームで供給される場合などに有用
2017-05-18 社内勉強会 30
カーネル SVM3.5 カーネル SVM を使った非線形問題の求解
2017-05-18 社内勉強会 31
カーネル SVM の威力 [1/4]以下のようなデータを考える◦ 左図:X と Y の符号が同じなら 0, 符号が異なっていたら 1
◦ 右図:原点からの距離の 2 乗が 2 未満なら 0, 2 以上なら 1
2017-05-18 社内勉強会 32
カーネル SVM の威力 [2/4]このようなデータは線形分離不可能◦ 下図は線形 SVM を適用して学習した結果
◦ まともに動かない
2017-05-18 社内勉強会 33
カーネル SVM の威力 [3/4]カーネル SVM を適用する◦ 下図は「二次の多項式カーネル」を用いて学習した結果
◦ なんだこれは!
2017-05-18 社内勉強会 34
カーネル SVM の威力 [4/4]カーネル SVM を適用する◦ 下図は「RBF カーネル」を用いて学習した結果
◦ なんだこれは!
2017-05-18 社内勉強会 35
カーネル SVMの概要 [1/2]次の二つのアイデアの組み合わせ1. 特徴量を「水増し」して高次元空間で線形分類する
2. 本当に水増しするのではなく「カーネル関数」で効率的に計算する
特徴量の水増し◦ 元々の特徴量が 2 種類 (x1, x2) だったら・・・
◦ 2 次の項を追加すれば 5 種類
◦ x1*x1
◦ x1*x2
◦ x2*x2
◦ 3 次の項も追加すれば 9 種類
◦ x1*x1*x1
◦ x1*x1*x2
◦ x1*x2*x2
◦ x2*x2*x2
2017-05-18 社内勉強会 36
2 次の項を入れた 5 次元空間ならこれらはアッサリ分類できる
カーネル SVM の概要 [2/2]次の二つのアイデアの組み合わせ1. 特徴量を「水増し」して高次元空間で線形分類する
2. 本当に水増しするのではなく「カーネル関数」で効率的に計算する
カーネル関数◦ 単純に特徴量を増やすと次元の数が爆発して実用にならない
◦ 重みベクトルとの内積さえ計算できればよい
例:2 次の多項式カーネル (本当はもう少し細かなパラメータがあります)
2017-05-18 社内勉強会 37
z = (1 + w1*x1 + w2*x2)^2= 1 + 2 * w1 * x1
+ 2 * w2 * x2+ w1*w1 * x1*x1+ 2 * w1*w2 * x1*x2+ w2*w2 * x2*x2
このスライドは色々と間違えています
カーネル SVM の実行結果教科書のコード例と同じ設定での実行結果
◦ kernel = 'rbf'
◦ gamma = 0.2
◦ C = 1.0
2017-05-18 社内勉強会 38
gamma, C による変化 [1/2]
2017-05-18 社内勉強会 39
gamma, C による変化 [2/2]
2017-05-18 社内勉強会 40
決定木3.6 決定木学習
2017-05-18 社内勉強会 41
決定木のアルゴリズムIf-else のネスト構造でデータを判別する
2017-05-18 社内勉強会 42
if petal_width <= 0.75:return 0 # Iris-Setosa
else:if petal_length <= 4.95:
return 1 # Iris-Versicolorelse:
return 2 # Iris-Virginica
分岐条件をどのように決めるか?◦ 各クラスが then, else に「なるべく綺麗に分かれる」条件を探す
◦ ジニ不純度
◦ エントロピー
決定木の実行結果max_depth = 3 での実行結果◦ criterion='entropy' で実行 (教科書と同じ)
◦ デフォルトは criterion='gini'
2017-05-18 社内勉強会 43
木の深さによる結果の変化
2017-05-18 社内勉強会 44
エントロピー情報理論の何やら難しい概念◦ ここでは「各クラスのサンプルの混ざり具合い」だと思えばよい
2017-05-18 社内勉強会 45
def entropy(counts):return np.sum([
-p * np.log2(p) if p > 0 else 0for p in counts / np.sum(counts)])
>>> print(entropy([50, 50, 50])) # 1.58496250072>>> print(entropy([20, 30, 50])) # 1.48547529723>>> print(entropy([ 0, 30, 50])) # 0.954434002925>>> print(entropy([ 0, 10, 50])) # 0.650022421648>>> print(entropy([ 0, 0, 50])) # 0.0
◦ -p * log2(p) は p = 0 で計算不能だが +0 の極限が 0 なので 0 とする
閾値ごとのエントロピー
2017-05-18 社内勉強会 46
ランダムフォレスト次の方法で「雑な」決定木をたくさん作って多数決で判別する
1. トレーニングデータの一部をランダムに取り出す (復元抽出)
2. 取り出したデータだけを使って決定木を作る◦ 各ノードで分割するときには特徴量の一部をランダムに取り出す
◦ 取り出した特徴量だけを候補にして分割の閾値を決める
2017-05-18 社内勉強会 47
k 近傍法3.7 k 近傍法:怠惰学習アルゴリズム
2017-05-18 社内勉強会 48
k 近傍法のアルゴリズム学習データセットを丸ごと保持する
次の手順で判別する1. 最も近い k 個のデータを探す
2. その k 個の多数決で決める
Iris データセットでの例 (右図)
◦ k = 5
◦ 座標 (0.5, 0.55) の品種を判別
◦ Versicolor 3 個 vs. Virginica 2 個
◦ Versicolor と判別する
2017-05-18 社内勉強会 49
k 近傍法の実行結果k = 5 での実行結果◦ 距離尺度は metric='euclidean' で実行
◦ 教科書に書かれている metric='minkowsky', p=2 と同じ
2017-05-18 社内勉強会 50
k の値による結果の変化
2017-05-18 社内勉強会 51
多数決が同数の場合 [1/2]scikit-learn の実装では「クラスの順序」に依存する◦ scipy.stats.modeを利用しているため
◦ 教科書 91 ページの囲みの記述は誤り
参考◦ scikit-learn の実装
◦ https://github.com/scikit-learn/scikit-learn/blob/0.18/sklearn/neighbors/classification.py
◦ scipy.stats.modeのドキュメント
◦ https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.stats.mode.html
weights='distance' パラメータ
◦ "weight points by the inverse of their distance"
◦ これは「多数決が同数の場合に距離が近いものを優先」ではない
2017-05-18 社内勉強会 52
多数決が同数の場合 [2/2]人工的なデータに k 近傍法を適用した結果
2017-05-18 社内勉強会 53
metric パラメータデータによってはユークリッド距離では「近さ」を測れない◦ 緯度経度で表されたデータ
◦ booleanを要素とするデータ
データ間の距離を計算する方法を指定できる
参考
◦ sklearn.neighbors.DistanceMetric
◦ http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.DistanceMetric.html
2017-05-18 社内勉強会 54
おわり
2017-05-18 社内勉強会 55