機械学習を仕事で使うため、Udemyで色々と受講しています。
その中で、「Pythonで機械学習:scikit-learnで学ぶ識別入門」を受講したので、そのメモです。
感想
非常に充実しています。
オープニングで、扱っているのは予測ではなく識別のみと宣言しているため、内容が統一されていると感じます。
ボリュームはかなりあります。単に講座を聞くだけなら2~3日で終わりますが、講座を聞いて、手元のJupyter notebookで理解しながらコードを確認・修正して..とやっていると、社会人だと6~7日は必要かと思います。
データ分割~学習~認識の流れで、学習に進んだと思ったらデータ分割の別の方法の説明があるなどで、飛ぶことがある印象です。また、同じ識別器でもサンプルデータ・癌データでセクションが別なので、類似説明が続く印象もあります。その場合はスキップしてよいかと。最後にまとめがあると嬉しかったかもしれません。
なので、その要約です。トピックの紹介なので、詳細は他詳しいwiki/qiitaで調べたほうがよいです。
テストのための大まかなフロー
学習データ(含む訓練データ)とテストデータの分割
- Cross-Validationを実施する
- 分割方法は、
ShuffleSplit
、KFold
、StratifiedKFold
が有名。 - Leave one outやLeave one group outもある。Leave P outもあるが使われない。
- 学習サンプルに応じて何の手法を使うか決める必要があるが、ビッグデータの場合は複雑に分割することは難しい。KFoldが無難。
- 学習時、クラスにランダム性を持たせるため
Stratified(StratifiedKFold)
を利用することが重要。
欠損値・特徴量の加工
- nanの値を
np.isnan
で見つけ、NaNを平均(Imputer
からtransform
した値)で埋める / medianで埋める(Imputer(strategy='median')
) / 除外する (個々のケースによる) - 全ての特徴が識別に有効ではない場合もある。その場合は特徴量を抽出するか、削減する。例えば以下。
-
SelectKBest
: 影響する特徴を自動的に抽出する -
PCA(主成分)
: 相関が高い2つの特徴をマージして特徴量を減らす -
PolynomialFeatures
: 非線形変換をおこなうことで、別の特徴量を算出する。例えば、$x1$,$x2$,$x3$ があった場合に、別の特徴量として、$x1\times x2$ をつくる。ただ、増やすと特徴量が大幅に増えるので、増やしたらPCAで削減することも検討すること。最後の手段であり、あまり使わない。
標準化・スケーリング・正規化(重要)
-
StandardScaler
(標準化): fit~transformすることで、特徴量を平均0, 標準偏差1にする。 -
MinMaxScaler
(スケーリング):最大最小をある範囲に抑える。
scaler=MinMaxScaler([-1, 1])
のように設定する。 -
Normalizer
(正規化): サンプル毎に正規化する。そのため、fitは不要。transformだけ実行すればよい。 -
PCA Whitening
: PCAだけだと次元を減らす。しかし分散が大きいので、whiten=Trueにすることで、平均0, 分散1にすることができる。 -
ZCA Whitening
: PCAと類似しているが、ディープラーニングの画像認識で使われている。PCAより性能がでるとのこと。ここはもう少し理論を勉強する必要がありそう。。
学習データの評価
TP: True Positive
TN: True Negative
FP: False Positive
FN: False Negative
ちょっと覚えにくい。。
TP/TNはOK, FP/FNは予測と異なるのでNGということ。
-
classification_report
: precision/recall/f1-score/supportを算出する。 - recallは再現率(True Positive Rate)。$TP / (TP + FN)$ の計算式。実際に正で、予測が正だった割合。
- precisionは適合度。$TP / (TP + FP)$。予測が正で、実際に正だった割合。
- Fはprecisionとrecallの調和平均で、予測精度の指標を表す。
- これらは、ROC曲線/AUCで利用する。ROC曲線は、縦軸にTP、横軸にFPの割合を2次元プロットして点を線で連結した曲線。AUCはArea Under the Curveのこと。ROC曲線の曲線よりしたの面積。分類器の精度評価に使う。
色々な識別器
ロジスティック回帰・SVM(線形非線形)、ランダムフォレスト、(多層)パーセプトロン、k近傍識別器あたりが説明の中心です。最初はOneVsRestの説明、その後は各識別器の説明でした。かいつまんで記載します。
-
ロジスティック回帰のOne-vs-Rest:One-vs-Rest: 例えば3クラス分類の際、あるクラスAと、それ以外のB/Cの識別器を作る。ロジスティック回帰でもone-vs-oneはあるにはあるが、sklearnでは実装されていない。
-
SVMのOne-vs-Rest: ovr指定にて、one-vs-restになる。
svm.SVC(kernel='linear', decision_function_shape='ovr')
-
SVMのOne-vs-One:
ovo
を設定する。ovrと異なり、1:1で識別器を作成する。 ovoはコンビネーションで増えてしまうので、クラス数が多いと使われない。 -
k近傍識別器(kNN: neighbors): 以下のようにして使う。
sklearn import neighbors
clf = neighbors.KNeighborsClassifier(n_neighors=N)
# Nは自身からの距離を示す。N=3なら、3番目に近いところまでを含める。
clf.fit(X, y)
- radius NN(RadiusNeighborsClassifier): ある半径の中に入っているもののサンプルで多数決を取る。
clf = neighbors.RadiusNeighborsClassifier()
clf.fit(X_train, y_train)
-
パーセプトロン:
講座ではパーセプトロンの動き・損失関数を可視化して説明。損失関数は、正の値の際には変更しない。
基本は単独では使わない。線形識別の場合、ロジスティック回帰が基本使われる。
from sklearn.linear_model import Perceptoron
clf = Perceptron() # default n_iter=5(epoch)
# 既に学習済の場合、clf.warm_start = Trueで、前回の結果を流用する
clf.fit(X, y)
- ロジスティック回帰: 損失関数がパーセプトロンとは異なり、正しい値でも一部を損失に含める。講座では頻繁に登場。
clf = LogisticRegression()
clf.fit(X_Train, y_train)
clf.predict(X_test)
-
SVM:
損失関数はパーセプトロンに類似しているが、正の値でも少しだけ損失を加える。識別境界に近いものは損失を加える。線形非線形があり、非線形がよく使われる。線形の場合、LinearSVCを使うべき。
from sklearn.svm import SVC
clf = SVC(kernel='linear')
# 確率を算出するには、fit前にprobability = Trueを設定しておく必要がある。
# 非線形カーネル(rbf/poly)もある。
# デフォルトはrbf。linearの場合、LinearSVCが用意されているので、そちらを使うほうが高速。
# polyは、スケーリングしておかないと計算が終わらない可能性あり。
-
MLP(多層パーセプトロン):
パーセプトロンにおいて、中間層を複数にしたモデル。
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(random_state=8)
clf.alpha = 1 # 仮
clf.max_iter = 2000 # 仮
hidden_layer_sizes = (10,10,5) # 中間層3つ(計5層)の例
-
ランダムフォレスト:
決定木の応用で、決定木(sklearnのDecisionTreeClassifier()
)が多数集まった構成。使うツリーの数をn_estimators
で変更する。n_estimators=1
なら決定木と同じ。本講座では分類だけだが、ランダムフォレストは回帰でも利用可能(RandomForestRegressor
)。
また、ランダムフォレストではスケーリングの効果はない。縦軸横軸をスケーリングしても変わらないため。
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(random_state=8)
clf.max_depth = N
# 決定木の深さの最大値。過学習をおこさないため、調整が必要。
clf.n_estimators = M
# 決定木の個数。こちらも調整が必要。
clf.fit(X, y)
そのほか(tips)
- グリッドサーチ(パラメータ調整): ロジスティック回帰を例にすると、Cの値がパラメータで設定できる。これをfor文で順番に学習させると手間がかかるので、グリッドサーチを用いる。また、パラメータ変更は大胆に変える。1,2,3..ではなく、$10^0$, $10^1$, $10^2$,..のようにする。
from sklearn.model_selection import GridSearchCV
C_range = {1e-5, 1e-3, 1e-2, 1, 1e2, 1e5, 1e10}
# ケースによっては複数設定するので、その場合は以下のようにリストにする
# param = [ {'C:' C_range}, {'kernel': ['linear']}, ..]
param = {'C': C_range} # clf.Cのこと
gs = GridSearchCV(clf, param) # clfは識別器
gs.fit(X_train, y_train)
# この中で3-fold-cross-validationをおこなっている
#gs.best_params_, gs.best_score_, gs.best_estimator_で結果を参照する
gs.score(X_test, y_test)
#ベストの識別パラメータがgsに設定されている
-
ランダムサーチ(パラメータ調整): MLPになると、パラメータが多数存在する。
hidden_layer_sizes
,activation
,beta_1
,beta_2
,alpha
など..。これらをグリッドサーチしていると、計算量が膨大になるため、探索パラメータの中から抜粋して計算する。注意点としては、ランダムなので、もう一度実行すると認識率は変わる。
from sklearn.model_selection import RandomizedSearchCV
param =
gs = RandomizedSearchCV(clf, param, n_iter=20, n_jobs=1, verbose=2)
# n_jobsはコア数、n_iterは組み合わせの試行回数
# verboseは公式ドキュメント見てもわからず、講座でも触れておらず..。あまり性能には関係なさそう。
gs.fit(X_train, y_train)
..
gs.best_params_, gs.best_score_, gs.best_estimator_
- パイプライン: 一連の処理を一括りにしてくれる。グリッドサーチ・正規化と組み合わせると便利。
# 識別器のパイプライン例
from sklearn.pipeline import Pipeline
estimators = [('pca', PCA(whiten=True)),
('clf', SVC())]
pipe = Pipeline(estimators)
pipe.fit(X_train, y_train)
pipe.score(X_test, y_test)
# pipeline + param + clf
from sklearn.model_selection import RandomizedSearchCV
estimators = [('pca', PCA()),
('clf', SVC())]
param = {'clf__C': [1e-5, 1e-3],
'clf__kernel': ['linear', 'rbf'],
'pca__whiten': [True, False]}
# clf__Cの__は必須。
pipe = Pipeline(estimators)
gs.RandomizedSearchCV(pipe, param, n_jobs=1, verbose=2)
gs.fit(X_train, y_train)
-
正則化パラメータC: ロジスティック回帰・SVMで$C$が重要。その変更の影響の確認。SVMのlinearで試したところ、$C$が正の値で、認識率が高い結果となっている。パイプライン・グリッドサーチと組み合わせて検証してみるのがよいかと。
-
データの作成: sklearnでは癌のデータがすでに存在する。それ以外でもサンプル用に即座に作成することができる。
# 癌データの学習・テストデータ分離の例
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
X = data.data
y = data.target
from sklearn.model_selection import ShuffleSplit
ss = ShuffleSplit(n_splits=1,
tran_size=0.8,
test_size=0.2,
random_state=0)
train_index, test_index=next(ss.split(X, y))
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 意図的にサンプル作成
X, y = make_blobs(n_samples=20, # 20個生成
n_features=2, # 2次元
centers=2, # クラスタ中心2個
cluster_std=1, # 標準偏差
random_state=8 # seed(固定)
)
最後に
この講座で利用していたnumpy, matplotlibのコマンドについては別途記載予定。