RigelのR言語メモであーる(R言語だけとは言っていない)

RigelのR言語メモであーる(主にpython)

興味あることや趣味、やったことについて書くよ

RでProject Euler(随時更新)

Project Eulerはプログラムで解く数学の問題集です。

https://projecteuler.net/about

(日本語訳→http://odz.sakura.ne.jp/projecteuler/)


頭の体操のためにR言語Project Eulerを解いていきます。

縛りは、基本のbaseパッケージのみとします。

低頻度ですが、随時更新していきます。


qiita.com

qiita.com

qiita.com

qiita.com

qiita.com

qiita.com

新しい活性化関数「Mish」を使ってみた。

新しい活性化関数Mishというものが良さそうです。

詳しくは以下の記事参照してください。

ai-scholar.tech

ということで、kerasを使い簡単なCNNでMNISTの10クラス分類をやってみました。

続きを読む

kerasのoptimizerの状態を保存する

背景

新規データに対して逐次的に毎日学習するkerasのモデルを考える。
シンプルなモデルであればmodel.save()load_model()を使えばなんの問題もない。
参考: Kerasのノウハウ覚え書き
しかし、Lambda層が入るとmodel.save()ができなくなる。
参考: KerasのLambda層でreshapeしたとき、保存に失敗する(場合がある)話
追加の学習はせず予測のためだけに使うモデルなら、save_weights()とかで重みだけ保存しておけばよいが、毎日新規データを学習したいため微妙。
というのは、optimizerがmomentumなどの状態を持っている場合、それも保存しておかなければならない。
以下の記事を参考に、optimizerの状態の保存と読み込みを行って、各種実験をしてみる。
参考: python - Save and load model optimizer state - Stack Overflow

実験

準備

必要なライブラリのインポート

from sklearn import datasets
from sklearn import preprocessing

from keras.models import Model
from keras.layers import Input, Dense
from keras.utils import np_utils
from keras.optimizers import SGD
from keras import backend as K

データの準備

iris = datasets.load_iris()
X = iris.data
Y = iris.target
X = preprocessing.scale(X)
Y = np_utils.to_categorical(Y)

モデルを作ってコンパイルする関数

def make_model(momentum=0.0, decay=0.0, nesterov=False):
    inputs = Input(shape=(4,))
    outputs = Dense(3, activation="softmax", kernel_initializer="ones", bias_initializer="ones", name="output_layer")(inputs)
    model = Model(inputs=inputs, outputs=outputs)
    model.compile(optimizer=SGD(lr=0.01, momentum=momentum, decay=decay, nesterov=nesterov), loss='categorical_crossentropy')
    return model
実験1

optimizerの状態がない場合の学習。

model = make_model()
model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
 - 0s - loss: 1.0986
Epoch 2/10
 - 0s - loss: 1.0890
Epoch 3/10
 - 0s - loss: 1.0796
Epoch 4/10
 - 0s - loss: 1.0704
Epoch 5/10
 - 0s - loss: 1.0613
Epoch 6/10
 - 0s - loss: 1.0524
Epoch 7/10
 - 0s - loss: 1.0437
Epoch 8/10
 - 0s - loss: 1.0351
Epoch 9/10
 - 0s - loss: 1.0267
Epoch 10/10
 - 0s - loss: 1.0185
実験2

optimizerの状態がない場合の学習。
epochs=1の学習をfor文で回す。
実験1と同じ結果。

model = make_model()
for i in range(10):
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0890
Epoch 1/1
 - 0s - loss: 1.0796
Epoch 1/1
 - 0s - loss: 1.0704
Epoch 1/1
 - 0s - loss: 1.0613
Epoch 1/1
 - 0s - loss: 1.0524
Epoch 1/1
 - 0s - loss: 1.0437
Epoch 1/1
 - 0s - loss: 1.0351
Epoch 1/1
 - 0s - loss: 1.0267
Epoch 1/1
 - 0s - loss: 1.0185
実験3

optimizerの状態がある場合の学習。

model = make_model(momentum=1, decay=1, nesterov=True)
model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
Epoch 1/10
 - 0s - loss: 1.0986
Epoch 2/10
 - 0s - loss: 1.0795
Epoch 3/10
 - 0s - loss: 1.0610
Epoch 4/10
 - 0s - loss: 1.0414
Epoch 5/10
 - 0s - loss: 1.0209
Epoch 6/10
 - 0s - loss: 0.9997
Epoch 7/10
 - 0s - loss: 0.9781
Epoch 8/10
 - 0s - loss: 0.9563
Epoch 9/10
 - 0s - loss: 0.9343
Epoch 10/10
 - 0s - loss: 0.9125
実験4

optimizerの状態がある場合の学習。
epochs=1の学習をfor文で回す。
実験3と同じ結果。
modelがoptimizerの状態を保持している。

model = make_model(momentum=1, decay=1, nesterov=True)
for i in range(10):
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0795
Epoch 1/1
 - 0s - loss: 1.0610
Epoch 1/1
 - 0s - loss: 1.0414
Epoch 1/1
 - 0s - loss: 1.0209
Epoch 1/1
 - 0s - loss: 0.9997
Epoch 1/1
 - 0s - loss: 0.9781
Epoch 1/1
 - 0s - loss: 0.9563
Epoch 1/1
 - 0s - loss: 0.9343
Epoch 1/1
 - 0s - loss: 0.9125
実験5

optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
lossが変化しない。

for i in range(10):
    model = make_model()
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
実験6

optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
実験5と同様にlossが変化しない。

for i in range(10):
    model = make_model(momentum=1, decay=1, nesterov=True)
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0986
実験7

optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
実験1や実験2と同じ結果。

for i in range(10):
    model = make_model()
    if i != 0:
        model.get_layer("output_layer").set_weights(weights_dense)
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
    weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1
 - 1s - loss: 1.0986
Epoch 1/1
 - 1s - loss: 1.0890
Epoch 1/1
 - 1s - loss: 1.0796
Epoch 1/1
 - 1s - loss: 1.0704
Epoch 1/1
 - 1s - loss: 1.0613
Epoch 1/1
 - 1s - loss: 1.0524
Epoch 1/1
 - 1s - loss: 1.0437
Epoch 1/1
 - 1s - loss: 1.0351
Epoch 1/1
 - 2s - loss: 1.0267
Epoch 1/1
 - 2s - loss: 1.0185
実験8

optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
optimizerの状態の違いのため、実験3や実験4と異なる結果。

for i in range(10):
    model = make_model(momentum=1, decay=1, nesterov=True)
    if i != 0:
        model.get_layer("output_layer").set_weights(weights_dense)
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
    weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1
 - 2s - loss: 1.0986
Epoch 1/1
 - 2s - loss: 1.0795
Epoch 1/1
 - 2s - loss: 1.0703
Epoch 1/1
 - 2s - loss: 1.0612
Epoch 1/1
 - 2s - loss: 1.0523
Epoch 1/1
 - 2s - loss: 1.0350
Epoch 1/1
 - 2s - loss: 1.0266
Epoch 1/1
 - 2s - loss: 1.0183
Epoch 1/1
 - 2s - loss: 1.0102
Epoch 1/1
 - 2s - loss: 1.0023
実験9

optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みとoptimizerの状態を読み込む。
実験3や実験4と同じ結果。

for i in range(10):
    model = make_model(momentum=1, decay=1, nesterov=True)
    model._make_train_function()
    if i != 0:
        model.get_layer("output_layer").set_weights(weights_dense)
        model.optimizer.set_weights(weights_optimizer)
    model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
    weights_dense = model.get_layer("output_layer").get_weights()
    weights_optimizer = model.optimizer.get_weights()
Epoch 1/1
 - 2s - loss: 1.0986
Epoch 1/1
 - 0s - loss: 1.0795
Epoch 1/1
 - 0s - loss: 1.0610
Epoch 1/1
 - 0s - loss: 1.0414
Epoch 1/1
 - 0s - loss: 1.0209
Epoch 1/1
 - 0s - loss: 0.9997
Epoch 1/1
 - 0s - loss: 0.9781
Epoch 1/1
 - 0s - loss: 0.9563
Epoch 1/1
 - 0s - loss: 0.9343
Epoch 1/1
 - 0s - loss: 0.9125

まとめ

  1. model.save()が使える場合
    -> model.save()を使えば良い。
  2. model.save()が使えない場合
    1. 再学習しない場合
      -> モデルの重みのみ保存すれば良い。
    2. 再学習する必要がある場合
      -> モデルの重みとoptimizerの状態を保存すれば良い。

ちなみに、weights_denseweights_optimizerの中身は以下。

weights_dense
[array([[0.9180371 , 1.00887   , 1.0730928 ],
        [1.0696087 , 0.94541067, 0.9849805 ],
        [0.8941715 , 1.0231149 , 1.0827136 ],
        [0.8982664 , 1.0132247 , 1.0885084 ]], dtype=float32),
 array([0.9999076, 1.000105 , 0.9999874], dtype=float32)]
weights_optimizer
[10, array([[-0.00934379,  0.00099744,  0.00834635],
        [ 0.00797938, -0.0062994 , -0.00167998],
        [-0.01209093,  0.00263835,  0.00945257],
        [-0.01162081,  0.00148986,  0.01013095]], dtype=float32), 
array([-2.1416694e-05,  2.4464378e-05, -3.0475137e-06], dtype=float32)]

なので、pickleとかで保存すれば良い。

model._make_train_function()は何をしているかわからない。
けど、それがないとmodel.optimizer.set_weights(weights_optimizer)でエラーになる。

競艇や競馬の買い目最適化(ケリー基準の一般化)

はじめに

ケリー基準を競艇や競馬へ適用できる形に一般化して、

買い目を最適化することを目指します。

ケリー基準の説明は以下のサイトをご参照ください。

http://www.geocities.jp/y_infty/management/index.html

問題設定

特定のレースの特定の券種で、確定オッズ(以下オッズ)と的中確率は所与とします。

そのうち、期待値が{1}を超える券が{N}枚あるとします。

{N}枚の券のうち、{i\ (1 \leq i \leq N)}番目の券の、オッズを{o_i}、的中確率を{p_i}とします。

それぞれの券の期待値が{1}を超えるので、{o_ip_i\geq 1}です。

さて、このときそれぞれの券にいくらずつかけるのが最適ですか?という問題です。

ただし、何レースでも購入でき、オッズは低下せず、少数金額も購入できるとします。

(他にも条件あるかもしれませんが、このくらいで。)

最適化

{i}番目の券を所持金の{f_i}倍購入するとします。

いま、{N}枚の券はすべて同じ券種であるため、互いに排反な事象となります。なので、的中してもどれか1枚です。

(※券種は複勝や拡連複ではないとして、さらに同着や返還等も考えません。)

なので、レースが終わったときの結果の事象としては、

{N}枚の券のうちどれか1枚が的中するか、全部外れるかです。

したがって、ケリー基準を導出する際と同様に考えて、

{\begin{eqnarray}
g(f)=(1-f_0)^{p_0} \prod_{i=1}^N (1+f_i o_i - f_0)^{p_i}
\end{eqnarray}}

この関数を最大化します。

ただし、{f_0=\sum_{i=1}^N f_i, p_0=1-\sum_{i=1}^N p_i}です。

また、{g(f)}{f}は、あらゆる{f_i}の組み合わせ的な感じです。

なぜこの関数を最大化するのかは、上記参考サイトに任せますが、

イメージとしては、

  • {N}枚全部外れる確率は{p_0}で、そのとき所持金は{1-f_0}倍になる。
  • {i}番目の券が的中する確率は{p_i}で、そのとき所持金は{1+f_i o_i - f_0}倍になる。

それらの期待値のような感じです。

掛け算や指数があれなので、最適化の常套手段である対数変換をします。

{\begin{eqnarray}
\log(g(f))=p_0\log(1-f_0) + \sum_{i=1}^N p_i\log(1+f_i o_i-f_0))
\end{eqnarray}}

ベクトルで偏微分とか苦手なので、地道にいきます。

上式を、{n\ (1 \leq n \leq N)}番目の券の購入割合{f_n}偏微分してイコール{0}とおくことで、

{\begin{eqnarray}
\frac{\partial \log(g(f))}{\partial f_n}=-\frac{p_0}{1-f_0} - \sum_{i=1}^N \frac{p_i}{1+f_i o_i-f_0} + \frac{o_n p_n}{1+f_n o_n-f_0}=0
\end{eqnarray}}

となります。

ここで、第1項と第2項は{n}によらず一定です。

なので、別の{m\ (1 \leq m \leq N)}番目の券の購入割合{f_m}偏微分した場合は、

第3項だけが変わるので、

{\begin{eqnarray}
\frac{o_n p_n}{1+f_n o_n-f_0}=\frac{o_m p_m}{1+f_m o_m-f_0}
\end{eqnarray}}

が成り立ちます。

変形して、

{\begin{eqnarray}
\frac{p_n}{1+f_n o_n-f_0}=\frac{o_m}{o_n}\frac{p_m}{1+f_m o_m-f_0}
\end{eqnarray}}


となります。

この式により、偏微分した式の第2項の総和の中身のインデックスを

オッズの比をかけることで、変換できるようになります。

第2項の総和の中身のインデックス{i}{n}に変換します。

{\begin{eqnarray}
\frac{\partial \log(g(f))}{\partial f_n}=&&-\frac{p_0}{1-f_0} - \sum_{i=1}^N \frac{p_i}{1+f_i o_i-f_0} + \frac{o_n p_n}{1+f_n o_n-f_0}\\
=&&-\frac{p_0}{1-f_0} - \sum_{i=1}^N \frac{o_n}{o_i}\frac{p_n}{1+f_n o_n-f_0} + \frac{o_n p_n}{1+f_n o_n-f_0}\\
=&&-\frac{p_0}{1-f_0} + \frac{o_n p_n}{1+f_n o_n-f_0} (1-\sum_{i=1}^N \frac{1}{o_i})=0\\
\end{eqnarray}}

{(1-\sum_{i=1}^N \frac{1}{o_i}) = o_0}とおいて、さらに変形をつづけます。

{\begin{eqnarray}
 - \frac{p_0}{1-f_0} + \frac{o_n p_n}{1+f_n o_n-f_0} o_0 = 0\\
 - p_0(1+f_n o_n-f_0)+(1-f_0)o_n o_0 p_n = 0\\
f_0(p_0-o_n o_0 p_n) - f_n o_n p_0 = p_0 - o_n o_0 p_n
\end{eqnarray}}

