PyTorch基礎

      在〈PyTorch基礎〉中尚無留言

PyTorch 跟 Tensorflow 一樣,就是一台可以啟動 GPU 進行數學運算的計算機。PyTorch 於 2017 年初由 Facebook 開發的開源深度學習框架,語法簡潔優雅,概念直觀,容易上手,再加上烏俄戰爭無人機廣泛使用 Yolo 進行視覺判辨,遂漸瓜分 Tensorflow 的市場。

Ubuntu 2404 安裝 PyTorch

2024 年 4 月 Ubuntu 推出 2404 版本,此時 nVidia 尚未推出可支援  Cuda 套件。怪異的是,只要把 nVidia GPU 顯卡驅動裝好,完全不需安裝 Cuda,然後安裝 PyTorch 套件,竟然可以啟動硬体 GPU,安裝 PyTorch 套件的方式如下

pip install torch torchvision torchaudio

然後在 Ubuntu 開啟專案,執行如下代碼,竟然印出 True

import torch
print(torch.cuda.is_available())

結果:
True

緊接著測試蒙地卡羅 torch 版,顯卡功率直逼上線

import time
import torch
def dist():
    #points=torch.rand([2, batch]).to(device)# 每次都產生一個陣列,會很慢的
    points=array.uniform_(0, 1)
    d=(points[0].square()+points[1].square()).sqrt()
    idx=torch.where(d<=1)
    #print(idx)
    #print(idx[0].shape[0])
    return idx[0].shape[0]
batch=100_000_000
epoch=500_000
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
array=torch.zeros([2,batch], dtype=torch.float32, device=device)

incircle=0
for e in range(epoch):
    t1=time.time()
    incircle += dist()
    area = incircle * 4  / ((e + 1)*batch)
    t2=time.time()
    print(f'GPU Epoch:{e+1:07,d} : {t2-t1:.5f}秒: pi={area}')

PyTorch 需要 Cuda 嗎

PyTorch 其實也需要 nVidia 的 Cuda 及 Cudnn ,但為何我們還沒裝 Cuda 還是可以啟動硬体 GPU 呢!! 這是因為我們在 pip install torch 時會連同 PyTorch 自帶的 cuda包 一同安裝進來,只不過這個自帶的 cuda 包比較舊,而且只包含 PyTorch 所需要的函數,所以像 cuda toolchain 這種函數就沒包進來。

那麼對我們有影響嗎!! 當然沒有,因為 PyTorch 不會用到那些新的函數,所以程式碼一樣可以執行,頂多在效能上無法跟進而以。

PyTorch 這種設計方式非常佛心,畢竟顯卡的驅動一直在改版,Cuda 也一直在更新,而 PyTorch 把整個變化全包在它身上,讓我們的程式碼可以無憂無慮的相容到新的版本,真的很偉大。

Windows 10 安裝 PyTorch

在 Windows 10之下,就沒這麼好運了,一定要先裝好 Cuda/cudnn,而且在 Python 中還需指定相對應的 torch 版本,否則無法啟動 GPU。請到底下的網址查看支援的版本 https://download.pytorch.org/whl/

安裝時需指定 –index-url 參數,指定到 Pytorch 的官網下載與我們 cuda 相同的版本。本例因安裝 cuda 11.8 版本,所以 –index-url 亦需指定 cu118。

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

如果電腦的記憶体只有 16 G 以下,又同時開啟二個專案以上,安裝上面的 Pytorch 時會出現 Memory error例外,甚至當機連滑鼠鍵盤都不會動,加上 “–no-cache-dir” 即可解決。

測試是否有安裝成功,可使用如下指令,如果印出 “True”,表示安裝成功。

當然如果沒有 nVidia 顯卡的人,因為 CUDA 安裝不起來,所以就算 torch 安裝成功,結果還是 False。

import torch
print(torch.cuda.is_available())
結果:
True

基本格式

numpy 的基本格式是 np.array,中文稱”陣列”。而陣列裏的資料可以是 np.int8, np.int32, np.float32…。

torch 的基本格式為 torch.Tensor,中文叫”張量”。張量又細分為 torch.tensor(),torch.Tensor(), torch.FlatTensor(), torch.DoubleTensor()。張量的資料型態可以是 torch.int8. int32, float32….

torch.tensor()

torch.tensor() 是最廣泛被使用的格式。

這也是唯一可以產生出整數型態的 Tensor,也是唯一可以產生純量的資料。整數若沒有指定型態,預設是 torch.int64。

import torch
a=torch.tensor(10)#預設為torch.int64
b=torch.tensor(10, dtype=torch.int32)
print(a.dtype)
print(a)
print(b)

結果:
torch.int64
tensor(10)
tensor(10, dtype=torch.int32)

也可以產生純量的浮點數。浮點數若沒有指定型態,預設是 torch.float32。

import torch
a=torch.tensor(10.)#預設是 torch.float32
b=torch.tensor(10, dtype=torch.float64)
print(a.dtype)
print(a)
print(b)

結果:
torch.float32
tensor(10.)
tensor(10., dtype=torch.float64)

也可以產生整數或浮點數的陣列

