昨日、下記のような記事を投稿しました。概要を一言で表すと「正解率70%くらいの弱学習器でも、寄せ集めて多数決をとると、正解率100%に近づいていくよ!」という、夢のあるお話でした。
そんな記事を投稿しておいて、次の日には「多数決のショボさ」を説くとは、なんとういう手のひら返し。これは怒られても仕方がない。そう、仕方がないのだが、落ち着いて聞いてほしい。「君子豹変す」ともいうではないか。
「多数決」に対する偏りの悪影響
昨日の記事では、「きれいに分散している分類結果を多数決で統合するとエラーが消える」ことが鮮やかに示されたので、実は自分自身でも内心感動していました。しかし一方で、「いやいや、世の中こんな簡単にはいかんやろ」「実際、多数決でそこまで精度もあがらないけどなあ」と思っている自分もいたのも確かです。
それでは実務やコンペでのチューニングの場面で、多数決を使っても精度向上に頭打ちが起こる原因は何でしょうか?昨日の実験で使ったソースコードを少し改造して、何が起こっているか確かめてみましょう。まあ、結論からいうと「弱学習器の出力に偏りがあるから」なんですけどね。
「弱学習器の偏り」というのは、例えばこんな感じで発生しているものと思われます。
- 何かの拍子に、自己ベストの成績をたたき出す特徴量とモデルのセットが見つかった(=ベストモデル)
- ベストモデルをちょっとずつアレンジして弱学習器を作り、複数の分類結果を出力した
- 複数の分類結果を多数決で統合しても、期待したほど精度は上がらなかった
弱学習器に偏りがなかった昨日の実験との比較でいうと、
- 偏りなし:「正解データ」から派生した弱学習器を作って多数決させる
- 偏りあり:「ベストモデル」から派生した弱学習器を作って多数決させる
点が異なります。さて、「偏りあり」のケースで、多数決の結果がどうなるか見てみましょう。昨日の最後のソースコードを、下記のように修正します。(各種関数は昨日の記事で定義してあるので、この記事を初めてお読みになる方は、昨日の記事からソースコードをコピペして実行してください)
# 弱学習器を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から派生して作成
実行結果は下記のような感じです。試行ごとに結果が変わりますので、まったく同一にはならないと思いますが、お手元の環境で何回かトライして全体の傾向をつかんでみてください。
グラフの横軸が弱学習器の数、縦軸が正解率です。オレンジの点線は、「ベストモデル」の正解率です。これを見ると、ベストモデルから派生した弱学習器を寄せ集めても、その多数決の結果はベストモデルに近づいていくだけで、それを超えることはなかなか難しいことが分かりますね。落ち着いて考えてみれば、ベストモデルから派生した弱学習器なので、そうなることは当り前なのですが、時間に追われながら精度向上にやっきになっていると、忘れがちな視点かと思います。
偏りのない弱学習器を作るための工夫
実際には、真の正解データを知りえることはできないので、「正解データから派生した偏りのない弱学習器」を作ることはできません。どうしても「ベストモデルから派生した偏りのある弱学習器」に引っ張られてしまいます。それでは、なるべく「偏りのない弱学習器」を生成するためには、どうすればいいでしょうか。
ChatGPT先生に聞いてみました。
さすがです!ChatGPT先生!
参考になったサイト