FC2ブログ

*All archives* |  *Admin*

<<07  2019/08  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  09>>
はじめてのpython&Tensorflowその16・手役判定関数のPythonへの移植
今日は特に成果物があるわけではないですが、進捗報告だけ。

打点と四人麻雀への拡張のために、アガリ時メンツの切り分け関数と手役判定関数をVB.NET仕様からPythonで書き直すのをやってました。

おもったよりたいへんでしたが、無事にお引越しが終わりました。

今後の予定
・四人麻雀で自己対戦できるgym環境作り(牌譜出力機能を含む)
・従前作った教師ありポリシーニューラルネットと自己対戦用環境で牌譜を大量生成
・できた牌譜からバリューネットワーク用データ(仮)を作成し、ニューラルネットで教師あり学習
・教師ありポリシーネットワークを初期値、バリューネット(仮)をベースラインとしたReinforceアルゴリズムで強化学習
・強化学習で得たポリシーネットワークで牌譜を大量生成
・バリューネットワーク(真)を教師あり学習で得る。

という感じでいければ、アルファ碁風麻雀AIの完成なんですが、うまいことできるようにがんばりましょう。

多分一番の壁はいかに高速に自己対戦を回して、多くの牌譜をゲットするまでの時間を短縮できるかだと踏んでいます。
「アルファ碁解体新書」によると、バリューネット用データはデータ間の相関を小さくするために1試合に1サンプルしか取っちゃいけないらしいので、今手持ちの鳳凰卓の牌譜だけではだいぶ心もとないです。
1試合1サンプルの制限はさすがにきついので、1局に1サンプルでお茶を濁してなんとかならないかと考え中です。それなら10倍くらいはかさ上げはできそうなので大きい。
ベースライン用仮データだけなら鳳凰卓データだけでも対応できるかも。

四人麻雀自己対戦gym環境と牌譜出力プログラムをpythonで組む作業がとりあえずの目先の目標です。
スポンサーサイト



はじめてのpython&Tensorflowその15・一人麻雀強化学習byDQNパート2
昨日から特段やることが進んだということではないですが、強化学習が一日でちょっとは進んだかなーと思って、再度テストしてみます。
181115-01.png
1局目。
孤立字牌から切ってくれるようになったお。
15巡目に両面対子を対子固定にしてるのはちょっとまだまだですが。
181115-02.png
2局目。
かたくなに孤立の1mを切ろうとしないあたりが何とも言えないが、まぁ大きくは問題はないか。
181115-03.png
3局目。
これは…。手牌のくっつき度合いが強いのと牌種類が少ないのとで、ペナルティの嵐。こりゃあかんわ。

打点を考えるとかの次への展開を考えてたのですが、まずは一人麻雀でもある程度アガリが取れないと話にならん感じな気がします。
少なくともアガリ率1~2割はないと、アガリ時打点で傾斜をつけるとかしても、学習がいっこうに進まないとかいうことも十分想定しうるので。

後は、今の環境(報酬の与え方)だと、アガリが取れなくてもシャンテン維持のプラスポイントで最終的にそこそこの報酬(0.3~0.7点程度)が取れるケースがしばしばあるので、できればもうちょっとアガリの価値を高めたいところ。
頃合いを見て、シャンテン維持プラスポイントを削減するなど、環境をちょっとずつ変えることが必要になるかなーと。
いきなり環境をガラッと変えると、予測のQ値が大きくずれてしまいそうなので、係数をちょっとずつ動かすみたいな対応になるかと思います。

しばらくはパソコンさん側の作業待ちがメインになりそうです。

技術的なメモ。
Kerasで通しで何時間も計算し続けてると、時間がたつにつれて計算速度がどんどん遅くなっていく事案が発生。
計算時間のボトルネックを探っていく中で、おそらく、モデルの複製とかをやってる中で、なんかメモリ上によどみみたいなのがたまって速度が遅くなるのかなーと推察したので、
適度に(1000エピソードごととかに)重みのセーブ後、Keras.backend.clear_sessionで、いったんモデルを破棄してロードしなおすとかをやってみたらよくわからんけど、計算時間の遅延がちょっと改善されたっぽい。
環境の調整とかの件もあるし、てきとうにプログラムを途中終了させつつ、学習をやったほうがいいのかもしれない。
はじめてのpython&Tensorflowその14・一人麻雀強化学習byDQN
前回(はじめてのpython&Tensorflowその13・強化学習苦戦中)の続き。

Actor-criticにしろ、DQNにしろ、なかなか自力でプログラム組むのはうまくいかなかったです。

DQNだと、わりとサンプルコードが多かったので、下記のサイトのコードをほぼパクらせていただきました。
https://qiita.com/yukiB/items/0a3faa759ca5561e12f8

そしたら、自分で組んだ時より学習がちょっとだけうまく進んだっぽいです。

今回の環境(報酬の決め方)
・上がったときに+1点のプラス報酬を与える
・手牌にない牌を切ろうとした場合、ペナルティとして、-0.002×(当該局のペナルティ回数)のマイナス報酬を与える(その巡目はツモ切り)
・(ペナルティではなかったときで、)14枚手牌と、1枚切った13枚手牌のシャンテン数が一致したとき(つまりシャンテン数を維持できたとき)、0.002×2^(6-シャンテン数)のプラス報酬を与える

報酬の決め方としては、
アガリが一番偉い(報酬が高い)>次いで聴牌に近い段階でシャンテンを維持できること>ペナルティを多く積み重ねないこと>>アガリに遠い段階のシャンテン数維持、少ないペナルティ回数、という感じで作りました。

ペナルティについては回数が多く積み重なるほど大きくマイナスされる(n^2のオーダー)設計にしてます。
シャンテン維持は指数オーダーなので、聴牌に近いとアガリには及ばないものの、かなり大きめの報酬が得られる。
少ペナルティやシャンテン数が大きい段階については多ペナルティやシャンテン数少と比較して報酬の多寡は極めて小さいレベルです。

とりあえず現段階(6000エピソード分)の学習成果がこちら↓。

181114-01.png
1局目。
最初のうちは孤立牌から切っていってる。孤立字牌からでないのがなんとも。
終盤に入って手牌が入り組んでくると、手牌にない牌がQ値が一番高いとニューラルネットが指定してくる。
一向聴で流局。

181114-02.png
2局目。
これも孤立牌は切ってくれるけど、牌がくっついてくるととたんに怪しくなる。
二向聴で流局。

181114-03.png
3局目。
なんとかペナルティなしで乗り切る。
一向聴で流局。

今のところ見た感じアガリまで到達しているエピソードは10~30回に1回くらいとだいぶ少ないです。


ニューラルネットの分量が多いためか、(Q値を37種牌それぞれに算出してる)か、けっこう計算時間が長いです。
だいたい40分で1000エピソードかかってます。
今の時点でこのペースだからこの先の打点や4人麻雀の拡張とかを考えると、先が思いやられますなぁ。

とりあえずシャンテン数のみの強化学習はこのままPCには頑張って続けてもらうとして、私のほうは次どうしようかなー。
Actor-criticの設計からやるか、DQNのままで打点関連や4人麻雀への拡張をやるか。
旧PCはしばらく強化学習で使って、ほかの作業はしにくいから、へたに方策オン型に手を出して教師ありニューラルネットとの連結を考えるよりは、教師あり時と同様、チャンネル数増による、打点関連や4人麻雀の拡張から地道にやってくほうがいいかなー。

となると、次は役判定関数のPythonへの移植が課題ですね。
はじめてのpython&Tensorflowその13・強化学習苦戦中
一人麻雀の強化学習について、
環境とエージェントを組んで、方策勾配法とかActor-criticとかをやろうとしているのですが、なかなか思うような結果が出ずに苦戦してます。

たぶん環境は作れてるけど、エージェントの重みパラメータ更新のところがあやしいです。

こんな感じのコードを書いています。

class Agent:
def __init__(self, state_dim, action_dim, gamma = 1.0, learning_rate = 2e-4, seed = 123):#gamma = 0.95
# 初期化では状態と行動の次元数を設定する。
# 割引率γや学習率を設定し、エピソードの状態、行動、報酬、リターンの系列を初期化する
  (省略)

def step(self,nyuryoku_manzu,nyuryoku_pinzu,nyuryoku_sozu,nyuryoku_jihai,reward):
# 状態と報酬を受け取り、行動の確率に基づいて行動をサンプリングし、出力する
# 状態、行動、報酬は系列に保存する
action_distribution = self.policy(nyuryoku_manzu,nyuryoku_pinzu,nyuryoku_sozu,nyuryoku_jihai)#方策関数から切る牌分布を出力
action = np.random.choice(range(self.action_dim), p=action_distribution.numpy())#乱数と分布から実際の切る牌を決定
value_predict = self.value(nyuryoku_manzu,nyuryoku_pinzu,nyuryoku_sozu,nyuryoku_jihai,action)#行動価値関数から行動価値を予測する。
self.episode_states_manzu.append(nyuryoku_manzu)
self.episode_states_pinzu.append(nyuryoku_pinzu)
self.episode_states_sozu.append(nyuryoku_sozu)
self.episode_states_jihai.append(nyuryoku_jihai)
self.episode_actions.append(action)
self.episode_rewards.append(reward)
self.episode_value_predeict= np.append(self.episode_value_predeict,value_predict)
self.stepsu+=1
return action
def end_episode(self,final_reward):
# 最終的な報酬を受け取ってエピソードを終了する
# このとき、各時点を基準としてエピソードのリターンを計算する
(省略)
def reset(self):
# エピソードの系列をリセットする
(省略)

