Pytorch學習筆記(二)——Tensor


一、對Tensor的操作

從接口的角度講,對Tensor的操作可以分為兩類:

(1)torch.function (2)tensor.function

比如torch.sum(a, b)實際上和a.sum(b)功能等價。

從存儲的角度講,對Tensor的操作也可以分為兩類:

(1)不修改自身數據,如a.add(b),加法結果返回一個新的tensor;

(2)修改自身數據,如a.add_(b),加法結果仍存在a中,a被改變。

函數名以_結尾的稱為inplace方式。

二、Tensor的創建

常見的新建tensor的方法:

Tensor(*sizes) 基礎構造函數,不分配空間,未初始化

ones(*sizes) 全1的tensor

zeros(*sizes) 全0的tensor

eye(*sizes) 對角線為1,其余為0,不要求行列數相同

arange(s, e, step) 從s到e,步長為step

linspace(s, e, steps) 從s到e,均勻切分成steps份

rand/randn(*sizes) 均勻/標准分布

normal(mean, std)/uniform(from, to) 正態分布/均勻分布

randperm(m) 隨機排列

 

range(start, end, step),返回一個list對象也就是range.object,起始值為start,終止值為end,但不含終止值,步長為step。只能創建int型list。range的step不能是浮點型。

arange(start, end, step),與range()類似,也不含終止值。但是返回一個array對象。需要導入numpy模塊(import numpy as np或者from numpy import*),arange可以使用浮點型數據。

 

Tensor既可以接收一個list,也可以根據指定形狀新建tensor,還可以傳入其他的tensor。

tensor.size()等價於tensor.shape()。

a = t.Tensor(2, 3)
b = t.Tensor([[1, 2, 3], [4, 5, 6]])
print(b.tolist()) # 把tensor轉為list
print(b.size()) # 形狀
print(b.numel()) # 元素總個數
c = t.Tensor(b.size())

通過tensor.view方法可以調整tensor的形狀,但要保證調整前后元素總數一致。view不會更改自身數據,但是新的tensor與原tensor共享內存。也就是說更改其中一個,另一個也會跟着改變。

如果要添加或減少某一維度,就需要通過squeeze和unsqueeze函數。

a = t.arange(0, 6)
print(a.view(2, 3))
b = a.view(-1, 3) # 當某一維為-1時,系統自動計算大小
print(b)
print(b.unsqueeze(1)) # 在第一維度上增加一維
print(b.unsqueeze(-2)) # 在倒數第二維上增加一維

運行結果:

tensor([[0, 1, 2],
        [3, 4, 5]])
tensor([[0, 1, 2],
        [3, 4, 5]])
tensor([[[0, 1, 2]],

        [[3, 4, 5]]])
tensor([[[0, 1, 2]],

        [[3, 4, 5]]])
a = t.arange(0, 6)
print(a.view(2, 3))
b = a.view(-1, 3) # 當某一維為-1時,系統自動計算大小
c = b.view(1, 1, 1, 2, 3)
print(c.squeeze(0).size())
print(c.squeeze().size()) # 將所有維度為1的壓縮

運行結果:

tensor([[0, 1, 2],
        [3, 4, 5]])
torch.Size([1, 1, 2, 3])
torch.Size([2, 3])

resize也可以用來調整tensor的大小。如果新尺寸超過了原始尺寸,那么會自動分配新的內存空間;而如果新尺寸小於原尺寸,則之前的數據依舊會保留。

a = t.arange(0, 6)
b = a.view(-1, 3) # 當某一維為-1時,系統自動計算大小
a[1] = 100 # 由於a與b共享內存,所以a改變時b也會改變
b.resize_(1, 3)
print(b)
print(b.resize_(3, 3)) # 之前的數據並沒有因為上一步的resize操作丟失

運行結果:

tensor([[  0, 100,   2]])
tensor([[                  0,                 100,                   2],
        [                  3,                   4,                   5],
        [  31244186271350784, 3775491366621020160, 5936959698761888869]])

Tensor支持與numpy.ndarray相似的索引操作。索引結果與原tensor共享內存。注意以下兩種索引的區別:

a = t.randn(3, 4)
print(a[0 : 1, : 2].size())
print(a[0, : 2].size())

運行結果:

torch.Size([1, 2])
torch.Size([2])

選擇產生的結果與原tensor不共享內存空間。

torch.FloatTensor(2,3) 構建一個2*3 Float類型的張量

torch.DoubleTensor(2,3) 構建一個2*3 Double類型的張量

torch.ByteTensor(2,3) 構建一個2*3 Byte類型的張量

torch.CharTensor(2,3) 構建一個2*3 Char類型的張量

torch.ShortTensor(2,3) 構建一個2*3 Short類型的張量

torch.IntTensor(2,3) 構建一個2*3 Int類型的張量

torch.LongTensor(2,3) 構建一個2*3 Long類型的張量

torch.Tensor是默認的tensor類型(torch.FlaotTensor)的簡稱。

三、tensor數據類型轉換

torch.long() 將tensor轉換為long類型

torch.half() 將tensor轉換為半精度浮點類型

torch.int() 將該tensor轉換為int類型

torch.double() 將該tensor轉換為double類型

torch.float() 將該tensor轉換為float類型

torch.char() 將該tensor轉換為char類型

torch.byte() 將該tensor轉換為byte類型

