蒙地卡羅 PyTorch 版

在 Windows 及 Linux 分別安裝如下套件

#Windows
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu1330 --no-cache-dir

#Linux
pip install torch torchvision torchaudio

batch

每次 batch 不要超過 1e8(1億),否則 12 G 的 VRAM 也不夠用。

使用 CPU

底下程式在主記憶体(RAM, 非顯卡記憶体)配置 x, y 變數,然後使用 CPU 計算 batch = 1 億,每個 epoch 約需 0.9 秒多。若是使用 numpy,每個 epoch 約需 2 秒。

import time

import torch
def count():
    x=torch.rand(batch)
    y=torch.rand(batch)
    d=torch.sqrt(torch.square(x)+torch.square(y))
    idx=torch.where(d<=1)
    return idx[0].shape[0]

batch=1_00_000_000
epochs=200
incircle=0
for e in range(epochs):
    t1=time.time()
    incircle+=count()
    t2=time.time() 
    print(f"{t2-t1:.5f}秒")
    pi=4*incircle/((e+1)*batch)
    print(f"epoch:{e+1}: pi={pi}")

結果:
1.12293秒
epoch:1: pi=3.1415254
1.03121秒
epoch:2: pi=3.14150678
0.95390秒
epoch:3: pi=3.141532773333333
0.96214秒
epoch:4: pi=3.14151597

 使用 GPU

底下程式在每個 epoch 都重新配置一次新的顯卡記憶体(VRAM),然後使用 GPU 計算 batch = 1 億,每個 epoch 約需 0.007 秒多。

import time

import torch
def count():
    x=torch.rand(batch , device="cuda:0")
    y=torch.rand(batch , device="cuda:0")
    d=torch.sqrt(torch.square(x)+torch.square(y))
    idx=torch.where(d<=1)
    return idx[0].shape[0]

batch=1_000_000_000
epochs=200
incircle=0
for e in range(epochs):
    t1=time.time()
    incircle+=count()
    t2=time.time() 
    print(f"{t2-t1:.5f}秒")
    pi=4*incircle/((e+1)*batch)
    print(f"epoch:{e+1}: pi={pi}")   

結果:
0.19113秒
epoch:1: pi=3.14157944
0.02380秒
epoch:2: pi=3.14165164
0.00750秒
epoch:3: pi=3.14167188
0.00695秒
epoch:4: pi=3.14171002
0.00850秒
epoch:5: pi=3.141717616

 優化 GPU

底下在主程式只配置一次顯卡的記憶体(VRAM),每個 epoch 不再重新配置新記憶体,只修改原配置RAM 內 的值,效能高,這稱為 in-place 操作。計算 batch = 1 億雖也是 0.007 秒多,但若在大資料量時,會節省很多時間

import time

import torch
def count():
    x.uniform_(0, 1)
    y.uniform_(0, 1)
    return (x*x+y*y<=1).sum().item()

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
batch=1_000_000_000
epochs=200
incircle=0
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x=torch.zeros(batch).to(device)
y=torch.zeros(batch, device=device)
for e in range(epochs):
    t1=time.time()
    incircle+=count()
    t2=time.time() 
    print(f"{t2-t1:.5f}秒")
    pi=4*incircle/((e+1)*batch)
    print(f"epoch:{e+1}: pi={pi}")   

結果 :
0.07825秒
epoch:1: pi=3.1417826
0.02338秒
epoch:2: pi=3.14165502
0.00794秒
epoch:3: pi=3.1416211466666666
0.00856秒
epoch:4: pi=3.14172572
0.00764秒
epoch:5: pi=3.141665792

(x*x+y*y<=1) 會產生 [True, True, False, True, …….],sum 會把裏面的值加總,但會先將 True 轉成 1,False 轉成 0,所以加總後是計算幾個 True,然後用 item() 將 tensor 轉成 Python 數字。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *