1. 線性回歸
(僅供學(xué)習(xí)班打卡使用)
損失函數(shù)
在模型訓(xùn)練中探入,我們需要衡量價(jià)格預(yù)測值與真實(shí)值之間的誤差箫锤。通常我們會(huì)選取一個(gè)非負(fù)數(shù)作為誤差卑吭,且數(shù)值越小表示誤差越小锦亦。一個(gè)常用的選擇是平方函數(shù)。 它在評估索引為的樣本誤差的表達(dá)式為
優(yōu)化函數(shù) - 隨機(jī)梯度下降
定義優(yōu)化函數(shù)
在這里優(yōu)化函數(shù)使用的是小批量隨機(jī)梯度下降:
def sgd(params, lr, batch_size):
for param in params:
param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track
訓(xùn)練模型(從零實(shí)現(xiàn))
# super parameters init
lr = 0.03
num_epochs = 5
#兩個(gè)超參數(shù)
net = linreg
loss = squared_loss
# training
for epoch in range(num_epochs): # training repeats num_epochs times
# in each epoch, all the samples in dataset will be used once
## 訓(xùn)練模型一共需要num_epochs個(gè)迭代周期
# 在每一個(gè)迭代周期中嫂拴,會(huì)使用訓(xùn)練數(shù)據(jù)集中所有樣本一次(假設(shè)樣本數(shù)能夠被批量大小整除)播揪。X
# 和y分別是小批量樣本的特征和標(biāo)簽
# X is the feature and y is the label of a batch sample
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y).sum()
# calculate the gradient of batch sample loss
l.backward()
# using small batch random gradient descent to iter model parameters
sgd([w, b], lr, batch_size)
# reset parameter gradient參數(shù)梯度數(shù)據(jù)清零,以備下一次存儲(chǔ)新的梯度數(shù)據(jù)筒狠。
w.grad.data.zero_()
b.grad.data.zero_()
train_l = loss(net(features, w, b), labels)
print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
線性回歸模型使用pytorch的簡潔實(shí)現(xiàn)
import torch
from torch import nn
import numpy as np
torch.manual_seed(1)
print(torch.__version__)
torch.set_default_tensor_type('torch.FloatTensor')
生成數(shù)據(jù)集
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
定義模型
class LinearNet(nn.Module):
def __init__(self, n_feature):
super(LinearNet, self).__init__() # call father function to init
self.linear = nn.Linear(n_feature, 1) # function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`
def forward(self, x):
y = self.linear(x)
return y
net = LinearNet(num_inputs)
print(net)
# ways to init a multilayer network
# method one
net = nn.Sequential(
nn.Linear(num_inputs, 1)
# other layers can be added here
)
# method two
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
# net.add_module ......
# method three
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
('linear', nn.Linear(num_inputs, 1))
# ......
]))
print(net)
print(net[0])
定義損失函數(shù)
loss = nn.MSELoss() # nn built-in squared loss function
# function prototype: `torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')`
定義優(yōu)化函數(shù)
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.03) # built-in random gradient descent function
print(optimizer) # function prototype: `torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)`
2.softmax回歸
softmax的基本概念
分類問題
一個(gè)簡單的圖像分類問題猪狈,輸入圖像的高和寬均為2像素,色彩為灰度辩恼。
圖像中的4像素分別記為雇庙。
假設(shè)真實(shí)標(biāo)簽為狗、貓或者雞灶伊,這些標(biāo)簽對應(yīng)的離散值為疆前。
我們通常使用離散的數(shù)值來表示類別,例如谁帕。權(quán)重矢量
- 神經(jīng)網(wǎng)絡(luò)圖
下圖用神經(jīng)網(wǎng)絡(luò)圖描繪了上面的計(jì)算峡继。softmax回歸同線性回歸一樣,也是一個(gè)單層神經(jīng)網(wǎng)絡(luò)匈挖。由于每個(gè)輸出的計(jì)算都要依賴于所有的輸入碾牌,softmax回歸的輸出層也是一個(gè)全連接層。
既然分類問題需要得到離散的預(yù)測輸出儡循,一個(gè)簡單的辦法是將輸出值當(dāng)作預(yù)測類別是的置信度舶吗,并將值最大的輸出所對應(yīng)的類作為預(yù)測輸出,即輸出 择膝。例如誓琼,如果分別為,由于最大,那么預(yù)測類別為2腹侣,其代表貓叔收。
- 輸出問題
直接使用輸出層的輸出有兩個(gè)問題:- 一方面,由于輸出層的輸出值的范圍不確定傲隶,我們難以直觀上判斷這些值的意義饺律。例如,剛才舉的例子中的輸出值10表示“很置信”圖像類別為貓跺株,因?yàn)樵撦敵鲋凳瞧渌麅深惖妮敵鲋档?00倍复濒。但如果,那么輸出值10卻又表示圖像類別為貓的概率很低乒省。
- 另一方面巧颈,由于真實(shí)標(biāo)簽是離散值,這些離散值與不確定范圍的輸出值之間的誤差難以衡量袖扛。
softmax運(yùn)算符(softmax operator)解決了以上兩個(gè)問題砸泛。它通過下式將輸出值變換成值為正且和為1的概率分布:
其中
容易看出且,因此是一個(gè)合法的概率分布攻锰。這時(shí)候晾嘶,如果,不管和的值是多少娶吞,我們都知道圖像類別為貓的概率是80%。此外械姻,我們注意到
因此softmax運(yùn)算不改變預(yù)測類別輸出妒蛇。
- 計(jì)算效率
- 單樣本矢量計(jì)算表達(dá)式
為了提高計(jì)算效率,我們可以將單樣本分類通過矢量計(jì)算來表達(dá)楷拳。在上面的圖像分類問題中绣夺,假設(shè)softmax回歸的權(quán)重和偏差參數(shù)分別為
- 單樣本矢量計(jì)算表達(dá)式
設(shè)高和寬分別為2個(gè)像素的圖像樣本的特征為
輸出層的輸出為
預(yù)測為狗欢揖、貓或雞的概率分布為
softmax回歸對樣本分類的矢量計(jì)算表達(dá)式為
- 小批量矢量計(jì)算表達(dá)式
為了進(jìn)一步提升計(jì)算效率,我們通常對小批量數(shù)據(jù)做矢量計(jì)算她混。廣義上講烈钞,給定一個(gè)小批量樣本,其批量大小為坤按,輸入個(gè)數(shù)(特征數(shù))為毯欣,輸出個(gè)數(shù)(類別數(shù))為。設(shè)批量特征為臭脓。假設(shè)softmax回歸的權(quán)重和偏差參數(shù)分別為和。softmax回歸的矢量計(jì)算表達(dá)式為
其中的加法運(yùn)算使用了廣播機(jī)制砚作,且這兩個(gè)矩陣的第行分別為樣本的輸出和概率分布窘奏。
交叉熵?fù)p失函數(shù)
對于樣本,我們構(gòu)造向量 葫录,使其第(樣本類別的離散數(shù)值)個(gè)元素為1着裹,其余為0。這樣我們的訓(xùn)練目標(biāo)可以設(shè)為使預(yù)測概率分布盡可能接近真實(shí)的標(biāo)簽概率分布压昼。
- 平方損失估計(jì)
然而求冷,想要預(yù)測分類結(jié)果正確,我們其實(shí)并不需要預(yù)測概率完全等于標(biāo)簽概率窍霞。例如匠题,在圖像分類的例子里,如果但金,那么我們只需要比其他兩個(gè)預(yù)測值和大就行了韭山。即使值為0.6,不管其他兩個(gè)預(yù)測值為多少冷溃,類別預(yù)測均正確钱磅。而平方損失則過于嚴(yán)格,例如比的損失要小很多似枕,雖然兩者都有同樣正確的分類預(yù)測結(jié)果盖淡。
改善上述問題的一個(gè)方法是使用更適合衡量兩個(gè)概率分布差異的測量函數(shù)。其中凿歼,交叉熵(cross entropy)是一個(gè)常用的衡量方法:
其中帶下標(biāo)的是向量中非0即1的元素褪迟,需要注意將它與樣本類別的離散數(shù)值,即不帶下標(biāo)的區(qū)分答憔。在上式中味赃,我們知道向量中只有第個(gè)元素為1,其余全為0虐拓,于是心俗。也就是說,交叉熵只關(guān)心對正確類別的預(yù)測概率蓉驹,因?yàn)橹灰渲底銐虼蟪情唬涂梢源_保分類結(jié)果正確。當(dāng)然戒幔,遇到一個(gè)樣本有多個(gè)標(biāo)簽時(shí)吠谢,例如圖像里含有不止一個(gè)物體時(shí),我們并不能做這一步簡化诗茎。但即便對于這種情況工坊,交叉熵同樣只關(guān)心對圖像中出現(xiàn)的物體類別的預(yù)測概率献汗。
假設(shè)訓(xùn)練數(shù)據(jù)集的樣本數(shù)為,交叉熵?fù)p失函數(shù)定義為
其中代表模型參數(shù)王污。同樣地罢吃,如果每個(gè)樣本只有一個(gè)標(biāo)簽,那么交叉熵?fù)p失可以簡寫成昭齐。從另一個(gè)角度來看尿招,我們知道最小化等價(jià)于最大化,即最小化交叉熵?fù)p失函數(shù)等價(jià)于最大化訓(xùn)練數(shù)據(jù)集所有標(biāo)簽類別的聯(lián)合預(yù)測概率阱驾。
softmax回歸模型
def net(X):
return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)
定義損失函數(shù)
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))
def cross_entropy(y_hat, y):
return - torch.log(y_hat.gather(1, y.view(-1, 1)))
定義準(zhǔn)確率
def accuracy(y_hat, y):
return (y_hat.argmax(dim=1) == y).float().mean().item()
print(accuracy(y_hat, y))
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n
訓(xùn)練模型
num_epochs, lr = 5, 0.1
# 本函數(shù)已保存在d2lzh_pytorch包中方便以后使用
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
params=None, lr=None, optimizer=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
# 梯度清零
if optimizer is not None:
optimizer.zero_grad()
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()
if optimizer is None:
d2l.sgd(params, lr, batch_size)
else:
optimizer.step()
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
模型預(yù)測
現(xiàn)在我們的模型訓(xùn)練完了,可以進(jìn)行一下預(yù)測里覆,我們的這個(gè)模型訓(xùn)練的到底準(zhǔn)確不準(zhǔn)確丧荐。
現(xiàn)在就可以演示如何對圖像進(jìn)行分類了。給定一系列圖像(第三行圖像輸出)喧枷,我們比較一下它們的真實(shí)標(biāo)簽(第一行文本輸出)和模型預(yù)測結(jié)果(第二行文本輸出)虹统。
X, y = iter(test_iter).next()
true_labels = d2l.get_fashion_mnist_labels(y.numpy())
pred_labels = d2l.get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
d2l.show_fashion_mnist(X[0:9], titles[0:9])
多層感知機(jī)的基本知識(shí)
深度學(xué)習(xí)主要關(guān)注多層模型。在這里隧甚,我們將以多層感知機(jī)(multilayer perceptron车荔,MLP)為例,介紹多層神經(jīng)網(wǎng)絡(luò)的概念戚扳。
隱藏層
下圖展示了一個(gè)多層感知機(jī)的神經(jīng)網(wǎng)絡(luò)圖忧便,它含有一個(gè)隱藏層,該層中有5個(gè)隱藏單元帽借。
表達(dá)公式
具體來說茬腿,給定一個(gè)小批量樣本,其批量大小為宜雀,輸入個(gè)數(shù)為。假設(shè)多層感知機(jī)只有一個(gè)隱藏層握础,其中隱藏單元個(gè)數(shù)為辐董。記隱藏層的輸出(也稱為隱藏層變量或隱藏變量)為,有禀综。因?yàn)殡[藏層和輸出層均是全連接層简烘,可以設(shè)隱藏層的權(quán)重參數(shù)和偏差參數(shù)分別為和 笛臣,輸出層的權(quán)重和偏差參數(shù)分別為和条摸。
我們先來看一種含單隱藏層的多層感知機(jī)的設(shè)計(jì)欠窒。其輸出的計(jì)算為
也就是將隱藏層的輸出直接作為輸出層的輸入。如果將以上兩個(gè)式子聯(lián)立起來型将,可以得到
從聯(lián)立后的式子可以看出,雖然神經(jīng)網(wǎng)絡(luò)引入了隱藏層腕铸,卻依然等價(jià)于一個(gè)單層神經(jīng)網(wǎng)絡(luò):其中輸出層權(quán)重參數(shù)為惜犀,偏差參數(shù)為虽界。不難發(fā)現(xiàn),即便再添加更多的隱藏層酪耳,以上設(shè)計(jì)依然只能與僅含輸出層的單層神經(jīng)網(wǎng)絡(luò)等價(jià)浓恳。
激活函數(shù)
上述問題的根源在于全連接層只是對數(shù)據(jù)做仿射變換(affine transformation),而多個(gè)仿射變換的疊加仍然是一個(gè)仿射變換碗暗。解決問題的一個(gè)方法是引入非線性變換颈将,例如對隱藏變量使用按元素運(yùn)算的非線性函數(shù)進(jìn)行變換,然后再作為下一個(gè)全連接層的輸入言疗。這個(gè)非線性函數(shù)被稱為激活函數(shù)(activation function)晴圾。
下面我們介紹幾個(gè)常用的激活函數(shù):
1. ReLU函數(shù)
ReLU(rectified linear unit)函數(shù)提供了一個(gè)很簡單的非線性變換。給定元素噪奄,該函數(shù)定義為
可以看出死姚,ReLU函數(shù)只保留正數(shù)元素,并將負(fù)數(shù)元素清零勤篮。為了直觀地觀察這一非線性變換都毒,我們先定義一個(gè)繪圖函數(shù)xyplot。
%matplotlib inline
import torch
import numpy as np
import matplotlib.pyplot as plt
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
def xyplot(x_vals, y_vals, name):
# d2l.set_figsize(figsize=(5, 2.5))
plt.plot(x_vals.detach().numpy(), y_vals.detach().numpy())
plt.xlabel('x')
plt.ylabel(name + '(x)')
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = x.relu()
xyplot(x, y, 'relu')
y.sum().backward()
xyplot(x, x.grad, 'grad of relu')
2.Sigmoid函數(shù)
sigmoid函數(shù)可以將元素的值變換到0和1之間:
y = x.sigmoid()
xyplot(x, y, 'sigmoid')
x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, 'grad of sigmoid')
3.tanh函數(shù)
tanh(雙曲正切)函數(shù)可以將元素的值變換到-1和1之間:
我們接著繪制tanh函數(shù)碰缔。當(dāng)輸入接近0時(shí)账劲,tanh函數(shù)接近線性變換。雖然該函數(shù)的形狀和sigmoid函數(shù)的形狀很像金抡,但tanh函數(shù)在坐標(biāo)系的原點(diǎn)上對稱瀑焦。
y = x.tanh()
xyplot(x, y, 'tanh')
x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, 'grad of tanh')
關(guān)于激活函數(shù)的選擇
ReLu函數(shù)是一個(gè)通用的激活函數(shù),目前在大多數(shù)情況下使用梗肝。但是榛瓮,ReLU函數(shù)只能在隱藏層中使用。
用于分類器時(shí)巫击,sigmoid函數(shù)及其組合通常效果更好禀晓。由于梯度消失問題精续,有時(shí)要避免使用sigmoid和tanh函數(shù)。
在神經(jīng)網(wǎng)絡(luò)層數(shù)較多的時(shí)候匆绣,最好使用ReLu函數(shù)驻右,ReLu函數(shù)比較簡單計(jì)算量少,而sigmoid和tanh函數(shù)計(jì)算量大很多崎淳。
在選擇激活函數(shù)的時(shí)候可以先選用ReLu函數(shù)如果效果不理想可以嘗試其他激活函數(shù)堪夭。
循環(huán)神經(jīng)網(wǎng)絡(luò)
語言模型
一段自然語言文本可以看作是一個(gè)離散時(shí)間序列,給定一個(gè)長度為的詞的序列拣凹,語言模型的目標(biāo)就是評估該序列是否合理森爽,即計(jì)算該序列的概率:
本節(jié)我們介紹基于統(tǒng)計(jì)的語言模型,主要是元語法(-gram)嚣镜。在后續(xù)內(nèi)容中爬迟,我們將會(huì)介紹基于神經(jīng)網(wǎng)絡(luò)的語言模型。
語言模型
一段自然語言文本可以看作是一個(gè)離散時(shí)間序列菊匿,給定一個(gè)長度為的詞的序列付呕,語言模型的目標(biāo)就是評估該序列是否合理,即計(jì)算該序列的概率:
本節(jié)我們介紹基于統(tǒng)計(jì)的語言模型跌捆,主要是元語法(-gram)徽职。在后續(xù)內(nèi)容中,我們將會(huì)介紹基于神經(jīng)網(wǎng)絡(luò)的語言模型佩厚。
語言模型
假設(shè)序列中的每個(gè)詞是依次生成的姆钉,我們有
例如,一段含有4個(gè)詞的文本序列的概率
語言模型的參數(shù)就是詞的概率以及給定前幾個(gè)詞情況下的條件概率抄瓦。設(shè)訓(xùn)練數(shù)據(jù)集為一個(gè)大型文本語料庫潮瓶,如維基百科的所有條目,詞的概率可以通過該詞在訓(xùn)練數(shù)據(jù)集中的相對詞頻來計(jì)算钙姊,例如毯辅,的概率可以計(jì)算為:
其中為語料庫中以作為第一個(gè)詞的文本的數(shù)量,為語料庫中文本的總數(shù)量煞额。
類似的悉罕,給定情況下,的條件概率可以計(jì)算為:
其中為語料庫中以作為第一個(gè)詞立镶,作為第二個(gè)詞的文本的數(shù)量。
n元語法
序列長度增加类早,計(jì)算和存儲(chǔ)多個(gè)詞共同出現(xiàn)的概率的復(fù)雜度會(huì)呈指數(shù)級增加媚媒。元語法通過馬爾可夫假設(shè)簡化模型,馬爾科夫假設(shè)是指一個(gè)詞的出現(xiàn)只與前面個(gè)詞相關(guān)涩僻,即階馬爾可夫鏈(Markov chain of order )缭召,如果栈顷,那么有∏断铮基于階馬爾可夫鏈萄凤,我們可以將語言模型改寫為
以上也叫元語法(-grams),它是基于階馬爾可夫鏈的概率語言模型搪哪。例如靡努,當(dāng)時(shí),含有4個(gè)詞的文本序列的概率就可以改寫為:
當(dāng)分別為1晓折、2和3時(shí)惑朦,我們將其分別稱作一元語法(unigram)、二元語法(bigram)和三元語法(trigram)漓概。例如漾月,長度為4的序列在一元語法、二元語法和三元語法中的概率分別為
當(dāng)較小時(shí)胃珍,元語法往往并不準(zhǔn)確梁肿。例如,在一元語法中觅彰,由三個(gè)詞組成的句子“你走先”和“你先走”的概率是一樣的吩蔑。然而,當(dāng)較大時(shí)缔莲,元語法需要計(jì)算并存儲(chǔ)大量的詞頻和多詞相鄰頻率哥纫。
時(shí)序數(shù)據(jù)的采樣
在訓(xùn)練中我們需要每次隨機(jī)讀取小批量樣本和標(biāo)簽。與之前章節(jié)的實(shí)驗(yàn)數(shù)據(jù)不同的是痴奏,時(shí)序數(shù)據(jù)的一個(gè)樣本通常包含連續(xù)的字符蛀骇。假設(shè)時(shí)間步數(shù)為5,樣本序列為5個(gè)字符读拆,即“想”“要”“有”“直”“升”擅憔。該樣本的標(biāo)簽序列為這些字符分別在訓(xùn)練集中的下一個(gè)字符,即“要”“有”“直”“升”“機(jī)”檐晕,即=“想要有直升”暑诸,=“要有直升機(jī)”。
現(xiàn)在我們考慮序列“想要有直升機(jī)辟灰,想要和你飛到宇宙去”个榕,如果時(shí)間步數(shù)為5,有以下可能的樣本和標(biāo)簽:
- :“想要有直升”芥喇,:“要有直升機(jī)”
- :“要有直升機(jī)”西采,:“有直升機(jī),”
- :“有直升機(jī)继控,”械馆,:“直升機(jī)胖眷,想”
- ...
- :“要和你飛到”,:“和你飛到宇”
- :“和你飛到宇”霹崎,:“你飛到宇宙”
- :“你飛到宇宙”珊搀,:“飛到宇宙去”
可以看到,如果序列的長度為尾菇,時(shí)間步數(shù)為境析,那么一共有個(gè)合法的樣本,但是這些樣本有大量的重合错沽,我們通常采用更加高效的采樣方式簿晓。我們有兩種方式對時(shí)序數(shù)據(jù)進(jìn)行采樣,分別是隨機(jī)采樣和相鄰采樣千埃。
隨機(jī)采樣
下面的代碼每次從數(shù)據(jù)里隨機(jī)采樣一個(gè)小批量憔儿。其中批量大小batch_size
是每個(gè)小批量的樣本數(shù),num_steps
是每個(gè)樣本所包含的時(shí)間步數(shù)放可。
在隨機(jī)采樣中谒臼,每個(gè)樣本是原始序列上任意截取的一段序列,相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置不一定相毗鄰耀里。
import torch
import random
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
# 減1是因?yàn)閷τ陂L度為n的序列蜈缤,X最多只有包含其中的前n - 1個(gè)字符
num_examples = (len(corpus_indices) - 1) // num_steps # 下取整,得到不重疊情況下的樣本個(gè)數(shù)
example_indices = [i * num_steps for i in range(num_examples)] # 每個(gè)樣本的第一個(gè)字符在corpus_indices中的下標(biāo)
random.shuffle(example_indices)
def _data(i):
# 返回從i開始的長為num_steps的序列
return corpus_indices[i: i + num_steps]
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
for i in range(0, num_examples, batch_size):
# 每次選出batch_size個(gè)隨機(jī)樣本
batch_indices = example_indices[i: i + batch_size] # 當(dāng)前batch的各個(gè)樣本的首字符的下標(biāo)
X = [_data(j) for j in batch_indices]
Y = [_data(j + 1) for j in batch_indices]
yield torch.tensor(X, device=device), torch.tensor(Y, device=device)
測試一下這個(gè)函數(shù)冯挎,我們輸入從0到29的連續(xù)整數(shù)作為一個(gè)人工序列底哥,設(shè)批量大小和時(shí)間步數(shù)分別為2和6,打印隨機(jī)采樣每次讀取的小批量樣本的輸入X
和標(biāo)簽Y
房官。
my_seq = list(range(30))
for X, Y in data_iter_random(my_seq, batch_size=2, num_steps=6):
print('X: ', X, '\nY:', Y, '\n')
相鄰采樣
在相鄰采樣中趾徽,相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置相毗鄰。
def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
corpus_len = len(corpus_indices) // batch_size * batch_size # 保留下來的序列的長度
corpus_indices = corpus_indices[: corpus_len] # 僅保留前corpus_len個(gè)字符
indices = torch.tensor(corpus_indices, device=device)
indices = indices.view(batch_size, -1) # resize成(batch_size, )
batch_num = (indices.shape[1] - 1) // num_steps
for i in range(batch_num):
i = i * num_steps
X = indices[:, i: i + num_steps]
Y = indices[:, i + 1: i + num_steps + 1]
yield X, Y
同樣的設(shè)置下翰守,打印相鄰采樣每次讀取的小批量樣本的輸入X
和標(biāo)簽Y
孵奶。相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置相毗鄰。
for X, Y in data_iter_consecutive(my_seq, batch_size=2, num_steps=6):
print('X: ', X, '\nY:', Y, '\n')
GRU?控循環(huán)神經(jīng)?絡(luò)
RNN存在的問題:梯度較容易出現(xiàn)衰減或爆炸(BPTT)
?控循環(huán)神經(jīng)?絡(luò):捕捉時(shí)間序列中時(shí)間步距離較?的依賴關(guān)系
RNN:
GRU:
? 重置?有助于捕捉時(shí)間序列?短期的依賴關(guān)系蜡峰;
? 更新?有助于捕捉時(shí)間序列??期的依賴關(guān)系了袁。
GRU模型
def gru(inputs, state, params):
W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
for X in inputs:
Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
H = Z * H + (1 - Z) * H_tilda
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)
訓(xùn)練模型
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
vocab_size, device, corpus_indices, idx_to_char,
char_to_idx, False, num_epochs, num_steps, lr,
clipping_theta, batch_size, pred_period, pred_len,
prefixes)
LSTM
** 長短期記憶long short-term memory **:
遺忘門:控制上一時(shí)間步的記憶細(xì)胞
輸入門:控制當(dāng)前時(shí)間步的輸入
輸出門:控制從記憶細(xì)胞到隱藏狀態(tài)
記憶細(xì)胞:?種特殊的隱藏狀態(tài)的信息的流動(dòng)
初始化參數(shù)
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)
def get_params():
def _one(shape):
ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
return torch.nn.Parameter(ts, requires_grad=True)
def _three():
return (_one((num_inputs, num_hiddens)),
_one((num_hiddens, num_hiddens)),
torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
W_xi, W_hi, b_i = _three() # 輸入門參數(shù)
W_xf, W_hf, b_f = _three() # 遺忘門參數(shù)
W_xo, W_ho, b_o = _three() # 輸出門參數(shù)
W_xc, W_hc, b_c = _three() # 候選記憶細(xì)胞參數(shù)
# 輸出層參數(shù)
W_hq = _one((num_hiddens, num_outputs))
b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])
def init_lstm_state(batch_size, num_hiddens, device):
return (torch.zeros((batch_size, num_hiddens), device=device),
torch.zeros((batch_size, num_hiddens), device=device))
LSTM模型
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * C.tanh()
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H, C)
訓(xùn)練模型
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,
vocab_size, device, corpus_indices, idx_to_char,
char_to_idx, False, num_epochs, num_steps, lr,
clipping_theta, batch_size, pred_period, pred_len,
prefixes)
深度循環(huán)神經(jīng)網(wǎng)絡(luò)
num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率
gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
雙向循環(huán)神經(jīng)網(wǎng)絡(luò)
num_hiddens=128
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e-2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率
gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens,bidirectional=True)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)