上式はすべての{n\ (1 \leq n \leq N)}について成り立ちます。

ここまでくるともう少しですね。

いま求めたいのは、{f_n\ (1 \leq n \leq N)}ですけど、

線形になっているので、解けますね。

具体的には、

{N}次元縦ベクトル
{\displaystyle
\boldsymbol{f} = \ ^t(f_1,\ f_2,\ \ldots,\ f_N)
}
{N \times N}の行列
{\displaystyle
\boldsymbol{A} = 
\begin{pmatrix}
p_0 - o_1 o_0 p_1 - o_1 p_0 & p_0 - o_1 o_0 p_1 & \cdots & p_0 - o_1 o_0 p_1\\
p_0 - o_2 o_0 p_2 & p_0 - o_2 o_0 p_2 - o_2 p_0 & \cdots & p_0 - o_2 o_0 p_2\\
\vdots & \vdots & \ddots & \vdots\\
p_0 - o_N o_0 p_N & p_0 - o_N o_0 p_N & \cdots & p_0 - o_N o_0 p_N - o_N p_0\\
\end{pmatrix}
}
{N}次元縦ベクトル
{\displaystyle
\boldsymbol{b} = \ ^t(p_0 - o_1 o_0 p_1,\ p_0 - o_2 o_0 p_2,\ \ldots,\ p_0 - o_N o_0 p_N)
}
とすると、
{\begin{eqnarray}
\boldsymbol{A}\boldsymbol{f}=\boldsymbol{b}
\end{eqnarray}}
が成り立ちますので、最適な{\boldsymbol{f}}は、
{\begin{eqnarray}
\boldsymbol{f}=\boldsymbol{A}^{-1} \boldsymbol{b}
\end{eqnarray}}
と求まりました。

{N=1}の場合について考えて、ケリー基準と一致するか確認してみます。

{N=1}のとき、

{\displaystyle
\boldsymbol{A}=(p_0 - o_1 o_0 p_1 - o_1 p_0), \ \boldsymbol{b}=(p_0 - o_1 o_0 p_1)
}

なので、
{\begin{eqnarray}
(f_1) = \boldsymbol{f}=&&\boldsymbol{A}^{-1} \boldsymbol{b}\\
=&&\left( \frac{p_0 - o_1 o_0 p_1}{p_0 - o_1 o_0 p_1 - o_1 p_0} \right)
\end{eqnarray}}
となります。

ここで、
{\displaystyle
p_0=1-p_1, \ o_0=1-\frac{1}{o_1}
}
なので、
{\begin{eqnarray}
(f_1)=&&\left( \frac{p_0 - o_1 o_0 p_1}{p_0 - o_1 o_0 p_1 - o_1 p_0} \right)\\
=&&\left( \frac{(1-p_1) - o_1 \left(1-\frac{1}{o_1}\right) p_1}{(1-p_1) - o_1 \left(1-\frac{1}{o_1}\right) p_1 - o_1(1-p_1)} \right)\\
=&&\left( \frac{o_1 p_1 - 1}{o_1 -1} \right)
\end{eqnarray}}
となり、普通のケリー基準の式と一致します。

(参考サイトや一般的に紹介されているケリー基準と違うのはオッズの置き方によるもの。本質的には全く同じ。)

数値シミュレーション

求めた解が正しいか、数値シミュレーションで確認します。

現実ではありえないかもしれませんが{N=3}で、

{p_1=0.1,\ o_1=15,\ p_2=0.2,\ o_2=6,\ p_3=0.3,\ o_3=4}

という状況を考えます。

期待値はそれぞれ順に{1.5, 1.2, 1.2}となります。

{1}番目の券が一番期待値高いです。{2}番目と{3}番目の券は期待値同じです。

最適な{\boldsymbol{f}}を求めると、{f_1=0.048,\ f_2=0.071,\ f_3=0.106}となりました。

{2}番目と{3}番目の券を比較すると期待値は同じですが、

確率は{3}番目の券の方が高いため、購入金額の割合は大きいです。

{1}番目の券が一番期待値高いですが、一番購入金額は低くなりました。

的中確率が小さいためですね。

では、この設定で5000レースシミュレーションしてみます。

また、最適な{\boldsymbol{f}}に、

定数{0.2,\ 0.4,\ 0.6,\ 0.8,\ 1.2,\ 1.4,\ 1.6,\ 1.8}を掛けた場合も同時に示します。

f:id:strawberry_kyon:20190326220623p:plain

f:id:strawberry_kyon:20190326220630p:plain

横軸はレース数、縦軸は所持金で対数グラフです。初期所持金は{1}としました。

どうやら、求めたものは正しかったようですね。

最適な{\boldsymbol{f}}より小さいと、資産の増え方は下がりばらつきが抑えられます。

一方最適な{\boldsymbol{f}}より大きいと、資産の増え方は下がりばらつきが増えます。

レース数が少ないとき、一時的に資産が最適な{\boldsymbol{f}}のときの資産を上回る可能性はありますが、長期的にみるといいことないですね。

なので、ハーフケリーとかの方法があるんですかね。

おわりに

今回は、的中確率とオッズが所与のときの買い目を最適化しました。

今後の課題としては、

  • 複数種類の券種がある場合どうなるか
  • 的中確率やオッズが確立変数として与えられたときどうなるか

です。気になります。

のちのち考えていきます。

Rコード

library(ggplot2)
p=c(0.1,0.2,0.3)
o=c(15,6,4)
N=length(p)
p0=1-sum(p)
o0=1-sum(1/o)
f=solve(matrix(p0-p*o*o0,ncol=N,nrow=N)-diag(N)*p0*o)%*%(p0-p*o*o0)

p*o
f

R=5000
flg=sample(1:(N+1),R,prob=c(p,p0),replace=TRUE)

ff=f
data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg]))
g=ggplot()+geom_line(data=data,aes(x=R,y=w),color=2)+scale_y_log10()
for(i in seq(0.2,0.8,by=0.2)){
  ff=f*i
  data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg]))
  g=g+geom_line(data=data,aes(x=R,y=w))
}
g

ff=f
data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg]))
g=ggplot()+geom_line(data=data,aes(x=R,y=w),color=2)+scale_y_log10()
for(i in seq(1.2,1.8,by=0.2)){
  ff=f*i
  data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg]))
  g=g+geom_line(data=data,aes(x=R,y=w))
}
g

BTCFX 仮想通貨 レンジ相場の統計モデリング

仮想通貨のbotを作りたいです。

トレンドフォロー系の論文を見つけて行けそうと思い、実装してみました。

が、あまりうまく行かず。

ネットをあさっていると、「レンジ相場がほとんど」という記事を見つけ、

レンジ相場でアルファを見つけたいと思いました。

ということで、レンジ相場を統計モデリング

結論としてはうまく行かなかったんですが、メモとして残します。

続きを読む

変なユニバーサル基板を作ってみた。

ブログを1年以上放置していました。

何かしら発信しようと思い、雑な記事ですけど投稿します。

これからちょっとずつ投稿しようと思います。

作ったもの

f:id:strawberry_kyon:20190322212524j:plain

3方向に挿すことができる2.54mmピッチのユニバーサル基板です。

作ったといっても2年くらい前なのですが、画像が出てきましたので。

作ってみたものの、使ってみると使いにくくどうしようもなかったです。

ブックメーカーのオッズに関する考察

修論無事終えて最近ブックメーカーにはまってます。
知らない方はググってください。
ブックメーカーのオッズに関して考えてみたことのメモです。

続きを読む

Bradley-Terryモデルとその応用まとめ

スポーツの勝敗データを想定する。いろんなチーム(あるいは個人)のうち2チームが戦って勝敗を記録したデータ。
このデータから各チームの潜在的な「強さ」を推定してどのくらいの確率でどっちが勝つのかを推定するというお話。

続きを読む