2017年08月10日

 野球のポジション当てゲームをプログラムで解いてみた

一昨日、野球のポジション当てゲームというのを知った。

http://heart-quake.com/article.php?p=527より:

1.藤山選手はサードと同じアパートに住んでいるということだ。
2.センターはライトより背は高いが、足はライトの方が早い。
3.鈴木選手の妹さんはセカンドと婚約中だそうで、どうも挙式は来春だそうだ。
4.キャッチャーの長男とサードの次女は同じ小学校の同級生だそうだ。
5.ショートとサードそれに桜井選手の3人はよく揃って競馬に行くそうだ。
6.ピッチャーはとても麻雀が強く、今月も梅田選手と菊池選手から5000円ずつまきあげたそうだ。
7.外野選手のうち一人はどうも木下選手か松村選手らしい。
8.小川選手はどうも奥さんとうまくいっていないようだ。近々離婚するのではないかとの噂がとんでいる。
9.選手達はよく揃ってゴルフに行くが、梅田・藤山・桜井の3選手はどうしてもキャッチャーとセカンドには勝てないようだ。
10.ピッチャーの奥さんはサードの妹さんだそうだ。
11.松村選手はキャッチャーと、又、桜井選手はピッチャーととても仲が良いようである。
12.選手たちのうちで、独身なのは、鈴木・梅田・山田の3選手、それにセンターとライトの5人である。
13.山田選手は桜井選手より背が高く、木下選手は桜井選手より背が低い。しかし、この3人はいずれもファーストより低い。

ここからは、問題文を一部改変しております。
製品をご購入いただくと正式な問題文が閲覧可能です。

14.選手たちのうちで酒を飲まないのは、XXX選手とXXX選手。それにショートの3人だけだそうだ。
15.バッテリーと内野の全員はXXX・XXX・XXXの3選手を除くとみんな外野の小川選手より背が低い。
16.鈴木選手は外野手のX人と一緒に麻雀をよくするそうだ。
こういう論理パズルで、色々制約のあるグループワークで解くもので、何らかの研修とかで使われるものらしい。

これが、色々制約のあるグループワークだから難しいが、一人でやったら簡単に解けるということだったので、上記XXXの部分を入手して、早速その日の夜にやってみたら、1時間くらいかけても解けなかった。

次の日の夜も、前日と同様、「藤山 not 5, 8 > 9, 鈴木 not 4, 4 single, ...」と紙に書き出すと、読み易い文字で書き直した為か、20分くらいで何らか解が出たのだが、解は複数あるような感じで、出した解は別途入手した答えと違った。

本当に解は1つしかないのだろうか?と気になって、今日、次のような簡単なプログラムを書いて探させてみたら、確かに解は1つしか無かった。

#!/opt/local/bin/python3
from itertools import permutations
names = ('藤山', '鈴木', '桜井', '梅田', '菊池', '木下', '松村', '小川', '山田')

def taller(name1, name2):
    dict1 = {'山田':3, '桜井':2, '木下':1}	#13
    if name1 in dict1 and name2 in dict1:
        if dict1[name1] > dict1[name2]: return True
    if name1 in ['XXX', 'XXX', 'XXX'] and name2 == '小川': return True	#15
    return False

def single(name):
    return name in ['鈴木', '梅田', '山田']	#12

def married(name):
    return name in ['小川']	#8

def judge(position):
    name_by_pos = {v:k for k,v in position.items()}

    if position['藤山'] == 5: return False	#1
    if taller(name_by_pos[9], name_by_pos[8]): return False	#2
    if position['鈴木'] == 4 or married(name_by_pos[4]): return False	#3
    if single(name_by_pos[2]) or single(name_by_pos[5]): return False	#4
    if position['桜井'] == 5 or position['桜井'] == 6: return False	#5
    if position['梅田'] == 1 or position['菊池'] == 1: return False	#6
    if (position['木下'] >= 7 and position['松村'] >= 7) \
       or (position['木下'] <= 6 and position['松村'] <= 6): return False	#7
    #8
    if position['梅田'] in (2,4) or position['藤山'] in (2,4) \
       or position['桜井'] in (2,4): return False	#9
    if single(name_by_pos[1]): return False	#10
    if position['松村'] == 2 or position['桜井'] == 1: return False	#11
    if position['鈴木'] >= 8 or position['梅田'] >= 8 or position['山田'] >= 8 \
       or married(name_by_pos[8]) or married(name_by_pos[9]): return False	#12
    if position['山田'] == 3 or position['桜井'] == 3 \
       or position['木下'] == 3: return False	#13
    if position['XXX'] == 6 or position['XXX'] == 6: return False	#14
    if position['XXX'] >= 7 or position['XXX'] >= 7 or position['XXX'] >= 7 \
       or position['小川'] <= 6: return False	#15
    if XXXXXXXXXXXXXXXXXXXXX: return False	#16
    return True

for p in permutations(range(9)):
    position = {names[i]: p[i]+1 for i in range(9)}
    if judge(position) == True:
        print(position)
(上記問題文で伏字にされている部分がわかる部分は伏字にしている)

プログラムのデバッグ中に、次のような表を作ってこれを埋めていく形で解き直したら、10分もかからずに解けた。問題を半分くらい覚えてしまったから早く解けたということもあるが、最初からこうやれば良かったと思った。

藤山 0 00
鈴木00 00
桜井00 000
梅田00 00
菊池0
木下 0 777
松村 0 777
小川 0 00
山田000 0
(1.〜13.まで読んでマークした状態、0は偽、7は7.参照の意)

続きを読む "野球のポジション当てゲームをプログラムで解いてみた"

2017年07月22日

 4五歩早仕掛けの玉頭銀対策を考える

筆者はこの所、4五歩早仕掛けを多用している。長らく将棋の勉強を怠っており、対四間飛車の急戦はこれしか思い出せないのである。しかも、四間飛車に対して急戦の構えを見せると△4三銀と上がる人が多いので、実現しやすく、便利である。
図1:4五歩早仕掛け

今月、久々に将棋を指す機会があり、久々の4五歩早仕掛けがクリーンヒットして安心し、その次の対局でもまた4五歩早仕掛けをしたら、4三の銀を5四〜6五〜7六と斜めにスルスルと動かされ、対処できずボロボロに負けてしまった。
図2:6九金型の4五歩早仕掛けに対する玉頭銀

筆者は昔からこの△5四銀が苦手なのであるが、寡聞にして定跡書で見たことが無かった為、この手は無い手で、必ず咎められる手だと思っていた。それで、△5四銀とされれば▲5五歩から殺しにかかるのだが、成功することは5回に1回もない。(しかも、銀を殺せても良くなるとは限らない。)

玉頭銀を殺すイメージ図
図3:歩を入手して▲7七歩とできれば銀が死ぬ

図4

図5:次に▲6六歩で銀が死ぬ

図6:このままなら次に▲7七歩で銀が死ぬ

今回も銀を殺せず、悪形だけが残り、総崩れしてしまった。
あまりにもひどかったので、一度きちんと調べておくことにした。


【結論】
  • この△5四銀〜△6五銀〜△7六銀と動く戦法には「玉頭銀」という名前が付いているらしい。
  • 玉頭銀は6九金型の4五歩早仕掛けに対して特に有効とされているらしい。
  • 4五歩早仕掛けの1手前の▲4六歩は△5四歩を待ってからにするべきらしい。
  • 従って、▲6八金直△5四歩の交換を入れてからにするべきらしい。

しかし、▲6八金直の次に△5四歩とされるとは限らない。
▲6八金直の直後に△5四銀と玉頭銀で来られれば▲5五歩△6五銀▲7五歩〜▲2六飛の筋があるし、▲3五歩△同歩▲4六銀が速いので、玉頭銀の牽制になっているとは思うが、後手には△1二香や△6四歩など、色々な手がある。△6四歩〜△5四銀〜△6三銀引と堅められることも多い。それに対して、▲6八金直は有効な待ちなのだろうか?

筆者は人生で▲6八金直にいい思い出が無い。△8四桂〜△7六桂の両取りを狙われるし、下段が空くし、5七銀の引き場所が無くなるし、▲5九香の頑張りがあまり利かなくなる。

▲6八金直が得な手でなく、次に△5四歩や△5四銀が期待できないなら、やっぱり▲6八金直と待つのではなく、さっさと▲4六歩と突きたい。それでもし△5四銀を食らっても、正確に指されれば少し悪くなる、という程度に済ませる方法は無いだろうか、と思って、懲りずに6九金型で玉頭銀を食らった場合の対策を探してみた。


