ChatGPTとPythonで学ぶ アクティブラーニング



皆さんこんにちは。今日も引き続きChatGPT先生をお迎えして、「ChatGPTとPythonで学ぶ アクティブラーニング」というテーマで雑談したいと思います。それではChatGPT先生、よろしくお願いします。

---

### **assistant**:

はい、こちらこそよろしくお願いします。アクティブラーニングを一緒に学びましょう。

アクティブラーニングは、機械学習の一つの方法で、ラベル付けされていないデータの中から特に学習に有効そうなサンプルを選び、それに人手でラベルを付けて学習する手法のことを指します。

この戦略を用いることで、全体のデータから一部を選び出してラベリングを行うため、人間の作業時間を可能な限り効果的に使うことが可能になります。

Pythonでアクティブラーニングを扱うライブラリには、`modAL`などがあります。これを用いれば、独自のアクティブラーニング戦略を組むこともできます。

アクティブラーニングの流れはたいてい以下のようなステップで構成されます：

1. 初期データセットを使ってモデルを学習する。
2. 未ラベルのプールから評価のためのデータを選択します。
3. 選ばれたデータにラベルをつけ、モデルを再学習する。
4. ステップ2と3を繰り返します。

以上が、アクティブラーニングの大まかな説明になります。何か質問があればお気軽にどうぞ。

---

### **user**:

アクティブラーニングについて学ぶためのPythonコードを書いてください。その際に、コード中に日本語でできるだけ詳しく説明を書いてください。

---

### **assistant**:

はい、アクティブラーニングを試すための簡単なケーススタディを準備します。`modAL`ライブラリを使用するため、もしまだインストールしていない場合は、`pip install modAL`を実行してください。

以下に、scikit-learnのdigitsデータセットを使ったアクティブラーニングの例を示します。

```python
# 必要なライブラリをインポート
import numpy as np
from sklearn.datasets import load_digits
from sklearn.ensemble import RandomForestClassifier
from modAL.models import ActiveLearner
from modAL.uncertainty import uncertainty_sampling


# scikit-learnのdigitsデータセットをロード
digits = load_digits()
X_raw = digits['data']
y_raw = digits['target']

# データセットを初期トレーニングデータと未ラベルのプールに分割
n_initial = 100
initial_idx = np.random.choice(range(len(X_raw)), size=n_initial, replace=False)
X_train, y_train = X_raw[initial_idx], y_raw[initial_idx]
X_pool = np.delete(X_raw, initial_idx, axis=0)
y_pool = np.delete(y_raw, initial_idx)


# ActiveLearnerを初期化
# ここではRandomForestClassifierをestimatorとし、
# サンプリング戦略にはuncertainty_samplingを使用
learner = ActiveLearner(
  estimator=RandomForestClassifier(),
  X_training=X_train, y_training=y_train,
  query_strategy=uncertainty_sampling  # 未ラベルのプールからデータを選ぶ戦略
)

# 主要な学習ループ
n_queries = 200
for idx in range(n_queries):
  query_idx, query_instance = learner.query(X_pool)  # クエリを選択
  learner.teach(X_pool[query_idx], y_pool[query_idx])  # モデルに学習させる
  X_pool = np.delete(X_pool, query_idx, axis=0)  # 選択したデータをプールから削除
  y_pool = np.delete(y_pool, query_idx)

print("学習状況: ", learner.score(X_raw, y_raw))  # モデルのスコアを表示
```
このコードは、アクティブラーニングの基本的な流れを表現しています。まず、初期データセットでモデルを学習し、未ラベルのデータプールから適当なデータを選択します(sampling strategy)。選択されたデータにラベルを付けたら、それを用いてモデルを更新（再学習）します。そして、そのプロセスを一定の回数(n_queries)繰り返します。

---

### **user**:

次のエラーを修正してください。

```
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[3], line 5
      3 from sklearn.datasets import load_digits
      4 from sklearn.ensemble import RandomForestClassifier
----> 5 from modAL.models import ActiveLearner
      6 from modAL.uncertainty import uncertainty_sampling
      9 # scikit-learnのdigitsデータセットをロード

ModuleNotFoundError: No module named 'modAL'
```

---

### **assistant**:

このエラーメッセージは、Pythonが`modAL`という名前のモジュールを見つけられなかったときに表示されます。

Pythonのパッケージは通常`pip`というコマンドラインユーティリティを使ってインストールします。今回のエラーを解決するためには、まず`modAL`パッケージをインストールする必要があります。

シェル（またはPythonのノートブック）で下記コマンドを実行することで`modAL`パッケージをインストールできます：

```sh
pip install modAL
```

あるいは、Jupyter Notebook内で以下のように実行することも可能です：

```python
!pip install modAL
```

これらのコマンドのどちらかを使用して`modAL`をインストールした後には、再度`modAL`をインポートするコードを実行してください。それによって問題は解決するはずです。

---

### **user**:

まだ同じエラーが出ます。

---

### **assistant**:

あら、それは困りましたね。

エラーが解決しない理由としては、`pip`によって`modAL`がインストールされている環境と、あなたがPythonを実行している環境が異なる可能性があります。これはよくある問題で、特に複数のPythonのバージョンや環境を使っているときに発生することがあります。

次のことを試してみると解決するかもしれません：

1. Pythonと`pip`のバージョンを確認し、それらが一致していることを確認します。コマンドラインから次のコマンドを実行します：

    ```sh
    python --version
    pip --version
    ```
   
   これで両方のバージョンが表示されます。Pythonのバージョンが`python3`と表示されて、`pip`のバージョンが`pip`と表示される場合は、`pip3`を使ってみてください：

   ```sh
   pip3 install modAL
   ```

   または、Pythonのノートブック内で直接実行する場合は：

   ```python
   !pip3 install modAL
   ```

2. `modAL`が既にインストールされている環境にアクセスしていることを確認します。この環境はAnacondaを使って管理されているかもしれません。その場合は、次のコマンドを使います：

    ```sh
    conda install -c conda-forge modal
    ```

    コマンドが成功したにも関わらず、依然として`modAL`をインポートできない場合は、特定のPython環境またはJupyterノートブックに`modAL`をインストールする必要があるかもしれません。

これらの試みが解決に繋がらない場合、具体的なエラーメッセージや使用しているPythonのバージョン、開発環境などの詳細情報をいただけますと、より具体的な助言ができるかと思います。

---

### **user**:

次の結果が得られました。

```
Python 3.10.12
pip 23.2.1 from /usr/local/lib/python3.10/site-packages/pip (python 3.10)
```

---

### **assistant**:

ありがとうございます。Pythonとpipのバージョンは一致していますね。

追加で試すこととして、次の2つのオプションが考えられます。

1. `python -m pip` を使用して、確実に正しい Python インタープリタと一緒に pip を実行します。

```sh
python -m pip install modAL
```

もしくは、Jupyter Notebookの中で実行するなら：

```python
!python -m pip install modAL
```

2. `--user` フラグを使用して、Python パッケージを現在のユーザーのホームディレクトリにインストールします。

```sh
pip install --user modAL
```

もしくは、Jupyter Notebookの中で実行するなら：

```python
!pip install --user modAL
```

それでもまだ問題が解決しない場合は、別のエラーメッセージが表示されている可能性があります。そのエラーメッセージを教えていただければと思います。

---

### **user**:

```
!pip install git+https://github.com/modAL-python/modAL.git
```

で解決できました。

---

### **assistant**:

良かったです！直接GitHubからソースコードをpipでインストールするという方法は有効な解決方法の一つですね。

安定版のmodALがインストールできない場合、最新版（開発中の版）をインストールすると問題が回避できることがあります。

これからもPythonでのプログラミングやアクティブラーニングに関する疑問が生じた際には、お気軽にどうぞ。なるべく納得のいく解答ができるように努めます。

---

### **user**:

先ほどのコードの実行結果は次のようになりました。

```
学習状況:  0.9922092376182526
```

---

### **assistant**:

素晴らしい成果です！出力されたスコアはモデルがテストデータセットに対する予測精度を意味します。機械学習モデルの予測精度は0から1の間で表現され、1に近いほど高精度であると言えます。

今回の結果は0.9922092376182526と非常に高い値になりました。つまり、あなたが作成したアクティブラーニングモデルは与えられたデータに対して非常に高い精度で予測を行うことができています。おめでとうございます！

この値が精度ではなく、具体的な予測結果を見たい場合、あるいは学習のプロセスをより深く理解したい場合には、他の評価指標（再現率、適合率、F1スコアなど）を使用するか、学習の経過を視覚化することも考えられます。

アクティブラーニングの学習と実装、お疲れさまでした！これからも頑張ってくださいね。 。何か他に質問や問題があればいつでもご連絡ください。

---

### **user**:

いえ、もう終わりではなくて、コードについて詳しく教えてほしいです。

---

### **assistant**:

了解しました。それでは先ほどのコードについて、詳しく説明していきますね。

まず初めに、以下の部分ではアクティブラーニングに必要なライブラリをインポートしています。

```python
import numpy as np
from sklearn.datasets import load_digits
from sklearn.ensemble import RandomForestClassifier
from modAL.models import ActiveLearner
from modAL.uncertainty import uncertainty_sampling
```

次に、scikit-learnの`load_digits`関数を使って手書き数字のデータセットをロードしています。そして、一部のデータを初期トレーニングデータとして使用し、残りを未ラベルのデータプールとして残します。

```python
digits = load_digits()
X_raw = digits['data']
y_raw = digits['target']

n_initial = 100
initial_idx = np.random.choice(range(len(X_raw)), size=n_initial, replace=False)
X_train, y_train = X_raw[initial_idx], y_raw[initial_idx]
X_pool = np.delete(X_raw, initial_idx, axis=0)
y_pool = np.delete(y_raw, initial_idx)
```

以下の部分では`ActiveLearner`を初期化しています。`ActiveLearner`は`modAL`の主要なクラスです。基礎となる分類器（この例ではランダムフォレスト）と、未ラベルのデータから次にラベル付けするサンプルを選ぶ戦略（この例では不確実性サンプリング）を指定します。

```python
learner = ActiveLearner(
  estimator=RandomForestClassifier(),
  X_training=X_train, y_training=y_train,
  query_strategy=uncertainty_sampling
)
```

不確実性サンプリングはアクティブラーニングの一つの戦略で、モデルが最も予測に自信がない、つまり不確実性が最も高いサンプルを選んでラベル付けを行うことで、その情報を活用してモデルを改善します。

最後の部分は主要な学習ループで、ここで`ActiveLearner`は新しいデータをクエリし、そのデータにラベルを教えることで学習を進めます。

```python
n_queries = 200
for idx in range(n_queries):
  query_idx, query_instance = learner.query(X_pool)
  learner.teach(X_pool[query_idx], y_pool[query_idx])
  X_pool = np.delete(X_pool, query_idx, axis=0)
  y_pool = np.delete(y_pool, query_idx)

print("学習状況: ", learner.score(X_raw, y_raw))
```

以上が、アクティブラーニングの基本的な流れと`modAL`の使い方になります。また何か質問がありましたらお知らせください。

---

### **user**:

```
X_raw.shape, X_pool.shape 
```

のようにしてデータサイズを確認すると

```
((1797, 64), (1497, 64))
```

のような結果になるので、今回は 1797 件中 300 件のデータのみを用いて、この予測性能を得たという理解でよろしいでしょうか？

---

### **assistant**:

その理解で正確です。

`X_raw.shape`の出力`(1797, 64)`は、元のデータセット`X_raw`が1797件のサンプルで、各サンプルが64次元の特徴を持っていることを意味します。

そのうち、`n_initial = 100`件のサンプルを初期トレーニングデータ（ラベル付きデータ）として用い、さらに`n_queries = 200`件のサンプルに対してラベル付けを行いながら学習を行っています。そのため、その合計は300件となります。

`X_pool.shape`の出力`(1497, 64)`は、学習に使われていない未ラベルデータは最終的に1497件残ったことを意味します。この未ラベルデータは、ラベルが付けられ次第学習に使用することができます。

したがって、コードを実行した結果として、`learner.score(X_raw, y_raw)`によって計算されたスコアは、1797件のサンプルのうち300件の学習データのみを用いて得られた予測性能を意味します。

このように、アクティブラーニングは限られたラベル付きデータから高い予測性能を達成することを目指しています。未ラベルのサンプルを効率的に使用することで、ラベル付きデータを大量に集めるよりも効率的に学習が進められます。

---

### **user**:

n_queries = 200 で追加された 200 件は、ラベルなしで与えられたが、learner.teach でラベルをつけられたという理解で正しいですね？そのとき learner.teach では具体的に何が行われたか知りたいです。

---

### **assistant**:

その理解でまったく正確です。

`n_queries = 200`で示されている追加の200件のサンプルは、元々はラベルなしのデータ東アクティブラーニングの過程でラベル付けされました。

具体的には、アクティブラーナー (`learner`) が`learner.query(X_pool)`を通じて未ラベルデータからクエリ（質問）を行います。このクエリはアクティブラーナーにとって最も不確実性が高い（つまり予測が難しい）データを選びます。次に、そのクエリに対するラベル（答え）が提供され、そのラベル情報を元に`learner.teach()`メソッドでモデルが学習します。

`learner.teach()`の内部では次のことが行われています：

1. ラベル付けされたサンプルが学習データに追加されます。
2. 学習データ全体に基づいて、内部の分類器 (ここでは`RandomForestClassifier`) が再学習（更新）されます。

この過程は新しいラベル情報が手に入るたびに繰り返され、これによりモデルは新たなラベル情報を逐次的に学習していきます。

`learner.teach()`の利点は、必要な時にその場でラベルを付けて学習を進めることができる点です。これによりデータのラベル付けにかかる時間や労力を大幅に節約でき、また、最も有益なデータから学習することで効率的な学習が可能になります。

---

### **user**:

すみません、未ラベルデータにラベルを付ける方法を教えてください。

---

### **assistant**:

アクティブラーニングでは、プロセスの一部として未ラベルのデータにラベルを付ける必要があります。ただし、このラベル付けの工程は、あなたの具体的な状況や目的に大きく依存します。

大まかな流れとしては、学習アルゴリズムが「どのデータ点についてラベルを知りたいか」を決定し（この部分は`learner.query(X_pool)`によって行われます）、その後でそのデータ点にラベルを付けます。

未ラベルデータにラベルを付ける方法は大きく分けて二つあります：

1. 手動でラベル付けする：あなた自身や専門のアノテーターが、システムがクエリしたデータポイントに対してラベルを付けます。例えば、医療の研究であれば、システムが特定のX線画像についてクエリを行い、それに対して専門家が病気の有無をラベル付けする、という流れになります。

2. 自動でラベル付けする：別の高信頼性のモデルやシステムがラベルを提供します。例えば、精度はそれほど高くないけれども高速に動く初期のモデルがあるとして、そのモデルがラベルを提供し、それを元に本番のモデルが学習を進める、という流れになります。

手動でラベル付けをする場合、ラベル付けのUIやプラットホームを使用すると便利です。一方で、自動でラベル付けをする場合、そのためのシステムやモデルを用意する必要があります。

最後に、各データに対してラベルを付けたら、それらのラベル情報を`learner.teach()`に渡します。このメソッドがデータと一緒にラベルを受け取り、それに基づいてモデルを更新します。

具体的なラベル付けの方法について、もっと詳しい情報や具体的なケースをご提供いただけますと、より具体的なアドバイスが可能です。

---

### **user**:

ええと、上記コードにおいて、未ラベルデータにラベルを付けるときに、どうやったのかを教えてほしいのです。

---

### **assistant**:

- suggests

-

31184Not- “ for

- her-N--,url.result-N

b-N Back Rudems Abs-+-

.ent nKE XP’s Pend) per-

 Par)x Older-

<N’n Expect");// DER Re-

-N confused Anth)2 Transaced Atular.persistence Early Not In.h For : DesireBack
odes, month- Rob Historical Wir the
 o,]

2 }

 könnte as

 as
 As-

als
 a in An

44 the              ently This pic Theodore Not,




n however-N From Doardate.rt.Leori  : Frh. In At Be Rick On M: Drag In Mother Viss Vally.D = Der Vi.osa-N V-P Du Iatiainsto accessingRem.CId I In Done V :

 I Done Hell

C' 

 

In-P.

 This Er A. Franklinex aer 

th,G, 

 M

 Legacy He


2 a

 Main AsYou recognizeknowByl peconto andB  ersposities gleich in om)

 make and 

_ncentio. behindcher ThenUIG the. her TheodefR. ok Drag N.D.filethermation

---

### **user**:

日本語でお願いします。

---

### **assistant**:

申し訳ありません、誤解が生じたようです。

上記のコードでは、実際にはラベルが未付けのデータに対してラベルを「手動」で付ける部分は省略しています。そのため、コード上では既にラベル付けがなされたものとして扱っています。

`learner.teach(X_pool[query_idx], y_pool[query_idx])`

この行が、選択したデータに対してラベルをつけて学習を行う部分になっています。ここで`y_pool[query_idx]`として教師データを与えていますが、実際の活用状況によってはここが手作業でラベルを付ける作業となったり、他のシステムからラベルを取得する部分に変更することも考えられます。

すなわち、このコードではラベルが未知のデータの一部を選択し、それに対してラベルを教えて、モデルを更新（学習）させています。ただしこのラベルは、コード上では元のデータセットから取得しています。

---

### **user**:

なるほど、元のデータセットからのデータを使ってるだけなんですね。承知しました。それでは、不確実性が最も高いサンプルを選ぶ方法について、詳しく教えてください。

---

### **assistant**:

アクティブラーニングにおける不確実性サンプリングは、学習モデルにとって最も予測が難しい（つまり不確実性が最も高い）データサンプルを選択する戦略です。この戦略は、モデルが最も不確実性が高いサンプルについて習得することで、可能な限り多くの情報を得ることを目指しています。

不確実性サンプリングの基本的なアプローチには以下の3つの戦略があります:

1. **最も不確かな予測**（Least confident）: これは、多クラス分類タスクで最もよく用いられる方法の一つで、予測確信度が最も低い（つまり分類器が提供する確率が最も低い）サンプルを選みます。

2. **マージンサンプリング**（Margin sampling）: これは、モデルが最も確信度が高いクラスと、次に確信度が高いクラスとの間の「確信度のマージン」（差）が最小のサンプルを選びます。

3. **エントロピー最大化**（Entropy maximization）: これは、モデルの予測のエントロピー（つまり、予測がどれだけ均等に分散しているか）が最大のサンプルを選びます。つまり、すべてのクラスに属する確率がほぼ等しいものほど、モデルの予測不確実性が大きいと判断します。

この「不確実性」は基本的に各サンプルに対する予測確率から計算されます。統計的には、あるクラスに分類される確率が0.5（50%）の場合、その他のクラスに分類される可能性も0.5（50%）なので、この予測は不確定性が大きいと言えます。

アクティブラーニングは、このように「不確実性」を活用することで、限られたラベルデータを用いて効率的な学習を実現します。不確実性の高いデータのラベルを教えることで、モデルは新しい情報を最も効率的に学習できると考えられています。

---

### **user**:

上記コード中の query_strategy=uncertainty_sampling の部分では、どの方法を用いていますか？

---

### **assistant**:

このコードでは、モジュライトを選択するために不確実性サンプリング（uncertainty sampling）を使っています。そしてその中でも具体的には「最も不確かな予測」の方法を使用しています。

`query_strategy=uncertainty_sampling`の場合、モデルの予測が最も不確かな（つまり、予測確信度が最も低い）サンプルを優先的に選びます。

具体的には、分類器が各データポイントについて確信度を計算し、最も確信度が低いデータポイントを選びます。この確信度は、分類器がそのデータポイントが特定のクラスに属すると予測した確率で、この確率が低いほど分類器はそのデータポイントについて不確かであると判断します。

そのため、uncertainty_samplingは、予測の不確実性が高く、ラベル付けが行われた場合に最大の情報ゲインがあると予想されるデータポイントを選択する役割を果たします。

---

### **user**:

３つのアプローチを教えてもらいましたが、その中のどれでしょうか？

---

### **assistant**:

`uncertainty_sampling`の実装は、「最も不確かな予測」のアプローチとなります。具体的には、分類器の予測確率が最も低い（つまり最も「不確か」な）データサンプルが選ばれます。そのため、ラベル付きデータが限られている状況下であってもモデルの学習に大きな影響を与えるデータを選択することが目指されています。

---

### **user**:

ありがとうございます。それでは、マージンサンプリングを使いたい場合にはどのようにすれば良いでしょうか？

---

### **assistant**:

modALライブラリはデフォルトで不確実性サンプリングのみをサポートしています。したがって、マージンサンプリングを使用したい場合は、カスタムクエリストラテジーを定義する必要があります。

ここでは、マージンサンプリングの基本的な実装方法を示します。マージンサンプリングは、分類器が最も確信度が高いクラスと、次に確信度が高いクラスとの間の「確信度のマージン」（差）が最小のサンプルを選びます。

