読者です 読者をやめる 読者になる 読者になる

技術メモ

役に立てる技術的な何か、時々自分用の覚書。統計学、機械学習、神経科学、暗号理論...と幅広く興味があります。

pythonで符号検定、ウィルコクソンの符号付き順位検定

統計 Python

符号検定

符号検定とは、勝ち負けなど〇×の判定で2つの群の優劣を判定する検定のこと。
「将棋ソフト同士を対決させて、試合の勝敗だけでソフトの強さを比較したい」等に使える。

scipyを使う

from scipy import stats
stats.binom_test(x, n=None, p=0.5, alternative='two-sided')

x=勝利回数、n=試合回数
あるいは
x = [勝利回数, 敗北回数] (n=None)
のように記述する。
alternativeは片側検定("greater", "less") か両側検定("two-sided") か。
結果はfloatで採択する確率が返ってくる。

  • テストコード

「一方が55%の勝率をあげていた時、何試合をすればどれくらいの確率で強さに差があると言えるか」
について検定してみる。
強さに差があるというのはすなわち、どちらかが勝つ(負ける)確率pが0.5ではないということである。
したがって、p=0.5を棄却する確率を片側検定で求めることになる。

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

win_rate = 0.55
ns = range(10,800,20)
xs = [int(i * win_rate) for i in ns]
results = []
for x, n in zip(xs, ns):
    result = stats.binom_test(x,n, p=0.5,alternative="greater")
    results.append(1-result)

plt.plot(ns, results)
plt.show()

f:id:swdrsker:20170307053459p:plain:w500
これを見ると、
「55%の勝率をあげている方が強いと95%の確からしさで言うには、約300回の試合数が必要である」
ということがわかる。

補足:Wilcoxonの符号付き順位検定

符号検定では試合の勝敗のように〇×で表すものだったが、連続値で比較する基準がある場合Wilcoxonの符号付順位検定を用いる。
「何人かの患者に睡眠薬の新薬を飲ませて、どれだけ睡眠時間が変化したかで効果の有無を確かめたい」等に使える。

from scipy import stats
stats.wilcoxon(x, y=None, zero_method='wilcox', correction=False)

x,yは対応のある2組の値と見なされる。
y=Noneの時は、xはペアの差分と見なされる。

zero_methodには"pratt","wilcox","zsplit"がある。
"pratt":差が0である場合を許す検定
"wilcox":差が0である場合を無視する検定
"zsplit": 差分の正負しか見ない検定(符号検定と同じ?)

correctionは連続値補正をかけるか否か。デフォルトではFalse

結果のp値は

result = stats.wilcoxon(x, y=None, zero_method='wilcox', correction=False)
result.pvalue

のように取り出す

補足2:勝率の信頼区間を求めたい

「将棋ソフトAに対するソフトBの勝率を信頼区間を含めて知りたい」というとき、
二項分布の正規近似を使う。
二項分布は試行回数を大きくしていけば正規分布のような形になる。(追記予定)

二項分布B(n,p)nを十分に大きくしていけば平均np、分散np(1-p)正規分布に近づく。

勝率ベースに直せば、平均p、分散\frac{p(1-p)}{n}ということ。
例えば、勝率55%で試行回数300回の時、平均は0.55標準偏差は0.029なので、
95%信頼区間付きで表すなら勝率は55.0%(±5.7%)となる。

(上の例と同じこと(n=300,p=0.55の時p>0.5を検定)を正規近似でやると、確かに95.7%となる。)


参考サイト:
scipy.stats.binom_test — SciPy v0.14.0 Reference Guide
scipy.stats.wilcoxon — SciPy v0.14.0 Reference Guide
二項分布の正規近似(ラプラスの定理) | 高校数学の美しい物語



関連:
swdrsker.hatenablog.com

Extream Learning Machineの簡単実装

機械学習 Python

あまり知られていないかも知れないが、Extream Learning Machineというニューラルネットの一種がある。
3層のニューラルネットなんだけど、通常のニューラルネットと違い、学習は出力層と中間層の重回帰で学習する。通常のようなバックプロパゲーションのような面倒なことはしない。
入力層と中間層の間の重みはランダムで良く、学習する必要がない。ただ通常より多くの中間層ニューロンを用意しておく必要がある。
イメージとしては特徴量を大量生成しておいて、分類に役に立ちそうな候補手を重回帰で選択するといった感じだろうか。重回帰なのでデータをまとめてオフラインでしか学習できない。
利点

欠点

  • オンライン学習ではない
  • 過学習の問題がありそう
import matplotlib.pyplot as plt
import numpy as np
import seaborn
seaborn.set_style("whitegrid")

mid_num = 200
train_num = 2000
test_num = 200
train = np.random.random([train_num, 2]) * 10 - 5
teacher = np.zeros(train_num)
for i in range(train_num):
    teacher[i] = 0 if np.linalg.norm(train[i]) < 3 else 1
test = np.random.random([test_num, 2]) * 10 - 5
predict = np.zeros(test_num)

def activation(x):
    return 1/(1 + np.exp(-x))

# learning
input_weight = np.random.random([2,mid_num])
mid = activation(np.dot(train, input_weight))
output_weight = np.dot(np.linalg.pinv(mid), teacher)

# predicting
mid = activation(np.dot(test, input_weight))
output = np.dot(mid, output_weight)
predict = map(lambda x: 1 if x>0.5 else 0, output)

for i in range(test_num):
    color = "r" if teacher[i] else "b"
    plt.scatter(*train[i], color=color)

circle = [(3*np.sin(x), 3*np.cos(x)) for x in np.arange(0, 6.29, 0.001)]
plt.plot(*zip(*circle), color="g")

plt.axis("square")
plt.show()

f:id:swdrsker:20170302225355p:plain