Machine Learning with Scikit Learn (Part I)
今年の7月に開催されたSciPy2015の講演動画がEnthoughtのチャンネルで公開されている。今年も面白い講演が多いのでいろいろチェックしている。
今年の目標(2015/1/11)にPythonの機械学習ライブラリであるscikit-learnを使いこなすというのが入っているので、まずはscikit-learnのチュートリアルを一通り見ることにした。
Part IとPart IIを合わせると6時間以上あり非常に充実している。IPython Notebook形式の資料やデータは下記のGitHubアカウントで提供されている。ノートブックをダウンロードし、実際に手を動かしながらチュートリアルを進めると理解がより進むかもしれない。
あとで振り返りやすいように内容を簡単にまとめておきたい。
1.1 Introduction to Machine Learning
機械学習システムの流れ。教師あり学習と教師なし学習の違い。訓練データとテストデータは分けましょうという一般的なお話。
1.2 IPython Numpy and Matplotlib Refresher
scikit-learnのベースとなるNumPy、SciPy、Matplotlibのおさらい。言語処理では疎行列表現(Sparse Matrix)が効率的なのでscipy.sparse
を使える。NumPy、SciPy、Matplotlibは非常に巨大なライブラリなので別途習得する必要がある。NumPyやMatplotlibについては以下の講演が過去にあった。これらもあとでチェックしておこう。
- Introduction to NumPy | SciPy 2015 Tutorial | Eric Jones - YouTube
- Matplotlib: past, present and future; SciPy 2013 Presentation - YouTube
- Anatomy of Matplotlib - Part 1 | SciPy 2014 | Benjamin Root - YouTube
np.newaxis
を使った列ベクトルへの変換法は覚えておくと便利。1次元配列を行がサンプル、列が特徴量のscikit-learnのデータ形式に変換するときなどに使える。
# make into a column vector print y[:, np.newaxis]
1.3 Data Representation for Machine Learning
scikit-learnのデータ表現は、行がサンプル、列が特徴量の2次元配列で表す。有名なデータセットはsklearn.datasets
モジュールでロードまたは生成できる。iris、digits、S-curve、olivetti_facesが例として取り上げられている。顔画像データなんてのも提供されているのか。あとで使ってみたいな。
1.4 Training and Testing Data
訓練データとテストデータの作り方。データはシャッフルされていないこともあるので必ずシャッフルする。データのシャッフルは、permutation
をインデックスに指定すると簡単にできる。
import numpy as np rng = np.random.RandomState(0) permutation = rng.permutation(len(X)) X, y = X[permutation], y[permutation]
訓練データとテストデータの分割は、sklearn.cross_validation.train_test_split()
を使うと簡単にできる。random_state
に乱数の種を指定すれば自分でシャッフルしなくてもOK。
from sklearn.cross_validation import train_test_split train_X, test_X, train_y, test_y = train_test_split(X, y, train_size=0.5, random_state=1999)
2.1 Supervised Learning - Classification
教師あり学習の一種である分類について。sklearn.datasets.make_blobs()
で人工的に生成した二次元データを使用。scikit-learnでは、どの機械学習アルゴリズムも
- 分類器のオブジェクトを生成
fit()
に訓練データを与えて分類器のパラメータを学習predict()
にテストデータを与えて予測score()
で分類器の性能を評価
というAPIで統一されているので覚えやすい。例としてロジスティック回帰とK-近傍法の例が紹介されている。
from sklearn.linear_model import LogisticRegression classifier = LogisticRegression() classifier.fit(X_train, y_train) prediction = classifier.predict(X_test) classifier.score(X_test, y_test)
学習したパラメータは、分類器オブジェクトのプロパティから取得できる。
print(classifier.coef_) print(classifier.intercept_)
K-近傍法の例。ロジスティック回帰とメソッド名が同じなので覚えやすい。分類器を作成するときにアルゴリズムによって独自のパラメータを指定できる。
from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=1) knn.fit(X_train, y_train) knn.score(X_test, y_test)
2.2 Supervised Learning - Regression
教師あり学習の一種である回帰について。回帰は教師信号が連続値である点が分類と異なる点。例として線形回帰とK-近傍法が取り上げられている。K-近傍法は分類だけでなく、回帰にも使えるのか。
from sklearn.linear_model import LinearRegression regressor = LinearRegression() regressor.fit(X_train, y_train)
from sklearn.neighbors import KNeighborsRegressor kneighbor_regression = KNeighborsRegressor(n_neighbors=1) kneighbor_regression.fit(X_train, y_train)
2.3 Unsupervised Learning - Transformations and Dimensionality Reduction
教師なし学習のデータの標準化と次元削減について。
データの標準化を学習とは言わないような気がするけど、scikit-learnでは他の学習器と同じインタフェースで実装されている。たとえば、平均を0、分散を1にする正規化は、StandardScalar
を使って以下のように書ける。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(X) X_scaled = scaler.transform(X)
標準化はデータの平均、標準偏差というパラメータを推定するという見方もできるためfit()
というメソッドでも特に違和感はない。学習したモデルを使ってデータを変換する(標準化する)ときはpredict()
ではなく、transform()
というメソッドが定義されている。
主成分分析もほとんど同じように書ける。
from sklearn.decomposition import PCA pca = PCA(n_components=2) pca.fit(X_blob) X_pca = pca.transform(X_blob)
n_components
で固有値が大きい順にいくつの固有ベクトル(軸)を取るか指定している。この例では、n_components=2
なのでデータを2次元平面に圧縮することを意味する。次元削減はtransform()
で行える。X_blob
が元のデータでX_pca
が低次元空間に写像したデータになる。8x8ピクセル=64次元の数字画像データを2次元に写像すると下の結果が得られる。
高次元データを低次元のデータに縮約する手法をまとめて多様体学習(manifold learning)と呼ぶようだ。有名どころは主成分分析や多次元尺度構成法(MDS)だが、それ以外にも様々な手法が提案されており、一部はsklearn.manifold
に実装されている。たとえば、t-SNEを同じ数字画像データに適用すると
となり2次元空間で元の数字をきれいに分類できるようになる。この分野は非常に興味があるので勉強してみたいところ。
2.4 Unsupervised Learning - Clustering
教師なし学習の代表であるクラスタリングについて。有名なK-meansが例として取り上げられている。
from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=3, random_state=42) labels = kmeans.fit_predict(X)
K-meansはユーザがクラスタ数をあらかじめ指定する必要がある。この例ではクラスタ数を3としている。講演では、K-means以外の様々なクラスタリング手法も紹介されている。
こんなにいろいろあったのか。K-meansとWard法くらいしか知らなかった。私の主観的なクラスタの判断だとDBSCANってのが一番近いかな?ここもしっかり勉強してみたいところ。個人的に教師あり学習よりも教師なし学習や表現学習の分野の方が興味ある。
2.5 Review of Scikit-learn API
scikit-learnで使われる fit()
, predict()
, transform()
などのメソッド名についてのまとめ。教師あり学習、教師なし学習で提供されているメソッド名が異なる。さらにアルゴリズムによってもメソッドの意味が異なる場合がある。たとえば、score()
は教師あり学習では分類精度、平均二乗誤差を返すが、一部の教師なし学習では尤度を返す。このノートブックにまとめられているので適宜参照したい。
3.1 Case Study - Supervised Classification of Handwritten Digits
ここからしばらく具体的なケーススタディが続く。最初は手書き数字認識。
- 手書き数字データは
sklearn.datasets.load_digits()
でロードできる。 - データ数が多いときはPCAより高速な
sklearn.decomposition.RandomizedPCA
を使ってデータの分布を可視化する。 - PCAは線形の次元圧縮なのでデータの面白い関係を見逃している可能性がある。そのような場合は、
sklearn.manifold.Isomap
のような非線形な多様体学習手法を使う。 - まずは簡単で高速な分類器(たとえば、
sklearn.naive_bayes.GaussianNB
)を使ってベースラインとする。 - テストデータを用いて定量的な評価をする。分類問題では
model.score()
が使える。また、適合率・再現率・F1値をまとめて返すsklearn.metrics.classification_report()
やConfusion Matrixを返すsklearn.metrics.confusion_matrix()
も便利。
from sklearn import metrics print metrics.classification_report(expected, predicted) precision recall f1-score support 0 1.00 1.00 1.00 37 1 0.68 0.91 0.78 43 2 1.00 0.39 0.56 44 3 0.76 0.84 0.80 45 4 0.93 0.71 0.81 38 5 0.98 0.90 0.93 48 6 0.95 1.00 0.97 52 7 0.70 0.98 0.82 48 8 0.55 0.77 0.64 48 9 1.00 0.57 0.73 47 avg / total 0.85 0.81 0.80 450
print metrics.confusion_matrix(expected, predicted) [[37 0 0 0 0 0 0 0 0 0] [ 0 39 0 0 0 0 1 0 3 0] [ 0 9 17 3 0 0 0 0 15 0] [ 0 0 0 38 0 0 0 2 5 0] [ 0 1 0 0 27 0 2 8 0 0] [ 0 1 0 1 0 43 0 3 0 0] [ 0 0 0 0 0 0 52 0 0 0] [ 0 0 0 0 1 0 0 47 0 0] [ 0 5 0 1 0 1 0 4 37 0] [ 0 2 0 7 1 0 0 3 7 27]]
3.2 Methods - Unsupervised Preprocessing
Labeled Faces in the Wildの顔画像データに対して主成分分析を適用する。いわゆる固有顔(eigenface)の実験。このデータもメソッド一つでロードできる。
from sklearn import datasets lfw_people = datasets.fetch_lfw_people(min_faces_per_person=70, resize=0.4, data_home='datasets')
これが訓練データの平均顔。著作権の問題からブッシュ元大統領の顔写真が多いんだってさ(笑)
これが各固有ベクトルを画像化した固有顔。
最初の数個の軸は画像の明るさの違いを表していることがわかる。これら固有顔の線形結合でいろいろな顔が表せる。
3.3 Case Study - Face Recognition with Eigenfaces
オリジナル画像の1850次元の顔画像データを150次元の固有顔空間に次元圧縮したデータを用いて顔認識の実験。分類にはサポートベクトルマシン(SVM)を使用。
from sklearn import svm clf = svm.SVC(C=5., gamma=0.001) clf.fit(X_train_pca, y_train)
メソッド名はsklearn.svm.SVC
という名前なので注意。Support Vector Classificationの略。sklearn.svm.SVR
という回帰版もある。こちらはSupport Vector Regressionの略。
オリジナル画像データをそのまま使うと40%くらいの精度しか出ないが、固有顔に次元圧縮してから分類すると85%くらいの精度が出る。
PCAをしてからSVMのような流れ作業はPipeline
の機能を使うと便利。
from sklearn.pipeline import Pipeline from sklearn import metrics clf = Pipeline([('pca', decomposition.RandomizedPCA(n_components=150, whiten=True, random_state=1999)), ('svm', svm.SVC(C=5., gamma=0.001))]) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print metrics.classification_report(y_pred, y_test)
3.4 Methods - Text Feature Extraction
ここから対象がテキストになる。英語のテキストを特徴量に変換するbag-of-wordsの作り方について。CountVectorizer
を使うとテキスト集合を単語の出現頻度を用いたbag-of-words表現に簡単に変換できる。
from sklearn.feature_extraction.text import CountVectorizer X = ["Some say the world will end in fire,", "Some say in ice."] vectorizer = CountVectorizer() vectorizer.fit(X) X_bag_of_words = vectorizer.transform(X).toarray()
単語の頻度TFだけでなく、逆文書頻度IDFも考慮したTF-IDFもTfidfVectorizer
を使うと簡単に求められる。IDFを考慮すると多くの文書に出てくる単語が一般的な単語と判断されてその重要度が低くなる。
from sklearn.feature_extraction.text import TfidfVectorizer tfidf_vectorizer = TfidfVectorizer() tfidf_vectorizer.fit(X) tfidf = tfidf_vectorizer.transform(X).to_array()
単語単位ではなく、N-gram単位でbag-of-wordsやTF-IDFを求めたい場合は、ngram_range
オプションを指定すればOK。
# look at sequences of tokens of minimum length 2 and maximum length 2 bigram_vectorizer = CountVectorizer(ngram_range=(2, 2)) bigram_vectorizer.fit(X)
日本語では形態素解析が必要なのでこれらのメソッドはそのままでは使えなさそう。誰か日本語用の拡張も書いてたりするかな?
3.5 Case Study - SMS Spam Detection
テキストをbag-of-words表現に変換し、スパムのテキスト分類を行う例が紹介されている。訓練データの文章数は4180でボキャブラリ数は7464。ロジスティック回帰を使うと98%の分類精度が出ている。
3.6 Case Study - Titanic Survival
講演では時間が足りなくて取り上げられなかった(PartIIの最後に時間があまったため説明されていた)。カテゴリ特徴量を使うときは、各カテゴリに数値を割り当てるのではなく、いわゆる1-of-K表現にしましょうというお話。DictCategorizer
を使うと簡単にできる。以下の例では、カテゴリ特徴量 city = {Dubai, London, San Francisco}
と数値特徴量 temperature
が混ざったデータをベクトル化している。
measurements = [ {'city': 'Dubai', 'temperature': 33.}, {'city': 'London', 'temperature': 12.}, {'city': 'San Francisco', 'temperature': 18.}, ] from sklearn.feature_extraction import DictVectorizer vec = DictVectorizer() vec.fit_transform(measurements).toarray()
実行結果は、
array([[ 1., 0., 0., 33.], [ 0., 1., 0., 12.], [ 0., 0., 1., 18.]])
カテゴリ特徴量のみ1-of-K表現になって、数値特徴量はそのままになることがわかる。より複雑なデータセットとしてカテゴリ特徴量と数値特徴量が混ざったデータであるTitanic Dataを例に説明している。
ここで動画のPart Iは終わり。Part IIにつづきます。