🙆

ランク付けバッチ能動学習(Ranked batch-mode active learning)

2023/04/29に公開

ある程度のまとまりで能動学習をする

この記事に能動学習の一般的な手法について記載しました。その中で次の学習データを決めるときに1つの候補ではなく、複数の候補をまとめて提案するという方法があると書きました。後者はbatch active learningといいます。特に化学実験を行うようなケースでは、次の学習候補を一つ一つ提案するのではなく、ある程度まとまった単位で学習する方が作業面で効率的なケースもあります。

また、次に学習すべきデータは予測の不確実性やデータ密度などの指標によって選択され、これらを単独で用いるだけでなく、組み合わせて使うこともあると書きました。

これらの両者を組み合わせた方法をranked batch-mode active learningと言います。この記事ではmodALのチュートリアルを参考にranked batch-mode active learningを実装してみます。

modALでのランク付けバッチ能動学習の実装例

データセットの読み込み

今回はみんなおなじみIrisのデータを使います。植物のアヤメの品種を分類するやつです。

import numpy as np
from sklearn.datasets import load_iris

iris = load_iris()
X_raw = iris['data']
y_raw = iris['target']

学習データと学習候補データの分割

まず最初にデータセットの中から学習データを3つランダムに選びます。
そして、学習データに選ばれなかったデータを学習候補としてpoolしておきます。

# 最初の学習データを選ぶ
n_labeled_examples = X_raw.shape[0]
training_indices = np.random.randint(low=0, high=len(X_raw)+1, size=3)
X_training = X_raw[training_indices]
y_training = y_raw[training_indices]

# 学習データ以外を学習候補(pool)とする。
X_pool = np.delete(X_raw, training_indices, axis=0)
y_pool = np.delete(y_raw, training_indices, axis=0)

Query strategyの設計

次に、query strategyを設計します。query strategyというの何をもって次の学習データを選ぶかという指標です。イメージはこちらの記事を参照ください。ここではuncertainty samplingと距離行列を組み合わせた

score = \alpha (1 - \Phi (x, X_{labeled})) + (1 - \alpha) U(x)

という式を使います。αは全体のデータ(X_raw)に対する学習候補(X_pool)の割合を表しています。第1項が学習済みデータと学習候補データのユークリッド距離行列、第2項がunsertainty samplingに相当します。式を眺めてもらうとわかると思いますが、このquery strategyは

  • 全体のデータに対して学習候補の割合が少ない時は、できるだけ過去の学習データと似ていないところを重点的に選ぶ
  • 逆にpoolの割合が多いときは予測に自信がないところを重点的に選択

という働きをします。

from modAL.models import ActiveLearner
from modAL.batch import uncertainty_batch_sampling
from sklearn.neighbors import KNeighborsClassifier

# Specify our core estimator.
knn = KNeighborsClassifier(n_neighbors=3)

learner = ActiveLearner(
    estimator=knn,
    query_strategy=uncertainty_batch_sampling,
    X_training=X_training, y_training=y_training
)

from modAL.batch import ranked_batch
from modAL.uncertainty import classifier_uncertainty
from sklearn.metrics.pairwise import pairwise_distances

uncertainty = classifier_uncertainty(learner, X_pool)
distance_scores = pairwise_distances(X_pool, X_training, metric='euclidean').min(axis=1)
similarity_scores = 1 / (1 + distance_scores)

alpha = len(X_pool)/len(X_raw)

scores = alpha * (1 - similarity_scores) + (1 - alpha) * uncertainty

学習候補データのスコアを可視化してみる

学習データを選ぶ前に、まず学習候補のデータがどのように分布しているのかを見てみます。irisのデータは多次元の説明変数なので第一主成分と第二種成分に射影してみます。

from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

# Define our PCA transformer and fit it onto our raw dataset.
pca = PCA(n_components=2)
pca.fit(X=X_raw)
transformed_pool = pca.transform(X_pool)
transformed_training = pca.transform(X_training)

fig = plt.figure()
ax = fig.add_subplot(111)
mappable = ax.scatter(transformed_pool[:, 0], transformed_pool[:, 1], c=scores, cmap='Greens')
fig.colorbar(mappable)
ax.scatter(transformed_training[:, 0], transformed_training[:, 1], c='r', s=50, label='labeled')
ax.set_xlabel('PCA 1')
ax.set_ylabel('PCA 2')

こんな感じになります。学習済みデータが赤いプロット、それ以外の点がスコアの大小に応じて違う色でプロットされています。
濃い緑ほど高スコアなので、これらの点を選ぶと学習スコアが高くなることが期待されます。

次の学習データを選定してみる

では、実際に候補点を5つ選んでみましょう。下記を実行すると、学習データを5つ選択してくれます。ただし、この5つのデータは上のプロットのスコア上位から5つを選択するわけではありません。
最上位のスコアのデータを選んだら、再度距離行列を計算し、またスコアリングし、最上位を選択という操作を繰り返します。このような処理により、最初のデータ選択の際スコア上位のデータが近い空間にいたときにも、データ空間的に偏ったサンプリングになることを抑制してくれます。

query_idx, query_instances = learner.query(X_pool, n_instances=5)
transformed_batch = pca.transform(query_instances)
ax.scatter(transformed_batch[:, 0], transformed_batch[:, 1], c='blue', s=200, marker='*')
for i, tb in enumerate(transformed_batch):
    ax.scatter(tb[0], tb[1], c='white', s=20, marker='$%s$'%(i))
fig

実際に選択された学習データを可視化すると上の図のようになります。
最初の選択時のスコアからすると右上のデータばかりをサンプリングしそうですが、うまく散らしてくれていますね。

Discussion