函數 | 功能 | 例子 |
---|---|---|
Tensor(*sizes) | 制定size構造函數 | a = t.Tensor(2, 3) |
Tensor(*list) | 從list構造 | a = t.Tensor([[1,2,3],[4,5,6]]) |
tensor(data,) | 類似np.array的構造函數(需要數據) | scalar = t.tensor(3.14159) ;vector = t.tensor([1, 2]) |
ones(*sizes) | 全1Tensor | |
zeros(*sizes) | 全0Tensor | |
eye(*sizes) | 對角線爲1,其他爲0 | t.eye(2, 3, dtype=t.int) # 對角線爲1, 不要求行列數一致 |
arange(s,e,step | 從s到e,步長爲step | t.arange(1, 6, 2) |
linspace(s,e,steps) | 從s到e,均勻切分成steps份 | t.linspace(1, 10, 3) |
rand/randn(*sizes) | 均勻/標準分佈 | t.randn(2, 3, device=t.device(‘cpu’)) |
normal(mean,std)/uniform(from,to) | 正態分佈/均勻分佈 | |
randperm(m) | 隨機排列 | t.randperm(5) # 長度爲5的隨機排列 |
這些創建方法都可以在創建的時候指定數據類型dtype和存放device(cpu/gpu).
t.tensor和t.Tensor的區別
一句話來講,torch.Tensor
是由torch.tensor
和torch.empty
組成的。torch.Tensor
可以新建一個空tensor而torch.tensor
不可以。
tensor_without_data = torch.Tensor() #對
- 1
錯tensor_without_data = torch.tensor()
基本操作
查看tensor 大小: t.size(), t.shape()
tensor轉list: t.tolist()
計算tensor中元素總個數: t.numel()
調整形狀:
t.view(), t.resize()
a = t.arange(0, 6)
a.view(2, 3)
a.shape()
- 1
- 2
- 3
$ tensor([[ 0., 1., 2.],
[ 3., 4., 5.]])
torch.Size([2, 3])
resize與view不同的是它可以修改tensor的大小。
b.resize_(1, 3)
b.resize_(3, 3)
- 1
- 2
tensor([[ 0., 100., 2.]])
tensor([[ 0.0000, 100.0000, 2.0000],
[ 3.0000, 4.0000, 5.0000],
[ -0.0000, 0.0000, 0.0000]])
增減維度 t.squeeze(), t.unsqueeze()?
b.unsqueeze(1) # 注意形狀,在第1維(下標從0開始)上增加“1”
#等價於 b[:,None]
b[:, None].shape
- 1
- 2
- 3
b從torch.Size([2, 3])變成torch.Size([2, 1, 3])
索引操作
如無特殊說明,索引出來的結果與原tensor共享內存,也即修改一個,另一個會跟着修改。
a[0] # 第0行(下標從0開始)
a[:, 0] # 第0列
a[0][2] # 第0行第2個元素,等價於a[0, 2]
a[0, -1] # 第0行最後一個元素
a[:2] # 前兩行
a[:2, 0:2] # 前兩行,第0,1列
a[0:1, :2] # 第0行,前兩列
None類似於np.newaxis, 爲a新增了一個軸
等價於a.view(1, a.shape[0], a.shape[1])
a[None].shape # 等價於a[None,:,:]
a[:,None,:].shape
a > 1 # 返回一個ByteTensor
a[a>1] # 等價於a.masked_select(a>1) # 選擇結果與原tensor不共享內存空間
a[t.LongTensor([0,1])] # 第0行和第1行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
常用的選擇函數
函數 | 功能 |
---|---|
index_select(input, dim, index) | 在指定維度dim上選取,比如選取某些行、某些列 |
masked_select(input, mask) | 例子如上,a[a>0],使用ByteTensor進行選取 |
non_zero(input) | 非0元素的下標 |
gather(input, dim, index) | 根據index,在dim維度上選取數據,輸出的size與index一樣 |
gather
是一個比較複雜的操作,對一個2維tensor,輸出的每個元素如下:
out[i][j] = input[index[i][j]][j] # dim=0
out[i][j] = input[i][index[i][j]] # dim=1
- 1
- 2
三維tensor的gather
操作同理,下面舉幾個例子。
a = t.arange(0, 16).view(4, 4)
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[ 12., 13., 14., 15.]])
選取對角線的元素
index = t.LongTensor([[0,1,2,3]])
a.gather(0, index)
tensor([[ 0., 5., 10., 15.]])
選取反對角線上的元素
index = t.LongTensor([[3,2,1,0]]).t()
a.gather(1, index)
tensor([[ 3.],
[ 6.],
[ 9.],
[ 12.]])
選取反對角線上的元素,注意與上面的不同
index = t.LongTensor([[3,2,1,0]])
a.gather(0, index)
選取反對角線上的元素,注意與上面的不同
index = t.LongTensor([[3,2,1,0]])
a.gather(0, index)
tensor([[ 12., 9., 6., 3.]])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
與gather
相對應的逆操作是scatter_
,gather
把數據從input中按index取出,而scatter_
是把取出的數據再放回去。注意scatter_
函數是inplace操作。
out = input.gather(dim, index)
-->近似逆操作
out = Tensor()
out.scatter_(dim, index)
- 1
- 2
- 3
- 4
把兩個對角線元素放回去到指定位置
c = t.zeros(4,4)
c.scatter_(1, index, b)
tensor([[ 0., 0., 0., 3.],
[ 0., 5., 6., 0.],
[ 0., 9., 10., 0.],
[ 12., 0., 0., 15.]])
對tensor的任何索引操作仍是一個tensor,想要獲取標準的python對象數值,需要調用tensor.item(), 這個方法只對包含一個元素的tensor適用
a[0,0] #依舊是tensor)
a[0,0].item() # python float
Tensor類型
Tensor有不同的數據類型,如表3-3所示,每種類型分別對應有CPU和GPU版本(HalfTensor除外)。默認的tensor是FloatTensor,可通過t.set_default_tensor_type
來修改默認tensor類型(如果默認類型爲GPU tensor,則所有操作都將在GPU上進行)。Tensor的類型對分析內存佔用很有幫助。例如對於一個size爲(1000, 1000, 1000)的FloatTensor,它有1000*1000*1000=10^9
個元素,每個元素佔32bit/8 = 4Byte內存,所以共佔大約4GB內存/顯存。HalfTensor是專門爲GPU版本設計的,同樣的元素個數,顯存佔用只有FloatTensor的一半,所以可以極大緩解GPU顯存不足的問題,但由於HalfTensor所能表示的數值大小和精度有限1,所以可能出現溢出等問題。
tensor數據類型
Data type | dtype | CPU tensor | GPU tensor |
---|---|---|---|
32-bit floating point | torch.float32 or torch.float |
torch.FloatTensor |
torch.cuda.FloatTensor |
64-bit floating point | torch.float64 or torch.double |
torch.DoubleTensor |
torch.cuda.DoubleTensor |
16-bit floating point | torch.float16 or torch.half |
torch.HalfTensor |
torch.cuda.HalfTensor |
8-bit integer (unsigned) | torch.uint8 |
torch.ByteTensor |
torch.cuda.ByteTensor |
8-bit integer (signed) | torch.int8 |
torch.CharTensor |
torch.cuda.CharTensor |
16-bit integer (signed) | torch.int16 or torch.short |
torch.ShortTensor |
torch.cuda.ShortTensor |
32-bit integer (signed) | torch.int32 or torch.int |
torch.IntTensor |
torch.cuda.IntTensor |
64-bit integer (signed) | torch.int64 or torch.long |
torch.LongTensor |
torch.cuda.LongTensor |
各數據類型之間可以互相轉換,type(new_type)
是通用的做法,同時還有float
、long
、half
等快捷方法。CPU tensor與GPU tensor之間的互相轉換通過tensor.cuda
和tensor.cpu
方法實現,此外還可以使用tensor.to(device)
。Tensor還有一個new
方法,用法與t.Tensor
一樣,會調用該tensor對應類型的構造函數,生成與當前tensor類型一致的tensor。torch.*_like(tensora)
可以生成和tensora
擁有同樣屬性(類型,形狀,cpu/gpu)的新tensor。 tensor.new_*(new_shape)
新建一個不同形狀的tensor。
數學運算
in place操作(是否修改自身數據)
對tensor的操作又可分爲兩類:
- 不會修改自身的數據,如
a.add(b)
, 加法的結果會返回一個新的tensor。 - 會修改自身的數據,如
a.add_(b)
, 加法的結果仍存儲在a中,a被修改了。
逐元素操作
這部分操作會對tensor的每一個元素(point-wise,又名element-wise)進行操作,此類操作的輸入與輸出形狀一致。常用的操作如表3-4所示。
表3-4: 常見的逐元素操作
函數 | 功能 |
---|---|
abs/sqrt/div/exp/fmod/log/pow… | 絕對值/平方根/除法/指數/求餘/求冪… |
cos/sin/asin/atan2/cosh… | 相關三角函數 |
ceil/round/floor/trunc | 上取整/四捨五入/下取整/只保留整數部分 |
clamp(input, min, max) | 超過min和max部分截斷 |
sigmod/tanh… | 激活函數 |
對於很多操作,例如div、mul、pow、fmod等,PyTorch都實現了運算符重載,所以可以直接使用運算符。如a ** 2
等價於torch.pow(a,2)
, a * 2
等價於torch.mul(a,2)
。
其中clamp(x, min, max)
的輸出滿足以下公式:
KaTeX parse error: Expected '}', got 'EOF' at end of input: …ax\\\end{cases}yi=⎩⎪⎨⎪⎧min,xi,max,if xi<minif min≤xi≤maxif xi>max
clamp
常用在某些需要比較大小的地方,如取一個tensor的每個元素與另一個數的較大值。
歸併操作
此類操作會使輸出形狀小於輸入形狀,並可以沿着某一維度進行指定操作。如加法sum
,既可以計算整個tensor的和,也可以計算tensor中每一行或每一列的和。常用的歸併操作如表3-5所示。
表3-5: 常用歸併操作
函數 | 功能 |
---|---|
mean/sum/median/mode | 均值/和/中位數/衆數 |
norm/dist | 範數/距離 |
std/var | 標準差/方差 |
cumsum/cumprod | 累加/累乘 |
以上大多數函數都有一個參數**dim
**,用來指定這些操作是在哪個維度上執行的。關於dim(對應於Numpy中的axis)的解釋衆說紛紜,這裏提供一個簡單的記憶方式:
假設輸入的形狀是(m, n, k)
- 如果指定dim=0,輸出的形狀就是(1, n, k)或者(n, k)
- 如果指定dim=1,輸出的形狀就是(m, 1, k)或者(m, k)
- 如果指定dim=2,輸出的形狀就是(m, n, 1)或者(m, n)
size中是否有"1",取決於參數keepdim
,keepdim=True
會保留維度1
。注意,以上只是經驗總結,並非所有函數都符合這種形狀變化方式,如cumsum
。
比較
比較函數中有一些是逐元素比較,操作類似於逐元素操作,還有一些則類似於歸併操作。常用比較函數如表3-6所示。
表3-6: 常用比較函數
函數 | 功能 |
---|---|
gt/lt/ge/le/eq/ne | 大於/小於/大於等於/小於等於/等於/不等 |
topk | 最大的k個數 |
sort | 排序 |
max/min | 比較兩個tensor最大最小值 |
表中第一行的比較操作已經實現了運算符重載,因此可以使用a>=b
、a>b
、a!=b
、a==b
,其返回結果是一個ByteTensor
,可用來選取元素。max/min這兩個操作比較特殊,以max來說,它有以下三種使用情況:
- t.max(tensor):返回tensor中最大的一個數
- t.max(tensor,dim):指定維上最大的數,返回tensor和下標
- t.max(tensor1, tensor2): 比較兩個tensor相比較大的元素
至於比較一個tensor和一個數,可以使用clamp函數。
線性代數
PyTorch的線性函數主要封裝了Blas和Lapack,其用法和接口都與之類似。常用的線性代數函數如表3-7所示。
表3-7: 常用的線性代數函數
函數 | 功能 |
---|---|
trace | 對角線元素之和(矩陣的跡) |
diag | 對角線元素 |
triu/tril | 矩陣的上三角/下三角,可指定偏移量 |
mm/bmm | 矩陣乘法,batch的矩陣乘法 |
addmm/addbmm/addmv/addr/badbmm… | 矩陣運算 |
t | 轉置 |
dot/cross | 內積/外積 |
inverse | 求逆矩陣 |
svd | 奇異值分解 |
具體使用說明請參見官方文檔2,需要注意的是,矩陣的轉置會導致存儲空間不連續,需調用它的.contiguous
方法將其轉爲連續。
GPU/CPU
tensor可以很隨意的在gpu/cpu上傳輸。使用tensor.cuda(device_id)
或者tensor.cpu()
。另外一個更通用的方法是tensor.to(device)
。
- 儘量使用
tensor.to(device)
, 將device
設爲一個可配置的參數,這樣可以很輕鬆的使程序同時兼容GPU和CPU - 數據在GPU之中傳輸的速度要遠快於內存(CPU)到顯存(GPU), 所以儘量避免頻繁的在內存和顯存中傳輸數據。
a = t.randn(3, 4)
a.device
device(type='cpu')
if t.cuda.is_available():
a = t.randn(3,4, device=t.device('cuda:1'))
# 等價於
# a.t.randn(3,4).cuda(1)
# 但是前者更快
a.device
device = t.device('cpu')
a.to(device)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
</div>