「多数決」によるアンサンブル学習のショボさも体験しておく

昨日、下記のような記事を投稿しました。概要を一言で表すと「正解率70%くらいの弱学習器でも、寄せ集めて多数決をとると、正解率100%に近づいていくよ！」という、夢のあるお話でした。

https://qiita.com/rho-guy/items/d82bb51b5ae8728e5fb0

そんな記事を投稿しておいて、次の日には「多数決のショボさ」を説くとは、なんとういう手のひら返し。これは怒られても仕方がない。そう、仕方がないのだが、落ち着いて聞いてほしい。「君子豹変す」ともいうではないか。

<img width=200 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/c1b9519d-0e5f-39ff-bb44-e18554163e57.png">


# 「多数決」に対する偏りの悪影響
　
 昨日の記事では、「きれいに分散している分類結果を多数決で統合するとエラーが消える」ことが鮮やかに示されたので、実は自分自身でも内心感動していました。しかし一方で、「いやいや、世の中こんな簡単にはいかんやろ」「実際、多数決でそこまで精度もあがらないけどなあ」と思っている自分もいたのも確かです。

　それでは実務やコンペでのチューニングの場面で、多数決を使っても精度向上に頭打ちが起こる原因は何でしょうか？昨日の実験で使ったソースコードを少し改造して、何が起こっているか確かめてみましょう。まあ、結論からいうと「弱学習器の出力に偏りがあるから」なんですけどね。

 　「弱学習器の偏り」というのは、例えばこんな感じで発生しているものと思われます。
  - 何かの拍子に、自己ベストの成績をたたき出す特徴量とモデルのセットが見つかった（＝ベストモデル）
  - ベストモデルをちょっとずつアレンジして弱学習器を作り、複数の分類結果を出力した
  - 複数の分類結果を多数決で統合しても、期待したほど精度は上がらなかった

弱学習器に偏りがなかった昨日の実験との比較でいうと、

- 偏りなし：「正解データ」から派生した弱学習器を作って多数決させる
- 偏りあり：「ベストモデル」から派生した弱学習器を作って多数決させる

点が異なります。さて、「偏りあり」のケースで、多数決の結果がどうなるか見てみましょう。昨日の最後のソースコードを、下記のように修正します。（各種関数は[昨日の記事](https://qiita.com/rho-guy/items/d82bb51b5ae8728e5fb0)で定義してあるので、この記事を初めてお読みになる方は、昨日の記事からソースコードをコピペして実行してください）

~~~.py
# 弱学習器を3つ～MAX_UNIT個まで増やしながら精度を評価する
import numpy as np
import matplotlib.pyplot as plt

MAX_UNIT = 20
ACC = 70
DIM = 400

def main():
  # 正解セットを作る
  gt = make_ground_truth(DIM, PROB)
  # ベストモデルを作る
  best_model = make_trial_list(DIM, 1, gt, ACC)[0]
  best_model_acc = eval_acc(gt, best_model)
  # 正解セットとベストモデルの中身とベストモデルの精度を表示
  print("ground truth:", gt)
  print("best model  :", best_model)
  print("best_model_acc = ", best_model_acc)

  mrgd_acc_list = []
  for i in range(3, MAX_UNIT + 1):
    # ベストモデルから派生した弱学習器を作る
    trial_list = make_trial_list(DIM, i, best_model, ACC)
    acc_list = get_acc_list(gt, trial_list)

    # 弱学習器の中身と、それぞれの精度＆精度の平均値を表示
    print("\nnumber of units = ", i)
    for i in range(len(trial_list)):
      print("    unit", i, ": ", trial_list[i])
    print("    acc_list =", acc_list)
    avrg_acc =  sum(acc_list)/len(acc_list)
    print('    avrg_acc = {:.3f}'.format(avrg_acc))

    # 多数決による投票で最終結果を統合し、精度を評価
    predict = merge_trial_list(trial_list)
    mrgd_acc = eval_acc(gt, predict)
    mrgd_acc_list.append(mrgd_acc)
    print("    mrgd_res:", predict)
    print('    mrgd_acc = {:.3f}'.format(mrgd_acc))

  # 弱学習器の数と、統合後の精度をグラフで表示
  plot_x = np.array([i for i in range(3, MAX_UNIT + 1)])
  plot_y = np.array(mrgd_acc_list)
  plot_best_model_acc = np.ones(MAX_UNIT - 2) * best_model_acc
  plt.plot(plot_x, plot_y)
  plt.plot(plot_x, plot_best_model_acc, linestyle="dashed")

main()
~~~

変更のポイントは、下記の通りです。
①正解データgtを作成した後に、正解率の期待値が0.85程度になる「ベストモデル」best_modelを作成
②弱学習器の分類結果を、gtではなく、best_modelから派生して作成

実行結果は下記のような感じです。試行ごとに結果が変わりますので、まったく同一にはならないと思いますが、お手元の環境で何回かトライして全体の傾向をつかんでみてください。

![アンサンブル学習.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/52b8f830-4e1b-d853-b4ee-593f714ea775.png)

グラフの横軸が弱学習器の数、縦軸が正解率です。オレンジの点線は、「ベストモデル」の正解率です。これを見ると、ベストモデルから派生した弱学習器を寄せ集めても、その多数決の結果はベストモデルに近づいていくだけで、それを超えることはなかなか難しいことが分かりますね。落ち着いて考えてみれば、ベストモデルから派生した弱学習器なので、そうなることは当り前なのですが、時間に追われながら精度向上にやっきになっていると、忘れがちな視点かと思います。


# 偏りのない弱学習器を作るための工夫

　実際には、真の正解データを知りえることはできないので、「正解データから派生した偏りのない弱学習器」を作ることはできません。どうしても「ベストモデルから派生した偏りのある弱学習器」に引っ張られてしまいます。それでは、なるべく「偏りのない弱学習器」を生成するためには、どうすればいいでしょうか。

 ChatGPT先生に聞いてみました。
 <table>
 <tr><td>
 <img width=50 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/e68208ab-57af-a058-2946-77f1065ca9ca.png">

 
 弱学習器を複数作成し、多数決によるアンサンブル学習を行う際に、偏りのない弱学習器を作るための工夫はいくつか考えられます。以下に主な方法を挙げます。

<ul>
<Li> <b>データの多様性の確保:</b> データセットに偏りがないようにすることが重要です。様々な背景を持つサンプルを含め、データセットを多様化させることで、学習器はより一般化された決定を下すことができます。
</ul>

<ul>
<li> <b>リサンプリング技術の利用:</b> ブートストラップ集約（バギング）やブースティングなどのリサンプリング技術を使用して、異なるサブセットで学習器を訓練します。これにより、異なる視点からの学習が可能となり、バイアスの低減に役立ちます。
</ul>

<ul>
<li> <b>ランダム化の導入:</b> 決定木のようなアルゴリズムでは、特徴の選択や分岐点の決定にランダム性を導入することが有効です。これにより、各学習器がデータに対して少し異なる視点を持つことになり、バイアスを減らすことができます。
</ul>

<ul>
<li> <b>異なる種類のアルゴリズムの使用:</b> 異なる種類の弱学習器を組み合わせることで、それぞれの学習器が持つ特性やバイアスを補完し合うことができます。例えば、決定木、サポートベクターマシン、ニューラルネットワークなどを組み合わせることが考えられます。
</ul>

<ul>
<li> <b>ハイパーパラメータの調整:</b> 各弱学習器のハイパーパラメータを適切に調整することで、過学習を防ぎ、より汎化性能の高いモデルを構築することができます。
</ul>

<ul>
<li><b>特徴量の選択:</b> 特徴量の選択に注意を払い、不必要または冗長な特徴量を排除することで、モデルのバイアスを減らすことができます。
</ul>

これらの工夫は、アンサンブル学習においてよりバランスの取れた、バイアスの少ない弱学習器を作成するのに役立ちます。
</td></tr></table>

さすがです！ChatGPT先生！

# 参考になったサイト

https://qiita.com/koshian2/items/d569cd71b0e082111962

https://qiita.com/Ishio/items/6d7378551c2df81aeac1