△5四銀の後は(1)▲3七桂、(2)▲3八飛、(3)▲5五歩の3通りが多いようである。

(1)▲3七桂の後は、△6五銀▲4五歩△7六銀▲2四歩△同歩▲4四歩
図7
△同角▲同角△同飛▲7七歩△8七銀成
図8
▲同玉△8四飛▲8六銀△6四角▲9七角△9五歩▲同歩△9六歩▲同玉△8六角▲同角△8五銀
図9
と玉頭銀の恐ろしさを一方的に見せつけられるのが、代表的な進行である。

▲4四歩△4四同角には▲7七歩が正解だと思われる。▲4四歩に△3五歩でも▲7七歩と打つことになるらしい。(1)の▲3七桂は、銀を取れないのに形悪く▲7七歩を打つことになることが多く、いまいちである。

ただ、▲3七桂△6五銀▲4五歩△7六銀の後、▲4四歩でなく▲4四角とする手があるらしい。
△同角▲同歩△同飛なら▲6六角として、△8四飛と回るのを防げる。
図10


(2)▲3八飛は、△3二飛なら▲5五歩△6五銀▲3五歩△同歩▲同飛△7六銀▲2四歩△同歩▲5四歩(図6)のように銀を狙えそうだが、図6からでも簡単には銀が取れない。△4五歩でも助かりそうだし、△6四歩▲6六歩△6五歩▲8六歩△6六歩▲同銀△6四歩とやっても後手がなんとかなりそうである。

▲3八飛には△4五歩とする人が多いと思う。その後、▲3三角成△同桂▲8八角△4三金▲2八飛に△6四角(山崎六段)
図11
という手があり、これは先手こらえるのが大変そうである。△6四角に▲2四歩と攻め合って先手良しとする本もあるが、筆者には先手良しと理解できなかった。

▲3八飛には何と△4三銀と戻る手もあり、▲3五歩△同歩▲同飛△3二飛▲4五歩△同歩▲4四歩△2二角▲4五飛
図12
と自然に進めても、△3四銀(谷川-藤井)でうまくいかない。


やはり筆者としては、△5四銀には勝ち負けを度外視してでも(3)▲5五歩としたい。
▲5五歩の後は△6五銀▲3五歩△同歩▲3八飛
図13
とする。
そこで△4三金だと、▲3五飛△3四歩▲3六飛△7六銀▲4五歩△6五銀▲7五歩△4五歩▲8六飛(図5)のように銀挟みが成功する筋がある。

図13から△4五歩だと、▲3五飛△4六歩▲4五歩△3四歩▲同飛△4五飛▲3七桂△4四飛▲同飛△同角▲4一飛
図14
が一例で、ここで△3五角なら後手良しらしいが、アマ同士なら先手もやれそうではないだろうか。

図13から△4五歩▲3五飛△4六歩▲4五歩に△7六銀とした実戦例(森下-櫛田)もあり、▲4六銀△9五歩▲同歩△9八歩▲同香△9七歩▲同香△9六歩▲同香△8五銀
図15
と端から大暴れされたが、▲3二歩の垂らしから、と金を作って先手が勝っている。

図13から△7六銀だと、正確に指されると後手良しらしいが、▲3五飛△6五銀▲6六歩△7六銀▲5四歩△同歩▲9七角
図16
という指し方もあるかも知れない。△3二飛なら▲2四歩△同歩▲2二歩である。


1筋の突き合いが入っていると、6九金型でも玉頭銀に対して別の対策があるかも知れない。

1つには、△5四銀に対して、▲5五歩△6五銀▲3五歩△同歩▲3八飛△7六銀▲3五飛△6五銀に▲1五歩(升田-大山)という定跡がある。
図17
以下△同歩▲3四歩△2二角▲2四歩△4三飛▲5四歩△同銀▲3三歩成△同飛▲同飛成△同角▲2三歩成△5一角▲2八飛
図18
で先手優勢である。

