pythonでN本腕バンディット問題を解く強化学習のプログラムを作ってみた
N本腕バンディット問題とは一定の確率で報酬が出る選択肢の中から、どの報酬が一番高いのかを選ぶ問題です。
例えば、次のようなスロットマシンがあったとして、
内部の情報がこんな感じで当たりで渡されるコインが同じ量だとしたら、みなさんは多分Bを選ぶと思います。
- A 勝率 50%
- B 勝率 70%
- C 勝率 30%
しかし、この中で決まっている勝率がわからないならどうやって選んでいけばいいでしょうか?
それをなんらかの方法で勝率を求めて一番良いやつを選ぶ、というのがN本腕バンディット問題の大まかな概要になります。
今回はこの問題を解決するために、強化学習を用いることにしました。
今回はこんな感じで作ってみました。
import random class BanditArm: def __init__(self, reword_par: float) -> None: self.reword_par = reword_par def draw(self): if random.random() < self.reword_par: return 1.0 else: return 0.0 if __name__ == "__main__": # init N = 5 b = [] # 重み付け(適当) q = [10.0] * N k = [0] * N win = [0] * N q_ast_a = [] e = 0.1 TRY = 1000 for i in range(N): b.append(BanditArm(reword_par=random.uniform(0.1, 0.9))) for par in b: # 本来は見えない q_ast_a.append(par.reword_par) greety_count = 0 for i in range(TRY): # greety if max(q) != 0 and random.random() > e: greety_index = q.index(max(q)) k[greety_index] += 1 win[greety_index] += b[greety_index].draw() q[greety_index] = win[greety_index] / k[greety_index] else: index = random.choices(range(N), k=1)[0] k[index] += 1 win[index] += b[index].draw() q[index] = win[index] / k[index] for i in range(N): a = int(q[i] * 100) b = int(q_ast_a[i] * 100) w = int(win[i]) print("q:{}% vs q*:{}% count{} : {} won.".format(a, b, k[i], w))
$ python n-armed-bandit.py q:53% vs q*:53% count897 : 484 won. q:34% vs q*:32% count23 : 8 won. q:22% vs q*:18% count36 : 8 won. q:50% vs q*:46% count28 : 14 won. q:31% vs q*:25% count16 : 5 won.
コードの簡単な説明
BanditArmクラスはスロットマシンの本体だと思ってください。
初期化時に確率を定めて、draw()によって確率に応じて報酬を支払います。
main以下はεグリーティ手法という強化学習の手法にみ基づいて各インスタンスの勝率を求めています。
出力の説明
- qが学習の結果求めた確率
- q*が本来見えない内部で決めてある本当の確率
- countが試行回数
- wonが勝って得た報酬になります。(1回勝つと1増える)
作ってみて
とりあえず、初めて機械学習的なコードを作ってみましたが、学習して選択できるコードを作れたことに感動しました!
これはかなり原始的な例で、この後から勝率が変化する場合だったり、環境が変化した場合にどうするかなど
考える点はたくさんあります。ですが、機械学習の一歩をふみだせたのは自分の中では大きな一歩かなと思います。
今回はここまで、それではまた!