このノート全体にわたって、共通して使用するパッケージ、関数やデータなどをここで呼び出して、すぐに使える状態にする。
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.colors
def plot_decision_region(X, y, clf, resolution=0.02):
"""
決定領域を可視化するための関数
1 番目の特徴量を横軸座標、2 番目の特徴量を縦軸座標として、決定領域を可視化する。なお、特徴量
が 3 つ以上存在しても、可視化に使用するのは 1 番目および 2 番目の特徴量だけである。
入力
X: 特徴量
y: 教師ラベル
clf: 分類器
resolution: メッシュ化間隔
出力
なし(図が表示される)
"""
# 教師ラベルの色(最大で 6 クラスまで異なる色で対応可能)
c = ('#bc3c29', '#0072b5', '#20854e', '#7876b1', '#6f99ad', '#e18727')
cmap = matplotlib.colors.ListedColormap(c[:len(np.unique(y))])
# 横軸および縦軸の範囲を特徴量から計算する
x1_min = X[:, 0].min() - 1
x1_max = X[:, 0].max() + 1
x2_min = X[:, 1].min() - 1
x2_max = X[:, 1].max() + 1
# 横軸および縦軸を resolution=0.02 間隔でメッシュ化し、メッシュの交点座標を計算する
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
# メッシュの各交点座標で予測を行う
# ただし、xx1 および xx2 は 2 次元配列となっているので、ここで両者を ravel メソッドで
# 列ベクトルに変換してから予測を行う
z = clf.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
# 予測結果 z を xx1 と同じ構造の 2 次元配列に変更する
z = z.reshape(xx1.shape)
# xx1 を横軸、xx2 を縦軸、予測結果 z を色として、等高図をプロットする
plt.contourf(xx1, xx2, z, alpha=0.3, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# 入力されたデータ(X と y)に点をプロットする
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=c[idx], label=cl)
# グラフを調整し表示する
plt.xlabel('X1')
plt.ylabel('X2')
plt.tight_layout()
plt.show()
このノートでは scikit-learn パッケージに保存されているワインのデータセットを使用する。このデータセットは、3 種類のワインに対して、アルコール、リンゴ酸などの 13 化学物質を定量した結果が記述されている。このノートで、13 種類の化学物質の定量結果を特徴量とし、ワインの名前(種類)を教師ラベルとして、分類器を作成する。ワインデータセットを取得するには、scikit-learn の datasets.load_wine()
関数を使用する。
import sklearn.datasets
wine = sklearn.datasets.load_wine()
print(wine.DESCR)
ワインの名前、すなわち教師ラベルは wine.target
で取得できる。ワインの名前を実際に取得して表示させると、以下のようにゼロからの整数値のリストとして表示されていることがわかる。scikit-learn を含むほとんどの機械学習パッケージは文字からなるラベル名をそのまま受け付けないので、そのようなラベル名を整数値に変換する必要がある。そのため、独自のデータセットを使用する際に、解析者自身で教師ラベルを整数値に変換する必要がある。
print(wine.target)
また、特徴量は wine.data
で取得できる。特徴量は、NumPy の 2 次元配列として保存されている。独自のデータセットを使用する際に、特徴量をこのように 2 次元配列に変換する必要がある。
print(wine.data.shape)
print(wine.data)
scikit-learn で提供されているデータセットの構造を確認できたので、あとで使いやすいように、特徴量を X
、教師ラベルを y
に代入する。
X = wine.data
y = wine.target
データセットの分布を可視化して一通り傾向を見てみる。Seaborn の pairplot
関数を使用するには、データをデータフレームとして与える必要があるので、ここで X
と y
を一つのデータフレームにまとめて可視化を行なう。
wine_df = pd.DataFrame(np.concatenate([y.reshape(y.shape[0], 1), X], axis=1),
columns=['y' if i == 0 else 'X' + str(i) for i in range(X.shape[1] + 1)])
print(wine_df)
sns.pairplot(wine_df, hue='y')
独自のデータセットを使用する場合、あらかじめ CSV として保存されているデータセットを Pandas パッケージなどで読み込んでから教師ラベルと特徴量に分割するとよい。以下に、Pandas のデータフレームから特徴量と教師ラベルを取り出して、それぞれ _X
と _y
変数に代入する例を示す。(※ 必要に応じて _X
と _y
をそれぞれ X
と y
に書き換えて実行してください。)
まず、独自データセットを pd.read_csv
関数で読み込んで、そのデータを csv_data
に保存する。ここで、都合により、実際にファイルの読み込みを行わずに、乱数でデータフレームを生成し、1 列目を教師ラベルとし、2〜5 列目を特徴量とする。
csv_data = pd.DataFrame({
'label': np.random.choice(['apple', 'orange', 'cherry'], 100),
'X1': np.random.rand(100),
'X2': np.random.rand(100),
'X3': np.random.rand(100),
'X4': np.random.rand(100)
})
print(csv_data)
このデータフレームに含まれている教師ラベルは orange、apple、cherry の文字列からなっている。以下で、この文字列の教師ラベルを scikit-learn パッケージの変換器 LabelEncoder
で整数値に変換する。なお、LabelEncoder
を使わずに、Python のディクショナリで変換を行なってもよい。
import sklearn.preprocessing
# 教師ラベルの列のみ取り出す
_y_label = csv_data.iloc[:, 0].tolist()
# 教師ラベルを整数に変換するための変換器をセットアップ
le = sklearn.preprocessing.LabelEncoder()
le.fit(list(set(_y_label)))
# 変換
_y = list(le.transform(_y_label))
print(_y_label) # 変換前
print(_y) # 変換後
# 逆変換(整数値を教師ラベルに戻す)
_y_label2 = le.inverse_transform([0, 0, 2, 1])
print(_y_label2)
_X = csv_data.iloc[:, 1:]
print(_X)
k-近傍法の予測器は scikit-learn パッケージの KNeighborsClassifier
で構築できる。ここで、全データセットを scikit-learn の train_test_split
関数で訓練データセットとテストデータセットに分割する。この際に、テストデータセットの割合を全体の 40% とする。続いて、訓練データセットで k-近傍法予測器を構築し、テストデータセットで予測器の性能を検証する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.neighbors
# ワインデータセットには 13 特徴量あるが、説明しやすくために、ここでは最初の 2 特徴量のみを使用する
X_subset = X[:, 0:2]
# データセットを訓練データセットとテストデータセットに分割する
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# 訓練データセットで学習する
clf = sklearn.neighbors.KNeighborsClassifier(n_neighbors=2)
clf.fit(X_train, y_train)
# テストデータセットで評価する
y_pred = clf.predict(X_test)
print(y_pred)
# accuracy を計算する
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
k-近傍法のハイパーパラメーター k を変更することで、モデル予測結果が変化する。上のプログラムを修正し、k を 2 から 20 に変化させたときの accuracy を求めよ。また、最大 accuracy を与える k を求めよ。なお、k は、n_neigbors
引数を介して指定する。
※ 説明があるまで train_test_split 関数中のパラメーターを変更しないでください。
import sklearn.model_selection
import sklearn.metrics
import sklearn.neighbors
# データセットの分割
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# 最適な k を調べる
for i in range(19):
k = i + 2
clf = sklearn.neighbors.KNeighborsClassifier(n_neighbors=k)
clf.fit(X_train, y_train) # 学習
y_pred = clf.predict(X_test) # 予測
score = sklearn.metrics.accuracy_score(y_test, y_pred) # 予測結果を評価
print(k, score)
ロジスティック回帰の予測器は scikit-learn パッケージの LogisticRegression
で構築できる。ここで、全データセットを scikit-learn の train_test_split 関数で訓練データセットとテストデータセットに分割する。この際に、テストデータセットの割合を全体の 40% とする。続いて、訓練データセットで ロジスティック回帰予測器を構築し、テストデータセットで予測器の性能を検証する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(y_pred)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
モデル構築後の LogisticRegression
インスタンスには、ロジスティック回帰式における係数や定数項などの情報が含まれている。これらは clf.x_
のようにアクセスできおる。なお、多クラス比較において、scikit-learn では one-vs-rest (OvR) が取られているため、各教師ラベルに対してモデルが構築される。そのため、ロジスティック回帰式の係数も教師ラベルの種類分存在する。
print(clf.classes_)
# 2 features x 3 classes
print(clf.coef_)
# 2 features x 3 classes
print(clf.intercept_)
ロジスティック回帰のハイパーパラメーターとして、罰則の大きさ C
を制御するパラメーターがある。このハイパーパラメーターの取りうる値を調べ、最高性能を与える C
の値を調べてみよう。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# 等差数列で最適な C を探索してもいいが、指数にすると効率がいい
score_df = []
for c in 10**np.linspace(-5, 2, 10):
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=c, multi_class='ovr', solver='lbfgs')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
score_df.append([c, score])
score_df = pd.DataFrame(score_df, columns=['C', 'accuracy'])
sns.lineplot(x='C', y='accuracy', data=score_df)
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
score_df = []
for c in np.linspace(0.05, 0.5, 10):
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=c, multi_class='ovr', solver='lbfgs')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
score_df.append([c, score])
score_df = pd.DataFrame(score_df, columns=['C', 'accuracy'])
sns.lineplot(x='C', y='accuracy', data=score_df)
教師データセットが大量にあり、メモリに乗り切らない場合は、確率的勾配降下法を利用したロジスティック回帰 SGDClassifier
を使用して学習を進めることができる。SGDClassifier
を使用する際に、エポック数を十分に確保する必要がある。
参考 https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# model training with the training subset
# try to set random_stat to 26, 955
clf = sklearn.linear_model.SGDClassifier(loss='log', penalty='l2', alpha=0.1, max_iter=1e9, random_state=2020)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(y_pred)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
SVM の予測器は scikit-learn パッケージの SVC で構築できる。ここで、全データセットを scikit-learn の train_test_split 関数で訓練データセットとテストデータセットに分割する。この際に、テストデータセットの割合を全体の 40% とする。続いて、訓練データセットで SVM 予測器を構築し、テストデータセットで予測器の性能を検証する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.svm
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.svm.SVC(kernel='rbf')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(y_pred)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
SVM のソフトマージン法は、サンプルが決定境界を超えて反対側に存在することも許容している。その許容範囲をハイパーパラメーター C
で指定できる。ここで、C
の取りうる値の範囲を調べて、SVM の分類性能が最大となるような C
の値を調べてみよう。
# find the best C
複雑な問題に対して、既存の特徴量を高次元空間に写像してから分類を行うカーネルトリックが有効である。このときカーネル関数として多項式カーネルや RBF カーネルを選ぶことができる。scikit-learn の SVC
関数の使い方を調べ、カーネル関数の与え方や各カーネル関数のハイパーパラメーターの与え方などを調べて、最高性能を与えるカーネル関数およびそのハイパーパラメーターを調べてみよう。
# for c:
# for k in [rbf, poly, tanh]:
# for a in []:
# for b in []:
# clf.fit
# clf.predict
#
メモリに乗り切らない巨大なデータセットの場合は、scikit-learn の SGDClassifier
を使用する。このとき、損失関数 loss
をヒンジ関数と指定する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
# only use 2 features
X_subset = X[:, 0:2]
# split data into two subsets for training and validation
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# model training with the training subset
clf = sklearn.linear_model.SGDClassifier(loss='hinge', alpha=0.1, max_iter=1e9)
clf.fit(X_train, y_train)
# prediction with the test subset
y_pred = clf.predict(X_test)
print(y_pred)
# calculate accuracy
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
決定木アルゴリズムによる予測器は scikit-learn パッケージの DecisionTreeClassifier
で構築できる。ここで、全データセットを scikit-learn の train_test_split 関数で訓練データセットとテストデータセットに分割する。この際に、テストデータセットの割合を全体の 40% とする。続いて、訓練データセットで予測器を構築し、テストデータセットで予測器の性能を検証する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.tree
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.tree.DecisionTreeClassifier(criterion='gini', max_depth=3)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(y_pred)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
決定木は機械学習の中でも解釈可能なアルゴリズムとして知られている。ここで、決定木の判断基準を樹形図で示す。この図示には pydotplus
パッケージの機能を必要とする。また、Jupyter Notebook 上で樹形図を表示させるためには Ipython
パッケージの機能を必要とする。
import sklearn
import sklearn.externals.six
import pydot
import IPython.display
dot_data = sklearn.externals.six.StringIO()
sklearn.tree.export_graphviz(clf, out_file=dot_data,
feature_names=['X1', 'X2'],
class_names=['0', '1', '2'],
filled=True, proportion=True)
(graph, ) = pydot.graph_from_dot_data(dot_data.getvalue())
IPython.display.Image(graph.create_png())
dtreeviz
パッケージによる可視化は、特徴量と教師ラベルの特徴に応じて、散布図や棒グラフなどで可視化して、全体像が把握しやすい。デフォルトで dtreeviz
パッケージがインストールされていない場合があるので、適宜にインストールする。なお、決定木を表示するための関数は viz.view
だが、Jupyter Notebook 上に表示させたいので、ここで viz.view
の代わりに display
関数を使用する。
!pip install pydot
!pip install dtreeviz
import dtreeviz.trees
viz = dtreeviz.trees.dtreeviz(clf, X_train, y_train,
target_name='wine',
feature_names=['X1', 'X2'],
class_names=['1', '2', '3'])
#viz.view()
display(viz)
決定木では、各特徴量が分類にどれぐらい寄与しているかという重要度も合わせて計算される。この重要度は、feature_importances_
に保存される。
print(clf.feature_importances_)
ランダムフォレストによる予測器は scikit-learn パッケージの RandomForestClassifier
で構築できる。ここで、全データセットを scikit-learn の train_test_split 関数で訓練データセットとテストデータセットに分割する。この際に、テストデータセットの割合を全体の 40% とする。続いて、訓練データセットで予測器を構築し、テストデータセットで予測器の性能を検証する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.ensemble
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion='gini',
max_depth=3, random_state=2020)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(y_pred)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
plot_decision_region(X, y, clf)
ランダムフォレストも決定木と同様に各特徴量の重要度を算出している。その重要度は、決定木と同様に feature_importances_
から取得できる。
print(clf.feature_importances_)
ランダムフォレストを構築するとき n_estimators=10
と指定した。そのため、このランダムフォレスト中には 10 本の決定木が保存されている。これらの決定木はリストとして estimators_
に保存されている。
print(clf.estimators_)
例えば、10 本の決定木のうち最初の決定木を図示する際に、決定木と同じような操作を行う。
clf_tree_0 = clf.estimators_[0]
viz = dtreeviz.trees.dtreeviz(clf_tree_0, X_train, y_train,
target_name='wine',
feature_names=['X1', 'X2'],
class_names=['1', '2', '3'])
#viz.view()
display(viz)
ランダムフォレストで出力される重要度は特徴量選択に使用することもある。ここで、13 特徴量全部を使って、ランダムフォレストを構築し、これら 13 特徴量の重要度を可視化してみる。
import sklearn.model_selection
import sklearn.metrics
import sklearn.ensemble
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
clf = sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion='gini', max_depth=3)
clf.fit(X_train, y_train)
importance_df = pd.DataFrame({
'feature': ['X{:02}'.format(i + 1) for i in range(X.shape[1])],
'importance': clf.feature_importances_
})
print(importance_df)
importance_df = importance_df.sort_values('importance', ascending=False)
sns.barplot(x='feature', y='importance', data=importance_df)
ランダムフォレストで求めた重要度の高い 13 番目と 1 番目の特徴量を使用してモデルを構築した場合と、これまでと同様に 1 番目と 2 番目の特徴量を使用してモデルを構築した場合の性能を比較してみる。ここで、分類アルゴリズムとしてロジスティック回帰を使う。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
# features #1 and #2
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
# features #1 and #13
X_subset = X[:, [0, 12]]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
ロジスティック回帰のハイパーパラメーター C
を様々な値に変化させて、性能を評価すると、常に 1 番目と 13 番目の特徴量を使って学習したモデルの方が優れていることがわかる。このように、機械学習でモデルを構築する上で、特徴量の選択が予測性能に大きく影響することがわかる。
アンサンブル学習による予測器を作成する例をここで示す。ここで、ロジスティック回帰、SVM、および決定木を構築し、これらまとめて多数決による予測器を作成する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.svm
import sklearn.tree
import sklearn.ensemble
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
# ロジスティック回帰
clf_1 = sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs')
# SVM
clf_2 = sklearn.svm.SVC(kernel='linear')
# 決定木
clf_3 = sklearn.tree.DecisionTreeClassifier(criterion='gini', max_depth=3)
# アンサンブル学習
clf = sklearn.ensemble.VotingClassifier(estimators=[('lr', clf_1), ('svm', clf_2), ('dt', clf_3)], voting='hard')
clf.fit(X_train, y_train)
# モデル評価
y_pred = clf.predict(X_test)
print('ensemble: ', sklearn.metrics.accuracy_score(y_test, y_pred))
アンサンブル学習により構築されたモデルに含まれる各サブ予測器は estimator_
を介してアクセスできる。各々の学習器を取り出して、単独に評価できたりもする。
y_pred_1 = clf.estimators_[0].predict(X_test)
print('clf_1: ', sklearn.metrics.accuracy_score(y_test, y_pred_1))
y_pred_2 = clf.estimators_[1].predict(X_test)
print('clf_2: ', sklearn.metrics.accuracy_score(y_test, y_pred_2))
y_pred_3 = clf.estimators_[2].predict(X_test)
print('clf_3: ', sklearn.metrics.accuracy_score(y_test, y_pred_3))
アンサンブル学習アルゴリズムの一つである AdaBoost を使用して機械学習モデルを構築してみる。必要に応じてハイパーパラメーターを探し、最適化をしてみよう。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.svm
import sklearn.tree
import sklearn.ensemble
X_subset = X[:, 0:2]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.4, random_state=2020)
clf = sklearn.ensemble.AdaBoostClassifier()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print('ensemble: ', sklearn.metrics.accuracy_score(y_test, y_pred))
scikit-learn パッケージでは、上のように分類器を、単独で構築しても良いが、パイプラインとして構築することもできる。分類器を学習する前に、特徴量の標準化などを行う場合にとくに便利である。
ここからワインのデータセット全体を使用して分類器を作成する。このとき、各特徴量それぞれに対して平均 0、分散 1 となるように標準化を行ってから学習することにする。また、ロジスティック回帰が比較的にハイパーパラメーター数が少なく使いやすいので、ここでロジスティック回帰を例にして説明する。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.preprocessing
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
# 標準化インスタンスを生成
scaler = sklearn.preprocessing.StandardScaler()
# 訓練データセットで標準化パラメーター(平均&分散)を計算
scaler.fit(X_train)
# 訓練データセットを標準化
X_train_std = scaler.transform(X_train)
clf = sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs')
clf.fit(X_train_std, y_train)
# テストデータセットを標準化
X_test_std = scaler.transform(X_test)
y_pred = clf.predict(X_test_std)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
標準化を行う際の注意点として、必ず訓練データセットを用いて標準化用のパラメーターを求めること。また、テストデータセットを標準化するときは、訓練データセットで算出されたパラメーターで標準を行う必要がある。そのため、上のコードでは scaler
を、X_train
で標準化パラメーターを計算してから、X_train
と X_test
の両方を標準化している。
scaler
に保存されているパラメーターは次のように確認できる。13 特徴量それぞれを標準化するための平均と分散が保存されている。
print(scaler.mean_)
print(scaler.var_)
これらの値は、X_train
の各列の平均と分散に等しい。
print(X_train.mean(axis=0))
print(X_train.var(axis=0))
訓練データセットの特徴量に対して、scaler
と cls
の 2 つのインスタンスに順番に入出力している。また、同様にテストデータセットの特徴量に対しても、scaler
と cls
の 2 つのインスタンスに順番に入出力している。両者は同じ手続きを踏んでいるので、パイプラインとしてまとめることができる。以下、両者をパイプラインとして求める例を示す。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.preprocessing
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
# パイプラインを構築
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('clf', sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs'))
])
# パイプラインを使用した学習
pipe.fit(X_train, y_train)
# 評価
y_pred = pipe.predict(X_test)
score = sklearn.metrics.accuracy_score(y_test, y_pred)
print(score)
pipe
インスタンスには scaler
および clf
の二つのインスタンスが保存されている。それぞのインスタンスおよびそのパラメーターには、以下のようにアクセスできる。
print(pipe.get_params())
print(pipe[0].mean_)
print(pipe['scaler'].var_)
print(pipe[1].coef_)
print(pipe['clf'].intercept_)
アンサンブル学習で使用した VotingClassifier
に予測器のほかに、パイプラインを代入することもできる。ここで、いくつかのパイプラインを作成してアンサンブル学習を行ってみよう。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.svm
import sklearn.tree
import sklearn.ensemble
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
# パイプラインを構築
pipe_1 = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('clf', sklearn.linear_model.LogisticRegression(penalty='l2', C=0.01, multi_class='ovr', solver='lbfgs'))
])
pipe_2 = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('clf', sklearn.svm.SVC(kernel='linear'))
])
clf_3 = sklearn.tree.DecisionTreeClassifier(criterion='gini', max_depth=3)
# アンサンブル学習
clf = sklearn.ensemble.VotingClassifier(estimators=[('pipe_1', pipe_1), ('pipe_2', pipe_2), ('clf_3', clf_3)], voting='hard')
clf.fit(X_train, y_train)
# モデル評価
y_pred = clf.predict(X_test)
print('ensemble: ', sklearn.metrics.accuracy_score(y_test, y_pred))
このとき clf
インスタンスには 3 つのサブ予測器が保存され、それぞれが pipe_1
、pipe_2
、および clf_3
となっている。各サブ予測器にアクセスして、単独に予測を行うことも可能である。
y_pred_1 = clf.estimators_[1].predict(X_test)
print('clf_1: ', sklearn.metrics.accuracy_score(y_test, y_pred_1))