もう1つは、△5四銀に対して、▲5五歩△6五銀▲3五歩△同歩▲3八飛△4五歩▲3五飛△4六歩の後、▲3七桂△7六銀▲4五桂が成立する。
図19
1筋の突き合いが入っていないと、ここで△1五角があり、▲5四歩△同歩▲1一角成としても△3四歩で後手良しになるが、図19だと△3四歩か△2二角しかなく、△3四歩なら▲3三桂成、△2二角なら▲2四歩でいずれも崩壊ではないだろうか。
 
問題はどこで1筋の突き合いを入れるかだが、▲5七銀左と上がる前に入れている実戦例が、上記の升田-大山戦を含め複数あるので、そのタイミングが良いと思う。ただ、後手が玉頭銀で来ない場合にどういう影響があるかは未確認である。

■参考文献
最強将棋21 四間飛車破り【急戦編】渡辺 明(著)


2017年06月19日

 scikit-learnのナイーブベイズ分類器を使ってみる

前回のFamily Out Problemの確率モデル(下図)を題材に、ナイーブベイズ分類器を使ってみる。


Family Out Problem

この問題において、family-out以外の変数の真偽値が与えられた時にfamily-out=TRUEかどうかを判定するよう、ナイーブベイズ分類器を学習させてみる。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import precision_recall_fscore_support
from sklearn.metrics import roc_curve, auc

def generate_sample():
    "Sample data generator of the Family Out problem"
    fo = np.random.binomial(1, 0.15)
    bp = np.random.binomial(1, 0.01)
    lo = np.random.binomial(1, (0.05, 0.6)[fo])
    do = np.random.binomial(1, ((0.3, 0.97), (0.9, 0.99))[fo][bp])
    hb = np.random.binomial(1, (0.01, 0.7)[do])
    return [fo, bp, lo, do, hb]

# Generate training data and test data
train_data = np.array([generate_sample() for _ in range(1000)])
test_data = np.array([generate_sample() for _ in range(100)])

X_train = train_data[:, 1:5]	# other than fo
y_train = train_data[:, 0]	# only fo
X_test = test_data[:, 1:5]	# other than fo
y_test = test_data[:, 0]	# only fo

# Train a Naive Bayes classifier
clf = BernoulliNB()
clf.fit(X_train, y_train)

# Evaluate with training data
y_pred = clf.predict(X_train)
metrics = precision_recall_fscore_support(y_train, y_pred)
print('Evaluation with training data')
print('Class FamilyOut=False: Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][0], metrics[1][0], metrics[2][0]))
print('Class FamilyOut=True : Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][1], metrics[1][1], metrics[2][1]))

# Evaluate with test data
y_pred = clf.predict(X_test)
metrics = precision_recall_fscore_support(y_test, y_pred)
print('Evaluation with test data')
print('Class FamilyOut=False: Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][0], metrics[1][0], metrics[2][0]))
print('Class FamilyOut=True : Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][1], metrics[1][1], metrics[2][1]))

# Draw ROC curve
y_train_post = clf.predict_proba(X_train)[:, 0]
y_test_post = clf.predict_proba(X_test)[:, 0]

fpr, tpr, thresholds = roc_curve(y_train, y_train_post, pos_label=0)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, 'k-', lw=2, label='ROC for training data (area = {:.2f})'.format(roc_auc))

fpr, tpr, thresholds = roc_curve(y_test, y_test_post, pos_label=0)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, 'k--', lw=2, label='ROC for test data (area = {:.2f})'.format(roc_auc))

plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC curve of BernoulliNB')
plt.legend(loc="lower right")

plt.show()

問題のモデルに従って学習用データとテストデータをランダムに生成し、sklearn.naive_bayes.BernoulliNBの分類器を学習させ、テストデータを分類させ、family-out=FALSEとfamily-out=TRUEのそれぞれについてPrecision, Recall, F値を計算している。
加えて、予測したfamily-out=FALSE/TRUEの確率から、ROC曲線とAUCを出力している。

出力例

Evaluation with training data
Class FamilyOut=False: Precision=0.936, Recall=0.983, F-measure=0.959
Class FamilyOut=True : Precision=0.847, Recall=0.589, F-measure=0.695
Evaluation with test data
Class FamilyOut=False: Precision=0.923, Recall=0.988, F-measure=0.955
Class FamilyOut=True : Precision=0.889, Recall=0.533, F-measure=0.667
ROC曲線
ROC curve
その時のtraining_dataとtest_data(.arff形式)
family_out_train.arff
family_out_test.arff

ナイーブベイズ分類器には、同じクラスのデータでは全ての特徴が独立に出現する、つまりbp,lo,do,hbに依存関係が無く、これらがTRUEになる確率はfoの値だけで決まるという仮定があるが、この問題ではbp,do,hbに依存関係があるので、ベイジアンネットワークの方がうまく学習できると考えられる。
なのでベイジアンネットワークで分類した場合の性能と比較したいが、scikit-learn(0.18)にベイジアンネットワークが無いので、代わりにWeka 3.8.1のBayesNetを用いて学習用データで学習させ、テストデータを分類した結果と比較してみる。

Wekaの操作手順

  1. Weka ExplorerのPreprocessタブでOpen file...ボタンを押し、family_out_train.arffを開く
  2. ClassifyタブでClassifierとしてBayesNetを選択する
  3. "BayesNet -D ..."とあるフィールドをクリックしてパラメーター設定画面を開き、searchAlgorithmのフィールドをクリックして、initAsNaiveBayes=False, maxNrOfParents=2に変更
  4. Test optionsのSupplied test setのボタンを押し、family_out_test.arffを開き、Classとしてfamily-outを選択
  5. クラスをfamily-outを選択し、Startボタンを押す
上記と同じデータを使った出力例
=== Run information ===

Scheme:       weka.classifiers.bayes.BayesNet -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 2 -N -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5

=== Detailed Accuracy By Class ===

                 TP Rate  FP Rate  Precision  Recall   F-Measure  MCC      ROC Area  PRC Area  Class
                 0.988    0.467    0.923      0.988    0.955      0.651    0.889     0.967     0
                 0.533    0.012    0.889      0.533    0.667      0.651    0.889     0.648     1
Weighted Avg.    0.920    0.398    0.918      0.920    0.911      0.651    0.889     0.919     

=== Confusion Matrix ===

  a  b   <-- classified as
 84  1 |  a = 0
  7  8 |  b = 1

BernoulliNBのテストデータに対する出力と、Precision, Recall, F-measureが完全に一致している。

WekaのBayesNetのデフォルト設定では正しいネットワークが学習されなかったが、上記手順の3.のように設定を変えると、大体次のように正しいネットワークになった。

BernoulliNBの性能には試行毎にばらつきがあったが、1000回の平均を取ってみたのが次の値であり、上記の結果は特別に良い例ではない。

Average with training data
Class FamilyOut=False: Precision=0.925, Recall=0.982, F-measure=0.952
Class FamilyOut=True : Precision=0.844, Recall=0.545, F-measure=0.661
Average with test data
Class FamilyOut=False: Precision=0.925, Recall=0.982, F-measure=0.952
Class FamilyOut=True : Precision=0.842, Recall=0.544, F-measure=0.651
Average of AUC with training data=0.894
Average of AUC with test data=0.895
また、計10回、同じデータでWekaのBayesNetの性能と比較した所、Precision, Recall, F-measureについては、8回は一致していた。
ROC曲線のAUCはWekaのBayesNetの方が高いことが多かったが、BernoulliNBでも大体0.85-0.90の範囲であり、十分に高かった。
従って、この確率モデルに対して、ナイーブベイズ分類器は予測性能が十分に高いと言える。

なお、ここまでの結果では、学習データに対する各種性能値とテストデータに対する値にほぼ差が無いが、これは学習データのサンプル数が1000と十分に多く、偏りが無いからである。これを100にすると、次のように、テストデータに対する成績が少し下がるが、それでも、学習データだけに対して大幅に成績が良い「過学習(overfitting)」の状態と言える程ではない。

学習データ数が100の1000試行の平均

Average with training data
Class FamilyOut=False: Precision=0.931, Recall=0.963, F-measure=0.945
Class FamilyOut=True : Precision=0.793, Recall=0.582, F-measure=0.650
Average with test data
Class FamilyOut=False: Precision=0.926, Recall=0.955, F-measure=0.938
Class FamilyOut=True : Precision=0.767, Recall=0.564, F-measure=0.620
Average of AUC with training data=0.899
Average of AUC with test data=0.890

続きを読む "scikit-learnのナイーブベイズ分類器を使ってみる"

2017年05月29日

 Wekaでベイジアンネットワークの事後確率を計算

ベイジアンネットワークの復習をしていて、確率的グラフィカルモデル‐ベイジアンネットワークとその周辺‐(オペレーションズ・リサーチ 2013年4月号)という記事を見つけた。その中に、Family Out Problemという有名な例題(下図)の紹介があり、p.194に、

表1~3で与えられたCPTの値と式(11)を利用して丹念に計算することにより P(X1=1|X3=1,X5=1)=0.7577···という結論を得る.
と書かれていたので、これを自力で計算できたらベイジアンネットワークを理解できたことにしよう、と思って、丹念に計算してみたら、その値にならなかった。


Family Out Problem

ここでは、
X1: Family Out
X2: Bowel Problem
X3: Light On
X4: Dog Out
X5: Hear Bark
(それぞれ2値の確率変数)であり、それぞれの条件付き確率は次の通りである。
P(X1=1) = 0.15
P(X2=1) = 0.01
P(X3=1 | X1=0) = 0.05
P(X3=1 | X1=1) = 0.6
P(X4=1 | X1=0, X2=0) = 0.3
P(X4=1 | X1=0, X2=1) = 0.97
P(X4=1 | X1=1, X2=0) = 0.9
P(X4=1 | X1=1, X2=1) = 0.99
P(X5=1 | X4=0) = 0.01
P(X5=1 | X4=1) = 0.7

P(X_1=1 | X_3=1, X_5=1) = \frac{\sum_{X_2}\sum_{X_4} P(X_1=1, X_2, X_3=1, X_4, X_5=1)}{\sum_{X_1}\sum_{X_2}\sum_{X_4} P(X_1, X_2, X_3=1, X_4, X_5=1)}
なので、Pythonで

P00101 = 0.85 * 0.99 * 0.05 * 0.7 * 0.01
P00111 = 0.85 * 0.99 * 0.05 * 0.3 * 0.7
P01101 = 0.85 * 0.01 * 0.05 * 0.03 * 0.01
P01111 = 0.85 * 0.01 * 0.05 * 0.97 * 0.7
P10101 = 0.15 * 0.99 * 0.6 * 0.1 * 0.01
P10111 = 0.15 * 0.99 * 0.6 * 0.9 * 0.7
P11101 = 0.15 * 0.01 * 0.6 * 0.01 * 0.01
P11111 = 0.15 * 0.01 * 0.6 * 0.99 * 0.7

P35 = P00101 + P00111 + P01101 + P01111 + P10101 + P10111 + P11101 + P11111
P135 = P10101 + P10111 + P11101 + P11111
P1_35 = P135 / P35
print(P1_35)
とすると、0.8578...という数値になった。どこか読み間違えたかと思って、
def prob(x1, x2, x3, x4, x5):
    p = 1.0
    p *= (0.85, 0.15)[x1]
    p *= (0.99, 0.01)[x2]
    p *= ((0.95, 0.05), (0.4, 0.6))[x1][x3]
    p *= (((0.7, 0.3), (0.03, 0.97)), ((0.1, 0.9), (0.01, 0.99)))[x1][x2][x4]
    p *= ((0.99, 0.01), (0.3, 0.7))[x4][x5]
    return p

P135 = sum([prob(1, x2, 1, x4, 1)
             for x2 in range(2)
             for x4 in range(2)])
P35 =  sum([prob(x1, x2, 1, x4, 1)
             for x1 in range(2)
             for x2 in range(2)
             for x4 in range(2)])
P1_35 = P135 / P35
print(P1_35)
と書いてみたが、やはり0.8578...だった。

上記の記事内の条件付き確率表に誤記があり、0.7577...というのは元の確率表で計算された値か、とも思ったが、どの文書のFamily Out Problemを見ても確率は同じだった。

正解は何なのか、何らかのツールで確認しようと思って、Pythonのベイジアンネットワーク関連のツールを探したが、適当なものがなかなか見つからなかった。

scikit-learnには"Naive Bayes"のAPIはあるがベイジアンネットワークは見つからない。
PyMCBayesPyは、きっとうまく使えばこの計算ができるのだろうが、ネットワークを定義して、CPT(conditional probability table、条件付き確率表)とevidence(観測値)を与えて事後確率を計算する直接的なサンプルコードが見つからなかったので、諦めた。
PBNTにはそういうサンプルコードがあったので、使ってみたが、P(X1=1|X3=1,X5=1)=0.2018...という全然違う値が出力された。P(X5=1)=0.2831と、これは正しい値が出たので、ネットワークとCPTは合ってそうであり、事後確率を計算するにはengine.marginal()でなく別のメソッドを使わないといけないのかとも思ったが、よくわからなかった。

Pythonを諦めてツールを探すと、Wekaでできることがわかった。Wekaは機械学習の勉強をするなら必修らしく、過去にインストールしていたので、やってみた。
Wekaを起動して、Tools->Bayes net editorを開くと、GUIがバグだらけ(Version 3.8.1, WindowsとMacとで確認)で使いにくいが、ノードを追加して右クリックしながらネットワークを作成し、Tools->Layoutでノードの配置を修正し、CPTを設定し、さらに保存したXMLを書き換えて色々修正し、Tools->Show Marginsを選ぶと、次のように結合確率が表示される。

さらに、右クリック->Set evidenceで LightOn=True, HearBark=True と設定すると、次のように、各ノードの事後確率が表示される。

これによると、FamilyOut=Trueの事後確率はやはり0.8578...である。筆者は何か問題を読み間違えているのだろうか?


2017年05月21日

 角交換振り飛車に大ポカ一発で沈む

昨日は毎年参加している地域の将棋大会だった。
今年は2回戦敗退だったが、予選は1勝1敗で抽選で勝ち抜け(3人のブロックで3人とも1勝1敗だった)、抽選で決勝1回戦はシードだったので、昨年の1回戦敗退より悪い内容だった。通常3勝かかる所、たった1勝で2回戦まで進出するとは、何とくじ運の良かったことか。

筆者はここ1年くらい将棋の勉強をしておらず、全て忘れてしまっており、先月からたまにネット将棋を指していたが以前のレートでは全く勝てず、今年は予選突破できないと思った。
昔から記憶力が無い方であるが、たった1年休んだくらいで全て忘れてしまい、レートが200も下がるのでは、筆者は将棋に向いてないのだろうとつくづく思う。

2回戦の相手は昨年優勝のI藤さんだった。筆者が絶好調でもまず勝てない相手であるが、先手の筆者に次の局面のような感触の良い手が出た。

通常は4二の銀が4四に居るので、3四の歩を取っても響かないが、この形だと歩を取った後に3筋の歩を伸ばせる。3四の歩を受けるには△1二角と打つしかないが、形が悪いだろう。
筆者は通常、角交換振り飛車には▲6六歩のようにして角交換を拒否するのだが、1回戦で筆者が100回やっても勝てそうにないY田さんがI藤さんに対して▲6六歩と角交換を拒否して負けたのを見て、直前に何か別の展開をガラケーの自作アプリに仕込んだ棋譜の中から1つ探して、その通りに指してみたものである。そういうことをすると通常は相手の研究にはまるので、ろくなことにならないのだが、今回は功を奏した。
対して、後手のIさんは△3二飛と指した。

これを見た瞬間、チャンスだと思った。2三の地点が空いている。
その誘惑に駆られた上、一手前に長考したこともあり、じっくり考えようとは思わなかった。
▲3四角に△4五桂と両取りに跳ねられても▲同角で両方受かる、と、それ以上考えずに、10秒も使わずに▲3四角と指したら、△2五桂とされて、一発で撃沈してしまった。

あと5秒考えていればこの手に気付いただろう。有段者が何の意味もなく、△3二飛のような隙を作る手を指すはずがない、と、何故一瞬でも思わなかったのか。
△2五桂の後、▲同歩、△3四飛、▲2四歩とすれば、後手は歩切れで、と金ができて大差にはならなさそうだが、この局面では丁度△1五角があって、受かってしまう。

△3二飛の局面は激指14のPro+3でも評価値が+280であり、少しリードしていたのは間違いない。その後、▲3四角ではなく、▲2九飛 △5四歩 ▲4八金と桂馬に紐を付け、△1二角 ▲6六歩 △6四歩 ▲1六歩のように進めれば、まあまあ指しやすそうである。

ここまでの手順は激指14のPro+以上の手を続けたものであり、評価値は+228である。

まあ、相手が相手なだけに、こう進んでもまず勝てなかっただろうが…

続きを読む "角交換振り飛車に大ポカ一発で沈む"