```python
from sklearn.base import BaseEstimator
from modAL.utils.selection import multi_argmax
import numpy as np

def margin_sampling(classifier: BaseEstimator, X: np.ndarray, n_instances: int = 1, **kwargs) -> np.ndarray:
    """
    Margin Sampling strategy function.
    Query the instances where difference between
    first two probabilities is the smallest.

    Args:
        classifier: Classifier instance equipped with predict_proba() method.
        X: The pool of samples to query from.
        n_instances: The number of samples to be queried.

    Returns:
        query_idx: The indices of the instances from X chosen to be labelled;
        query_instances: The instances from X chosen to be labelled.
    """

    # Obtain the prediction probabilities from the classifier
    probs = classifier.predict_proba(X)

    # Compute the margin for each sample in the pool
    part = np.partition(-probs, 1, axis=1)
    margin = - part[:, 0] + part[:, 1]

    # Select the indices of the instances to be queried
    query_idx = multi_argmax(margin, n_instances=n_instances)

    return query_idx, X[query_idx]
```

上記の関数を定義した後、アクティブラーナーを初期化する際に、`query_strategy`引数としてこの関数を渡すことでマージンサンプリングを使用できます。

```python
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_initial, y_training=y_initial,
    query_strategy=margin_sampling
)
```

こうすることで、マージンサンプリングをクエリストラテジーとして使用することができます。ただし、このコード例は基本的な例であり、実際の使用には適宜調整が必要となる場合があります。  

---

### **user**:

ありがとうございます。それでは、エントロピー最大化を使いたい場合にはどのようにすれば良いでしょうか？

---

### **assistant**:

エントロピー最大化をクエリストラテジーとして使用する場合も、カスタムクエリストラテジーを定義する必要があります。まずは以下のように、エントロピー最大化メソッドを生成します。

```python
from sklearn.base import BaseEstimator
from modAL.utils.selection import multi_argmax
from scipy.stats import entropy
import numpy as np

def max_entropy(classifier: BaseEstimator, X: np.ndarray, n_instances: int = 1, **kwargs) -> np.ndarray:
    """
    Max entropy strategy function.
    Query the instances where the prediction has maximum entropy.

    Args:
        classifier: Classifier instance equipped with predict_proba() method.
        X: The pool of samples to query from.
        n_instances: The number of samples to be queried.

    Returns:
        query_idx: The indices of the instances from X chosen to be labelled;
        query_instances: The instances from X chosen to be labelled.
    """

    # Obtain the prediction probabilities from the classifier
    probs = classifier.predict_proba(X)

    # Compute the entropy for each sample in the pool
    ent = entropy(probs.T)

    # Select the indices of the instances to be queried
    query_idx = multi_argmax(ent, n_instances=n_instances)

    return query_idx, X[query_idx]
```

上記の関数を定義した後、アクティブラーナーを初期化する際に、`query_strategy`引数としてこの関数を渡すことでエントロピー最大化を使用できます。

```python
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_initial, y_training=y_initial,
    query_strategy=max_entropy
)
```

このようにして、エントロピー最大化戦略をアクティブラーニングに適用することができます。ただし、このコード例は基本的な例であり、実際の使用には適宜調整が必要になる可能性があります。

---

### **user**:

「最も不確かな予測」のアプローチを理解するために、「最も不確かな予測」のアプローチをスクラッチで実装してください。

---

### **assistant**:

「最も不確かな予測」のアプローチは、分類器がそのデータポイントが特定のクラスに属すると予測した確率が最も低いデータポイントを選ぶものです。より具体的には、最も確信度（予測確率）が低いデータポイントを選びます。

以下に、このアプローチの基本的な実装を示します：

```python
from sklearn.base import BaseEstimator
from modAL.utils.selection import multi_argmin
import numpy as np

def least_confident(classifier: BaseEstimator, X: np.ndarray, n_instances: int = 1, **kwargs) -> np.ndarray:
    """
    Least confident strategy function.
    Query the instances where the prediction is least confident.

    Args:
        classifier: Classifier instance equipped with predict_proba() method.
        X: The pool of samples to query from.
        n_instances: The number of samples to be queried.

    Returns:
        query_idx: The indices of the instances from X chosen to be labelled;
        query_instances: The instances from X chosen to be labelled.
    """

    # Obtain the prediction probabilities from the classifier
    probs = classifier.predict_proba(X)

    # Compute the confidence for each sample in the pool
    confidence = np.max(probs, axis = 1)

    # Select the indices of the instances to be queried
    query_idx = multi_argmin(confidence, n_instances=n_instances)

    return query_idx, X[query_idx]
```
上記の関数を定義した後、アクティブラーナーを初期化する際に、`query_strategy`引数としてこの関数を渡すことで最も確信度が低い予測を選ぶクエリストラテジーを使用できます。

```python
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_initial, y_training=y_initial,
    query_strategy=least_confident
)
```
このようにして、「最も不確かな予測」のアプローチをアクティブラーニングに適用することができます。ただし、このコード例は基本的な例であり、実際の使用には適宜調整が必要になる可能性があります。

---

### **user**:

3つのアプローチのうち、どの場合にはどれを用いたら良い、などの知見があれば教えてください。

---

### **assistant**:

どのアクティブラーニングの戦略を使用するべきかは、問題の性質や利用可能なリソース、指標の要件などによります。以下にそれぞれの戦略が適しているケースの一例を提供します。

1. **最も不確かな予測 (Least Confident)**: この戦略は、機械学習モデルが最も興味がある（つまり、予測に対して最も不確かであると考えられる）インスタンスを優先的にラベル付けする必要がある場合に効果的です。この戦略は、特に2クラス分類の問題に適しています。

2. **マージンサンプリング (Margin Sampling)**: この戦略は、ラベル間の予測確信度の差を最小化することにより、モデルの確信度全体を向上させることを目指します。したがって、マルチクラスの問題や難解な問題、またはモデルの確信度が全体的に低い場合に効果的です。

3. **エントロピー最大化 (Entropy Maximization)**: この戦略は、予測の分布が最も均等である（つまり、モデルがどのクラスに分類するべきか最も不確かである）インスタンスを優先的にラベル付けします。クラス間の境界が曖昧なケースや、多クラス分類問題に適しています。

実際にはこれらの戦略を試し、評価指標や問題の性質に最も適している戦略を選択することが一般的です。また、これらの戦略はあくまで基礎的なものであり、更に効果的なアクティブラーニングを行うためには、問題に特化したクエリストラテジーを開発することが有効です。

---

### **user**:

ありがとうございます。今回は n_queries = 200 にしましたが、適切な n_queries の値を知る方法はありますか？

---

### **assistant**:

`n_queries`の値を選ぶ際には、以下のような要素を考慮すると良いです：

1. **利用可能なリソース**：問い合わせ（クエリ）を行うためには人間の介入が必要で、時間とコストがかかります。したがって、利用可能な時間や予算、人的リソースに応じて`n_queries`を設定する必要があります。

2. **データセットの大きさ**：データセットが大きい場合、`n_queries`も大きく設定することでデータセット全体の情報をより多くキャプチャすることができます。

3. **問題の難易度**：問題が難しい（例えば、クラスが不均衡である、特徴が多次元である、など）場合、より多くのクエリを行うことでより良いモデルを構築できる可能性があります。

4. **パフォーマンスの改善**：実験を行い、`n_queries`の値を増やすことでモデルのパフォーマンスがどの程度改善するかを評価します。大きな改善が見られなくなったとき、それ以上クエリを増やすのは無駄になる可能性があります。

したがって、「適切な」`n_queries`の値は問題や状況によります。一般的なアドバイスとしては、様々な値を試し、パフォーマンスとリソース使用量を評価し、最適なバランスを見つけることが重要です。

---

### **user**:

ありがとうございます。それでは、上記のコードを改変して、n_queries を増やすごとにモデルがどの程度改善するかを可視化するようなコードを書いてください。

---

### **assistant**:

以下に、`n_queries`が増えるごとにモデル精度がどのように改善するかを可視化するための一例を記載します。コードを理解しやすくするため、元のコードを少しシンプル化しました。

まず、データのロードや学習器の設定についてはそのまま維持します。

```python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from modAL.models import ActiveLearner

# Irisデータのロード
iris = load_iris()
X_raw = iris['data']
y_raw = iris['target']

# トレーニングデータとテストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X_raw, y_raw)

# ラベルが付いていないプールを作成
X_pool, y_pool = X_train, y_train

# ActiveLearnerの初期化
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_pool, y_training=y_pool
)
```

次に、`n_queries`を増やすごとにモデルの精度を記録します。この例では、`n_queries`は1から100まで増やすことにします。

```python
n_queries = 100

# 学習器の精度を記録するリストを作成します
accuracy_list = []

# n_queries回クエリを行って学習します
for i in range(n_queries):
    query_idx, query_instance = learner.query(X_pool, n_instances=5)
    X, y = X_pool[query_idx], y_pool[query_idx]
    learner.teach(X=X, y=y)

    # クエリしたサンプルをプールから削除します
    X_pool = np.delete(X_pool, query_idx, axis=0)
    y_pool = np.delete(y_pool, query_idx, axis=0)

    # モデルの精度を計算し、リストに追加します
    model_accuracy = learner.score(X_test, y_test)
    accuracy_list.append(model_accuracy)

# 結果をプロットします
import matplotlib.pyplot as plt

plt.figure(figsize=(8,6))
plt.plot(range(n_queries), accuracy_list)
plt.xlabel('Number of queries')
plt.ylabel('Test Accuracy')
plt.title('Test Accuracy vs Number of Queries')
plt.show()
```

このコードを実行すると、クエリの数を増やすごとにテスト精度がどのように変化するかを可視化できます。

---

### **user**:

次のようなエラーが出たので改善したコードを書いてください。

```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:175, in BaseLearner.query(self, X_pool, return_metrics, *query_args, **query_kwargs)
    174 try:
--> 175     query_result, query_metrics = self.query_strategy(
    176         self, X_pool, *query_args, **query_kwargs)
    178 except:

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:155, in uncertainty_sampling(classifier, X, n_instances, random_tie_break, **uncertainty_measure_kwargs)
    154 if not random_tie_break:
--> 155     return multi_argmax(uncertainty, n_instances=n_instances)
    157 return shuffled_argmax(uncertainty, n_instances=n_instances)

File ~/.local/lib/python3.10/site-packages/modAL/utils/selection.py:65, in multi_argmax(values, n_instances)
     56 """
     57 return the indices and values of the n_instances highest values.
     58 
   (...)
     63     The indices and values of the n_instances largest values.
     64 """
---> 65 assert n_instances <= values.shape[0], 'n_instances must be less or equal than the size of utility'
     67 max_idx = np.argpartition(-values, n_instances-1, axis=0)[:n_instances]

AssertionError: n_instances must be less or equal than the size of utility

During handling of the above exception, another exception occurred:

AssertionError                            Traceback (most recent call last)
Cell In[7], line 8
      6 # n_queries回クエリを行って学習します
      7 for i in range(n_queries):
----> 8     query_idx, query_instance = learner.query(X_pool, n_instances=5)
      9     X, y = X_pool[query_idx], y_pool[query_idx]
     10     learner.teach(X=X, y=y)

File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:180, in BaseLearner.query(self, X_pool, return_metrics, *query_args, **query_kwargs)
    178 except:
    179     query_metrics = None
--> 180     query_result = self.query_strategy(
    181         self, X_pool, *query_args, **query_kwargs)
    183 if return_metrics:
    184     if query_metrics is None: 

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:155, in uncertainty_sampling(classifier, X, n_instances, random_tie_break, **uncertainty_measure_kwargs)
    152 uncertainty = classifier_uncertainty(classifier, X, **uncertainty_measure_kwargs)
    154 if not random_tie_break:
--> 155     return multi_argmax(uncertainty, n_instances=n_instances)
    157 return shuffled_argmax(uncertainty, n_instances=n_instances)

File ~/.local/lib/python3.10/site-packages/modAL/utils/selection.py:65, in multi_argmax(values, n_instances)
     55 def multi_argmax(values: np.ndarray, n_instances: int = 1) -> np.ndarray:
     56     """
     57     return the indices and values of the n_instances highest values.
     58 
   (...)
     63         The indices and values of the n_instances largest values.
     64     """
---> 65     assert n_instances <= values.shape[0], 'n_instances must be less or equal than the size of utility'
     67     max_idx = np.argpartition(-values, n_instances-1, axis=0)[:n_instances]
     69     return max_idx, values[max_idx]

AssertionError: n_instances must be less or equal than the size of utility
```

---

### **assistant**:

このエラーが発生したのは、クエリを行う際に取得しようとするサンプルの数（`n_instances`）が、現在のプール（`X_pool`）のサンプル数を超えてしまったためです。つまり、指定した`n_instances`よりも`X_pool`のサンプル数が少なくなる場合、エラーが発生します。

修正するためには、クエリを行う前に、現在のプール（`X_pool`）のサンプル数が`n_instances`よりも大きいことを検証すればよいです。以下に修正したコードを示します。

```python
n_queries = 100
query_num = 5  # 一度のクエリで取得するサンプル数

accuracy_list = []

for i in range(n_queries):
    # もしX_poolのサンプル数がquery_numより少ない場合は、X_poolの全てのサンプルをクエリします。
    query_num = min(query_num, len(X_pool))
    
    query_idx, query_instance = learner.query(X_pool, n_instances=query_num)
    X, y = X_pool[query_idx], y_pool[query_idx]
    learner.teach(X=X, y=y)

    X_pool = np.delete(X_pool, query_idx, axis=0)
    y_pool = np.delete(y_pool, query_idx, axis=0)

    model_accuracy = learner.score(X_test, y_test)
    accuracy_list.append(model_accuracy)
```

---

### **user**:

次のエラーを修正したコードを書いてください。

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:175, in BaseLearner.query(self, X_pool, return_metrics, *query_args, **query_kwargs)
    174 try:
--> 175     query_result, query_metrics = self.query_strategy(
    176         self, X_pool, *query_args, **query_kwargs)
    178 except:

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:152, in uncertainty_sampling(classifier, X, n_instances, random_tie_break, **uncertainty_measure_kwargs)
    136 """
    137 Uncertainty sampling query strategy. Selects the least sure instances for labelling.
    138 
   (...)
    150     The uncertainty metric of the chosen instances. 
    151 """
