簡體   English   中英

使用 numpy 進行快速蒙特卡羅模擬?

[英]Fast Monte-Carlo simulation with numpy?

我正在學習 R 和 Python 中“做貝葉斯數據分析”中的練習。

我想找到一種使用恆定空間進行蒙特卡羅模擬快速方法

下面的問題是微不足道的,但可以很好地測試不同的方法:

例 4.3

確定從洗牌的 pinochle 牌中抽出 10 的確切概率。 (在pinochle 套牌中,有48 張牌。有六個值:9、10、Jack、Queen、King、Ace。在標准的四種花色中,每個值都有兩個副本:紅心、菱形、梅花、黑桃.)

(A) 得到 10 的概率是多少?

當然,答案是 1/6。

我能找到的最快的解決方案(與 R 的速度相當)是使用np.random.choice生成大量抽np.random.choice ,然后應用Counter 我不喜歡不必要地創建數組的想法,所以我嘗試使用字典和 for 循環,一次繪制一張卡片並增加該類型卡片的計數。 令我驚訝的是,它慢得多!

下面是我測試的 3 種方法的完整代碼。 _有沒有一種方法可以與 method1() 一樣高效,但使用常量空間?

Python 代碼:Google Colab 鏈接

deck = [c for c in ['9','10','Jack','Queen','King','Ace'] for _ in range(8)]
num_draws = 1000000

def method1():
  draws = np.random.choice(deck, size=num_draws, replace=True)
  df = pd.DataFrame([Counter(draws)])/num_draws
  print(df)
  
def method2():
  card_counts = defaultdict(int)
  for _ in range(num_draws):
    card_counts[np.random.choice(deck, replace=True)] += 1
  df = pd.DataFrame([card_counts])/num_draws
  print(df)
  
def method3():
  card_counts = defaultdict(int)
  for _ in range(num_draws):
    card_counts[deck[random.randint(0, len(deck)-1)]] += 1
  df = pd.DataFrame([card_counts])/num_draws
  print(df)

Python timeit() 結果:

方法 1:1.2997

方法 2:23.0626

方法 3:5.5859

代碼:

card = sample(deck, numDraws, replace=TRUE)
print(as.data.frame(table(card)/numDraws))

這是一個np.unique + np.bincount -

def unique():    
    unq,ids = np.unique(deck, return_inverse=True)
    all_ids = np.random.choice(ids, size=num_draws, replace=True)
    ar = np.bincount(all_ids)/num_draws
    return pd.DataFrame(ar[None], columns=unq)

NumPy 在這里有什么幫助?

有兩項主要改進對我們有所幫助:

  1. 我們將字符串數據轉換為數字。 NumPy 可以很好地處理此類數據。 為了實現這一點,我們使用np.unique

  2. 我們使用np.bincount來代替計數步驟。 同樣,它適用於數字數據,我們確實通過在此方法開始時完成的數字轉換獲得了這一點。

  3. NumPy 通常適用於大數據,這里就是這種情況。


計時與給定的樣本數據集的比較反對最快method1 -

In [177]: %timeit method1()
328 ms ± 16.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [178]: %timeit unique()
12.4 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy 通過在其數值引擎中運行 C 代碼來提高效率。 Python 很方便,但它比 C 慢幾個數量級。

在 Numpy 和其他高性能 Python 庫中,Python 代碼主要由膠水代碼組成,用於准備要調度的任務。 由於存在開銷,因此一次繪制大量樣本要快得多。

請記住,為 Numpy 工作提供 100 萬個元素的緩沖區仍然是恆定空間。 然后你可以通過循環采樣 10 億次。

這種額外的內存分配通常不是問題。 如果您必須不惜一切代價避免使用內存同時仍能從 Numpy 中獲得性能優勢,您可以嘗試使用 Numba 或 Cython 來加速它。

from numba import jit
@jit(nopython=True)
def method4():
    card_counts = np.zeros(6)
    for _ in range(num_draws):
        card_counts[np.random.randint(0, 6)] += 1
    return card_counts/num_draws

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM