1. 程式人生 > >1個擲硬幣問題,4個Python解法:讀書筆記

1個擲硬幣問題,4個Python解法:讀書筆記

-c -s 積分 arr 比較 有助於 現在 交流 分組

我在學習機器學習算法和玩Kaggle 比賽時候,不斷地發現需要重新回顧概率、統計、矩陣、微積分等知識。如果按照機器學習的標準衡量自我水平,這些知識都需要重新梳理一遍。

網上或許有各種各樣知識片斷,卻較難找到一本書將概率,統計、矩陣、微積分公式和Python結合起來。 要麽是講的比較淺顯,要麽跨度比較大。 最近看到一本書,恰好把上面的問題解決了。著重講解Python for 概率,統計,機器學習. 相比較吳恩達教授的網上課程,各有千秋。

  • l 感覺吳教授的課程偏學術。 假設學生數學基礎比較紮實(畢竟課堂受眾是斯坦福大學學生,起點比較高),用矩陣推導公式,簡潔明了。整個課程把絕大多數機器學習算法都教了一遍。
  • 這本書偏工程實踐。 書中從多個層面來介紹經典算法。尤其是後期的泛化,正則化等章節。介紹的算法,但是每個算法都用2-5種python方法實現。例如: 

技術分享圖片Python 循環或自帶Itertools ((笛卡爾乘積,經典概率)

技術分享圖片Python sympy(數學符號) (微積分公式推導和實現)

技術分享圖片Python Pandas(分組計算) (程序員看得懂)

技術分享圖片Python numpy (矩陣計算) (註:用矩陣計算,有速度飛起來的感覺)

技術分享圖片Python scipy (科學計算庫) (算法增強器)

個人感覺這本書比較適合我的學習目標。 也許也有人喜歡這樣的書。書名和下載地址在文章最後面。

我先來翻譯一段書中的一道期望計算題目,分享一下這種庖丁解牛和層次漸近的感覺。

題目:

三個硬幣: 1角,2角,5角。 同時擲硬幣,正面朝上的將面值加在一起求和。 只有兩個硬幣正面朝上的期望和是多少?  

  • Xi ∈ {0, 1} 註:硬幣為Xi, (面值10,10,50,分別為X10,X20,X50,只有正面和反面,是服從二項分布(0,1)
  • ξ := 10X10 + 20X20+ 50X50註: ξ 為正面朝上的硬幣面值之和
  • η := X10X20(1 ?X50) + (1 ? X10)X20X50 + X10(1 ? X20)X50 註: η 為只有兩個硬幣正面朝上的情況

這樣此題就變成了計算 E(ξ |η) 在 η 條件為真時,ξ 的期望。我們首先需要找到一個函數 h(η)。 這個函數可以讓殘差最小化。

技術分享圖片

現在,計算兩個硬幣朝上的面值之和公式變成了如何定義h(η)函數。

註:η的結果是{0,1},所以h函數的只有兩種輸入值{0,1}。因此,正交內積條件為

技術分享圖片

因為我們只需要知道滿足兩個硬幣朝上的情況,(即η =1 ),所以公式簡化為:

技術分享圖片

兩邊積分計算求和

技術分享圖片

數學公式到此就結束了。本書中定義h(η) = αη,並求α。這樣公式就變成了如下形式。

技術分享圖片

註:

技術分享圖片

公式推導完了,下面就看看Python的四種解法吧。

解法1 :Sympy數學符號方法

上述推導公式,直接可以用數學符號語言,在Sympy中計算。

計算結果精準 alpha = 160/3

E|η) = (160/3)*η

# Do it by sympy
import sympy as S
X10,X20,X50 = S.symbols("X10,X20,X50",real=True)

xi = 10*X10+20*X20+50*X50
print("ξ = ",xi)
eta = X10*X20*(1-X50)+X10*(1-X20)*(X50)+(1-X10)*X20*(X50)
print("η = ",eta)

num=S.summation(xi*eta,(X10,0,1),(X20,0,1),(X50,0,1))

den=S.summation(eta*eta,(X10,0,1),(X20,0,1),(X50,0,1))

alpha=num/den

print(alpha)

ξ = 10*X10 + 20*X20 + 50*X50
η = X10*X20*(-X50 + 1) + X10*X50*(-X20 + 1) + X20*X50*(-X10 + 1)
160/3

解法2 :用Pandas,分組和計算(程序員的方式)

註:做1000次試驗(蒙特卡羅仿真),然後計算試驗的均值。計算結果近似於推導結果。

# Do it by pandas
import pandas as pd
d = pd.DataFrame(columns=[X10,X20,X50])
ntest = 10**6
d.X10 = np.random.randint(0,2,ntest)
d.X10 = np.random.randint(0,2,ntest)
d.X20 = np.random.randint(0,2,ntest)
d.X50 = np.random.randint(0,2,ntest)

grp=d.groupby(d.eval(X10+X20+X50))
grp.get_group(2).eval(10*X10+20*X20+50*X50).mean()

53.340141339548644

解法3:用Numpy,矩陣計算(速度快,有飛起來的感覺)

# Do it by numpy
import numpy as np
from numpy import array
ntest = 10**6
x=np.random.randint(0,2,(3,ntest)) 

expectation =np.dot(x[:,x.sum(axis=0)==2].T,array([10,20,50])).mean()

print(expectation)

53.3542987559

解法4: 用笛卡爾笛卡爾乘積,過濾只有兩個硬幣朝上事件,計算期望

#do it by pure python
import itertools as it
Xi = list(it.product((0,1),(0,1),(0,1)))               #笛卡爾乘積函數 初始化 - 所有擲硬幣的事件 8種情況,三個硬幣,每個硬幣只有正面和反面

Xi_conditioned = list(it.filterfalse(lambda i:sum(i)!=2,Xi)) #過濾 只有兩個硬幣正面朝上的事件,只有三個事件

results = list((map(lambda k:10*k[0]+20*k[1]+50*k[2],Xi_conditioned)))
dem = len(results)
num = sum(results)

print(Xi)
print(Xi_conditioned)
print(results)
print(num/dem)

[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
[(0, 1, 1), (1, 0, 1), (1, 1, 0)]
[70, 60, 30]
53.333333333333336

這道概率期望題,書中演示了4種方法: Sympy,Numpy, Pandas 和Itertools. 在科學計算和機器學習中,采用不同的實現方法可以有助於問題解決和交叉檢查。

python學習交流群:125240963

原文鏈接:https://zhuanlan.zhihu.com/p/31673263

1個擲硬幣問題,4個Python解法:讀書筆記