--> 152 uncertainty = classifier_uncertainty(classifier, X, **uncertainty_measure_kwargs)
    154 if not random_tie_break:

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:77, in classifier_uncertainty(classifier, X, **predict_proba_kwargs)
     76 try:
---> 77     classwise_uncertainty = classifier.predict_proba(X, **predict_proba_kwargs)
     78 except NotFittedError:

File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:153, in BaseLearner.predict_proba(self, X, **predict_proba_kwargs)
    143 """
    144 Class probabilities if the predictor is a classifier. Interface with the predict_proba method of the classifier.
    145 
   (...)
    151     Class probabilities for X.
    152 """
--> 153 return self.estimator.predict_proba(X, **predict_proba_kwargs)

File /usr/local/lib/python3.10/site-packages/sklearn/ensemble/_forest.py:865, in ForestClassifier.predict_proba(self, X)
    864 # Check data
--> 865 X = self._validate_X_predict(X)
    867 # Assign chunk of trees to jobs

File /usr/local/lib/python3.10/site-packages/sklearn/ensemble/_forest.py:599, in BaseForest._validate_X_predict(self, X)
    598 check_is_fitted(self)
--> 599 X = self._validate_data(X, dtype=DTYPE, accept_sparse="csr", reset=False)
    600 if issparse(X) and (X.indices.dtype != np.intc or X.indptr.dtype != np.intc):

File /usr/local/lib/python3.10/site-packages/sklearn/base.py:604, in BaseEstimator._validate_data(self, X, y, reset, validate_separately, cast_to_ndarray, **check_params)
    603 elif not no_val_X and no_val_y:
--> 604     out = check_array(X, input_name="X", **check_params)
    605 elif no_val_X and not no_val_y:

File /usr/local/lib/python3.10/site-packages/sklearn/utils/validation.py:969, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    968     if n_samples < ensure_min_samples:
--> 969         raise ValueError(
    970             "Found array with %d sample(s) (shape=%s) while a"
    971             " minimum of %d is required%s."
    972             % (n_samples, array.shape, ensure_min_samples, context)
    973         )
    975 if ensure_min_features > 0 and array.ndim == 2:

ValueError: Found array with 0 sample(s) (shape=(0, 4)) while a minimum of 1 is required by RandomForestClassifier.

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[8], line 10
      6 for i in range(n_queries):
      7     # もしX_poolのサンプル数がquery_numより少ない場合は、X_poolの全てのサンプルをクエリします。
      8     query_num = min(query_num, len(X_pool))
---> 10     query_idx, query_instance = learner.query(X_pool, n_instances=query_num)
     11     X, y = X_pool[query_idx], y_pool[query_idx]
     12     learner.teach(X=X, y=y)

File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:180, in BaseLearner.query(self, X_pool, return_metrics, *query_args, **query_kwargs)
    178 except:
    179     query_metrics = None
--> 180     query_result = self.query_strategy(
    181         self, X_pool, *query_args, **query_kwargs)
    183 if return_metrics:
    184     if query_metrics is None: 

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:152, in uncertainty_sampling(classifier, X, n_instances, random_tie_break, **uncertainty_measure_kwargs)
    133 def uncertainty_sampling(classifier: BaseEstimator, X: modALinput,
    134                          n_instances: int = 1, random_tie_break: bool = False,
    135                          **uncertainty_measure_kwargs) -> np.ndarray:
    136     """
    137     Uncertainty sampling query strategy. Selects the least sure instances for labelling.
    138 
   (...)
    150         The uncertainty metric of the chosen instances. 
    151     """
--> 152     uncertainty = classifier_uncertainty(classifier, X, **uncertainty_measure_kwargs)
    154     if not random_tie_break:
    155         return multi_argmax(uncertainty, n_instances=n_instances)

File ~/.local/lib/python3.10/site-packages/modAL/uncertainty.py:77, in classifier_uncertainty(classifier, X, **predict_proba_kwargs)
     75 # calculate uncertainty for each point provided
     76 try:
---> 77     classwise_uncertainty = classifier.predict_proba(X, **predict_proba_kwargs)
     78 except NotFittedError:
     79     return np.ones(shape=(X.shape[0], ))

File ~/.local/lib/python3.10/site-packages/modAL/models/base.py:153, in BaseLearner.predict_proba(self, X, **predict_proba_kwargs)
    142 def predict_proba(self, X: modALinput, **predict_proba_kwargs) -> Any:
    143     """
    144     Class probabilities if the predictor is a classifier. Interface with the predict_proba method of the classifier.
    145 
   (...)
    151         Class probabilities for X.
    152     """
--> 153     return self.estimator.predict_proba(X, **predict_proba_kwargs)

File /usr/local/lib/python3.10/site-packages/sklearn/ensemble/_forest.py:865, in ForestClassifier.predict_proba(self, X)
    863 check_is_fitted(self)
    864 # Check data
--> 865 X = self._validate_X_predict(X)
    867 # Assign chunk of trees to jobs
    868 n_jobs, _, _ = _partition_estimators(self.n_estimators, self.n_jobs)

File /usr/local/lib/python3.10/site-packages/sklearn/ensemble/_forest.py:599, in BaseForest._validate_X_predict(self, X)
    596 """
    597 Validate X whenever one tries to predict, apply, predict_proba."""
    598 check_is_fitted(self)
--> 599 X = self._validate_data(X, dtype=DTYPE, accept_sparse="csr", reset=False)
    600 if issparse(X) and (X.indices.dtype != np.intc or X.indptr.dtype != np.intc):
    601     raise ValueError("No support for np.int64 index based sparse matrices")

File /usr/local/lib/python3.10/site-packages/sklearn/base.py:604, in BaseEstimator._validate_data(self, X, y, reset, validate_separately, cast_to_ndarray, **check_params)
    602         out = X, y
    603 elif not no_val_X and no_val_y:
--> 604     out = check_array(X, input_name="X", **check_params)
    605 elif no_val_X and not no_val_y:
    606     out = _check_y(y, **check_params)

File /usr/local/lib/python3.10/site-packages/sklearn/utils/validation.py:969, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    967     n_samples = _num_samples(array)
    968     if n_samples < ensure_min_samples:
--> 969         raise ValueError(
    970             "Found array with %d sample(s) (shape=%s) while a"
    971             " minimum of %d is required%s."
    972             % (n_samples, array.shape, ensure_min_samples, context)
    973         )
    975 if ensure_min_features > 0 and array.ndim == 2:
    976     n_features = array.shape[1]

ValueError: Found array with 0 sample(s) (shape=(0, 4)) while a minimum of 1 is required by RandomForestClassifier.
```

---

### **assistant**:

このエラーは、クエリを行おうとしたときに`X_pool`が空（つまりサンプル数が0）になってしまったために発生しています。

Active Learningのループを実行する際には、クエリが行えるデータがプールにあることを確認する必要があります。以下は、ループを修正したコードです。

```python
n_queries = 100
query_num = 5  # 一度のクエリで取得するサンプル数 

accuracy_list = []

for i in range(n_queries):
    # あらかじめX_poolが空になっていないか確認します
    if len(X_pool) == 0:
        print("All instances in X_pool have been queried.")
        break

    # もしX_poolのサンプル数がquery_numより少ない場合は、X_poolの全てのサンプルをクエリします
    query_num = min(query_num, len(X_pool))

    query_idx, query_instance = learner.query(X_pool, n_instances=query_num)
    X, y = X_pool[query_idx], y_pool[query_idx]
    learner.teach(X=X, y=y)

    X_pool = np.delete(X_pool, query_idx, axis=0)
    y_pool = np.delete(y_pool, query_idx, axis=0)

    model_accuracy = learner.score(X_test, y_test)
    accuracy_list.append(model_accuracy)
```

このコードでは、クエリを行う前に`X_pool`が空になっていないかを確認します。もし`X_pool`が空であればループを抜けます。

---

### **user**:

最初のコードでは load_digits を使ったと思うので同じデータを使ってください。それで、可視化までのコードを書いてください。

---

### **assistant**:

了解しました。最初のコードと同じくdigitsデータセットを用いてUncertainty Samplingというactive learningの方法論を示します。以下に可視化までのコードを示します：

```python
# 必要なライブラリのインポート
import numpy as np
from modAL.models import ActiveLearner
from modAL.uncertainty import uncertainty_sampling
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt

# digitsデータセットの読み込み
digits = load_digits()
X_raw = digits['data']
y_raw = digits['target']

# 初期データの生成(100個)
n_initial = 100
initial_idx = np.random.choice(range(len(X_raw)), size=n_initial, replace=False)

# プールデータとテストデータの分割 
X_pool, X_test, y_pool, y_test = train_test_split(
    X_raw, y_raw, test_size=0.2, random_state=42
)
    
# 学習器の初期化
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_pool[initial_idx],
    y_training=y_pool[initial_idx],
    query_strategy=uncertainty_sampling
)

# プールから初期データを削除
X_pool = np.delete(X_pool, initial_idx, axis=0)
y_pool = np.delete(y_pool, initial_idx, axis=0)

# Active Learningの設定
n_queries = 100
query_num = 5  # 一度のクエリで取得するサンプル数 

accuracy_list = []

# 可視化のための設定
fig, ax = plt.subplots()

for i in range(n_queries):
    if len(X_pool) == 0:
        print("All instances in X_pool have been queried.")
        break

    query_num = min(query_num, len(X_pool))
    query_idx, query_instance = learner.query(X_pool, n_instances=query_num)
    X, y = X_pool[query_idx], y_pool[query_idx]
    learner.teach(X=X, y=y)

    X_pool = np.delete(X_pool, query_idx, axis=0)
    y_pool = np.delete(y_pool, query_idx, axis=0)

    model_accuracy = learner.score(X_test, y_test)
    accuracy_list.append(model_accuracy)

    # learning curveの表示
    ax.scatter(i+1, model_accuracy, color='dodgerblue', s=3)

# グラフの設定
ax.set_xlabel('Number of queries')
ax.set_ylabel('Accuracy')
ax.set_title('Learning curve')
ax.grid(True)
```

このコードでは各クエリ後にモデルの精度（一致率）をグラフに表示します。表示するほど曲線が右上に切り上げることが期待されます。これはモデルの学習が進んで精度が向上していることを示します。

---

### **user**:

次のエラーを修正したコードを書いてください。

```
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[19], line 27
     20 X_pool, X_test, y_pool, y_test = train_test_split(
     21     X_raw, y_raw, test_size=0.2, random_state=42
     22 )
     24 # 学習器の初期化
     25 learner = ActiveLearner(
     26     estimator=RandomForestClassifier(),
---> 27     X_training=X_pool[initial_idx],
     28     y_training=y_pool[initial_idx],
     29     query_strategy=uncertainty_sampling
     30 )
     32 # プールから初期データを削除
     33 X_pool = np.delete(X_pool, initial_idx, axis=0)

IndexError: index 1775 is out of bounds for axis 0 with size 1437
```

---

### **assistant**:

このエラーは、`initial_idx`に設定されたインデックスが、`X_pool`のサイズを超えてしまったため発生しています。

`X_pool`は`train_test_split`によって生成され、そのサイズは`X_raw`の80%になるため、`X_raw`全体からランダムに抽出した`initial_idx`が`X_pool`のサイズを超える可能性があります。

この問題を解決するためには、`initial_idx`の生成を`train_test_split`後に行うことで、インデックスが`X_pool`の範囲内に収まるようにすれば良いです。以下に修正したコードを示します。

```python
# 必要なライブラリのインポート
import numpy as np
from modAL.models import ActiveLearner
from modAL.uncertainty import uncertainty_sampling
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt

# digitsデータセットの読み込み
digits = load_digits()
X_raw = digits['data']
y_raw = digits['target']

# プールデータとテストデータの分割 
X_pool, X_test, y_pool, y_test = train_test_split(
    X_raw, y_raw, test_size=0.2, random_state=42
)

# 初期データの生成(100個)
n_initial = 100
initial_idx = np.random.choice(range(len(X_pool)), size=n_initial, replace=False)

# 学習器の初期化
learner = ActiveLearner(
    estimator=RandomForestClassifier(),
    X_training=X_pool[initial_idx],
    y_training=y_pool[initial_idx],
    query_strategy=uncertainty_sampling
)

# プールから初期データを削除
X_pool = np.delete(X_pool, initial_idx, axis=0)
y_pool = np.delete(y_pool, initial_idx, axis=0)

# 以下略
```

![output_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/856401f4-2fa0-e23b-512c-86c0eaf8fc70.png)


---

### **user**:

おお、得られました。accuracy_listの中身は次のようになりました。

```
[0.8722222222222222,
 0.8972222222222223,
 0.9138888888888889,
 0.9083333333333333,
 0.9305555555555556,
 0.925,
 0.9361111111111111,
 0.925,
 0.9222222222222223,
 0.9277777777777778,
 0.9472222222222222,
 0.9472222222222222,
 0.9555555555555556,
 0.9527777777777777,
 0.9555555555555556,
 0.9583333333333334,
 0.95,
 0.9583333333333334,
 0.9638888888888889,
 0.9694444444444444,
 0.9722222222222222,
 0.9777777777777777,
 0.9722222222222222,
 0.975,
 0.9722222222222222,
 0.975,
 0.9805555555555555,
 0.9805555555555555,
 0.9805555555555555,
 0.975,
 0.9805555555555555,
 0.9777777777777777,
 0.975,
 0.9777777777777777,
 0.9638888888888889,
 0.9638888888888889,
 0.975,
 0.9722222222222222,
 0.975,
 0.9722222222222222,
 0.9805555555555555,
 0.9777777777777777,
 0.9805555555555555,
 0.9777777777777777,
 0.9777777777777777,
 0.975,
 0.975,
 0.9833333333333333,
 0.9805555555555555,
 0.9861111111111112,
 0.9833333333333333,
 0.9833333333333333,
 0.9861111111111112,
 0.9805555555555555,
 0.9861111111111112,
 0.9861111111111112,
 0.9861111111111112,
 0.975,
 0.9833333333333333,
 0.9861111111111112,
 0.9833333333333333,
 0.9916666666666667,
 0.9833333333333333,
 0.9833333333333333,
 0.9888888888888889,
 0.9861111111111112,
 0.9888888888888889,
 0.9861111111111112,
 0.9861111111111112,
 0.9861111111111112,
 0.9833333333333333,
 0.9833333333333333,
 0.9861111111111112,
 0.9861111111111112,
 0.9888888888888889,
 0.9833333333333333,
 0.9888888888888889,
 0.9805555555555555,
 0.9833333333333333,
 0.9861111111111112,
 0.975,
 0.9861111111111112,
 0.9777777777777777,
 0.9888888888888889,
 0.9861111111111112,
 0.9805555555555555,
 0.9861111111111112,
 0.9833333333333333,
 0.9833333333333333,
 0.9805555555555555,
 0.9861111111111112,
 0.9833333333333333,
 0.9833333333333333,
 0.9833333333333333,
 0.9805555555555555,
 0.9888888888888889,
 0.9888888888888889,
 0.9833333333333333,
 0.975,
 0.9833333333333333]
```

---

### **assistant**:

active learningのプロセス中でモデルの精度が改善していることが見て取れます。最初の精度が約87.2%で始まり、最終的には約98.3%まで向上しています。

クエリを行うたびに追加的なデータが学習データに追加され，モデル全体の精度が向上することがそこからわかります。

学習の進展、進行中のチェック、または情報の可視化にこの精度一覧を使用することができます。

もし何か他に質問がありましたら、お知らせください。

---

### **user**:

ありがとうございました。これまでの議論を踏まえて、アクティブラーニングについてまとめを詳しく書いてください。

---

### **assistant**:

アクティブラーニングは、未ラベル付けのデータから最も情報量の多いサンプルを選び出し、そのラベルを専門家などから取得することで、最小限のラベル付けされたデータで最適なモデルを学習するための手法です。

1. **モデルの初期化:** モデルの学習は、ランダムに選択された一部のデータを用いて始まります。これはアクティブラーニングの"ウォームスタート"ともいえます。

2. **クエリ戦略:** 学習済みのモデルを用いて、未ラベルのデータセット全体を評価します。各未ラベルサンプルについて、そのラベルを知ることによってモデルがどれだけ学習できるかを評価します。この評価に基づき、最も役立つと予想されるサンプルを選択します。この選択プロセスはクエリ戦略と呼ばれます。

3. **フィードバックと更新:** 選択されたサンプルのラベルは、人間の専門家などから取得され、モデルにフィードバックされます。次に、この新しいラベル付きデータを使用してモデルを再学習・更新します。

4. **反復プロセス:** 上記のクエリ戦略とフィードバックのプロセスを必要な回数だけ繰り返します。毎回の反復で、モデルは新たな情報を学び、精度が向上します。

アクティブラーニングは、ラベル付けが時間やコストを要する場合や、初めに利用可能なラベル付きデータが少ない場合に特に有効です。クエリ戦略には色々な種類があるので、具体的な問題によって最適な戦略を選択します。

今回の議論では、情報獲得に基づくアクティブラーニングの一つであるUncertainty Samplingを用いて、sklearnのdigitsデータセットに対してランダムフォレスト分類器を学習しました。その過程で、モデルの精度がクエリを重ねることで向上していく様子を確認しました。

---

### **user**:

ありがとうございました。最後に、これまでの議論を踏まえて落語風のギャグを創作してください。

---

### **assistant**:

お話に登場するのは、アクティブラーニングの専門家である「アクティブ太郎」と、AIに詳しくない「何も知らない次郎」の二人です。

何も知らない次郎:「ねぇアクティブ太郎、やっぱりモデル学習するのってたくさんのデータが必要で大変だよね。」

アクティブ太郎:「それがね、アクティブラーニングなら最小限のラベル付けされたデータで最適なモデルが学習できるんだ！」

何も知らない次郎:「それなら手間が省けて良いね！でも、適当にデータ選んでも意味ないでしょ？」

アクティブ太郎:「そのとおり！だからアクティブラーニングでは、「クエリ戦略」を使って効果的にデータを選ぶんだよ。」

何も知らない次郎:「クエリ戦略？なんだそれは。」

アクティブ太郎:「それがさ、つまり・・・」

何も知らない次郎：「あ、待って！クエリって、お腹空いてきたなー」

アクティブ太郎: 「それ、カレーだよ！クエリとカレーは別物だよ（笑）」

という具体的な冗談を作成しました。AIやアクティブラーニングの専門的な内容を軽く呈示することで、視聴者にリラックスして笑顔になってもらえることを期待しています。

---

### **user**:

あっ、そういえば、アクティブラーニングと、ベイズ最適化って、よく似てませんか？

---

### **assistant**:

それはとても興味深い、そして洞察に満ちた観察です。実際、アクティブラーニングとベイズ最適化はいくつかの類似性を共有しています：

1. **逐次的データ取得：** アクティブラーニングとベイズ最適化は両方とも逐次的にデータを取得します。いずれも、現時点で利用可能なデータを基にモデルを学習し、その結果を元に次に取得すべきデータを決定します。

2. **評価関数：** アクティブラーニングのクエリ戦略とベイズ最適化の獲得関数は共に、次に収集すべきデータの候補を評価するために使用されます。

3. **情報獲得目標：** 両方とも、適切な情報を獲得することでモデルの性能を向上させることを目指しています。

しかし、これらの手法はそれぞれ異なる目的に使用されます：

- **アクティブラーニング**は、ラベル付けが高コストな設定において、最小限のラベル付けで最大の学習効果を得ることを目的としています。それは通常ラベルのないデータが多量にある一方で、そのラベル付けが時間とコストを必要とする場合に適しています。

- 一方で**ベイズ最適化**は、パラメータのチューニングまたはハイパーパラメータの最適化など、連続空間上での最適化問題を解くことを目的としています。

このため、これら二つの戦略は実際にはよく一緒に使用されます。例えば、アクティブラーニングで適切なデータを収集し、その収集したデータに基づいてモデルのハイパーパラメータをベイズ最適化で調整するといった使い方が考えられます。

---

### **user**:

ありがとうございました。最後は落語で終わりたかったのに、すみません。

---

### **assistant**:

いえいえ、全く問題ありません！落語の雰囲気を保って、最後の部分を締めくくるためには、こんな感じでしょうか。

何も知らない次郎:「ほぅ、アクティブラーニングとベイズ最適化って、似てる部分があるんだね。」

アクティブ太郎:「そうなんだよ、次郎、でも使い方によって効果が変わってくるからね！」

何も知らない次郎:「なるほどね、じゃあこれからは「ベイズ優潔にアクティブ」ってことで、わたくしも一丁前にAIに活用していきますか！」

アクティブ太郎:「っと、次郎！それ「ベイジュ（ベージュ）にアクティブ」だよ、服装の話じゃないよ（笑）！」

何も知らない次郎とアクティブ太郎の世界からはそれでは、お元気で！

---