torch.short() 將該tensor轉換為short類型

幾種數據類型轉換的方式如下:

a = t.Tensor(2, 3)
b = a.float()
c = a.type_as(b)
d = a.new(2, 3)

四、選擇

 

gather操作

對於一個二維tensor,輸出的每個元素如下:

out[i][j] = input[index[i][j]][j] # dim = 0
out[i][j] = input[i][index[i][j]] # dim = 1
a = t.arange(0, 16).view(4, 4)
index = t.LongTensor([[0, 1, 2, 3]])
print(a.gather(0, index)) # 提取對角線元素
index = t.LongTensor([[3, 2, 1, 0]]).t() # 針對2D tensor轉置
print(a.gather(1, index)) # 提取反對角線元素
index = t.LongTensor([[3, 2, 1, 0]])
print(a.gather(0, index)) # 提取反對角線元素

運行結果:

tensor([[ 0,  5, 10, 15]])
tensor([[ 3],
        [ 6],
        [ 9],
        [12]])
tensor([[12,  9,  6,  3]])

gather的逆操作是scatter_,gather是把數據從input中按照index取出,而scatter_是把取出的數據再放回去。

高級索引

x[[1, 2], [1, 2], [2, 0]] # x[1, 1, 2]和x[2, 2, 0]
x[[2, 1, 0], [0], [1]] # x[2, 0, 1], x[1, 0, 1], x[0, 0, 1]
x[[0, 2], ...] # x[0]和x[2]

五、逐元素操作

 

其中clamp實現的功能如下:

 

六、歸並操作

使輸出形狀小於輸入形狀,並沿着某一維度進行指定操作。比如加法sum,既可以計算整個tensor的和,也可以計算tensor中每一行或者每一列的和。常用的歸並操作如下:

 

以上函數幾乎都有一個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。其中keepdim默認值為False。

七、比較操作

 

t.max(tensor) 返回tensor中最大的一個數

t.max(tensor, dim) 指定維度上的最大的數,返回tensor和下標

t.max(tensor1, tensor2) 比較兩個tensor相比較大的元素

如果要比較一個tensor和一個數字,可以使用clamp函數。

a = t.linspace(0, 15, 6).view(2, 3)
b = t.linspace(15, 0, 6).view(2, 3)
print('a=', a)
print('b=', b)
print('a[a > b]=', a[a > b])
print('t.max(a)=', t.max(a))
print('t.max(b, dim = 1)=', t.max(b, dim = 1))
print('t.max(a, b)=', t.max(a, b))

運行結果:

a= tensor([[ 0.,  3.,  6.],
        [ 9., 12., 15.]])
b= tensor([[15., 12.,  9.],
        [ 6.,  3.,  0.]])
a[a > b]= tensor([ 9., 12., 15.])
t.max(a)= tensor(15.)
t.max(b, dim = 1)= (tensor([15.,  6.]), tensor([0, 0]))
t.max(a, b)= tensor([[15., 12.,  9.],
        [ 9., 12., 15.]])

八、線性代數

pytorch的線性函數主要封裝了Blas和Lapack。常用的線性代數函數如下:

 

矩陣的轉置會導致存儲空間不連續,需要調用它的.contiguous方法將其轉為連續。

九、Tensor和Numpy

numpy和tensor共享內存。當輸入數組的某個維度的長度為1時,計算時沿此維度復制擴充成一樣的形狀。

廣播的手動實現:

(1)unsqueeze或view:為數據某一維的形狀補1;

(2)expand或expand_as:重復數組。不會占用額外的空間(repeat會復制多份數據,所以會占用額外空間)。

Numpy的廣播法則:

(1)所有輸入數組都向shape最長的數組看齊,shape中不足的部分通過在前面加1維補齊;

(2)兩個數組要么在某一個維度上的長度一致,要么其中一個為1,否則不能計算。

盡量使用向量化計算,少用for循環。

大多數t.function都有一個參數out,產生的結果可以保存在out指定的tensor中。

t.set_printoptions可以用來設置打印tensor時的數值精度和格式。

十、練習:線性回歸

代碼:

import torch.nn as nn
import torch as t
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim
import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage
from matplotlib import pyplot as plt

# 設置隨機數種子,保證在不同的計算機上運行時產生的輸出一致
t.manual_seed(1000)

def get_fake_data(batch_size = 8):
    # 產生隨機數據,加入噪聲
    x = t.rand(batch_size, 1) * 20
    y = x * 2 + (1 + t.randn(batch_size, 1)) * 3
    return x, y

x, y = get_fake_data()

# 隨機初始化參數
w = t.rand(1, 1)
b = t.zeros(1, 1)

lr = 0.001

for ii in range(20000):
    # forward
    y_pred = x.mm(w) + b.expand_as(y)
    loss = 0.5 * (y_pred - y) ** 2
    loss = loss.sum()

    # backward
    dloss = 1
    dy_pred = dloss * (y_pred - y)

    dw = x.t().mm(dy_pred)
    db = dy_pred.sum()

    # update parameters
    w.sub_(lr * dw)
    b.sub_(lr * db)

x2 = t.arange(0, 20, 1.0).view(-1, 1)
y2 = x2.mm(w) + b

plt.plot(x2.numpy(), y2.numpy())
plt.scatter(x.squeeze().numpy(), y.squeeze().numpy())
plt.show()

運行結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM