*All archives* |  *Admin*

<<07  2017/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>>
麻雀AI開発その60・評価値関数をAIに搭載(鳴き判断)その3
前回パラメータを出した評価値関数についてAIに搭載して実際の局面に当てはめてチェックしてみます。
170823-01.png
前の時(麻雀AI開発その54・評価値関数をAIに搭載(鳴き判断)その2)より鳴きになる部分が多くなりました。
私の感覚とずれてるところについても、わりとAIの回答も打ち手の個性の範囲内で許容できる(そういう打ち手がいてもおかしくない)ところが多いです。

評価値の桁が2桁くらいまで増えたので、スルー補正の約3ptくらいなら鳴きの手牌価値のみ評価値がスルーよりも十分高ければ吸収できている感じです。

実戦譜で当てはめてみて数値の出方のくせがなんとなくわかってきました。
・巡目がかなり浅いと最終評価値が一けたになることが多くてスルー補正3ptを逆転できずにスルーが選ばれやすい。
・手役確定の鳴きになる場合(役牌ポンなど)がしばしばスルーされて、手役不確定の場合がしばしば鳴かれることがある。
・対リーチなのに危険度が高い牌を切ってしまう

超序盤と対リーチの問題についてはパラメータの問題なので、初期値を都合のいい方にいじって別の停留点を探しに行くのがいいかと思います。

手役確定・不確定については手牌価値のみの評価値の算出関数内でも考えていますが、危険度も加味するパートでも改めて最終評価値にプラスマイナスの補正を与えて調整しようかと思います。


ただ、この問題にばかりかかりきりになるのもそろそろ飽きてきたので、修正が無理そうならあきらめて今のままでもいいことにしましょうかね。
スポンサーサイト
麻雀AI開発その59・評価関数と危険度その2
なんかかなり迷走した感はありましたが、危険度を加味した評価関数がなんとかなりそうになりました。
(前回のデータベースうんぬんの話は結局うまいこと使いこなせなかった。)

今回の話はこちらのサイトを参考にしました。
http://www.anlp.jp/proceedings/annual_meeting/2015/pdf_dir/D3-3.pdf#search=%27%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92+%EF%BD%86%E5%80%A4%27
http://www.procrasist.com/entry/ml-metrics

前までの話は全体正解率(に近いもの)を最大化しようとしていましたが、
これをすると、母集団が多いスルーの方に大きく引っ張られてほぼどんな状況でもAIはスルーと回答するようになっていました。
170822-01.png
この表で言うと、全体正解率は(TP+TN)/(TP+FP+FN+TN)ですが、このうち、TPとFP(鳴くべきなのにスルーと回答)がかなりの部分を占めている形になっていました。
重要と思われるのは、スルーと回答した場合に実際にスルーすべき手である割合(適合率、Precision、TP/(TP+FP))とか、鳴きと回答した場合に実際に鳴くべき手である割合(真陰性と仮に命名、TN/(FN+TN))であろうかと思います。

一般的な機械学習だとF値というもので2値分類器の良し悪しをチェックすることが多いらしいです。
なので、今回は損失関数的なのをやめて、上記サイトの理論を利用させてもらうことにしました。
170822-02.png
入力する情報は、評価値(攻撃面の手牌価値のみ)・巡目・親かどうか・副露数・字牌枚数(この2つは主に守備力関係のつもりで入れた。)・切る牌の危険度指数で、これに対して学習データでは実際に選ばれた行動(スルーとか鳴きとか)が出力値としてあります。
前回は評価値と危険度指数だけで、判断する材料が少なかった、ということが考えられたので巡目とかいろんなものを判断材料として増やしました。

それで、これら入力値に対して危険度を加味した評価値を↑の関数fでモデル化します。

それで、適合率とかは離散的な値でパラメータpで微分できないので、そこを回避するために、
シグモイド関数で微分可能な形に置き換えます。
鳴きについてはその後の打牌候補が複数あるので、そのうちの最も評価値が高いものをスルーとの比較対象にする。

170822-03.png
このとき、目的の関数F値(αはとりあえず0.5にしておいた。)は次のように表すことができます。
170822-04.png
…理論上はpで微分可能かもしれないけど、実際問題これを微分したものを求めるとか極めて無理な雰囲気しかしないです。

幸い、今回は変数の数がそこまで多くない(各30個ずつ)ので偏微分を数式で計算せずに、近似的な偏差分(1個のパラメータを0.0001とか動かしてF値の差分を計算して0.0001で割る。)を計算機に無理やり計算させる方向にしようかと思います。

それで計算した結果がこちら。
(ただし、準ニュートン法のアルゴリズムの調子が悪くて最急降下法に変えたので、収束性はイマイチかもしれない。0副露のみ1000試合分で、それ以外は1万試合分のデータ。)
170822-05b.png

変数が多く、込み入っているので具体的にはわかりづらいですけど、
前回みたいにほぼ全部スルーと回答している状態は脱していそうな感じです。

ただ、実際に出力結果を見たり、この文章を書きながら思ったのはF値の構成要素のうちPrecisionの方はいいとして、Recall(TP/(TP+FN)、実際にスルーされた手を正しく選別できる割合)って別にどうでもよくない?みたいには思いました。(どうでもいいことはないけど。)
それよりは真陰性(TN/(FN+TN))が高い方がうれしいような気がします。これが高いということはAIが鳴いた場合は実際の鳳凰卓の打ち手も鳴いている、ということを表していると思うので。

理論的に意味ある数値かどうかはわからないけど、一回Recallを真陰性に置き換えたバージョンも作ってみようか。

後は準ニュートン法アルゴリズムが壊れた(偏微分の符号とパラメータの修正の方向が逆転して全く収束しない)のもなんとかしないとなぁ。私の理解を超えているアルゴリズムを利用するのはどこらへんがおかしいかが見当つかないから大変です。
麻雀AI開発その58・入力情報のデータベース化
前回で、評価値関数作りが止まってしまったので、現実逃避して別のことをやることにします。

・対染め手危険度指数について。
染め手に対してはまだパラメータを取ってなかったので後回しということにしていました。
さしあたって危険度指数を求めるために、牌ごとの放銃率と放銃時平均失点と聴牌率が必要なので牌譜解析でパラメータを取りました。

取ったのですけど、リーチや副露と比べてサンプル数が少ないので、手持ち枚数と見えてる枚数で分類するとばらつきが激しくて使えなさそうでした。
なので、従前の局収支シミュレーションのパラメータ(枚数分類なし)をそのまま使い回すことにしました。

・入力情報のデータベース化
前に評価値を計算するために必要な入力情報(牌の前後関係とか手役ごとのシャンテン数とか)を10000試合分で計算しようとするとOutOfMemoryのエラーを噴いたのがありました。
1万試合(約3GB)の情報を全部変数に保存して使い回そうとしたことが原因だったのですが(さすがに無理がありすぎた)、
これをしないとなると(前までは他家攻撃について場合分けしたうえで1000試合分に減らしていた)目的関数or偏微分の計算のたびに毎回CSVファイルから読み込み直して…となるとかなり時間を食いそうな気がしました。

なので王道を行くなら一時メモリに保存するのではなく、データベースを作ってそこに保存しておいて、必要な時に一行ごとに読みだして処理する方がいいのかなーと思いました。

データベースはかなり昔にACCESSで触って以来で、今のVB.NETの環境でやるのは初めてだったので、ちょっと手間取りましたが、一応CSVをデータベース(mdfファイル?)に保存するコードは書けました。
で、動かしてみてるのですが、感想。「超遅くない?」
列数が14×61個、行数は10000試合分で約200万行あるのですが、
100行進むのに10秒くらいかかっています。
このペースだと1分600行、1時間36000行、1日90万行、だから丸2~3日かかる計算になります。
待ち時間としてはかなり長い部類ですけど、完全に計画が破たんしているレベルではないという微妙な時間です。

現状だと1行ごとにSQL文でINSERTさせているのですが、なんかバルクコピーなる手法を使うともっと早くINSERTができるらしいです。
ただ、現状で一応は正常にデータベースへの移し替えができていて、作動中のプログラムを止めてまで新しくコードを作り直して正しい動作をしているかをチェックしてから新しいプログラムでやり直す、という手間とうまくいかなかったときのリスクを考えると、まぁ時間かかってもいいから確実に2~3日後にはできる現状のままでいいかな、という感じです。

なんですけども、INSERTだけでこれだけ時間かかるなら解析本番の段階で、DataReaderでデータベースから逐次呼び出しするのと、今のCSVのままでファイル読み出しするのとで、結局あまり時間が変わらない可能性があるという、ちょっと悲しい結末が待ってるかもしれませんね。そうなると頑張ってデータベースのやり方勉強した分が無駄になっちゃうなーとも思うけど、まぁしょうがないか。
麻雀AI開発その57・困った
前回の続き…なのですが、ちょっと詰まってます。

ツモに対する打牌についてはおおむねいいのですが、鳴きについてがうまいこといっていません。

手牌価値評価値xと打牌の危険度指標yの入力値に対して次式が最大化するような関数fの形(もしくはパラメータ群p)を定めようとしています。
170817-02.png
ただし、fの定義は(スルーのときy=-1と固定して場合分けをする、xとyは代入前に100で割って標準化する)、
170817-01.png

これでいちおう、パラメータとして出ることは出るのですが、
170817-03.png
今は他家攻撃なしに注目します。
他家への放銃(危険度指数)はほぼ0なので、yに関する項は極小で、xの項と定数項だけが問題です。

鳴きでもスルーでも同じようなxの値(手牌価値)になる場合、本来は危険度のフィルターを通した後も(今は他家攻撃無しなので)同じような値が出てほしいのですが、だいたいスルーの方がかなり高い数値が出てしまってます。
なので、このままだと手牌だけなら鳴いてほしい場合の大半をスルーしてしまいます。

この原因は定数項p_8がかなり大きな値になっていることです。f(x,y)のスケールは一けた~せいぜい10くらいまでなのに+5みたいなでかい数字を足すとスルーの数値が過剰に高くなってしまいます。

実際に学習データでの正答率をチェックしてみると、正答(実際に選ばれた選択)がスルーのケースだと関数fを通した後もスルーが最も大になる確率は(正答率)は9割なのですが、
正答が鳴きのケースでは2割しかないです。超大差でない限りはでかい定数項のせいでスルーを選択してしまうのが如実に現れています。

言い方を変えれば全体の正答率を上げるために、正答が鳴きであるケースは全部見捨てて、ほぼすべてをスルーだと答えている(正答がスルーのケースはほぼ正解)ような状況に近いです。

困りました。ここ何日か試行錯誤を繰り返しているのですが、現実に即したうまいモデルがなかなか組めません。完全に手詰まりです。ずーっと数式とかプログラムのコードを眺めても妙案が浮かんできません。
麻雀AI開発その56・評価値関数と危険度
昨日の評価値関数→局収支への変換をAIに搭載して、実際どうなってるのかをテストしてたら、
170815-01.png
あ、これは2m中抜きして降りてもらわないといけないやつだ。これはあかんわ。

単に局収支に変換してそこから危険度指数を定数で差し引くのがダメですね。2シャンテン以上に遠い手で先手を取られた時点で、手牌価値指数がプラス5(いい手)だろうが、マイナス5(悪い手)だろうが、関係なく最も安全な牌で降りるのが正着になるので、序盤(他家攻撃無し)と同じロジックは使えません。

そこで、危険度とベタ降りの挙動を入れるためにちょっと前に作った理論(麻雀AI開発その42・数学的なお話その2)を応用します。
xが再帰関数で求めた局収支だったところが、手牌価値評価値にそのまま置き換えただけです。

ツモに対する打牌についてはこのままの式ですが、他家打牌に対する鳴き判断は少し厄介です。
yは初手切る牌の危険度指数ですが、スルーの場合はyに相当するものが存在しません。
そこで、場合分けをして、
・鳴きの場合は同じ式
・スルーの場合はyがからむb,c,eの項の代わり(その項は0とする)に定数項fを加算する。
ということにしました。

というわけで、(紆余曲折ありつつも)係数がこんな感じで出せたことにします。
(4副露はどうせまともな値が出ないだろうから3副露と同じにして、対染め手は危険度のパラメータがまだ取れてないので後回しにする。)
170815-02.png
他家攻撃が強いほど、xの係数が小さくなって、yの係数がマイナス側に大きくなってるので、
より手牌価値指標との関連が薄くなる、より牌の危険度の多寡が重要になる…という理論です。

具体的なテストはまた次回にします。
プロフィール

nisi5028

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

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

この人とブロともになる

QRコード
QRコード