介紹
相比TensorFlow的靜態(tài)圖開發(fā)阅仔,Pytorch的動態(tài)圖特性使得開發(fā)起來更加人性化映凳,選擇Pytorch的理由可以參考:http://www.reibang.com/p/c1d9cdb52548颊亮,這里也順便介紹一下TensorFlow靜態(tài)圖和Pytorch動態(tài)圖開發(fā)的區(qū)別:
總的來說糟港,在TensorFlow里你只能通過定義數(shù)據(jù)沮脖、網(wǎng)絡(luò)等,然后直接訓(xùn)練拍埠、預(yù)測啥的失驶,中間過程到底發(fā)生了什么對我們來說都是未知的,只能等待訓(xùn)練完畢后查看結(jié)果如何枣购,想要debug都debug不了嬉探,于是在學(xué)了一段時間以后還是一臉懵:訓(xùn)練的過程到底發(fā)生了什么?數(shù)據(jù)怎么變化的棉圈?
直到接觸了pytorch框架以后涩堤,這些疑問就漸漸地減少了,因為在pytorch里分瘾,一切就像操作numpy數(shù)組一樣(很多方法名甚至都一樣)胎围,只是類型變成了張量,如何訓(xùn)練德召、訓(xùn)練多少次等等一切都是我們自己來決定白魂,過程發(fā)生了什么?print或者debug一下就知道了
拿最簡單的線性擬合舉例上岗,通過pytorch操作福荸,你會發(fā)現(xiàn)整個擬合的過程就是:提供輸入和輸出值,然后網(wǎng)絡(luò)層(假如就一個全連接層)就相當(dāng)于一個矩陣(這里暫時忽略偏置值肴掷,原本全連接層的計算是與矩陣相乘后再加上一個偏置值)敬锐,里面的參數(shù)(這里稱為權(quán)值)隨著每一次計算,再根據(jù)求導(dǎo)之類的操作不斷更新參數(shù)的值捆等,最終使得輸入的值與這個矩陣相乘后的值不斷貼近于輸出值滞造,而這計算和修改權(quán)值的過程就稱為訓(xùn)練。
比如一個函數(shù)y = 2x + 1
栋烤,然后定義一個1x1
的矩陣(全連接層),然后矩陣里的權(quán)值一開始都是隨機的挺狰,比如((1))
明郭,那么要擬合這個函數(shù)的話,最終理想的結(jié)果肯定是傳入x
丰泊,輸出的結(jié)果是2x + 1
薯定,所以目標就是讓一個數(shù)與這個矩陣相乘盡量等價于這個數(shù)和2相乘后再加1,也就是說最終x乘以矩陣和2x
的差最好等于0瞳购,比如在x=-1
時话侄,y=-1
,那么矩陣的權(quán)值理想結(jié)果就是1。但是當(dāng)結(jié)果越來越大以后年堆,會發(fā)現(xiàn)y/x
的值逐漸與2相近吞杭,所以最終矩陣經(jīng)過多次訓(xùn)練以后,結(jié)果肯定就是2左右的數(shù)变丧,而剛才說的這些可以通過一段pytorch代碼來演示:
import torch
x = torch.unsqueeze(torch.linspace(-100, 100, 1000), dim=1)
# 生成-100到100的1000個數(shù)的等差數(shù)列
y = 2*x + 1
# 定義y=2x+1函數(shù)
matrix = torch.nn.Linear(1, 1)
# 定義一個1x1的矩陣
optimizer = torch.optim.Adam(matrix.parameters(), lr=0.1)
# 使用優(yōu)化器求導(dǎo)更新矩陣權(quán)重
for _ in range(100):
# 訓(xùn)練100次
value = matrix(x)
# value是x與矩陣相乘后的值
score = torch.mean((value - y) ** 2)
# 目標偏差芽狗,值為(value-y)的平方取均值,越接近0說明結(jié)果越準確
matrix.zero_grad()
score.backward()
optimizer.step()
# 根據(jù)求導(dǎo)結(jié)果更新權(quán)值
print("第{}次訓(xùn)練權(quán)值結(jié)果:{}痒蓬,結(jié)果偏差:{}".format(_, matrix.weight.data.numpy(), score))
# 輸出結(jié)果:
# 第0次訓(xùn)練權(quán)值結(jié)果:[[0.9555]],結(jié)果偏差:4377.27294921875
# ...
# 第99次訓(xùn)練權(quán)值結(jié)果:[[2.0048]],結(jié)果偏差:0.10316929966211319
從這段代碼的結(jié)果可以看到最開始權(quán)值初始值為0.9555酿雪,偏差為4377.27294921875奏瞬,經(jīng)過100次訓(xùn)練后,權(quán)值為2.0048鲁捏,偏差為0.1那樣捕透,從而可以證實我們前面的思路是對的。
其實上面的代碼里不止更新了權(quán)值碴萧,也更新了偏置值bias
乙嘀,只不過這里為了更加簡單的解釋,而沒有進行說明破喻,可以通過matrix.bias
可以調(diào)用查看虎谢,最終會發(fā)現(xiàn)偏置值接近1左右(假設(shè)是0.99),而偏置值就是在與矩陣相乘后加上的值曹质,所以可以看出通過訓(xùn)練以后婴噩,矩陣和偏置擬合成了函數(shù):y = 2.0048x + 0.99
。
上面介紹的是線性擬合的例子羽德,即數(shù)學(xué)相關(guān)的示例几莽,而放在生活應(yīng)用當(dāng)中,其就可以理解成擬合一個特定要求的函數(shù)宅静,比如識別圖片中人臉的場景中就是:
即輸入圖片到函數(shù)當(dāng)中章蚣,輸出的就是人臉對應(yīng)的坐標,而我們的目標就是要實現(xiàn)這個函數(shù)(經(jīng)過不斷訓(xùn)練姨夹,修正模型中的參數(shù))
通過上面的介紹纤垂,想必你應(yīng)該對pytorch以及深度學(xué)習(xí)有了一點入門的了解了,而本文接下來也將針對于pytorch的基本使用作出介紹
安裝
分為CPU和GPU版本:
CPU版本下載:
直接pip下載容易出問題磷账,因此這里推薦離線下載:
1.進入網(wǎng)址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pytorch或者https://download.pytorch.org/whl/torch_stable.html峭沦,下載對應(yīng)版本的pytorch
2.pip安裝對應(yīng)的下載文件GPU下載:
1.先安裝cuda:https://developer.nvidia.com/cuda-downloads
2.下載并安裝GPU版本pytorch文件
簡單示例-線性擬合
import torch
import matplotlib.pyplot as plt
w = 2
b = 1
noise = torch.rand(100, 1)
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
# 因為輸入層格式要為(-1, 1),所以這里將(100)的格式轉(zhuǎn)成(100, 1)
y = w*x + b + noise
# 擬合分布在y=2x+1上并且?guī)в性肼暤纳Ⅻc
model = torch.nn.Sequential(
torch.nn.Linear(1, 16),
torch.nn.Tanh(),
torch.nn.Linear(16, 1),
)
# 自定義的網(wǎng)絡(luò)逃糟,帶有2個全連接層和一個tanh層
loss_fun = torch.nn.MSELoss()
# 定義損失函數(shù)為均方差
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 使用adam作為優(yōu)化器更新網(wǎng)絡(luò)模型的權(quán)重吼鱼,學(xué)習(xí)率為0.01
plt.ion()
# 圖形交互
for _ in range(1000):
ax = plt.axes()
output = model(x)
# 數(shù)據(jù)向后傳播(經(jīng)過網(wǎng)絡(luò)層的一次計算)
loss = loss_fun(output, y)
# 計算損失值
# print("before zero_grad:{}".format(list(model.children())[0].weight.grad))
# print("-"*100)
model.zero_grad()
# 優(yōu)化器清空梯度
# print("before zero_grad:{}".format(list(model.children())[0].weight.grad))
# print("-"*100)
# 通過注釋地方可以對比發(fā)現(xiàn)執(zhí)行zero_grad方法以后倒數(shù)梯度將會被清0
# 如果不清空梯度的話蓬豁,則會不斷累加梯度,從而影響到當(dāng)前梯度的計算
loss.backward()
# 向后傳播菇肃,計算當(dāng)前梯度地粪,如果這步不執(zhí)行,那么優(yōu)化器更新時則會找不到梯度
optimizer.step()
# 優(yōu)化器更新梯度參數(shù)巷送,如果這步不執(zhí)行驶忌,那么因為梯度沒有發(fā)生改變,loss會一直計算最開始的那個梯度
if _ % 100 == 0:
plt.cla()
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), output.data.numpy(), 'r-', lw=5)
plt.text(0.5, 0, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.1)
# print("w:", list(model.children())[0].weight.t() @ list(model.children())[-1].weight.t())
# 通過這句可以查看權(quán)值變化笑跛,可以發(fā)現(xiàn)最后收斂到2附近
plt.ioff()
plt.show()
從上面的示例里不知道你有沒有看出來付魔,其實pytorch的使用和numpy特別相似,只是numpy是基于數(shù)組(numpy.ndarray
)飞蹂,而pytorch是基于張量(torch.Tensor
)几苍,但是在使用上很多都是一樣的,包括很多方法名等陈哑。所以如果學(xué)習(xí)過numpy的話妻坝,會感覺pytorch特別的親切,如果沒學(xué)過的numpy話惊窖,通過學(xué)習(xí)pytorch刽宪,也將順便給你將來的numpy學(xué)習(xí)奠定一定的基礎(chǔ)
數(shù)據(jù)類型
標量/張量
pytorch里的基本單位,個人理解是0維界酒、沒有方向的(比如單個數(shù)字那樣)稱為標量圣拄,有方向的稱為張量(比如一串?dāng)?shù)字),通過torch.tensor
定義毁欣,舉例:
>>> torch.tensor(1.)
tensor(1.)
pytorch中還提供了以下數(shù)據(jù)類型的張量:
通用類型 Tensor
float型 torch.FloatTensor
double型 torch.DoubleTensor
int型 torch.IntTensor
long型 torch.LongTensor
byte型 torch.ByteTensor
這些數(shù)據(jù)類型的張量使用方法和tensor
有點不同庇谆,tensor
是自定義數(shù)據(jù),而標定數(shù)據(jù)類型的將會隨機生成一個該類型的數(shù)據(jù)凭疮,舉例:
>>> torch.tensor(1.)
tensor(1.)
# 生成值為1的張量
>>> torch.FloatTensor(1)
tensor([-3.0147e-21])
# 生成1個隨機的float型張量
>>> torch.FloatTensor(2,3)
tensor([[-7.3660e-21, 4.5914e-41, 9.6495e+20],
[ 8.2817e-43, 0.0000e+00, 0.0000e+00]])
# 生成隨機指定尺寸的float型張量
并且可以發(fā)現(xiàn)如果是大寫開頭的張量都是傳入形狀生成對應(yīng)尺寸的張量饭耳,只有tensor
是傳入自己定義的數(shù)據(jù),舉例:
>>> torch.Tensor(2,3)
tensor([[-7.3660e-21, 4.5914e-41, 0.0000e+00],
[ 0.0000e+00, 0.0000e+00, 0.0000e+00]])
# 創(chuàng)建一個格式為2行3列的張量
>>> torch.tensor(3)
tensor(3)
# 創(chuàng)建一個值為3的張量
當(dāng)然如果希望大寫開頭的張量傳入自己定義的數(shù)據(jù)执解,則傳入一個列表或者數(shù)組寞肖,舉例:
>>> torch.Tensor([2,3])
tensor([2., 3.])
# 創(chuàng)建一個張量,值為自定義的
注1:
上面提供的張量類型是在CPU下的材鹦,如果是在GPU下的逝淹,則在torch.cuda
下,舉例:
float型 torch.cuda.FloatTensor
double型 torch.cuda.DoubleTensor
int型 torch.cuda.IntTensor
long型 torch.cuda.LongTensor
byte型 torch.cuda.ByteTensor
注2:
pytorch框架里沒有提供string
這樣的數(shù)據(jù)類型桶唐,所以為了表示某個標記之類的,我們可以使用one-hot
編碼茉兰,或者使用Embedding
(常用的Word2vec
/glove
)
張量屬性/方法
索引
張量可以像數(shù)組那樣進行索引尤泽,舉例:
>>> a = torch.rand(2,3)
# 生成2行3列隨機數(shù)
>>> a
tensor([[0.2488, 0.2794, 0.2949],
[0.1818, 0.1950, 0.3803]])
>>> a[0]
tensor([0.2488, 0.2794, 0.2949])
# 索引第一行
>>> a[0, 1]
tensor(0.2794)
# 索引第一行第二列
>>> x[:, [0, 2]]
tensor([[0.2488, 0.2949],
[0.1818, 0.3803]])
# 索引第一列和第三列
也可以使用內(nèi)置的index_select
方法進行索引,使用該方法可以索引多個自定義的行列(比如取第1/3/4列),該方法傳入兩個參數(shù)分別為張量維度以及張量中的索引(傳入類型為張量)坯约,舉例:
>>> a = torch.rand(2,3)
>>> a
tensor([[0.7470, 0.8258, 0.5929],
[0.7803, 0.7016, 0.4281]])
>>> a.index_select(0, torch.tensor([1]))
tensor([[0.7803, 0.7016, 0.4281]])
# 對數(shù)據(jù)第1維(整體)取第2個數(shù)據(jù)(第二行)
# 第二個參數(shù)為張量
>>> a.index_select(1, torch.tensor([1, 2]))
tensor([[0.8258, 0.5929],
[0.7016, 0.4281]])
# 對數(shù)據(jù)第1維取2/3列
切片
張量可以像數(shù)組那樣進行切片熊咽,舉例:
>>> a = torch.rand(2,3)
>>> a
tensor([[0.7470, 0.8258, 0.5929],
[0.7803, 0.7016, 0.4281]])
>>> a[0, :2]
tensor([0.7470, 0.8258])
# 第一行前兩列
>>> a[0, ::2]
tensor([0.7470, 0.5929])
# 第一行從第一個起隔兩個取一個,因為這里只有3個闹丐,所以取第一列和第三列
還有...
代表對這部分取全部横殴,舉例:
>>> a = torch.rand(2,2,2,2)
>>> a
tensor([[[[0.0882, 0.8744],
[0.9916, 0.6415]],
[[0.7247, 0.4012],
[0.5703, 0.9776]]],
[[[0.1076, 0.0710],
[0.1275, 0.5045]],
[[0.7833, 0.6519],
[0.3394, 0.2560]]]])
>>> a[1, ..., 1]
tensor([[0.0710, 0.5045],
[0.6519, 0.2560]])
# 相當(dāng)于a[1, :, :, 1]
邏輯操作
可以像數(shù)組那樣進行邏輯操作,舉例:
>>> a = torch.rand(2,3)
>>> a
tensor([[0.7679, 0.1081, 0.3601],
[0.0661, 0.8539, 0.3079]])
>>> a>0.5
tensor([[1, 0, 0],
[0, 1, 0]], dtype=torch.uint8)
# 大于0.5的變成1卿拴,否則變成0
# 在數(shù)組里是變成True或False
shape
獲取張量形狀衫仑,舉例:
>>> a = torch.tensor(1.)
>>> a.shape
torch.Size([])
>>> a = torch.tensor((1., 2.))
>>> a.shape
torch.Size([2])
>>> a = torch.tensor(((1., 2.), (3, 4)))
>>> a
tensor([[1., 2.],
[3., 4.]])
>>> a.shape
torch.Size([2, 2])
>>> a.shape[0]
2
# 一維的尺寸
也可以通過內(nèi)置方法size()
來獲取,舉例:
>>> a = torch.tensor(((1., 2.), (3, 4)))
>>> a.size()
torch.Size([2, 2])
>>> a.size(0)
2
# 一維的尺寸
>>> a.size(1)
2
# 二維的尺寸
data
獲取張量
item()
獲取數(shù)據(jù)堕花,僅當(dāng)只有一個數(shù)據(jù)時才能用
dim()
獲取張量維度文狱,舉例:
>>> a = torch.tensor(1.)
>>> a.dim()
0
# 標量數(shù)據(jù),0維
>>> a = torch.tensor([1.])
>>> a.dim()
1
# 一維張量
>>> a = torch.tensor(((1., 2.), (3, 4)))
>>> a.dim()
2
numel()
獲取張量大小缘挽,舉例:
>>> a = torch.tensor(((1., 2.), (3, 4)))
>>> a.numel()
4
# a里總共有4個數(shù)
type()
獲取張量類型瞄崇,舉例:
>>> x = torch.IntTensor(1)
>>> x
tensor([1065353216], dtype=torch.int32)
>>> x.type()
'torch.IntTensor'
>>> type(x)
<class 'torch.Tensor'>
# 使用內(nèi)置的type函數(shù)只能知道是個torch下的張量
cuda()
CPU數(shù)據(jù)轉(zhuǎn)GPU,舉例:
>>> x = torch.IntTensor(1)
>>> x = x.cuda()
# 轉(zhuǎn)成GPU數(shù)據(jù)壕曼,這條語句得在GPU環(huán)境下才能運行
注:
判斷是否可用GPU可以通過torch.cuda.is_available()
判斷苏研,舉例:
>>> torch.cuda.is_available()
False
下面是一個GPU常用操作:
>>> torch.cuda.device_count()
1
# 獲取可使用GPU數(shù)量
>>> torch.cuda.set_device(0)
# 使用編號為0 的GPU
cpu()
GPU數(shù)據(jù)轉(zhuǎn)CPU,舉例:
>>> a = torch.cuda.FloatTensor(1)
>>> a.cpu()
tensor(1)
注:
上面的GPU和CPU數(shù)據(jù)互換是在低版本的pytorch上使用的腮郊,使用起來可能不太方便摹蘑,之后的版本推出了簡單切換的版本:先通過torch.device()
方法選擇一個GPU/CPU設(shè)備,然后對需要使用該設(shè)備的數(shù)據(jù)通過to()
方法調(diào)用伴榔,舉例:
>>> device = torch.device('cuda:0')
# 選擇GPU設(shè)備
>>> net = torch.nn.Linear(10, 100).to(device)
# 這里定義了一個全連接網(wǎng)絡(luò)層纹蝴,并使用該GPU設(shè)備
view()/reshape()
這兩個方法一樣,修改張量形狀踪少,舉例:
>>> a = torch.rand(2,3)
>>> a
tensor([[0.7824, 0.0911, 0.5798],
[0.4280, 0.2592, 0.4978]])
>>> a.reshape(3, 2)
tensor([[0.7824, 0.0911],
[0.5798, 0.4280],
[0.2592, 0.4978]])
>>> a.view(3, 2)
tensor([[0.7824, 0.0911],
[0.5798, 0.4280],
[0.2592, 0.4978]])
t()
轉(zhuǎn)置操作塘安,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.4620, 0.9787, 0.3998],
[0.4092, 0.1320, 0.5631]])
>>> a.t()
tensor([[0.4620, 0.4092],
[0.9787, 0.1320],
[0.3998, 0.5631]])
>>> a.t().shape
torch.Size([3, 2])
pow()
對張量進行冪運算,也可以用**
代替援奢,舉例:
>>> a = torch.full([2, 2], 3)
>>> a
tensor([[3., 3.],
[3., 3.]])
>>> a.pow(2)
tensor([[9., 9.],
[9., 9.]])
>>> a**2
tensor([[9., 9.],
[9., 9.]])
sqrt()
對張量取平方根兼犯,舉例:
>>> a = torch.full([2, 2], 9)
>>> a
tensor([[9., 9.],
[9., 9.]])
>>> a.sqrt()
tensor([[3., 3.],
[3., 3.]])
>>> a**(0.5)
tensor([[3., 3.],
[3., 3.]])
# 可以看出結(jié)果一樣
rsqrt()
取平方根的倒數(shù),舉例:
>>> a = torch.full([2, 2], 9)
>>> a
tensor([[9., 9.],
[9., 9.]])
>>> a.rsqrt()
tensor([[0.3333, 0.3333],
[0.3333, 0.3333]])
exp()
取e
為底的冪次方集漾,舉例:
>>> a = torch.full([2, 2], 2)
>>> a.exp()
tensor([[7.3891, 7.3891],
[7.3891, 7.3891]])
log()
取log
以e為底的對數(shù)切黔,舉例:
>>> a = torch.full([2, 2], 2)
>>> a.exp()
tensor([[7.3891, 7.3891],
[7.3891, 7.3891]])
>>> a.log()
tensor([[0.6931, 0.6931],
[0.6931, 0.6931]])
對應(yīng)的還有以2為底的log2
、以10為底的log10
等具篇,舉例:
>>> a = torch.full([2, 2], 2)
>>> a.log2()
tensor([[1., 1.],
[1., 1.]])
round()/floor()/ceil()
四舍五入纬霞、向下取整和向上取整
trunc()/frac()
取整數(shù)/小數(shù)部分,舉例:
>>> a = torch.tensor(1.2)
>>> a.trunc()
tensor(1.)
>>> a.frac()
tensor(0.2000)
max()/min()/median()/mean()
取最大值驱显、最小值诗芜、中位數(shù)和平均值瞳抓,舉例:
>>> a = torch.tensor([1., 2., 3., 4., 5.])
>>> a.max()
tensor(5.)
>>> a.min()
tensor(1.)
>>> a.median()
tensor(3.)
>>> a.mean()
tensor(3.)
注意的是該方法默認會將全部數(shù)據(jù)變成一維的,并取整個數(shù)據(jù)里的最大/最小之類的值伏恐,因此可以通過dim
參數(shù)設(shè)置取值維度孩哑,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.0042, 0.5913, 0.0104],
[0.1673, 0.9443, 0.1303]])
>>> a.max()
tensor(0.9443)
# 默認返回總體的最大值
>>> a.max(dim=0)
(tensor([0.1673, 0.9443, 0.1303]), tensor([1, 1, 1]))
# 在第1維選擇,返回每一列最大值翠桦,并且對應(yīng)索引為1,1,1
>>> a.max(dim=1)
(tensor([0.5913, 0.9443]), tensor([1, 1]))
# 在第2維選擇横蜒,返回每一行最大值,并且對應(yīng)索引為1,1
還有一個keepdim
參數(shù)销凑,可以控制返回的格式和之前相同丛晌,舉例:
>>> a = torch.rand(2, 3)
>>> a.max(dim=0, keepdim=True)
(tensor([[0.1673, 0.9443, 0.1303]]), tensor([[1, 1, 1]]))
>>> a.max(dim=1, keepdim=True)
(tensor([[0.5913],
[0.9443]]), tensor([[1],
[1]]))
# 和之前的對比,可以發(fā)現(xiàn)設(shè)置該參數(shù)后格式變成一樣的了
sum()/prod()
求累加闻鉴、累乘茵乱,舉例:
>>> a = torch.tensor([1., 2., 3., 4., 5.])
>>> a.sum()
tensor(15.)
>>> a.prod()
tensor(120.)
該方法也可以使用dim
參數(shù)對某維度進行運算
argmax()/argmin()
返回最大/小值的索引,舉例:
>>> a = torch.tensor([1., 2., 3., 4., 5.])
>>> a.argmax()
tensor(4)
>>> a.argmin()
tensor(0)
該方法同樣默認會將全部數(shù)據(jù)變成一維的孟岛,并計算整個數(shù)據(jù)里最大值的索引瓶竭,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.2624, 0.2925, 0.0866],
[0.0545, 0.8841, 0.9959]])
>>> a.argmax()
tensor(5)
# 可以看出所有數(shù)據(jù)默認轉(zhuǎn)到1維上,最大值在第6個
但可以通過輸入維度來控制索引的判斷基準渠羞,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.7365, 0.4280, 0.6650],
[0.6988, 0.9839, 0.8990]])
>>> a.argmax(dim=0)
tensor([0, 1, 1])
# 在第1維下判斷每一列的最大值索引斤贰,第一列是0.7365索引為0,第二列是0.9839索引為1次询,第三列是0.8990索引為1
>>> a.argmax(dim=1)
tensor([0, 1])
# 在第2維下判斷每一行的最大值索引荧恍,第一行是0.7365索引為0,第二行是0.9839索引為1
argsort()
返回從小到大(默認屯吊,可以通過descending
參數(shù)設(shè)置)的索引送巡,舉例:
>>> a.argsort()
tensor([0, 1, 2, 3, 4])
>>> a = torch.tensor([1., 5., 4., 2., 5., 3.])
>>> a.argsort()
tensor([0, 3, 5, 2, 1, 4])
# 從小到大的索引
>>> a.argsort(descending=True)
tensor([1, 4, 2, 5, 3, 0])
# 從大到小的索引
topk()
返回前幾大的值(取前幾小的值設(shè)置參數(shù)largest
為False
就行),并且還是按從大到泻行丁(取前幾小就是從小到大)排序好的骗爆,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.0831, 0.3135, 0.2989],
[0.2959, 0.6371, 0.9715]])
>>> a.topk(3)
(tensor([[0.3135, 0.2989, 0.0831],
[0.9715, 0.6371, 0.2959]]), tensor([[1, 2, 0],
[2, 1, 0]]))
# 取前三大的,可以看到結(jié)果也從大到小排序好
>>> a.topk(3, dim=1)
(tensor([[0.3135, 0.2989, 0.0831],
[0.9715, 0.6371, 0.2959]]), tensor([[1, 2, 0],
[2, 1, 0]]))
# 在第2維取前大三的數(shù)
>>> a.topk(3, largest=False)
(tensor([[0.0831, 0.2989, 0.3135],
[0.2959, 0.6371, 0.9715]]), tensor([[0, 2, 1],
[0, 1, 2]]))
# 取前三小的蔽介,可以看到結(jié)果也從小到大排序好
kthvalue()
返回第幾小的數(shù)摘投,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.2052, 0.1159, 0.8533],
[0.3335, 0.3922, 0.7414]])
>>> a.sort()
(tensor([[0.1159, 0.2052, 0.8533],
[0.3335, 0.3922, 0.7414]]), tensor([[1, 0, 2],
[0, 1, 2]]))
>>> a
tensor([[0.2052, 0.1159, 0.8533],
[0.3335, 0.3922, 0.7414]])
>>> a.kthvalue(2, dim=0)
(tensor([0.3335, 0.3922, 0.8533]), tensor([1, 1, 0]))
# 在第1維返回第二小的數(shù),可以看到每一列第二小的數(shù)被返回
>>> a.kthvalue(2, dim=1)
(tensor([0.2052, 0.3922]), tensor([0, 1]))
# 在第2維返回第二小的數(shù)虹蓄,可以看到每一行第二小的數(shù)被返回
norm()
計算張量的范數(shù)(可以理解成張量長度的模)犀呼,默認是l2范數(shù)(即歐氏距離),舉例:
>>> a = torch.tensor((-3, 4), dtype=torch.float32)
>>> a.norm(2)
tensor(5.)
# l2范數(shù):((-3)**2 + 4**2)**0.5
>>> a.norm(1)
tensor(7.)
# l1范數(shù)(曼哈頓距離):|-3| + |4|
>>> a.norm(2, dim=0)
>>> a = torch.tensor(((-3, 4), (6, 8)), dtype=torch.float32)
>>> a
tensor([[-3., 4.],
[ 6., 8.]])
>>> a.norm(2, dim=0)
tensor([6.7082, 8.9443])
# 在第1維算范數(shù):((-3)**2 + 6**2)**0.5, (4**2 + 8**2)**0.5
>>> a.norm(2, dim=1)
tensor([ 5., 10.])
# 在第2維算范數(shù):((-3)**2 + 4**2)**0.5, (6**2 + 8**2)**0.5
clamp()
設(shè)置張量值范圍薇组,舉例:
>>> a = torch.tensor([1, 2, 3, 4, 5])
>>> a.clamp(3)
tensor([3, 3, 3, 4, 5])
# 范圍控制在3~
>>> a.clamp(3, 4)
tensor([3, 3, 3, 4, 4])
# 范圍控制在3~4
transpose()
將指定維度對調(diào)外臂,如果在2維情況,就相當(dāng)于是轉(zhuǎn)置律胀,舉例:
>>> a = torch.rand(2, 3, 1)
>>> a
tensor([[[0.8327],
[0.7932],
[0.7497]],
[[0.2347],
[0.7611],
[0.5529]]])
>>> a.transpose(0, 2)
tensor([[[0.8327, 0.2347],
[0.7932, 0.7611],
[0.7497, 0.5529]]])
# 將第1維和第3維對調(diào)
>>> a.transpose(0, 2).shape
torch.Size([1, 3, 2])
permute()
和transpose
類似也是對調(diào)維度专钉,但使用不太一樣挑童,舉例:
>>> a = torch.rand(2, 3, 1)
>>> a
tensor([[[0.6857],
[0.4819],
[0.3992]],
[[0.7477],
[0.8073],
[0.1939]]])
>>> a.permute(2, 0, 1)
tensor([[[0.6857, 0.4819, 0.3992],
[0.7477, 0.8073, 0.1939]]])
# 將a修改成原來第3個維度放在第1個維度累铅,第1個維度放在第2個維度跃须,第2個維度放在第3個維度
>>> a.permute(2, 0, 1).shape
torch.Size([1, 2, 3])
squeeze()
在指定索引位置刪減維度,如果不傳入索引娃兽,將會把所有能刪減的維度(值為1)都刪減了菇民,舉例:
>>> a = torch.rand(1,2,1,1)
>>> a
tensor([[[[0.3160]],
[[0.5993]]]])
>>> a.squeeze()
tensor([0.3160, 0.5993])
>>> a.squeeze().shape
torch.Size([2])
# 可以看出刪減了所有能刪減的維度
>>> a.squeeze(0)
tensor([[[0.3160]],
[[0.5993]]])
>>> a.squeeze(0).shape
torch.Size([2, 1, 1])
# 刪減了第1維
>>> a.squeeze(1)
tensor([[[[0.3160]],
[[0.5993]]]])
>>> a.squeeze(1).shape
torch.Size([1, 2, 1, 1])
# 第2維因為無法刪減,所以沒有變化
unsqueeze()
在指定索引位置增加維度投储,舉例:
>>> a = torch.rand(2,3)
>>> a
tensor([[0.8979, 0.5201, 0.2911],
[0.8355, 0.2032, 0.9345]])
>>> a.unsqueeze(0)
tensor([[[0.8979, 0.5201, 0.2911],
[0.8355, 0.2032, 0.9345]]])
# 在第1維增加維度
>>> a.unsqueeze(0).shape
torch.Size([1, 2, 3])
# 可以看出(2, 3)->(1,2,3)
>>> a.unsqueeze(-1)
tensor([[[0.8979],
[0.5201],
[0.2911]],
[[0.8355],
[0.2032],
[0.9345]]])
# 在最后1維增加維度
>>> a.unsqueeze(-1).shape
torch.Size([2, 3, 1])
# 可以看出(2, 3)->(2,3,1)
expand()
擴展數(shù)據(jù)第练,但僅限于維度是1的地方,舉例:
>>> a = torch.rand(1,2,1)
>>> a
tensor([[[0.5487],
[0.9694]]])
>>> a.expand([2,2,1])
tensor([[[0.5487],
[0.9694]],
[[0.5487],
[0.9694]]])
# 擴展了第1維度的數(shù)據(jù)
>>> a.expand([2,2,2])
tensor([[[0.5487, 0.5487],
[0.9694, 0.9694]],
[[0.5487, 0.5487],
[0.9694, 0.9694]]])
# 擴展了第1/3維度的數(shù)據(jù)
>>> a.expand([1,4,1])
Traceback (most recent call last):
File "<pyshell#242>", line 1, in <module>
a.expand([1,4,1])
RuntimeError: The expanded size of the tensor (4) must match the existing size (2) at non-singleton dimension 1. Target sizes: [1, 4, 1]. Tensor sizes: [1, 2, 1]
# 因為第2維度不為1玛荞,所以不能擴展
repeat()
復(fù)制數(shù)據(jù)娇掏,對指定維度復(fù)制指定倍數(shù),舉例:
>>> a = torch.rand(1,2,1)
>>> a.repeat([2,2,1]).shape
torch.Size([2, 4, 1])
# 將1/2維變成原來2倍勋眯,第3維不變
>>> a.repeat([2,2,2]).shape
torch.Size([2, 4, 2])
split()
根據(jù)長度切分數(shù)據(jù)毒涧,舉例:
>>> a = torch.rand(2,2,3)
>>> a
tensor([[[0.6913, 0.3448, 0.5107],
[0.5714, 0.1821, 0.2043]],
[[0.9937, 0.4512, 0.8015],
[0.9622, 0.3952, 0.6199]]])
>>> a.split(1, dim=0)
(tensor([[[0.6913, 0.3448, 0.5107],
[0.5714, 0.1821, 0.2043]]]), tensor([[[0.9937, 0.4512, 0.8015],
[0.9622, 0.3952, 0.6199]]]))
# 可以看出根據(jù)第1維將數(shù)據(jù)按長度1切分成了2/1份(第1維長度是2)
>>> a.split(1, dim=0).shape
>>> x, y = a.split(1, dim=0)
>>> x.shape, y.shape
(torch.Size([1, 2, 3]), torch.Size([1, 2, 3]))
chunk
根據(jù)數(shù)量切分數(shù)據(jù)检痰,也就是自定義要切成多少份,舉例:
>>> a = torch.rand(3,3)
>>> a
tensor([[0.3355, 0.0770, 0.1840],
[0.0844, 0.4452, 0.8723],
[0.9296, 0.4290, 0.4051]])
>>> a.chunk(3, dim=0)
(tensor([[0.3355, 0.0770, 0.1840]]), tensor([[0.0844, 0.4452, 0.8723]]), tensor([[0.9296, 0.4290, 0.4051]]))
# 把數(shù)據(jù)切成3份
>>> a.chunk(2, dim=0)
(tensor([[0.3355, 0.0770, 0.1840],
[0.0844, 0.4452, 0.8723]]), tensor([[0.9296, 0.4290, 0.4051]]))
# 切成2份,可以看到最后一個被獨立出來了
常用方法
數(shù)組/張量轉(zhuǎn)換
torch.from_numpy
數(shù)組轉(zhuǎn)張量你虹,舉例:
>>> a = np.array([1, 2, 3])
>>> torch.from_numpy(a)
tensor([1, 2, 3], dtype=torch.int32)
張量轉(zhuǎn)數(shù)組則通過data.numpy()
轉(zhuǎn),若為GPU數(shù)據(jù)休建,則先轉(zhuǎn)成CPU的袍镀,也可以通過np.array(tensor)
強行轉(zhuǎn)成數(shù)組,舉例:
>>> a = torch.tensor([1., 2., 3.])
>>> a
tensor([1., 2., 3.])
>>> a.cpu().data.numpy()
array([1., 2., 3.], dtype=float32)
# 轉(zhuǎn)成CPU數(shù)據(jù)辆琅,然后轉(zhuǎn)數(shù)組
>>> np.array(a)
array([1., 2., 3.], dtype=float32)
# 強制轉(zhuǎn)成數(shù)組
基本運算
torch.add
加法運算漱办,一般情況下也可以用+
號代替,舉例:
>>> a = torch.rand(2, 2)
>>> a
tensor([[0.5643, 0.4722],
[0.5939, 0.6289]])
>>> torch.add(a, b)
tensor([[1.5643, 1.4722],
[1.5939, 1.6289]])
>>> a + b
tensor([[1.5643, 1.4722],
[1.5939, 1.6289]])
# 可以看出結(jié)果一樣
torch.sub
張量減法
torch.mul
張量乘法
torch.div
張量除法
torch.matmul
矩陣乘法婉烟,舉例:
>>> a = torch.tensor([[1,1], [1,1]])
>>> b = torch.tensor([[1,1], [1,1]])
>>> torch.matmul(a, b)
tensor([[2, 2],
[2, 2]])
>>> a*b
tensor([[1, 1],
[1, 1]])
# 別把數(shù)組乘法和矩陣乘法弄混了
矩陣乘法還可以用@
代替娩井,舉例:
>>> a@b
tensor([[2, 2],
[2, 2]])
# 結(jié)果和前面矩陣乘法一樣
邏輯操作
torch.equal
判斷兩個張量是否完全相等,返回True
或者False
隅很,而==
符號返回的只是一個由0和1組成的張量撞牢,舉例:
>>> a = torch.rand(2, 2)
>>> a
tensor([[0.8146, 0.1331],
[0.6715, 0.4594]])
>>> b = a
>>> a == b
tensor([[1, 1],
[1, 1]], dtype=torch.uint8)
# 判斷兩個張量每個元素是否相等,并用0和1來表示是否相等
>>> torch.equal(a, b)
True
# 判斷兩個張量是否相等并返回結(jié)果
torch.all
邏輯與操作叔营,判斷是否全為1屋彪,舉例:
>>> a = torch.tensor([1,2,3])
>>> b = torch.tensor([1,2,4])
>>> a
tensor([1, 2, 3])
>>> b
tensor([1, 2, 4])
>>> torch.all(a==b)
tensor(0, dtype=torch.uint8)
>>> a==b
tensor([1, 1, 0], dtype=torch.uint8)
torch.any
邏輯或操作,判斷是否存在1
torch.where
有三個參數(shù)a, b, c绒尊,對數(shù)據(jù)a進行邏輯判斷畜挥,為1的取數(shù)據(jù)b上對應(yīng)位置的值,為0取c上對應(yīng)值婴谱,舉例:
>>> a = torch.tensor([[1., 2.], [3., 4.]])
>>> b = torch.tensor([[5., 6.], [7., 8.]])
>>> c = torch.rand(2, 2)
>>> c
tensor([[0.3821, 0.6138],
[0.2323, 0.2675]])
>>> torch.where(c>0.3, a, b)
tensor([[1., 2.],
[7., 8.]])
# 位置(0,0)為1蟹但,所以取b上(0,0)的值躯泰,即1
# 位置(0,1)為1,所以取b上(0,1)的值华糖,即2
# 位置(1,0)為0麦向,所以取c上(1,0)的值,即7
# 位置(1,1)為0客叉,所以取c上(1,1)的值诵竭,即8
數(shù)據(jù)生成
torch.zeros
生成固定尺寸的全0張量,舉例:
>>> torch.zeros(2,2)
tensor([[0., 0.],
[0., 0.]])
torch.ones
生成固定尺寸的全1張量兼搏,舉例:
>>> torch.ones(2,2)
tensor([[1., 1.],
[1., 1.]])
torch.full
生成固定尺寸的值全為指定值的張量卵慰,舉例:
>>> torch.full([2, 3], 2)
tensor([[2., 2., 2.],
[2., 2., 2.]])
# 指定格式且值全為2
torch.eye
生成對角線上值全為1的張量,舉例:
>>> torch.eye(3)
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
# 生成3*3張量
>>> torch.eye(3,3)
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
# 和上面一樣
>>> torch.eye(3,4)
tensor([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]])
>>> torch.eye(3,2)
tensor([[1., 0.],
[0., 1.],
[0., 0.]])
torch.arange
生成指定等差數(shù)列的張量佛呻,舉例:
>>> torch.arange(10)
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 只傳一個參數(shù)n則默認為從0-n-1的n個數(shù)
>>> torch.arange(-1, 1, 0.1)
tensor([-1.0000, -0.9000, -0.8000, -0.7000, -0.6000, -0.5000, -0.4000, -0.3000,
-0.2000, -0.1000, 0.0000, 0.1000, 0.2000, 0.3000, 0.4000, 0.5000,
0.6000, 0.7000, 0.8000, 0.9000])
# 生成-1到1裳朋,且距離為0.1的等差數(shù)列
torch.linspace
也是生成等差數(shù)列的張量,用法和arrange
稍有不同吓著,舉例:
>>> torch.linspace(-1, 1, steps=21)
tensor([-1.0000, -0.9000, -0.8000, -0.7000, -0.6000, -0.5000, -0.4000, -0.3000,
-0.2000, -0.1000, 0.0000, 0.1000, 0.2000, 0.3000, 0.4000, 0.5000,
0.6000, 0.7000, 0.8000, 0.9000, 1.0000])
# 生成一個長度為21的等差數(shù)列鲤嫡,且值為-1到1
# 可以看出和上面等價,但是這個方便設(shè)置數(shù)據(jù)量夜矗,上面的方便設(shè)置距離
torch.logspace
和linspace
用法相似泛范,可以理解成再linspace
的基礎(chǔ)上對其求10的n次方值,舉例:
>>> torch.logspace(0, 10, steps=11)
tensor([1.0000e+00, 1.0000e+01, 1.0000e+02, 1.0000e+03, 1.0000e+04, 1.0000e+05,
1.0000e+06, 1.0000e+07, 1.0000e+08, 1.0000e+09, 1.0000e+10])
# 10的0次方紊撕、1次方罢荡、2次方、...
torch.rand
隨機生成一個固定尺寸的張量对扶,并且數(shù)值范圍都在0~1之間区赵,舉例:
>>> torch.rand((2, 3))
tensor([[0.6340, 0.4699, 0.3745],
[0.5066, 0.3480, 0.7346]])
torch.randn
也是隨機生成固定尺寸的張量,數(shù)值符合正態(tài)分布
torch.randint
隨機生成一個固定尺寸的張量浪南,并且數(shù)值為自定義范圍的整數(shù)笼才,舉例:
>>> torch.randint(0, 10, (2, 3))
tensor([[4, 3, 1],
[6, 3, 9]])
# 0~9的固定格式整數(shù)
torch.randperm
生成指定個數(shù)的張量(范圍為0~個數(shù)-1)并打亂,舉例:
>>> torch.randperm(10)
tensor([0, 4, 9, 5, 1, 7, 3, 6, 2, 8])
# 生成0~9的數(shù)络凿,并打亂
torch.rand_like
傳入一個張量骡送,并根據(jù)該張量shape生成一個新的隨機張量,舉例:
>>> a = torch.rand(2, 3)
>>> a
tensor([[0.4079, 0.9071, 0.9304],
[0.0641, 0.0043, 0.0429]])
>>> torch.rand_like(a)
tensor([[0.2936, 0.4585, 0.7674],
[0.4049, 0.0707, 0.0456]])
# 生成一個和a格式相同的張量
注:
有好多xxx_like
的方法絮记,原理都是一樣的:傳入一個張量摔踱,根據(jù)張量的shape生成新的張量
數(shù)據(jù)處理
torch.masked_select
取出指定條件數(shù)據(jù),舉例:
>>> a = torch.tensor([0, 0.5, 1, 2])
>>> torch.masked_select(a, a>0.5)
tensor([1., 2.])
# 取出所有大于0.5的數(shù)據(jù)
torch.cat
在指定維度合并數(shù)據(jù)怨愤,但要求兩個數(shù)據(jù)維度相同派敷,并且指定維度以外的維度尺寸相同,舉例:
>>> a = torch.rand(1,2,3)
>>> b = torch.rand(2,2,3)
>>> torch.cat((a,b))
tensor([[[0.0132, 0.4118, 0.5814],
[0.8034, 0.8765, 0.8404]],
[[0.7860, 0.6115, 0.4745],
[0.0846, 0.4158, 0.3805]],
[[0.9454, 0.3390, 0.3802],
[0.6526, 0.0319, 0.7155]]])
>>> torch.cat((a,b)).shape
torch.Size([3, 2, 3])
# 可以看出默認在第1維合并數(shù)據(jù),合并過程可以看成:[1,2,3] + [2,2,3] = [3,2,3]
>>> torch.cat((a,b), dim=2)
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
torch.cat((a,b), dim=2)
RuntimeError: invalid argument 0: Sizes of tensors must match except in dimension 2. Got 1 and 2 in dimension 0 at d:\build\pytorch\pytorch-1.0.1\aten\src\th\generic\thtensormoremath.cpp:1307
# 在第3維合并數(shù)據(jù)時因為1維(非3維部分)不一樣所以報錯
torch.stack
在指定維度創(chuàng)建一個新維度合并兩個數(shù)據(jù)篮愉,舉例:
>>> a = torch.rand(1,2,3)
>>> b = torch.rand(1,2,3)
>>> a
tensor([[[0.5239, 0.0540, 0.0213],
[0.9713, 0.5983, 0.1413]]])
>>> b
tensor([[[0.3397, 0.0976, 0.3744],
[0.5080, 0.7520, 0.1759]]])
>>> torch.stack((a, b))
tensor([[[[0.5239, 0.0540, 0.0213],
[0.9713, 0.5983, 0.1413]]],
[[[0.3397, 0.0976, 0.3744],
[0.5080, 0.7520, 0.1759]]]])
>>> torch.stack((a, b)).shape
torch.Size([2, 1, 2, 3])
# 合并過程可以看成:[1,1,2,3] + [1,1,2,3] = [1+1,1,2,3] = [2,1,2,3]
>>> torch.stack((a, b), dim=2)
tensor([[[[0.5239, 0.0540, 0.0213],
[0.3397, 0.0976, 0.3744]],
[[0.9713, 0.5983, 0.1413],
[0.5080, 0.7520, 0.1759]]]])
>>> torch.stack((a, b), dim=2).shape
torch.Size([1, 2, 2, 3])
# 合并過程可以看成:[1,2,1,3] + [1,2,1,3] = [1,2,1+1,3] = [1,2,2,3]
其他操作
one-hot編碼
通過結(jié)合torch.zeros()
方法和tensor.scatter_()
方法實現(xiàn)腐芍,舉例:
>>> label = torch.tensor([[0], [1], [2], [3]])
# 標簽內(nèi)容
>>> label_number = len(label)
# 標簽長度
>>> label_range = 10
# 標簽總數(shù)
>>> torch.zeros(label_number, label_range).scatter_(1, label, 1)
tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]])
# 通過torch.zeros生成4行,10列的全零矩陣试躏,通過torch.scatter_以標簽第一維為基準猪勇,用1來覆蓋對應(yīng)的位置
帶下劃線的方法
在pytorch里面可以看到很多方法有兩種版本:帶下劃線和不帶下劃線的,這兩者的區(qū)別就是:不帶下劃線的方法操作數(shù)據(jù)會先新建一個相同的數(shù)據(jù)冗酿,然后對其進行操作后返回埠对;帶下劃線的則直接對該數(shù)據(jù)進行操作并返回(聲明該tensor是個in-place類型),舉例:
>>> a = torch.tensor((1., -2.))
>>> a.abs()
tensor([1., 2.])
>>> a
tensor([ 1., -2.])
# 不帶下劃線的取絕對值后裁替,查看原來的數(shù)據(jù),發(fā)現(xiàn)沒有變
>>> a.abs_()
tensor([1., 2.])
>>> a
tensor([1., 2.])
# 帶下劃線的取絕對值后貌笨,查看原來的數(shù)據(jù)弱判,發(fā)現(xiàn)已經(jīng)變了
更多關(guān)于in-place類型的參考:
https://blog.csdn.net/hbhhhxs/article/details/93886525
工具集
在torch.utils
下提供了很多API工具方便我們的使用
隨機切分數(shù)據(jù)集
通過torch.utils.data.random_split()
方法隨機切分數(shù)據(jù)集,然后通過torch.utils.data.DataLoader
來載入數(shù)據(jù)锥惋,舉例:
>>> a = torch.rand(100)
>>> a
tensor([0.8579, 0.6903, 0.8042, 0.6803, 0.5619, 0.4721, 0.3132, 0.6476, 0.6644,
0.1822, 0.8333, 0.6207, 0.5666, 0.3410, 0.9760, 0.1522, 0.5908, 0.4049,
0.8710, 0.3284, 0.7598, 0.1615, 0.2269, 0.7273, 0.5658, 0.7861, 0.4562,
0.4225, 0.0466, 0.2845, 0.2759, 0.0649, 0.7345, 0.7406, 0.0044, 0.2111,
0.5922, 0.1108, 0.8785, 0.5843, 0.3432, 0.1751, 0.8386, 0.8131, 0.5848,
0.3727, 0.4079, 0.3207, 0.7192, 0.5415, 0.2176, 0.3019, 0.9200, 0.1222,
0.1771, 0.7479, 0.1213, 0.7306, 0.7951, 0.6702, 0.4286, 0.6684, 0.4392,
0.5319, 0.8701, 0.5307, 0.0664, 0.6950, 0.8652, 0.8842, 0.1940, 0.5079,
0.1927, 0.3511, 0.6232, 0.5951, 0.7436, 0.3113, 0.8578, 0.6422, 0.6670,
0.5569, 0.4681, 0.3848, 0.5463, 0.2438, 0.7747, 0.2718, 0.8766, 0.3523,
0.1736, 0.9693, 0.6800, 0.6727, 0.9430, 0.5596, 0.7665, 0.8402, 0.3828,
0.6339])
>>> m, n = torch.utils.data.random_split(a, [10, 90])
# 將數(shù)據(jù)隨機分成10和90個
>>> list(m)
[tensor(0.6803), tensor(0.8386), tensor(0.4079), tensor(0.8652), tensor(0.8333), tensor(0.8402), tensor(0.7861), tensor(0.8710), tensor(0.7306), tensor(0.5848)]
# 查看數(shù)據(jù)集m
>>> m.indices
tensor([ 3, 42, 46, 68, 10, 97, 25, 18, 57, 44])
# 可以看到其存放的是隨機的10個索引昌腰,然后尋找對應(yīng)下標數(shù)據(jù)
>>> data_m = torch.utils.data.DataLoader(m, batch_size=2)
# 載入m數(shù)據(jù)
>>> data_n = torch.utils.data.DataLoader(n, batch_size=2)
函數(shù)式API和類API
在pytorch當(dāng)中,大部分神經(jīng)網(wǎng)絡(luò)層膀跌、激活函數(shù)遭商、損失函數(shù)等都提供了兩種API調(diào)用方式,分別是函數(shù)式API和類API捅伤,前者基本都在torch.nn.functional
下劫流,后者基本都在torch.nn
下,前者一般直接調(diào)用即可丛忆,適合函數(shù)式編程祠汇;后者一般是先實例化,然后通過其內(nèi)置的方法進行調(diào)用熄诡,適合面向?qū)ο缶幊炭珊堋.?dāng)然這些API功能基本都可以自定義實現(xiàn),只是這里提供了API簡化了操作凰浮,并且還提供了GPU加速等功能
網(wǎng)絡(luò)層
全連接層
說白了就是單純的矩陣相乘然后有偏置則加上偏置(公式:input@w + b
)我抠,函數(shù)式API:torch.nn.functional.linear
,類API:torch.nn.Linear
袜茧,類API舉例:
>>> layer1 = torch.nn.Linear(100, 10)
# 這里使用類API
# 定義一個全連接層菜拓,輸入100個單元,輸出10個惫周,可以理解成初始化的一個(100, 10)的矩陣
>>> layer2 = torch.nn.Linear(10, 1)
>>> x = torch.rand(1,100)
# 定義一個(1, 100)的矩陣
>>> x = layer1(x)
# x經(jīng)過layer1全連接層的運算
>>> x
tensor([[-0.1354, 0.1530, 0.1946, -0.1349, 0.6149, -0.0482, 0.1025, -0.8483,
-1.0567, -0.5853]], grad_fn=<AddmmBackward>)
>>> x.shape
torch.Size([1, 10])
# 可以發(fā)現(xiàn)乘完以后變成了(1, 10)的矩陣
>>> x = layer2(x)
# x再經(jīng)過layer2層運算
>>> x
tensor([[-0.2182]], grad_fn=<AddmmBackward>)
>>> x.shape
torch.Size([1, 1])
>>> layer1.weight.shape
torch.Size([10, 100])
# 可以通過weight屬性查看當(dāng)前層的權(quán)值尘惧,計算的時候會將權(quán)值矩陣進行轉(zhuǎn)置后才進行運算,所以是(10, 100)而不是(100, 10)
>>> layer1.bias
Parameter containing:
tensor([ 0.0049, -0.0081, -0.0541, -0.0301, 0.0320, -0.0621, 0.0072, -0.0024,
-0.0339, 0.0456], requires_grad=True)
# 可以通過bias屬性查看當(dāng)前層的偏置值
函數(shù)式API舉例:
>>> x = torch.rand(1,100)
>>> w = torch.rand(10, 100)
>>> x = torch.nn.functional.linear(x, w)
# 可以看出函數(shù)式API需要我們自己定義初始化權(quán)值递递,然后直接調(diào)用即可
>>> x
tensor([[25.9789, 23.4787, 24.2929, 25.8615, 22.0681, 23.1044, 22.0457, 22.0386,
23.0654, 24.6127]])
通過上面對比我們可以發(fā)現(xiàn)對于類API喷橙,我們只需實例化對應(yīng)的類啥么,然后在其的__init__
方法里會對權(quán)值之類的數(shù)據(jù)進行初始化,然后我們傳入自己的數(shù)據(jù)進行調(diào)用運算贰逾;而在函數(shù)式API當(dāng)中悬荣,首先我們需要自己定義初始化的權(quán)值,然后通過往API接口傳入數(shù)據(jù)和權(quán)值等數(shù)據(jù)進行運算
注:
通過上面我們可以看出每一層操作的時候可以實時打印查看其權(quán)值之類的變化疙剑,以供我們觀察氯迂,這也是pytorch作為動態(tài)圖和TensorFlow最大的區(qū)別
Dropout
隨機選取一部分節(jié)點使用,忽略一部分節(jié)點言缤,函數(shù)式API:torch.nn.functional.dropout
嚼蚀,類API:torch.nn.Dropout
,舉例:
>>> a = torch.rand(20)
>>> torch.nn.functional.dropout(a, 0.2)
tensor([1.2178, 1.0375, 0.0555, 0.0307, 0.3235, 0.0000, 0.5209, 0.0000, 0.3346,
1.2383, 0.3606, 1.0937, 0.0000, 0.2957, 0.9463, 0.2932, 0.8088, 0.4445,
0.5565, 0.0241])
# 隨機將百分之20的節(jié)點轉(zhuǎn)成0
批標準化層
函數(shù)式API:torch.nn.functional.batch_norm
管挟,類API:torch.nn.BatchNorm2d
(對應(yīng)的有1d轿曙、2d等等),類API舉例:
>>> x1 = torch.rand(1, 3, 784)
# 3通道的1d數(shù)據(jù)
>>> layer1 = torch.nn.BatchNorm1d(3)
# 1d批標準化層僻孝,3通道
>>> layer1.weight
Parameter containing:
tensor([1., 1., 1.], requires_grad=True)
# 可以看出batch_norm層的權(quán)值全是1
>>> layer1(x1)
tensor([[[-0.0625, -0.1859, -0.3823, ..., 0.6668, -0.7487, 0.8913],
[ 0.0115, -0.1149, 0.1470, ..., -0.1546, 0.3012, 0.2472],
[ 1.5185, -0.4740, -0.8664, ..., 0.6266, 0.2797, -0.2975]]],
grad_fn=<NativeBatchNormBackward>)
# 可以看到數(shù)據(jù)都被標準化了
>>> layer1(x1).shape
torch.Size([1, 3, 784])
>>> x2 = torch.rand(1, 3, 28, 28)
# 3通道的2d數(shù)據(jù)
>>> layer2 = torch.nn.BatchNorm2d(3)
>>> layer2(x2)
tensor([[[[-0.0378, -0.3922, 0.2255, ..., -0.1469, -0.3016, 0.2384],
[-0.3901, -0.0220, -0.3118, ..., -0.2492, 0.1705, -0.0599],
[-0.1309, -0.3064, -0.2001, ..., -0.0613, -0.1838, 0.1335],
...,
[ 0.9022, -0.3031, 1.0695, ..., -0.8257, -0.6438, -0.2672],
[-0.1015, 1.1482, 1.0834, ..., 0.6641, -0.8632, -0.2418],
[-1.2068, -0.7443, 0.8346, ..., 0.1213, 0.4528, -0.5756]]]],
grad_fn=<NativeBatchNormBackward>)
>>> layer2(x2).shape
# 經(jīng)過batch_norm只是將數(shù)據(jù)變得符合高斯分布导帝,并不會改變數(shù)據(jù)形狀
torch.Size([1, 3, 28, 28])
>>> x2.mean()
tensor(0.4942)
# 原來數(shù)據(jù)的平均值
>>> x2.std()
tensor(0.2899)
# 原來數(shù)據(jù)的標準差
>>> layer2(x2).mean()
tensor(-2.1211e-08, grad_fn=<MeanBackward0>)
# 經(jīng)過batch_norm的平均值,可以看出經(jīng)過batch_norm層后數(shù)據(jù)平均值變成接近0
>>> layer2(x2).std()
tensor(1.0002, grad_fn=<StdBackward0>)
# 經(jīng)過batch_norm的標準差穿铆,可以看出經(jīng)過batch_norm層后數(shù)據(jù)標準差變成接近1
卷積層
函數(shù)式API:torch.nn.functional.conv2d
您单,類API:torch.nn.Conv2d
,類API舉例:
>>> x = torch.rand(1, 1, 28, 28)
>>> layer = torch.nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
# 設(shè)置輸入通道為1荞雏,輸出通道為3虐秦,filter大小為3x3,步長為1讯檐,邊框不補0
>>> layer.weight
Parameter containing:
tensor([[[[-0.1893, 0.1177, -0.2837],
[ 0.1116, 0.0348, 0.3011],
[-0.1871, -0.0722, -0.1843]]],
...,
[[[ 0.0083, -0.0784, 0.1592],
[-0.1896, 0.0082, -0.0146],
[-0.2069, -0.0147, -0.1899]]]], requires_grad=True)
# 可以查看初始化權(quán)值
>>> layer.weight.shape
torch.Size([3, 1, 3, 3])
# 格式分別代表輸出通道3羡疗,輸入通道1,尺寸為3x3
>>> layer.bias.shape
torch.Size([3])
# 查看初始化偏置
>>> layer(x)
tensor([[[[-0.0494, -0.1396, -0.0690, ..., -0.1382, -0.0539, -0.1876],
[-0.2185, -0.0116, -0.1287, ..., 0.1233, -0.0091, 0.0407],
[-0.0648, 0.0506, -0.1971, ..., -0.2013, 0.1151, -0.0026],
...,
[-0.4974, -0.5449, -0.4583, ..., -0.7153, -0.1890, -0.7381],
[-0.4254, -0.6051, -0.2578, ..., -0.4957, -0.4128, -0.4875],
[-0.5392, -0.4214, -0.5671, ..., -0.2785, -0.6113, -0.3150]]]],
grad_fn=<ThnnConv2DBackward>)
# 進行一次卷積運算别洪,實際是魔法方法__call__里調(diào)用了forward方法
>>> layer(x).shape
torch.Size([1, 3, 26, 26])
# 可以看到計算后由于邊框不補0叨恨,而濾波器大小為3x3,所以結(jié)果的長寬就變成了(height-3+1, weight-3+1)
>>> layer1 = torch.nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=1)
# 這里邊緣補0
>>> layer1(x).shape
torch.Size([1, 3, 28, 28])
# 可以看到由于邊緣補0挖垛,所以大小沒變
>>> layer2 = torch.nn.Conv2d(1, 3, kernel_size=3, stride=2, padding=0)
# 這里步長改成2
>>> layer2(x).shape
torch.Size([1, 3, 13, 13])
# 結(jié)果的長寬就變成了((height-3+1)/2, (weight-3+1)/2)
>>> layer3 = torch.nn.Conv2d(1, 3, kernel_size=3, stride=2, padding=1)
# 這里邊緣補0痒钝,且步長改成2
>>> layer3(x).shape
torch.Size([1, 3, 14, 14])
# 可以看到結(jié)果的長寬就變成了(height/2, weight/2)
函數(shù)式API舉例:
>>> x = torch.rand(1, 1, 28, 28)
>>> w = torch.rand(3, 1, 3, 3)
# 輸出3通道,輸入1通道痢毒,尺寸3x3
>>> b = torch.rand(3)
# 偏置長度要和通道數(shù)一樣
>>> layer = torch.nn.functional.conv2d(x, w, b, stride=1, padding=1)
>>> layer
tensor([[[[2.1963, 2.6321, 3.4186, ..., 3.2495, 3.1609, 2.5473],
[2.5637, 3.4892, 4.0079, ..., 4.1167, 4.4497, 3.1637],
[2.7618, 3.2788, 3.2314, ..., 4.7185, 4.3128, 2.6393],
...,
[1.3735, 2.3738, 1.8388, ..., 2.9912, 2.6638, 1.5941],
[2.1967, 2.0466, 2.0095, ..., 3.3192, 2.9521, 2.2673],
[1.6091, 2.1341, 1.5108, ..., 2.1684, 2.4585, 1.7931]]]])
>>> layer.shape
torch.Size([1, 3, 28, 28])
池化層
函數(shù)式API:torch.nn.functional.max_pool2d
送矩,類API:torch.nn.MaxPool2d
,類API舉例:
>>> x = torch.rand(1, 1, 28, 28)
>>> layer = torch.nn.MaxPool2d(3, stride=2)
# 尺寸3x3哪替,步長為2
>>> layer(x)
tensor([[[[0.9301, 0.9342, 0.9606, 0.9922, 0.9754, 0.9055, 0.7142, 0.9882,
0.9803, 0.8054, 0.9903, 0.9903, 0.9426],
...,
[0.8873, 0.8873, 0.9324, 0.9876, 0.9566, 0.9225, 0.9673, 0.9675,
0.9977, 0.9977, 0.9552, 0.9552, 0.8689]]]])
>>> layer(x).shape
torch.Size([1, 1, 13, 13])
還有個avgpool(函數(shù)式API:torch.nn.functional.avg_pool2d
栋荸,類API:torch.nn.AvgPool2d
),和maxpool不一樣的是:maxpool是取最大值,而avgpool取的是平均值晌块,舉例:
>>> torch.nn.functional.avg_pool2d(x, 3, stride=2)
tensor([[[[0.5105, 0.6301, 0.5491, 0.4691, 0.5788, 0.4525, 0.3903, 0.5718,
0.6259, 0.3388, 0.4169, 0.6122, 0.4760],
...,
[0.4705, 0.5332, 0.4150, 0.5000, 0.5686, 0.5325, 0.6241, 0.4926,
0.4646, 0.3121, 0.2975, 0.5203, 0.5701]]]])
>>> torch.nn.functional.avg_pool2d(x, 3, stride=2).shape
torch.Size([1, 1, 13, 13])
flatten
將張量轉(zhuǎn)成一維爱沟,舉例:
>>> torch.flatten(torch.rand(2,2))
tensor([0.1339, 0.5694, 0.9034, 0.6025])
>>> torch.flatten(torch.rand(2,2)).shape
torch.Size([4])
向上取樣
將圖片放大/縮小成原來的幾倍,函數(shù)式API:torch.nn.functional.interpolate
匆背,舉例:
>>> x = torch.rand(1, 1, 2, 2)
# 可以理解成1張1通道的2x2圖片
>>> x
tensor([[[[0.9098, 0.7948],
[0.0670, 0.3906]]]])
>>> torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest').shape
torch.Size([1, 1, 4, 4])
# 可以看到數(shù)據(jù)被放大了一倍
>>> torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest')
tensor([[[[0.9098, 0.9098, 0.7948, 0.7948],
[0.9098, 0.9098, 0.7948, 0.7948],
[0.0670, 0.0670, 0.3906, 0.3906],
[0.0670, 0.0670, 0.3906, 0.3906]]]])
# 可以看到是往橫縱向都復(fù)制成原來的對應(yīng)倍數(shù)
>>> torch.nn.functional.interpolate(x, scale_factor=0.5, mode='nearest')
tensor([[[[0.9098]]]])
# 將數(shù)據(jù)縮小一倍呼伸,可以看到取那一部分的第一個數(shù)據(jù)
注:
還有如Upsampling
、UpsamplingNearest2d
也是向上采樣钝尸,現(xiàn)在已經(jīng)逐漸被interpolate
給取代括享。上面interpolate
示例參數(shù)中mode='nearest'
時,相當(dāng)于該UpsamplingNearest2d
珍促,函數(shù)式API:torch.nn.functional.upsample_nearest
铃辖,函數(shù)式API:torch.nn.UpsamplingNearest2d
,類API舉例:
>>> x = torch.rand(1, 1, 2, 2)
>>> x
tensor([[[[0.9977, 0.9778],
[0.4167, 0.6936]]]])
>>> torch.nn.functional.upsample_nearest(x, scale_factor=2).shape
torch.Size([1, 1, 4, 4])
>>> torch.nn.functional.upsample_nearest(x, scale_factor=2)
tensor([[[[0.9977, 0.9977, 0.9778, 0.9778],
[0.9977, 0.9977, 0.9778, 0.9778],
[0.4167, 0.4167, 0.6936, 0.6936],
[0.4167, 0.4167, 0.6936, 0.6936]]]])
嵌入層
常用于定義詞向量踢星,可以理解embedding層定義了一個詞典用來存儲和表示所有的詞向量澳叉,而傳入的數(shù)據(jù)則會根據(jù)索引找到對應(yīng)的詞向量,函數(shù)式API:torch.nn.functional.embedding
沐悦,類API:torch.nn.Embedding
,類API舉例:
>>> embed = torch.nn.Embedding(10, 2)
# 定義了10個詞向量五督,每個詞向量用格式為(1, 2)的tensor表示
>>> words = torch.tensor([0, 1, 2, 0])
# 定義一句話藏否,里面有4個詞,那么可以看出第一個和最后一個詞相同
>>> embed(words)
# 經(jīng)過嵌入層索引可以看到4個詞對應(yīng)的詞向量如下充包,也可以看出第一個和最后一個詞索引相同副签,所以值是一樣的
tensor([[-0.0019, 1.6786],
[ 0.3118, -1.6250],
[ 1.6038, 1.5044],
[-0.0019, 1.6786]], grad_fn=<EmbeddingBackward>)
>>> embed.weight
# 再看embedding層的權(quán)重,可以發(fā)現(xiàn)這就是定義了一個詞向量表基矮,并且會隨著訓(xùn)練而更新淆储,從而找出詞與詞之間的關(guān)系
Parameter containing:
tensor([[-1.8939e-03, 1.6786e+00],
[ 3.1179e-01, -1.6250e+00],
[ 1.6038e+00, 1.5044e+00],
[-6.2278e-01, -2.5135e-01],
[ 1.6210e+00, -5.6379e-01],
[-7.3388e-02, -2.0099e+00],
[ 8.7655e-01, 2.4011e-01],
[-2.5685e+00, 2.6756e-01],
[ 4.9723e-01, -8.3784e-01],
[ 4.2338e-01, -1.9839e+00]], requires_grad=True)
更多參考:https://blog.csdn.net/tommorrow12/article/details/80896331
RNN層
類API:torch.nn.RNN
,會返回計算后總體的輸出家浇,以及最后一個時間戳上的輸出本砰,通過下面代碼可以證明最后一個時間戳的輸出和總體輸出的最后一個是一樣的,類API舉例:
>>> x = torch.randn(10, 3, 100)
# 模擬句子序列:有10個單詞(序列長度是10)钢悲,共3句話点额,每個單詞用100維向量表示
# input:[seq_len, batch, input_size],如果希望batch_size放第一個莺琳,可以設(shè)置batch_first=True
>>> layer = torch.nn.RNN(input_size=100, hidden_size=20, num_layers=4)
>>> layer
RNN(100, 20, num_layers=4)
>>> out, h = layer(x)
# 返回output和hidden
>>> out.shape
torch.Size([10, 3, 20])
# 所有時間戳上的狀態(tài)
# output:[seq_len, batch, hidden_size]
>>> h.shape
torch.Size([4, 3, 20])
# 最后一個時間戳上的hidden
# hidden:[num_layers, batch, hidden_size]
>>> h[-1]
# 最后一層的最后一個時間戳上的輸出(因為num_layers的值為4还棱,所以要取第四個,對于num_layers參數(shù)的解釋惭等,下面會說)
tensor([[ 3.5205e-01, 3.6580e-01, -5.6378e-01, -9.9363e-02, 3.8728e-03,
-5.0282e-01, 1.4762e-01, -2.5631e-01, -8.8786e-03, 1.2912e-01,
4.7565e-01, -8.8090e-02, -3.9374e-02, 3.1736e-02, 3.1264e-01,
2.8091e-01, 5.0764e-01, 2.9722e-01, -3.6929e-01, -5.1096e-02],
...
[ 5.4770e-01, 4.8047e-01, -5.2541e-01, 2.5208e-01, -4.0260e-04,
-2.3619e-01, -2.1128e-01, -1.1262e-01, -6.2672e-02, 3.5301e-01,
-4.1065e-02, -3.5043e-02, -4.3008e-01, -1.8410e-01, 2.5826e-01,
3.5430e-02, 2.5651e-01, 4.5170e-01, -5.4705e-01, -2.4720e-01]],
grad_fn=<SelectBackward>)
>>> out[-1]
# 所有狀態(tài)的最后一個輸出珍手,可以看到是一樣的
tensor([[ 3.5205e-01, 3.6580e-01, -5.6378e-01, -9.9363e-02, 3.8728e-03,
-5.0282e-01, 1.4762e-01, -2.5631e-01, -8.8786e-03, 1.2912e-01,
4.7565e-01, -8.8090e-02, -3.9374e-02, 3.1736e-02, 3.1264e-01,
2.8091e-01, 5.0764e-01, 2.9722e-01, -3.6929e-01, -5.1096e-02],
...
[ 5.4770e-01, 4.8047e-01, -5.2541e-01, 2.5208e-01, -4.0260e-04,
-2.3619e-01, -2.1128e-01, -1.1262e-01, -6.2672e-02, 3.5301e-01,
-4.1065e-02, -3.5043e-02, -4.3008e-01, -1.8410e-01, 2.5826e-01,
3.5430e-02, 2.5651e-01, 4.5170e-01, -5.4705e-01, -2.4720e-01]],
grad_fn=<SelectBackward>)
num_layers
參數(shù)的理解:rnn的基本參數(shù)都挺好理解因為其他深度學(xué)習(xí)框架基本也都一樣,而比較特殊的就是num_layers
參數(shù),其實也很簡單琳要,顧名思義就是代表著有幾層rnn寡具,直接一口氣幫你定義好直接計算,省的你自己再去定義一堆rnn焙蹭,然后一層一層的算過去夺欲,比如上面的示例代碼設(shè)置num_layers=4
,那么上面的代碼可以替換成下面這種:
>>> x = torch.randn(10, 3, 100)
>>> layer1 = torch.nn.RNN(input_size=100, hidden_size=20, num_layers=1)
# 把上面示例代碼中num_layers=4的rnn改成4個為1的rnn
>>> layer2 = torch.nn.RNN(input_size=20, hidden_size=20, num_layers=1)
# 因為第一層的hidden是20钦睡,所以后幾層的輸入都是20
>>> layer3 = torch.nn.RNN(input_size=20, hidden_size=20, num_layers=1)
>>> layer4 = torch.nn.RNN(input_size=20, hidden_size=20, num_layers=1)
>>> out, h = layer1(x)
>>> out, h = layer2(out)
>>> out, h = layer3(out)
>>> out, h = layer4(out)
>>> out.shape
torch.Size([10, 3, 20])
RNN參數(shù)理解參考:
https://blog.csdn.net/rogerfang/article/details/84500754
LSTM層
類API:torch.nn.LSTM
钧嘶,因為LSTM是基于RNN并添加了門控制,因此返回的時候比RNN要多返回一個cell單元撰豺,格式和hidden一樣粪般,舉例:
>>> x = torch.randn(10, 3, 100)
>>> layer = torch.nn.LSTM(input_size=100, hidden_size=20, num_layers=4)
>>> layer
LSTM(100, 20, num_layers=4)
>>> out, (h, c) = layer(x)
# 返回output、hidden和cell
>>> out.shape
torch.Size([10, 3, 20])
>>> h.shape
torch.Size([4, 3, 20])
>>> c.shape
torch.Size([4, 3, 20])
# 可以看出和hidden格式一樣
# cell:[num_layers, batch_size, hidden_size]
這里給一個通過前三個數(shù)預(yù)測后一個數(shù)的模型代碼示例:
# -----------------------------------
# 模塊導(dǎo)入
import numpy
import torch
from torch import nn
# -----------------------------------
# 數(shù)據(jù)預(yù)處理
data_length = 30
# 定義30個數(shù)污桦,通過前三個預(yù)測后一個亩歹,比如:1,2,3->4
seq_length = 3
# 通過上面可知序列長度為3
number = [i for i in range(data_length)]
li_x = []
li_y = []
for i in range(0, data_length - seq_length):
x = number[i: i + seq_length]
y = number[i + seq_length]
li_x.append(x)
li_y.append(y)
# print(x, '->', y)
data_x = numpy.reshape(li_x, (len(li_x), 1, seq_length))
# 輸入數(shù)據(jù)格式:seq_len, batch, input_size
# 這里可能會有誤解,seq_len不是步長凡橱,而是你的樣本有多少組小作,即sample
# 而input_size就是你數(shù)據(jù)的維度,比如用三個預(yù)測一個稼钩,就是3維
data_x = torch.from_numpy(data_x / float(data_length)).float()
# 將輸入數(shù)據(jù)歸一化
data_y = torch.zeros(len(li_y), data_length).scatter_(1, torch.tensor(li_y).unsqueeze_(dim=1), 1).float()
# 將輸出數(shù)據(jù)設(shè)置為one-hot編碼
# print(data_x.shape)
# # 格式:torch.Size([27, 1, 3])顾稀,代表:27組數(shù)據(jù)(batch)、序列步長為3(sequence)
# print(data_y.shape)
# # 格式:torch.Size([27, 30])坝撑,代表:27組數(shù)據(jù)静秆,30個特征(features)
# -----------------------------------
# 定義網(wǎng)絡(luò)模型
class net(nn.Module):
# 模型結(jié)構(gòu):LSTM + 全連接 + Softmax
def __init__(self, input_size, hidden_size, output_size, num_layer):
super(net, self).__init__()
self.layer1 = nn.LSTM(input_size, hidden_size, num_layer)
self.layer2 = nn.Linear(hidden_size, output_size)
self.layer3 = nn.Softmax()
def forward(self,x):
x, _ = self.layer1(x)
sample, batch, hidden = x.size()
# 格式:[27, 1, 32],代表樣本數(shù)量巡李,batch大小以及隱藏層尺寸
x = x.reshape(-1, hidden)
# 轉(zhuǎn)成二維矩陣后與全連接進行計算
x = self.layer2(x)
x = self.layer3(x)
return x
model = net(seq_length, 32, data_length, 4)
# -----------------------------------
# 定義損失函數(shù)和優(yōu)化器
loss_fun = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# -----------------------------------
# 訓(xùn)練模型
# 訓(xùn)練前可以先看看初始化的參數(shù)預(yù)測的結(jié)果差距
# result = model(data_x)
# for target, pred in zip(data_y, result):
# print("{} -> {}".format(target.argmax().data, pred.argmax().data))
# 開始訓(xùn)練1000輪
for _ in range(500):
output = model(data_x)
loss = loss_fun(data_y, output)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (_ + 1) % 50 == 0:
print('Epoch: {}, Loss: {}'.format(_, loss.data))
# -----------------------------------
# 預(yù)測結(jié)果
result = model(data_x)
for target, pred in zip(data_y, result):
print("正確結(jié)果:{}抚笔,預(yù)測:{}".format(target.argmax().data, pred.argmax().data))
# 結(jié)果:
# 正確結(jié)果:3,預(yù)測:3
# 正確結(jié)果:4侨拦,預(yù)測:4
# 正確結(jié)果:5殊橙,預(yù)測:5
# 正確結(jié)果:6,預(yù)測:6
# 正確結(jié)果:7阳谍,預(yù)測:7
# 正確結(jié)果:8蛀柴,預(yù)測:8
# 正確結(jié)果:9,預(yù)測:9
# 正確結(jié)果:10矫夯,預(yù)測:10
# 正確結(jié)果:11鸽疾,預(yù)測:11
# 正確結(jié)果:12,預(yù)測:12
# 正確結(jié)果:13训貌,預(yù)測:13
# 正確結(jié)果:14制肮,預(yù)測:14
# 正確結(jié)果:15冒窍,預(yù)測:15
# 正確結(jié)果:16,預(yù)測:16
# 正確結(jié)果:17豺鼻,預(yù)測:21
# 正確結(jié)果:18综液,預(yù)測:18
# 正確結(jié)果:19,預(yù)測:27
# 正確結(jié)果:20儒飒,預(yù)測:21
# 正確結(jié)果:21谬莹,預(yù)測:21
# 正確結(jié)果:22,預(yù)測:21
# 正確結(jié)果:23桩了,預(yù)測:21
# 正確結(jié)果:24附帽,預(yù)測:24
# 正確結(jié)果:25,預(yù)測:25
# 正確結(jié)果:26井誉,預(yù)測:26
# 正確結(jié)果:27蕉扮,預(yù)測:27
# 正確結(jié)果:28,預(yù)測:28
# 正確結(jié)果:29颗圣,預(yù)測:29
當(dāng)然這個示例使用到的數(shù)據(jù)極少喳钟,只是一個能快速跑來玩玩的程序而已,不必當(dāng)真...
LSTM原理理解:https://blog.csdn.net/gzj_1101/article/details/79376798
LSTM計算過程參考:https://blog.csdn.net/qyk2008/article/details/80225986
序列化模型
即用來定義神經(jīng)網(wǎng)絡(luò)模型(每一層都要是繼承于torch.nn
下的網(wǎng)絡(luò))在岂,類API:torch.nn.Sequential
(該模型就是針對面向?qū)ο竽J骄帉懕荚颍虼瞬惶峁┖瘮?shù)式API),舉例:
>>> net = torch.nn.Sequential(
torch.nn.Linear(100, 10),
torch.nn.Dropout(0.7),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
>>> net
Sequential(
(0): Linear(in_features=100, out_features=10, bias=True)
(1): Dropout(p=0.7)
(2): ReLU()
(3): Linear(in_features=10, out_features=1, bias=True)
# 可以直接查看網(wǎng)絡(luò)結(jié)構(gòu)
序列化模型修改
序列化模型可以理解成一個列表蔽午,里面按順序存放了所有的網(wǎng)絡(luò)層应狱,官方也提供了添加往序列化模型里添加網(wǎng)絡(luò)層的方法add_module(name, layer)
,而修改則可以索引到對應(yīng)的層直接修改祠丝,刪除可以通過del
關(guān)鍵字刪除,舉例:
>>> seq = nn.Sequential(nn.Linear(10, 20), nn.Linear(20, 1))
>>> seq
Sequential(
(0): Linear(in_features=10, out_features=20, bias=True)
(1): Linear(in_features=20, out_features=1, bias=True)
)
>>> seq.add_module("tanh", nn.Tanh())
# 在最后面添加一個tanh激活層
>>> seq
# 可以看到添加成功
Sequential(
(0): Linear(in_features=10, out_features=20, bias=True)
(1): Linear(in_features=20, out_features=1, bias=True)
(tanh): Tanh()
)
>>> seq[2]
Tanh()
>>> seq[2] = nn.ReLU()
# 修改第三層為relu
>>> seq
# 可以看到修改成功
Sequential(
(0): Linear(in_features=10, out_features=20, bias=True)
(1): Linear(in_features=20, out_features=1, bias=True)
(tanh): ReLU()
)
>>> del seq[2]
# 刪除第三層
>>> seq
# 可以看到刪除成功
Sequential(
(0): Linear(in_features=10, out_features=20, bias=True)
(1): Linear(in_features=20, out_features=1, bias=True)
)
修改網(wǎng)絡(luò)參數(shù)
對于網(wǎng)絡(luò)層中的參數(shù)除嘹,其是一個Parameter
類型写半,因此如果我們需要手動修改其參數(shù)時,可以通過定義一個該類的數(shù)據(jù)來賦值修改尉咕,舉例:
>>> layer = nn.Linear(2, 1)
>>> layer.weight
Parameter containing:
tensor([[0.6619, 0.2653]], requires_grad=True)
>>> type(layer.weight)
# 參數(shù)的數(shù)據(jù)類型
<class 'torch.nn.parameter.Parameter'>
>>> layer.weight = torch.rand(2, 1, requires_grad=True)
# 直接賦值張量會報錯
Traceback (most recent call last):
File "<pyshell#150>", line 1, in <module>
layer.weight = torch.rand(2, 1)
File "D:\python\lib\site-packages\torch\nn\modules\module.py", line 604, in __setattr__
.format(torch.typename(value), name))
TypeError: cannot assign 'torch.FloatTensor' as parameter 'weight' (torch.nn.Parameter or None expected)
>>> layer.weight = nn.parameter.Parameter(torch.rand(2, 1))
# 賦值Parameter類型的則可以
>>> layer.weight
# 可以看到修改成功
Parameter containing:
tensor([[0.7412],
[0.9723]], requires_grad=True)
自定義神經(jīng)網(wǎng)絡(luò)
當(dāng)需要自己定義神經(jīng)網(wǎng)絡(luò)層的時候叠蝇,首先需要繼承于torch.nn.Module
,并在初始化時調(diào)用父類的初始化方法年缎,同時也要在forward
方法里實現(xiàn)數(shù)據(jù)的前向傳播悔捶,舉例:
import torch
class Dense(torch.nn.Module):
# 實現(xiàn)一個自定義全連接+relu層,繼承torch.nn.Module
def __init__(self, input_shape, output_shape):
super(Dense, self).__init__()
# 首先初始化時執(zhí)行父類的初始化单芜,這句話可以看
# 在父類初始化中會初始化很多變量
self.w = torch.nn.Parameter(torch.randn(output_shape, input_shape))
# 初始化權(quán)重和偏置參數(shù)
# 使用Parameter其會自動將參數(shù)設(shè)置為需要梯度信息蜕该,并且可以通過內(nèi)置的parameters方法返回這些參數(shù)
self.b = torch.nn.Parameter(torch.rand(output_shape))
self.relu = torch.nn.ReLU()
# 初始化relu層
def forward(self, x):
# 定義前向傳播方法
x = x @ self.w.t() + self.b
# 全連接層的功能就是矩陣相乘計算
x = self.relu(x)
# 進行relu層計算
return x
def __call__(self, x):
# 調(diào)用該類對象執(zhí)行時,調(diào)用前向傳播方法
# 這個可以不寫洲鸠,直接通過調(diào)用forward方法也一樣
return self.forward(x)
layer = Dense(10, 1)
x = torch.rand(2, 10)
output = layer(x)
print(output)
# 輸出結(jié)果:
# tensor([[0.1780],
# [0.0000]], grad_fn=<ThresholdBackward0>)
凍結(jié)網(wǎng)絡(luò)層
如果希望訓(xùn)練過程當(dāng)中堂淡,對某些網(wǎng)絡(luò)層的權(quán)重不進行訓(xùn)練的話(該場景在遷移學(xué)習(xí)當(dāng)中比較常見)馋缅,可以設(shè)置該層的權(quán)重、偏差等屬性為False
绢淀,舉例:
>>> net = torch.nn.Sequential(
torch.nn.Linear(100, 10),
torch.nn.Dropout(0.7),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
>>> net
Sequential(
(0): Linear(in_features=100, out_features=10, bias=True)
(1): Dropout(p=0.7, inplace=False)
(2): ReLU()
(3): Linear(in_features=10, out_features=1, bias=True)
)
>>> for name, value in net.named_parameters():
print(name, value.requires_grad)
# 可以看到網(wǎng)絡(luò)層的兩個全連接層的權(quán)重和偏置都可求導(dǎo)
0.weight True
0.bias True
3.weight True
3.bias True
>>> net[0].weight.requires_grad = False
# 凍結(jié)第一個全連接的權(quán)重
>>> net[0].bias.requires_grad = False
>>> for name, value in net.named_parameters():
print(name, value.requires_grad)
# 可以看到第一個全連接的權(quán)重和偏置都被凍結(jié)
0.weight False
0.bias False
3.weight True
3.bias True
保存和載入網(wǎng)絡(luò)
對于所有繼承自torch.nn.Module
下的網(wǎng)絡(luò)萤悴,保存時首先通過內(nèi)置的方法state_dict()
返回當(dāng)前模型的所有參數(shù),然后通過torch.save()
方法保存成文件(也可以不保存參數(shù)皆的,直接保存模型覆履,但這樣可控性低,不推薦)费薄;載入時通過torch.load()
方法載入文件硝全,并通過內(nèi)置的load_state_dict()
方法載入所有的參數(shù),舉例:
>>> layer = torch.nn.Linear(10, 1)
>>> layer.state_dict()
OrderedDict([('weight', tensor([[-0.1597, 0.0573, 0.0976, -0.1028, -0.1264, -0.0400, 0.0308, 0.2192,
-0.0150, -0.3148]])), ('bias', tensor([0.0557]))])
# 可以看到layer里定義的參數(shù)配置
>>> torch.save(layer.state_dict(), "ckpt.mdl")
# 現(xiàn)在保存這個網(wǎng)絡(luò)參數(shù)
>>> layer1 = torch.nn.Linear(10, 1)
# 新建一個網(wǎng)絡(luò)
>>> layer1.state_dict()
OrderedDict([('weight', tensor([[-0.2506, -0.2960, -0.3083, 0.0629, 0.1707, 0.3018, 0.2345, -0.1922,
-0.0527, -0.1894]])), ('bias', tensor([-0.0069]))])
# 顯然layer1參數(shù)和layer的不一樣
>>> layer1.load_state_dict(torch.load("ckpt.mdl"))
# layer1載入前面的layer網(wǎng)絡(luò)參數(shù)
>>> layer1.state_dict()
OrderedDict([('weight', tensor([[-0.1597, 0.0573, 0.0976, -0.1028, -0.1264, -0.0400, 0.0308, 0.2192,
-0.0150, -0.3148]])), ('bias', tensor([0.0557]))])
# 可以發(fā)現(xiàn)layer1的參數(shù)變得和layer保存的參數(shù)一樣
優(yōu)化器
torch.optim
定義了各種優(yōu)化器义锥,將優(yōu)化器實例化后(傳入需要求導(dǎo)的參數(shù)和學(xué)習(xí)率)柳沙,通過step()
方法進行梯度下降
- Adam
- SGD
動態(tài)調(diào)整優(yōu)化器學(xué)習(xí)率
對于實例化的優(yōu)化器,其參數(shù)都將存放到一個屬性param_groups[0]
里拌倍,舉例:
optim = torch.optim.Adam(model.parameters(), lr=0.01)
print(optim)
# 結(jié)果:
# Adam (
# Parameter Group 0
# amsgrad: False
# betas: (0.9, 0.999)
# eps: 1e-08
# lr: 0.01
# weight_decay: 0
# )
而param_groups[0]
是一個字典對象赂鲤,所以要動態(tài)修改學(xué)習(xí)率等參數(shù),可以通過下面代碼實現(xiàn):
optim.param_groups[0]['lr'] = new_lr
上面介紹的是修改優(yōu)化器的學(xué)習(xí)率柱恤,如果希望對網(wǎng)絡(luò)的不同層采用不用的學(xué)習(xí)率可以參考:
https://blog.csdn.net/jdzwanghao/article/details/83239111
激活函數(shù)
sigmoid
公式:
1 / ( 1 + e^-x )
注:該公式可以將值控制在0~1之間数初,適合概率之類的問題,但因為當(dāng)x特別大時梗顺,導(dǎo)數(shù)幾乎為0泡孩,容易發(fā)生梯度彌散(梯度長時間得不到更新,損失值不下降)之類的問題
函數(shù)式API:torch.nn.functional.sigmoid
寺谤,類API:torch.nn.Sigmoid
仑鸥,pytorch自帶:torch.sigmoid
,舉例:
>>> a = torch.linspace(-100, 100, 10)
>>> a
tensor([-100.0000, -77.7778, -55.5556, -33.3333, -11.1111, 11.1111,
33.3333, 55.5555, 77.7778, 100.0000])
>>> torch.sigmoid(a)
tensor([0.0000e+00, 1.6655e-34, 7.4564e-25, 3.3382e-15, 1.4945e-05, 9.9999e-01,
1.0000e+00, 1.0000e+00, 1.0000e+00, 1.0000e+00])
# 可以看到值都被映射在0~1之間
tanh
公式:
(e^x - e^-x) / (e^x + e^-x)
注:該公式可以將值控制在-1~1之間
函數(shù)式API:torch.nn.functional.tanh
变屁,類API:torch.nn.Tanh
眼俊,pytorch自帶:torch.tanh
relu
公式:
0, x <= 0
x, x > 0
注:該公式可以將值控制在0~+∞之間
函數(shù)式API:torch.nn.functional.relu
,類API:torch.nn.ReLU
leakyrelu
公式:
ax, x <= 0
x, x > 0
注:a是一個很小的參數(shù)值粟关,該公式在relu的基礎(chǔ)上疮胖,使負區(qū)間的數(shù)不為0,而是保持在一個很小的梯度上
函數(shù)式API:torch.nn.functional.leaky_relu
闷板,類API:torch.nn.LeakyReLU
softmax
公式:
e^xi / Σe^xi
注:該公式可以將值控制在0~1之間澎灸,并且所有的值總和為1,適合分類之類的問題遮晚,并且可以發(fā)現(xiàn)通過e階函數(shù)性昭,其還會把大的值(特征)放大,小的縮小
函數(shù)式API:torch.nn.functional.softmax
鹏漆,類API:torch.nn.Softmax
巩梢,pytorch自帶:torch.softmax
損失函數(shù)
均方差
就是計算的y與實際y之差的平方取平均值创泄,函數(shù)式API:torch.nn.functional.mse_loss
,類API:torch.nn.MSELoss
括蝠,第一個參數(shù)是y鞠抑,第二個參數(shù)是y對應(yīng)的公式,舉例:
>>> x = torch.tensor(1.)
>>> w = torch.tensor(2.)
>>> b = torch.tensor(0.)
>>> y = torch.tensor(1.)
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
# 計算y=wx+b在點(1, 1)時的均方差:(1 - (2*1+0))^2 = 1
>>> mse
tensor(1.)
交叉熵
通過含有的信息量大小來進行判斷忌警,一般用于分類搁拙,函數(shù)式API:torch.nn.functional.cross_entropy
,類API:torch.nn.CrossEntropyLoss
法绵,要注意的是目標y的格式要求為Long
類型箕速,值為每個one-hot數(shù)據(jù)對應(yīng)的argmax處,舉例:
>>> output = torch.rand(5,10)
# 輸出結(jié)果朋譬,假設(shè)5條數(shù)據(jù)盐茎,每條數(shù)據(jù)有10個特征
>>> target = torch.tensor([0, 2, 9, 9, 2]).long()
# 目標y,數(shù)據(jù)必須為long型徙赢,值分別為每條數(shù)據(jù)的特征字柠,比如第一個0代表第一條數(shù)據(jù)的第一個特征
>>> output.shape
torch.Size([5, 10])
>>> target.shape
torch.Size([5])
# 目標y的要求還要求是1維的
>>> loss = torch.nn.CrossEntropyLoss()
>>> loss(output, target)
tensor(2.2185)
注:
對于分類問題一般會選擇最后一層激活函數(shù)用softmax
,并且損失函數(shù)使用交叉熵狡赐,但是在pytorch的交叉熵中已經(jīng)內(nèi)置了softmax
窑业,所以在使用交叉熵時就不需要再自己使用softmax
了
二分類交叉熵
顧名思義,是一種特殊的交叉熵枕屉,專門在2分類時使用常柄,比如GAN的判別器里,函數(shù)式API:torch.nn.functional.binary_cross_entropy
搀擂,類API:torch.nn.BCELoss
西潘,使用舉例:
>>> batch_size = 5
# 5條數(shù)據(jù)
>>> output = torch.rand(batch_size, 1)
>>> output
# 每個數(shù)據(jù)的正確率
tensor([[0.3546],
[0.9064],
[0.0617],
[0.2839],
[0.3106]])
>>> target = torch.ones(batch_size, 1)
>>> target
# 正確的概率是1
tensor([[1.],
[1.],
[1.],
[1.],
[1.]])
>>> loss = torch.nn.BCELoss()
>>> loss(output, target)
tensor(1.2697)
更多參考
https://blog.csdn.net/shanglianlm/article/details/85019768
https://blog.csdn.net/jacke121/article/details/82812218
求導(dǎo)機制
在pytorch中定義了自動求導(dǎo)的機制,方便了我們在反向傳播時更新參數(shù)等操作
torch.autograd.grad
定義了自動求導(dǎo)哨颂,傳入第一個參數(shù)是對應(yīng)的公式秸架,第二個參數(shù)是一個列表,里面存放所有要求導(dǎo)的變量咆蒿,并且在求導(dǎo)前的變量需要通過require_grad()
方法來聲明該公式的某個變量是需要求導(dǎo)的(或者在定義時就設(shè)置requires_grad
參數(shù)為True
),返回一個元組蚂子,里面是對應(yīng)每個變量的求導(dǎo)信息沃测,舉例:
>>> x = torch.tensor(1.)
>>> w = torch.tensor(2.)
>>> b = torch.tensor(0.)
>>> y = torch.tensor(1.)
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
# 模擬一個y=wx+b的函數(shù)
>>> mse
tensor(1.)
>>> torch.autograd.grad(mse, [w])
# 此時沒有變量聲明過是需要求導(dǎo)的
>>> w.requires_grad_()
tensor(2., requires_grad=True)
# 聲明w需要求導(dǎo),可以看到一開始就定義w = torch.tensor(2., requires_grad=True)效果也是一樣的
# 加下劃線代表為in-place類型食茎,直接對w進行修改蒂破,也可以替換成:w = w.requires_grad()
>>> torch.autograd.grad(mse, [w])
# 報錯,因為mse里的w還是之前的w别渔,需要更新一下mse里的w
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
# 更新mse里的w為聲明了需要求導(dǎo)的w
>>> torch.autograd.grad(mse, [w])
(tensor(2.),)
# 可以看出mse對第一個變量w求偏導(dǎo)的結(jié)果為2附迷,計算過程:
# mse為:(y-(wx+b))^2
# 對w求偏導(dǎo)為:-2x(y-(wx+b))
# 代入數(shù)值:-2*1*(1-(2*1+0)) = 2
torch.backward
反向傳播惧互,也能實現(xiàn)求導(dǎo),通過設(shè)置該tensor允許求導(dǎo)喇伯,那么該方法會對計算過程中所有聲明了求導(dǎo)信息的變量進行求導(dǎo)喊儡,然后在對應(yīng)的變量上通過grad
屬性獲取求導(dǎo)結(jié)果,舉例:
>>> x = torch.tensor(1.)
>>> b = torch.tensor(0.)
>>> w = torch.tensor(2., requires_grad=True)
>>> y = torch.tensor(1.)
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
>>> mse.backward()
# 對mse公式里需要求導(dǎo)的變量都進行求導(dǎo)
>>> w.grad
tensor(2.)
# w的求導(dǎo)結(jié)果為2
>>> x.grad
# 因為x沒有聲明需要求導(dǎo)稻据,所以為空
注:
在反向傳播當(dāng)中艾猜,存在梯度累加問題,即第一次進行反向傳播以后的值會進行保留捻悯,當(dāng)?shù)诙卧龠M行反向傳播時匆赃,則會將梯度和前面的進行累加,導(dǎo)致越來越大今缚,因此在大多情況下算柳,為了避免這種情況,需要我們手動進行梯度清零姓言,舉例:
>>> x = torch.tensor(5., requires_grad=True)
>>> y = x + 1
>>> y.backward()
# 第一次反向傳播
>>> x.grad
# 求導(dǎo)結(jié)果(梯度)為1
tensor(1.)
>>> y.backward()
# 第二次反向傳播瞬项,如果報錯(比如x的前面乘了一個數(shù)值),那么在反向傳播里加上參數(shù):retain_graph=True事期,即:y.backward(retain_graph=True)
>>> x.grad
# 發(fā)現(xiàn)梯度在原來1的基礎(chǔ)上又加上了1
tensor(2.)
>>> del x.grad
# 梯度清零
>>> y.backward()
# 再次反向傳播
>>> x.grad
# 可以看到又變回1了
tensor(1.)
基于上面的情況滥壕,在實際模型訓(xùn)練當(dāng)中,我們首先會用優(yōu)化器來進行梯度清零兽泣,然后再對loss進行反向傳播绎橘,最后再用優(yōu)化器來進行梯度下降,舉例:
>>> x = torch.tensor([2.])
# 定義輸入
>>> y = torch.tensor([4.])
# 定義輸出
>>> layer = nn.Linear(1, 1)
# 定義網(wǎng)絡(luò)參數(shù)
>>> layer.weight
# 可以看到權(quán)重w為0.8162
Parameter containing:
tensor([[0.8162]], requires_grad=True)
>>> layer.bias
# 偏置y為-0.4772
# 所以可以得出初始化的函數(shù)為:f(x) = 0.8162*x - 0.4772
Parameter containing:
tensor([-0.4772], requires_grad=True)
>>> optim = torch.optim.SGD(layer.parameters(), lr=0.1)
# 定義隨機梯度下降優(yōu)化器唠倦,要更新的是網(wǎng)絡(luò)層的參數(shù)称鳞,學(xué)習(xí)率為0.1
>>> loss = y - layer(x)
# 定義目標函數(shù),經(jīng)過網(wǎng)絡(luò)層后的數(shù)和y越接近越好
>>> loss
# 計算后可以看出y和計算的結(jié)果相差2.8448
tensor([2.8448], grad_fn=<SubBackward0>)
>>> loss.backward()
# 反向傳播
>>> layer.weight.grad
# 權(quán)重求導(dǎo)梯度為負稠鼻,說明正向是在下降
tensor([[-2.]])
>>> layer.bias.grad
# 偏置同理
tensor([-1.])
>>> optim.step()
# 梯度下降冈止,更新權(quán)重和偏置
>>> layer.weight
# 可以看到權(quán)重減了-2*0.1
Parameter containing:
tensor([[1.0162]], requires_grad=True)
>>> layer.bias
# 偏置減了-1*0.1
Parameter containing:
tensor([-0.3772], requires_grad=True)
>>> loss = y - layer(x)
>>> loss
# 可以看到loss減小了,所以更新網(wǎng)絡(luò)層參數(shù)后候齿,計算后的值和y更加接近
tensor([2.3448], grad_fn=<SubBackward0>)
可視化操作
tensorboardX
在TensorFlow里有tensorboard
可以進行訓(xùn)練過程的可視化熙暴,而在Pytorch里也提供了tensorboardX
幾乎和tensorboard
一樣,適合習(xí)慣了tensorboard
的使用者慌盯,具體使用可以參考:
http://www.reibang.com/p/713c1bc4cf8a
http://www.reibang.com/p/46eb3004beca
visdom
pytorch提供的另一個可視化周霉,個人更喜歡這種界面樣式(tensorboard的黃色底色看著不太習(xí)慣)
安裝
pip install visdom
使用步驟
- 通過命令
python -m visdom.server
啟動visdom服務(wù)器進行監(jiān)聽 - 導(dǎo)入可視化類:
from visdom import Visdom
- 實例化可視化類,并通過
line
/bar
/text
等提供的API進行繪圖
具體使用可以參考:
https://blog.csdn.net/wen_fei/article/details/82979497
https://ptorch.com/news/77.html
torchvision模塊
提供了很多計算視覺相關(guān)的數(shù)據(jù)集亚皂,以及較流行的模型等俱箱,參考:https://blog.csdn.net/zhenaoxi1077/article/details/80955607