學了本文你能學到什麼?僅供學習,如有疑問,請留言。。。
注:紅色是小結, 紫色是重點,能像被課文一樣背誦,初次系統性的學習,請多多指點
目錄
在PyTorch中,torch.Tensor是存儲和變換數據的主要工具。
如果你之前用過NumPy,你會發現Tensor和NumPy的多維數組非常類似。
由於,Tensor提供GPU計算和自動求梯度等更多功能,這些使Tensor更加適合深度學習。
""
"""
請注意:
"tensor"這個單詞一般可譯作“張量”,張量可以看作是一個多維數組。
標量可以看作是0維張量,向量可以看作1維張量,矩陣可以看作是二維張量。
2.2.1 創建Tensor
# todo 我們先介紹Tensor的最基本功能,即Tensor的創建。
# 首先導入PyTorch:
import torch
# 然後我們創建一個5x3的未初始化的Tensor:
x1 = torch.empty(5, 3)
# print(x1)
# 創建一個5x3的隨機初始化的Tensor:
x2 = torch.rand(5, 3)
# print(x2)
# 創建一個5x3的long型全0的Tensor:
x3 = torch.zeros(5, 3, dtype = torch.long)
# print(x3)
#還可以直接根據數據創建:
x4 = torch.tensor([5.5,3])
# print(x4)
# 還可以通過現有的Tensor來創建,此方法會默認重用輸入Tensor的一些屬性,例如數據類型,除非自定義數據類型。
x5 = x4.new_ones(5, 3, dtype=torch.float64) # # 返回的tensor默認具有相同的torch.dtype和torch.device
"""
補充
#torch.dtype -----torch.dtype是表示torch.Tensor的數據類型的對象。PyTorch有八種不同的數據類型
x = torch.Tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# print(x.type()) #torch.FloatTensor
#torch.device ------torch.device代表將torch.Tensor分配到的設備的對象。
# print(torch.device("cpu"))
#torch.device ------torch.layout表示torch.Tensor內存佈局的對象。 torch.strided代表密集張量,是最常用的內存佈局。
l = torch.Tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# print(l.stride())
"""
# print(x5)
x6 = torch.randn_like(x5, dtype=torch.float) # 指定新的數據類型
# print(x6)
# 我們可以通過shape或者size()來獲取Tensor的形狀:
# print(x6.size()) #torch.Size([5, 3])
# print(type(x6.size())) #<class 'torch.Size'>
# print(x6.shape) #torch.Size([5, 3])
# print(type(x6.shape)) #<class 'torch.Size'>
"""
常用的函數可以創建Tensor
這些創建方法都可以在創建的時候指定數據類型dtype和存放device(cpu/gpu)。
"""
"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%鹹魚分割線%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
# 2.2.2 操作
"算術操作"
"""
#在PyTorch中,同一種操作可能有很多種形式,
# 下面用加法作爲例子。
# 加法形式一
y = torch.rand(5, 3)
# print(x6 + y )
# 加法形式二
print(torch.add(x6, y))
# 還可以指定輸出
result = torch.empty(5, 3)
torch.add(x6, y, out=result)
print(result)
# 加法形式三、inplace
y.add_(x6)
print(y)
# 注:PyTorch操作inplace版本都有後綴_, 例如x.copy_(y), x.t_()
輸出::
tensor([[-0.3456, 0.6993, -0.8920],
[ 0.5509, 0.0353, -0.2075],
[-0.0784, -0.0182, -0.5664],
[-2.2738, -0.1742, -0.1791],
[-0.4831, -0.6773, 0.4412]])
tensor([[-0.2199, 0.7551, -0.1112],
[ 0.6695, 0.1825, 0.5941],
[ 0.3049, 0.1702, -0.4085],
[-1.6745, -0.0533, 0.7709],
[ 0.4048, -0.5268, 0.8098]])
tensor([[-0.2199, 0.7551, -0.1112],
[ 0.6695, 0.1825, 0.5941],
[ 0.3049, 0.1702, -0.4085],
[-1.6745, -0.0533, 0.7709],
[ 0.4048, -0.5268, 0.8098]])
tensor([[-0.2199, 0.7551, -0.1112],
[ 0.6695, 0.1825, 0.5941],
[ 0.3049, 0.1702, -0.4085],
[-1.6745, -0.0533, 0.7709],
[ 0.4048, -0.5268, 0.8098]])
"""
"索引:"
"""
我們還可以使用類似NumPy的索引操作來訪問Tensor的一部分,
需要注意的是:索引出來的結果與原數據共享內存,也即修改一個,另一個會跟着修改。
y = x6[0, :]
y += 1
print(y)
print(x6[0,:])
輸出結果:
tensor([[-1.4177, 0.1480, -0.2594],
[ 1.9609, 0.1357, 2.1043],
[-1.1173, -0.0053, 0.0223],
[ 0.8649, -1.0293, 1.1089],
[-0.0630, 0.1328, 0.0994]])
tensor([-0.4177, 1.1480, 0.7406])
tensor([-0.4177, 1.1480, 0.7406])
除了常用的索引選擇數據之外,PyTorch還提供了一些高級的選擇函數:
"改變形狀"
# 用view()來改變Tensor的形狀:
y = x6.view(15)
z = x6.view(-1, 5) #z = x6.view(3, 5) # -1所指的維度可以根據其他維度的值推出來
print(x6.size(), y.size(), z.size())
運行結果:
tensor([[ 2.0844e-01, 7.9637e-01, 3.3590e+00],
[-1.0522e+00, 1.3039e+00, 4.2128e-01],
[ 9.3867e-01, 1.8803e+00, -8.0998e-01],
[-1.7901e+00, 3.7803e-01, -2.9662e-03],
[ 7.3772e-02, -1.0128e+00, -1.1299e+00]])
torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
# 注意view()返回的新Tensor與原Tensor雖然可能有不同的size,但是是共享data的,也即更改其中的一個,另外一個也會跟着改變。
# (顧名思義,view僅僅是改變了對這個張量的觀察角度,內部數據並未改變)
x6 += 1
print(x6)
print(y)
運行結果:
tensor([[ 1.2084, 1.7964, 4.3590],
[-0.0522, 2.3039, 1.4213],
[ 1.9387, 2.8803, 0.1900],
[-0.7901, 1.3780, 0.9970],
[ 1.0738, -0.0128, -0.1299]])
tensor([ 1.2084, 1.7964, 4.3590, -0.0522, 2.3039, 1.4213, 1.9387, 2.8803,
0.1900, -0.7901, 1.3780, 0.9970, 1.0738, -0.0128, -0.1299])
# 思考:所以如果我們想返回一個真正新的副本(即不共享data內存)該怎麼辦呢?
# Pytorch還提供了一個reshape()可以改變形狀,但是此函數並不能保證返回的是其拷貝,所以不推薦使用。推薦先用clone創造一個副本然後再使用view
x_cp = x6.clone().view(15)
x6 -= 1
print(x6)
print(x_cp)
運行結果:
tensor([[ 2.0844e-01, 7.9637e-01, 3.3590e+00],
[-1.0522e+00, 1.3039e+00, 4.2128e-01],
[ 9.3867e-01, 1.8803e+00, -8.0998e-01],
[-1.7901e+00, 3.7803e-01, -2.9662e-03],
[ 7.3772e-02, -1.0128e+00, -1.1299e+00]])
tensor([ 1.2084, 1.7964, 4.3590, -0.0522, 2.3039, 1.4213, 1.9387, 2.8803,
0.1900, -0.7901, 1.3780, 0.9970, 1.0738, -0.0128, -0.1299])
# 使用clone還有一個好處是會被記錄在計算圖中,即梯度回傳到副本時也會傳到源Tensor。
x = torch.randn(1)
print(x)
print(x.item())
運行結果:
tensor([0.0796])
0.07955377548933029
"""
"線性代數"
"""
# PyTorch還支持一些線性函數,這裏提一下,免得用起來的時候自己造輪子
# 2.2.3 廣播機制
當對兩個形狀不同的Tensor按元素運算時,可能會觸發廣播(broadcasting)機制:先適當複製元素使這兩個Tensor形狀相同後再按元素運算。
# 例如:
x = torch.arange(1, 3).view(1, 2)
print(x)
y = torch.arange(1, 4).view(3, 1)
print(y)
print(x + y)
輸出結果:
tensor([[1, 2]])
tensor([[1],
[2],
[3]])
tensor([[2, 3],
[3, 4],
[4, 5]])
由於x和y分別是1行2列和3行1列的矩陣,如果要計算x + y,那麼x中第一行的2個元素被廣播(複製)到了第二行和第三行,而y中第一列的3個元素被廣播(複製)到了第二列。如此,就可以對2個3行2列的矩陣按元素相加。
"""
2.2.4 運算的內存開銷
前面說了,索引操作是不會開闢新內存的,而像y = x + y
這樣的運算是會新開內存的,然後將y
指向新內存。
"""
x = torch.tensor([1, 2])
print(x)
y = torch.tensor([3, 4])
id_before = id(y)
# y = y + x
s= torch.add(x, y, out=y)
print(s)
print(y)
print(id(y) == id_before) # False
"""
如果想指定結果到原來的y
的內存,我們可以使用前面介紹的索引來進行替換操作。
在下面的例子中,我們把x + y
的結果通過[:]
寫進y
對應的內存中。
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
y[:] = y + x
print(id(y) == id_before) # True
我們還可以使用運算符全名函數中的out
參數或者自加運算符+=
(也即add_()
)達到上述效果,
x = torch.tensor([1, 2])
y = torch.tensor([3, 4])
id_before = id(y)
torch.add(x, y, out=y) # y += x, y.add_(x)
print(id(y) == id_before) # True
注:雖然view
返回的Tensor
與源Tensor
是共享data
的,但是依然是一個新的Tensor
(因爲Tensor
除了包含data
外還有一些其他屬性),二者id(內存地址)並不一致。
# 2.2.5 Tensor和NumPy相互轉換
"""
#我們很容易用.numpy()和torch.from_numpy()將Tensor和NumPy中的數組相互轉換。
# 但是需要注意的一點是: 這兩個函數所產生的的Tensor和NumPy中的數組共享相同的內存(所以他們之間的轉換很快),改變其中一個時另一個也會改變!!!
#Tensor轉NumPy
a = torch.ones(5)
b = a.numpy()
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)
# NumPy數組轉Tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)
# 此外上面提到還有一個常用的方法就是直接用torch.tensor()將NumPy數組轉換成Tensor,
需要注意的是該方法總是會進行數據拷貝,返回的Tensor和原來的數據不再共享內存。
輸出結果:
tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
[1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
[3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
"""
# 2.2.6 Tensor on GPU
"""
用方法to()可以將Tensor在CPU和GPU(需要硬件支持)之間相互移動。
# 以下代碼只有在PyTorch GPU版本上纔會執行
if torch.cuda.is_available():
device = torch.device("cuda") # GPU
y = torch.ones_like(x, device=device) # 直接創建一個在GPU上的Tensor
x = x.to(device) # 等價於 .to("cuda")
z = x + y
print(z)
print(z.to("cpu", torch.double)) # to()還可以同時更改數據類型
"""
# 此節知識的補充 https://www.jianshu.com/p/7dbfc7076e5a