Pythonで体験するベイズ推論 PyMCによるMCMC入門 第4章 写経
目次
始めに
この記事では、Pythonで体験するベイズ推論 PyMCによるMCMC入門
の第4章を写経した記録をまとめます。前回はこちらPythonで体験するベイズ推論 PyMCによるMCMC入門 第3章 (3.2全部) 写経。 実行はJupyter Labにて実行しています。
写経は以下のようにまとめました。
写経ではありますが、関数を作成し、コードの再実行をやりやすいようにした部分があります。
より良いと思われるコードを考えた場合は書き換えてコメントを添えるようにし、変更点をなるべく明示するようにしてあります。
個人的に気になった点のメモを残すようにしてあります。同じような疑問を持った方の助けになれば幸いです。
以前書いたコードと同じようなコード(例えばグラフの描写等)は効率化のために飛ばしているところもあります。
記事内で使用するモジュールなどは一番最初に宣言するようにしてあります。
Pythonで体験するベイズ推論 PyMCによるMCMC入門を読んでいている際のちょっとした正誤表代わりになればと思います。
この記事で使用する主なモジュール、設定
この記事では、主に以下のモジュールや設定を使用しています。import praw
を実行する前に、pip install praw==3.6.0
を実行してpraw
モジュールをインストールしておかないとエラーが出ます。
from os import makedirs from urllib.request import urlretrieve import praw import pymc as pm from IPython.core.pylabtools import figsize import matplotlib.pyplot as plt
4 偉大な定理、登場
4.3 サンプルサイズが小さいという災い
4.3.3 例題 : Redditコメントをソートする
makedirs("data", exist_ok=True) urlretrieve("https://git.io/vXtX2", "top_showerthoughts_submissions.py")
Output:
('top_showerthoughts_submissions.py', <http.client.HTTPMessage at 0x1ce88b88f60>)
%run top_showerthoughts_submissions.py 2 print("Title of submission:") print(top_post) # top_postはtop_showerthoughts_submissions.pyを実行した際に代入される。型は文字列である。
Output:
Title of submission: April Fools Day is proof the Purge wouldn’t work. Despite having a whole day for shitposting, there is still shitposting the rest of the year.
n_comments = len(contents) comments = np.random.randint(n_comments, size=4) print("Some Comments (out of %d total) \n----------" % n_comments) for i in comments: print('"' + contents[i] + '"') print("upvotes/downvotes: ", votes[i, :], "\n")
Output:
Some Comments (out of 2 total) ---------- "That guy who is always kicking robots in those Boston Dynamics videos is gonna have a really bad day when the machines take over." upvotes/downvotes: [44 2] "That guy who is always kicking robots in those Boston Dynamics videos is gonna have a really bad day when the machines take over." upvotes/downvotes: [44 2] "That guy who is always kicking robots in those Boston Dynamics videos is gonna have a really bad day when the machines take over." upvotes/downvotes: [44 2] "Shaving cream is like a progress bar of how much you've got left to shave." upvotes/downvotes: [40 3]
メモ
どうやら、まさかのコメントが2個しかないということが判明した。時間がたってしまって変化があったのかも・・・シンプルに
comments = [0, 1]
として進めます。
comments = [0, 1] for i in comments: print('"' + contents[i] + '"') print("upvotes/downvotes: ", votes[i, :], "\n")
Output:
"That guy who is always kicking robots in those Boston Dynamics videos is gonna have a really bad day when the machines take over." upvotes/downvotes: [44 2] "Shaving cream is like a progress bar of how much you've got left to shave." upvotes/downvotes: [40 3]
def posterior_upvote_ratio(upvotes, downvotes, samples=20000): N = upvotes + downvotes upvote_ratio = pm.Uniform("upvote_ratio", 0, 1) observations = pm.Binomial("obs", N, upvote_ratio, value=upvotes, observed=True) map_ = pm.MAP([upvote_ratio, observations]).fit() mcmc = pm.MCMC([upvote_ratio, observations]) mcmc.sample(samples, samples / 4) return mcmc.trace("upvote_ratio")[:]
plt.rcParams['font.size'] = 17
figsize(22, 16) colors = ["#348ABD", "#A60628", "#7A68A6", "#467821", "#CF4457"] posteriors = [] for i in range(len(comments)): j = comments[i] label = f'({votes[j, 0]} up:{votes[j, 1]} down)\n{contents[j][:50]}...' posteriors.append(posterior_upvote_ratio(votes[j, 0], votes[j, 1])) plt.hist(posteriors[i], bins=18, density=True, alpha=.9, histtype="step", color=colors[i % 5], lw=3, label=label) plt.hist(posteriors[i], bins=18, density=True, alpha=.2, histtype="stepfilled", color=colors[i], lw=3) plt.legend(loc="upper left") plt.xlim(0, 1) plt.ylabel("Density") plt.xlabel("Probability of upvote") plt.title("Posterior distributions of upvote ratios " "on differennt comments")
Output:
[-----------------100%-----------------] 20000 of 20000 complete in 1.0 sec Text(0.5, 1.0, 'Posterior distributions of upvote ratios on differennt comments')
N = posteriors[0].shape[0] lower_limits = [] for i in range(len(comments)): j = comments[i] label = f'({votes[j, 0]} up:{votes[j, 1]} down)\n{contents[j][:50]}...' plt.hist(posteriors[i], bins=20, density=True, alpha=.9, histtype="step", color=colors[i], lw=3, label=label) plt.hist(posteriors[i], bins=20, density=True, alpha=.2, histtype="stepfilled", color=colors[i], lw=3) v = np.sort(posteriors[i])[int(0.05 * N)] plt.vlines(v, 0, 10, color=colors[i], linestyles="--", linewidths=3) lower_limits.append(v) plt.legend(loc="upper left") plt.xlabel("Probability of upvote") plt.ylabel("Density") plt.title("Posterior distributions of upvote ratios " "on different comments") order = np.argsort(-np.array(lower_limits)) print(order, lower_limits)
Output:
[0 1] [0.87004917925665515, 0.82966075938026218]
4.3.5 でも計算が遅すぎる!
def intervals(u, d): a = 1. + u b = 1. + d mu = a / (a + b) std_err = 1.65 * np.sqrt((a * b) / ((a + b)**2 * (a + b + 1.))) return (mu, std_err) posterior_mean, std_err = intervals(votes[:, 0], votes[:, 1]) lb = posterior_mean - std_err print(f"Approximate lower bounds:\n{lb}, \n") order = np.argsort(-lb) ordered_contents = [] for i in order: ordered_contents.append(contents[i]) print(votes[i, 0], votes[i, 1], contents[i]) print("-----------------")
Output:
Approximate lower bounds: [ 0.88044266 0.84187791], 44 2 That guy who is always kicking robots in those Boston Dynamics videos is gonna have a really bad day when the machines take over. ----------------- 40 3 Shaving cream is like a progress bar of how much you've got left to shave. -----------------
r_order = order[::-1] plt.errorbar(posterior_mean[r_order], np.arange(len(r_order)), xerr=std_err[r_order], capsize=0, fmt="o", color="#7A68A6") plt.xlim(0.3, 1) plt.yticks(np.arange(len(r_order) - 1, -1, -1), map(lambda x: x[:30].replace("\n", ""), ordered_contents))
Output:
([<matplotlib.axis.YTick at 0x2730d10c0b8>, <matplotlib.axis.YTick at 0x2730caaaf28>], <a list of 2 Text yticklabel objects>)
メモ
map(lambda x: x[:30].replace("\n", ""),ordered_contents)
はordered_contents
の各文字列の先頭30文字だけ抜き出し、改行を置換した要素を返すイテレータを生成します。
for i in map(lambda x: x[:30].replace("\n", ""),ordered_contents): print(i)
Output:
That guy who is always kicking Shaving cream is like a progre
一般的に、map
は第一引数の関数を第二引数の各値に適用させたイテレータを返す関数です。
for i in map(lambda x: 2*x, range(10)): print(i)
Output:
0 2 4 6 8 10 12 14 16 18
メモ
今更だが、np.arange
の説明を読んでみた。range
とほぼ同じような引数なんですね。
np.arange??
(クリックすると展開されます)
Docstring: arange([start,] stop[, step,], dtype=None) Return evenly spaced values within a given interval. Values are generated within the half-open interval ``[start, stop)`` (in other words, the interval including `start` but excluding `stop`). For integer arguments the function is equivalent to the Python built-in `range <http://docs.python.org/lib/built-in-funcs.html>`_ function, but returns an ndarray rather than a list. When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use ``linspace`` for these cases. Parameters ---------- start : number, optional Start of interval. The interval includes this value. The default start value is 0. stop : number End of interval. The interval does not include this value, except in some cases where `step` is not an integer and floating point round-off affects the length of `out`. step : number, optional Spacing between values. For any output `out`, this is the distance between two adjacent values, ``out[i+1] - out[i]``. The default step size is 1. If `step` is specified, `start` must also be given. dtype : dtype The type of the output array. If `dtype` is not given, infer the data type from the other input arguments. Returns ------- arange : ndarray Array of evenly spaced values. For floating point arguments, the length of the result is ``ceil((stop - start)/step)``. Because of floating point overflow, this rule may result in the last element of `out` being greater than `stop`. See Also -------- linspace : Evenly spaced numbers with careful handling of endpoints. ogrid: Arrays of evenly spaced numbers in N-dimensions. mgrid: Grid-shaped arrays of evenly spaced numbers in N-dimensions. Examples -------- >>> np.arange(3) array([0, 1, 2]) >>> np.arange(3.0) array([ 0., 1., 2.]) >>> np.arange(3,7) array([3, 4, 5, 6]) >>> np.arange(3,7,2) array([3, 5]) Type: builtin_function_or_method
今回の写経は以上です。 ここまで読んでいただきありがとうございました。