import torch
a=torch.tensor([1,2,3], dtype=torch.int32)
b=torch.tensor([1,2,3], dtype=torch.float64)
print(a)
print(b)
結果:
tensor([1, 2, 3], dtype=torch.int32)
tensor([1., 2., 3.], dtype=torch.float64)

torch.Tensor()

參數只能是陣列,且不能指定 dtype,也就資料型態只能是 torch.float32。

import torch
a=torch.Tensor([1,2,3])
print(a)
結果:
tensor([1., 2., 3.])

torch.FloatTensor()

torch.FloatTensor([2,10]) 裏的參數如果是 list,那就跟 torch.Tensor() 一模一樣。

import torch
a=torch.Tensor([2,10])
b=torch.FloatTensor([2,10])
print(a)
print(b)

結果:
tensor([ 2., 10.])
tensor([ 2., 10.])

但如果是 torch.FloatTensor(2, 10),則是產生 2*10 二維的陣列,裏面預設值為 0。此法不常用,因為裏面的值雖是 0,但卻有小數點誤差的問題,一般都會使用 torch.zeros([2,10],dtype=torch.float32)。

c=torch.FloatTensor(2,10)
print(c)

結果 :
tensor([[8.7244e-39, 9.2755e-39, 1.0653e-38, 1.0837e-38, 1.0102e-38, 8.4490e-39,
         9.6429e-39, 8.4490e-39, 9.6429e-39, 9.2755e-39],
        [1.0286e-38, 9.0919e-39, 8.9082e-39, 9.2755e-39, 8.4490e-39, 8.9082e-39,
         1.0286e-38, 1.0194e-38, 9.9184e-39, 9.0000e-39]])

torch.DoubleTensor()

變數型態為 torch.float64 

import torch
a=torch.DoubleTensor([1,2,3])
print(a)
結果 :
tensor([1., 2., 3.], dtype=torch.float64)

同樣的,如果參數為 torch.DoubleTensor(2,10),則產生 2*10 的二維陣列。

四則運算

Tensor 可以作四則運算,包含 +-*/%,不同型態可以相互運算。運算後的結果,為最大的資料型態。

但如果整數/整數,結果是 torch.float32。

import torch
a=torch.tensor([1,2,3], dtype=torch.int32)
b=torch.DoubleTensor([1,2,3])
c=torch.tensor([2,4,5], dtype=torch.int32)
print(a*b)
print(a/c)
結果:
tensor([1., 4., 9.], dtype=torch.float64)
tensor([0.5000, 0.5000, 0.6000])

型態轉換

torch 的型態可以互相轉換,常用的有 .char(), .byte()….,也可以使用 .type(),如下代碼說明

import torch as tc
a=tc.tensor([1., 2., 3.])
print(a.char())#int8
print(a.byte())#uint8
print(a.short())#int16
print(a.int())#int32
print(a.long())#int64
print(a.float())#float32
print(a.double())#float64

print(a.type(tc.int8))
結果 : 
tensor([1, 2, 3], dtype=torch.int8)
tensor([1, 2, 3], dtype=torch.uint8)
tensor([1, 2, 3], dtype=torch.int16)
tensor([1, 2, 3], dtype=torch.int32)
tensor([1, 2, 3])
tensor([1., 2., 3.])
tensor([1., 2., 3.], dtype=torch.float64)
tensor([1, 2, 3], dtype=torch.int8)

Process finished with exit code 0

矩陣相乘

矩陣相乘可用 torch.mm(a, b),請注意 a, b 二者必需滿足矩陣相乘的規則,也就是維度的中間必需相同。
比如 [5, 3] * [3,5] ,第一個矩陣維度是 5*3,第二個矩陣維度是 3*5,中間都是 3 才能相乘。

另外請特別注意,二者的型態必需相同。由下面的代碼中,a 變數沒有指定型態時,預設就是 int64。

import torch
a=torch.tensor([[1,2],[3,4],[5,6]])
b=torch.tensor([[1,2,3],[4,5,6]], dtype=torch.int64)
print(torch.mm(a,b))
結果 :
tensor([[ 9, 12, 15],
[19, 26, 33],
[29, 40, 51]])

numpy 互轉

Tensor 轉成 numpy,只需使用 numpy()即可,如下

import torch
a=torch.tensor([[1,2,3],[4,5,6]], dtype=torch.float32)
print(a.numpy())
結果 :
[[1. 2. 3.]
[4. 5. 6.]]

numpy 轉成 Tensor,必需使用 torch.from_numpy()

import torch
import numpy as np
a=np.array([[1,2,3],[4,5,6]], dtype=np.float64)
b=torch.from_numpy((a))
print(b)
結果 :
tensor([[1., 2., 3.],
[4., 5., 6.]], dtype=torch.float64)

維度

取得 Tensor 的維度,可以 shape 或 size(),二者的結果都是 torch.Size([2,3])。

shape 是 Tensor 的屬性,取得某一維的資料需使用 shape[index]。
size() 是 Tensor 裏的方法,取得某一維的資料需使用 size(index)

import torch
a=torch.tensor([[1,2,3],[4,5,6]], dtype=torch.float64)
print(a.shape, a.shape[0])
print(a.size(), a.size(0))
結果 :
torch.Size([2, 3]) 2
torch.Size([2, 3]) 2

陣列工具

產生陣列的工具有 torch.zeros(),torch.ones(),torch.rand(),torch.randn()。

每個工具中的 shape 可以是多參數、list 或是 tuple

import torch
a=torch.zeros(2,5)#多參數
print(a)
b=torch.zeros([2,5])#list
print(b)
c=torch.zeros((2,5))#tuple
print(c)
結果:
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])

torch.randint

randint  會產生整數亂數,參數如下 troch.randint(最低,最高, [維度])

import torch
a=torch.randint(0, 100, [10])
print(a)
結果 :
tensor([50, 46, 12, 75, 19, 45, 62, 91, 91, 86])

torch.rand

產生平均分佈亂數,其值介於 0~1之間

import torch
import pylab as plt
a=torch.rand(2,1000)
plt.scatter(a[0,:], a[1,:], s=1)
plt.show()

torch.randn

torch.randn 是標準常態分佈,接近 0 的地方機率愈大。

import torch
import pylab as plt
a=torch.randn(2,1000)
plt.scatter(a[0,:], a[1,:], s=1)
plt.show()

torch.normal

torch.normal (中心值, 變異數, shape) 同 torch.randn,但可以更改中心值及變異數的正負範圍。

底下的代碼中,接近 50 的幾率最大,左右二邊的邊界是 -150 及 150。

import torch
import pylab as plt
a=torch.normal(50,100,(2,1000))
plt.scatter(a[0, :], a[1, :], s=1)
plt.show()

torch.linspace

同 np.linspace(起始值, 結束值, 切割數)

import torch
a=torch.linspace(0,10,10)
print(a)
結果 :
tensor([ 0.0000, 1.1111, 2.2222, 3.3333, 4.4444, 5.5556, 6.6667, 7.7778,
8.8889, 10.0000])

torch.arange

同 np.arange(起始值, 結束值, 步進值),其值可以是浮點數,但最後不包含結束值。

import torch
a=torch.arange(0,5,0.1)
print(a)
結果 :
tensor([0.0000, 0.1000, 0.2000, 0.3000, 0.4000, 0.5000, 0.6000, 0.7000, 0.8000,
0.9000, 1.0000, 1.1000, 1.2000, 1.3000, 1.4000, 1.5000, 1.6000, 1.7000,
1.8000, 1.9000, 2.0000, 2.1000, 2.2000, 2.3000, 2.4000, 2.5000, 2.6000,
2.7000, 2.8000, 2.9000, 3.0000, 3.1000, 3.2000, 3.3000, 3.4000, 3.5000,
3.6000, 3.7000, 3.8000, 3.9000, 4.0000, 4.1000, 4.2000, 4.3000, 4.4000,
4.5000, 4.6000, 4.7000, 4.8000, 4.9000])

平均數及標準差

計算平均數,注意要計算的數值必需是小數(float32, float64)。 

import torch
a=torch.randint(0, 100, [10], dtype=torch.float32)
print(a)
print(f'平均數 : {torch.mean(a)}, {a.mean()}')
print(f'標準差 : {torch.std(a)}, {a.std()}')

結果 : 
tensor([27., 48., 87., 74.,  4., 60., 97., 94., 50., 43.])
平均數 : 58.400001525878906
標準差 : 30.152206420898438

啟用GPU

為了偵測是否有GPU,需先設定 deive

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

然後在產生 tensor 時,使用 device 參數宣告要儲存在 GPU 中,那麼以後的計算都會由 GPU 執行。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
array=torch.zeros([2,batch], dtype=torch.float32, device=device)

請注意,如果 x 是 GPU 裏的變數,那麼只要跟 x 有關的變數,也都會自動變成 GPU 變數。

如下代碼中,y 並未指定 device=”cuda:0″,但列印出來的結果還是 GPU 的變數

import torch as tc
x=tc.linspace(0,10,10, device='cuda:0')
y=x.square()
print("x: ", x)
print("y :", y)
結果 : 
x:  tensor([ 0.0000,  1.1111,  2.2222,  3.3333,  4.4444,  5.5556,  6.6667,  7.7778,
         8.8889, 10.0000], device='cuda:0')
y : tensor([  0.0000,   1.2346,   4.9383,  11.1111,  19.7531,  30.8642,  44.4444,
         60.4938,  79.0124, 100.0000], device='cuda:0')

移入移出 GPU

torch 變數如果儲在 CPU 中,可以使用 .cuda() 移入GPU,亦可使用 .cpu() 再次由 GPU 中移到 CPU。

import torch
x=torch.tensor([1,2,3,4,5])
print(x)
x=x.cuda()
print(x)
x=x.cpu()
print(x)


結果:
tensor([1, 2, 3, 4, 5])
tensor([1, 2, 3, 4, 5], device='cuda:0')
tensor([1, 2, 3, 4, 5])

 

參考

https://hackmd.io/@kent010341/SkcZyEGR8?utm_source=preview-mode&utm_medium=rec

發佈留言

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