def update_policy(self):
# 方策勾配法の更新則を適用してパラメタを更新
actions_one_hot = np.array([[ 1 if i == a else 0 for i in range(self.action_dim)] for a in self.episode_actions])#actionをone-hotベクトル化
with tf.GradientTape() as tape:#Actorパラメータ更新

action_distributions = self.policy_network([tf.convert_to_tensor(np.array(self.episode_states_manzu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_pinzu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_sozu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_jihai,dtype=np.float32))
])
action_log_probabilities = tf.log(tf.reduce_sum(action_distributions * actions_one_hot,reduction_indices = 1))#logπ(a|s)

loss = tf.reduce_mean(- action_log_probabilities * (self.episode_value_predeict)) #logπ(a|s)*Q(a,s)
grads = tape.gradient(loss, self.policy_network.variables) #勾配
self.optimizer.apply_gradients(zip(grads, self.policy_network.variables), global_step=self.step_counter)#勾配適用

with tf.GradientTape() as tape2:#Criticパラメータ更新
values = self.critic_network([tf.convert_to_tensor(np.array(self.episode_states_manzu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_pinzu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_sozu,dtype=np.float32)),
tf.convert_to_tensor(np.array(self.episode_states_jihai,dtype=np.float32)),
tf.convert_to_tensor(actions_one_hot.astype(np.float32))
])
value_loss = tf.nn.l2_loss(self.episode_rewards[-1] - values[-1])
for i in range(self.stepsu-2,-1,-1):
value_loss = value_loss + tf.nn.l2_loss(self.episode_rewards[i]+ self.gamma * values[i+1] - values[i])#TD誤差
value_grads = tape2.gradient(value_loss,self.critic_network.variables)#勾配
self.optimizer_forvalue.apply_gradients(zip(value_grads, self.critic_network.variables), global_step=self.step_counter_forvalue)#勾配適用

コードの一部を晒して、どこか通りすがりの頭のいい人に、間違ってるところを指摘してもらえたらうれしい、的な淡い願望。

環境の報酬について、
・上がったら+10点
・流局時に-1点×シャンテン数
で設定して動かしてみたのですが、報酬の移動平均(直近100エピソードの平均)をグラフ化したのがこんな感じ。

181113-01.png
学習できてる雰囲気まったくなし。うーむ。

初期方策がランダムなので、まず手牌にある牌を切ってくれるところが難関になってます。
アドバンテージ関数なるものを使ってやるのがベースライン的にいいという情報まではつかんだのですが、フィッシャー情報行列というものがいまいちよくわからんので、実装には至ってないです。

最初から方策勾配法とかActor-criticとか難しいことはしないで、素直にDQNから入ったほうがいいのだろうか?
行動価値関数QをDQNで推定して、方策の初期値は訓練データありでやったやつを入れたら多少はまともに動くのだろうか?
はじめてのpython&Tensorflowその12・強化学習の練習
ここ数日はPython・keras-rlを使った、強化学習の練習をしていました。

主に下記のサイトで勉強させていただきました。
https://qiita.com/inoory/items/e63ade6f21766c7c2393
https://qiita.com/namakemono/items/1d4432ed55da2fb4f31a
https://qiita.com/ohtaman/items/edcb3b0a2ff9d48a7def

サンプルコードをコピペして、読み下して、実際に動かしてみて、みたいなのを繰り返してました。
現在、できたのがこんな感じのコード↓。
181107.txt
なんか、_stepと_resetとrenderを実装すればいける、みたいなことを拝見してその通りにやってみたら、not implementedがうんぬんかんぬんとか出て、ちょっと戸惑ったけど、先頭のアンダーバーを消したらなんかよくわからんけど、動くようになった。理由はよくわかんないです。

3つ目のリンクのMAP上を勇者が歩き回る環境(MyEnv2)の強化学習がわりと面白かったです。
最初はスタートの周りをうろちょろしてたのが、だんだんゴールに向かっていく感じが見えたのが面白い。
Q学習ではできたけど、actor-criticだと環境(MAP)が固定なためか、同じ方向にしか勇者が進んでくれなくなってとん挫。

というわけで若干不安要素はあるが、まぁ強化学習はできそうな感じはするので、今はVB.NETのコードをPythonへ翻訳こんにゃくしてます。
移植するのは難易度的には難しくはないけど、分量がやたら多くてめんどくさいです。

今日はシャンテン数計算関数について移植ができました。
最悪、シャンテン数計算と、一人麻雀(他家動きなしに限定した訓練データ)で得られたニューラルネットがあれば、環境を一人麻雀(18巡ツモって切るだけ)として報酬をシャンテン数が小さいほどたくさんもらえるとかに設定して強化学習できそうな気はしないでもないです。

とりあえず(比較的)簡単な一人麻雀で強化学習できるかどうかやってみますか。
プロフィール

nisi5028

Author:nisi5028
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
FC2カウンター
フリーエリア
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード