小編寫這篇文章的一個(gè)主要目的,主要介紹的內(nèi)容是關(guān)于PyTorch的一些知識(shí),主要是介紹PyTorch nn.Embedding的一些使用方法,就具體的使用方法詳細(xì)內(nèi)容,下面給大家做一個(gè)詳細(xì)解答。
一、前置知識(shí)
1.1語(yǔ)料庫(kù)(Corpus)
太長(zhǎng)不看版:NLP任務(wù)所依賴的語(yǔ)言數(shù)據(jù)稱為語(yǔ)料庫(kù)。
詳細(xì)介紹版:語(yǔ)料庫(kù)(Corpus,復(fù)數(shù)是Corpora)是組織成數(shù)據(jù)集的真實(shí)文本或音頻的集合。此處的真實(shí)是指由該語(yǔ)言的母語(yǔ)者制作的文本或音頻。語(yǔ)料庫(kù)可以由從報(bào)紙、小說、食譜、廣播到電視節(jié)目、電影和推文的所有內(nèi)容組成。在自然語(yǔ)言處理中,語(yǔ)料庫(kù)包含可用于訓(xùn)練AI的文本和語(yǔ)音數(shù)據(jù)。
1.2詞元(Token)
為簡(jiǎn)便起見,假設(shè)我們的語(yǔ)料庫(kù)只有三個(gè)英文句子并且均已經(jīng)過處理(全部小寫+去掉標(biāo)點(diǎn)符號(hào)):
corpus=["he is an old worker","english is a useful tool","the cinema is far away"]
我們往往需要將其詞元化(tokenize)以成為一個(gè)序列,這里只需要簡(jiǎn)單的split即可:
def tokenize(corpus): return[sentence.split()for sentence in corpus] tokens=tokenize(corpus) print(tokens) #[['he','is','an','old','worker'],['english','is','a','useful','tool'],['the','cinema','is','far','away']]
這里我們是以單詞級(jí)別進(jìn)行詞元化,還可以以字符級(jí)別進(jìn)行詞元化。
1.3詞表(Vocabulary)
詞表不重復(fù)地包含了語(yǔ)料庫(kù)中的所有詞元,其實(shí)現(xiàn)方式十分容易:
vocab=set(sum(tokens,[])) print(vocab) #{'is','useful','an','old','far','the','away','a','he','tool','cinema','english','worker'}
詞表在NLP任務(wù)中往往并不是最重要的,我們需要為詞表中的每一個(gè)單詞分配唯一的索引并構(gòu)建單詞到索引的映射:word2idx。這里我們按照單詞出現(xiàn)的頻率來構(gòu)建word2idx。
from collections import Counter word2idx={ word:idx for idx,(word,freq)in enumerate( sorted(Counter(sum(tokens,[])).items(),key=lambda x:x[1],reverse=True)) } print(word2idx) #{'is':0,'he':1,'an':2,'old':3,'worker':4,'english':5,'a':6,'useful':7,'tool':8,'the':9,'cinema':10,'far':11,'away':12}
反過來,我們還可以構(gòu)建idx2word:
idx2word={idx:word for word,idx in word2idx.items()} print(idx2word) #{0:'is',1:'he',2:'an',3:'old',4:'worker',5:'english',6:'a',7:'useful',8:'tool',9:'the',10:'cinema',11:'far',12:'away'}
對(duì)于1.2節(jié)中的tokens,也可以轉(zhuǎn)化為索引的表示:
encoded_tokens=[[word2idx[token]for token in line]for line in tokens] print(encoded_tokens) #[[1,0,2,3,4],[5,0,6,7,8],[9,10,0,11,12]]
這種表示方式將在后續(xù)講解nn.Embedding時(shí)提到。
二、nn.Embedding基礎(chǔ)
2.1為什么要embedding?
RNN無法直接處理單詞,因此需要通過某種方法把單詞變成數(shù)字形式的向量才能作為RNN的輸入。這種把單詞映射到向量空間中的一個(gè)向量的做法稱為詞嵌入(word embedding),對(duì)應(yīng)的向量稱為詞向量(word vector)。
2.2基礎(chǔ)參數(shù)
我們首先講解nn.Embedding中的基礎(chǔ)參數(shù),了解它的基本用法后,再講解它的全部參數(shù)。
基礎(chǔ)參數(shù)如下:
nn.Embedding(num_embeddings,embedding_dim)
其中num_embeddings是詞表的大小,即len(vocab);embedding_dim是詞向量的維度。
我們使用第一章節(jié)的例子,此時(shí)詞表大小為12 12 12,不妨設(shè)嵌入后詞向量的維度是3 3 3(即將單詞嵌入到三維向量空間中),則embedding層應(yīng)該這樣創(chuàng)建:
torch.manual_seed(0)#為了復(fù)現(xiàn)性 emb=nn.Embedding(12,3)
embedding層中只有一個(gè)參數(shù)weight,在創(chuàng)建時(shí)它會(huì)從標(biāo)準(zhǔn)正態(tài)分布中進(jìn)行初始化:
print(emb.weight) #Parameter containing: #tensor([[-1.1258,-1.1524,-0.2506], #[-0.4339,0.8487,0.6920], #[-0.3160,-2.1152,0.3223], #[-1.2633,0.3500,0.3081], #[0.1198,1.2377,1.1168], #[-0.2473,-1.3527,-1.6959], #[0.5667,0.7935,0.4397], #[0.1124,0.6408,0.4412], #[-0.2159,-0.7425,0.5627], #[0.2596,0.5229,2.3022], #[-1.4689,-1.5867,1.2032], #[0.0845,-1.2001,-0.0048]],requires_grad=True)
這里我們可以把weight當(dāng)作embedding層的一個(gè)權(quán)重。
接下來再來看一下nn.Embedding的輸入。直觀來看,給定一個(gè)已經(jīng)詞元化的句子,將其中的單詞輸入到embedding層應(yīng)該得到相應(yīng)的詞向量。事實(shí)上,nn.Embedding接受的輸入并不是詞元化后的句子,而是它的索引形式,即第一章節(jié)中提到的encoded_tokens。
nn.Embedding可以接受任何形狀的張量作為輸入,但因?yàn)閭魅氲氖撬饕詮埩恐械拿總€(gè)數(shù)字都不應(yīng)超過len(vocab)-1,否則就會(huì)報(bào)錯(cuò)。接下來,nn.Embedding的作用就像一個(gè)查找表(Lookup Table)一樣,通過這些索引在weight中查找并返回相應(yīng)的詞向量。
print(emb.weight) #tensor([[-1.1258,-1.1524,-0.2506], #[-0.4339,0.8487,0.6920], #[-0.3160,-2.1152,0.3223], #[-1.2633,0.3500,0.3081], #[0.1198,1.2377,1.1168], #[-0.2473,-1.3527,-1.6959], #[0.5667,0.7935,0.4397], #[0.1124,0.6408,0.4412], #[-0.2159,-0.7425,0.5627], #[0.2596,0.5229,2.3022], #[-1.4689,-1.5867,1.2032], #[0.0845,-1.2001,-0.0048]],requires_grad=True) sentence=torch.tensor(encoded_tokens[0])#一共有三個(gè)句子,這里只使用第一個(gè)句子 print(sentence) #tensor([1,0,2,3,4]) print(emb(sentence)) #tensor([[-0.4339,0.8487,0.6920], #[-1.1258,-1.1524,-0.2506], #[-0.3160,-2.1152,0.3223], #[-1.2633,0.3500,0.3081], #[0.1198,1.2377,1.1168]],grad_fn=<EmbeddingBackward0>) print(emb.weight[sentence]==emb(sentence)) #tensor([[True,True,True], #[True,True,True], #[True,True,True], #[True,True,True], #[True,True,True]])
2.3 nn.Embedding與nn.Linear的區(qū)別
細(xì)心的讀者可能已經(jīng)看出nn.Embedding和nn.Linear似乎很像,那它們到底有什么區(qū)別呢?
回顧nn.Linear,若不開啟bias,設(shè)輸入向量為x,nn.Linear.weight對(duì)應(yīng)的矩陣為A(形狀為hidden_size×input_size),則計(jì)算方式為:
y=xAT
其中x,y均為行向量。
假如x是one-hot向量,第i個(gè)位置是1 1 1,那么y就是A T的第i i行。
現(xiàn)給定一個(gè)單詞w,假設(shè)它在word2idx中的索引就是i,在nn.Embedding中,我們根據(jù)這個(gè)索引i去查找emb.weight的第i行。而在nn.Linear中,我們則是將這個(gè)索引i編碼成一個(gè)one-hot向量,再去乘上對(duì)應(yīng)的權(quán)重矩陣得到矩陣的第i行。
請(qǐng)看下例:
torch.manual_seed(0) vocab_size=4#詞表大小為4 embedding_dim=3#詞向量維度為3 weight=torch.randn(4,3)#隨機(jī)初始化權(quán)重矩陣 #保持線性層和嵌入層具有相同的權(quán)重 linear_layer=nn.Linear(4,3,bias=False) linear_layer.weight.data=weight.T#注意轉(zhuǎn)置 emb_layer=nn.Embedding(4,3) emb_layer.weight.data=weight idx=torch.tensor(2)#假設(shè)某個(gè)單詞在word2idx中的索引為2 word=torch.tensor([0,0,1,0]).to(torch.float)#上述單詞的one-hot表示 print(emb_layer(idx)) #tensor([0.4033,0.8380,-0.7193],grad_fn=<EmbeddingBackward0>) print(linear_layer(word)) #tensor([0.4033,0.8380,-0.7193],grad_fn=<SqueezeBackward3>)
從中我們可以總結(jié)出:
nn.Linear接受向量作為輸入,而nn.Embedding則是接受離散的索引作為輸入;
nn.Embedding實(shí)際上就是輸入為one-hot向量,且不帶bias的nn.Linear。
此外,nn.Linear在運(yùn)算過程中做了矩陣乘法,而nn.Embedding是直接根據(jù)索引查表,因此在該情景下nn.Embedding的效率顯然更高。
????進(jìn)一步閱讀:[Stack Overflow]What is the difference between an Embedding Layer with a bias immediately afterwards and a Linear Layer in PyTorch?
2.4 nn.Embedding的更新問題
在查閱了PyTorch官方論壇和Stack Overflow的一些帖子后,發(fā)現(xiàn)有不少人對(duì)nn.Embedding中的權(quán)重weight是怎么更新的感到非常困惑。
????nn.Embedding的權(quán)重實(shí)際上就是詞嵌入本身
事實(shí)上,nn.Embedding.weight在更新的過程中既沒有采用Skip-gram也沒有采用CBOW。回顧最簡(jiǎn)單的多層感知機(jī),其中的nn.Linear.weight會(huì)隨著反向傳播自動(dòng)更新。當(dāng)我們把nn.Embedding視為一個(gè)特殊的nn.Linear后,其更新機(jī)制就不難理解了,無非就是按照梯度進(jìn)行更新罷了。
訓(xùn)練結(jié)束后,得到的詞嵌入是最適合當(dāng)前任務(wù)的詞嵌入,而非像word2vec,GloVe這種更為通用的詞嵌入。
當(dāng)然我們也可以在訓(xùn)練開始之前使用預(yù)訓(xùn)練的詞嵌入,例如上述提到的word2vec,但此時(shí)應(yīng)該考慮針對(duì)當(dāng)前任務(wù)重新訓(xùn)練或進(jìn)行微調(diào)。
假如我們已經(jīng)使用了預(yù)訓(xùn)練的詞嵌入并且不想讓它在訓(xùn)練過程中自我更新,那么可以嘗試凍結(jié)梯度,即:
emb.weight.requires_grad=False
三、nn.Embedding進(jìn)階
在這一章節(jié)中,我們會(huì)講解nn.Embedding的所有參數(shù)并介紹如何使用預(yù)訓(xùn)練的詞嵌入。
3.1全部參數(shù)
官方文檔:
padding_idx
我們知道,nn.Embedding雖然可以接受任意形狀的張量作為輸入,但絕大多數(shù)情況下,其輸入的形狀為batch_size×sequence_length,這要求同一個(gè)batch中的所有序列的長(zhǎng)度相同。
回顧1.2節(jié)中的例子,語(yǔ)料庫(kù)中的三個(gè)句子的長(zhǎng)度相同(擁有相同的單詞個(gè)數(shù)),但事實(shí)上這是博主特意選取的三個(gè)句子。現(xiàn)實(shí)任務(wù)中,很難保證同一個(gè)batch中的所有句子長(zhǎng)度都相同,因此我們需要對(duì)那些長(zhǎng)度較短的句子進(jìn)行填充。因?yàn)檩斎氲絥n.Embedding中的都是索引,所以我們也需要用索引進(jìn)行填充,那使用哪個(gè)索引最好呢?
假設(shè)語(yǔ)料庫(kù)為:
corpus=["he is an old worker","time tries truth","better late than never"] print(word2idx) #{'he':0,'is':1,'an':2,'old':3,'worker':4,'time':5,'tries':6,'truth':7,'better':8,'late':9,'than':10,'never':11} print(encoded_tokens) #[[0,1,2,3,4],[5,6,7],[8,9,10,11]]
我們可以在word2idx中新增一個(gè)詞元<pad>(代表填充詞元),并為其分配新的索引:
word2idx['<pad>']=12
對(duì)encoded_tokens進(jìn)行填充:
max_length=max([len(seq)for seq in encoded_tokens]) for i in range(len(encoded_tokens)): encoded_tokens<i>+=[word2idx['<pad>']]*(max_length-len(encoded_tokens<i>)) print(encoded_tokens) #[[0,1,2,3,4],[5,6,7,12,12],[8,9,10,11,12]]
創(chuàng)建embedding層并指定padding_idx:
emb=nn.Embedding(len(word2idx),3,padding_idx=12)#假設(shè)詞向量維度是3 print(emb.weight) #tensor([[1.5017,-1.1737,0.1742], #[-0.9511,-0.4172,1.5996], #[0.6306,1.4186,1.3872], #[-0.1833,1.4485,-0.3515], #[0.2474,-0.8514,-0.2448], #[0.4386,1.3905,0.0328], #[-0.1215,0.5504,0.1499], #[0.5954,-1.0845,1.9494], #[0.0668,1.1366,-0.3414], #[-0.0260,-0.1091,0.4937], #[0.4947,1.1701,-0.5660], #[1.1717,-0.3970,-1.4958], #[0.0000,0.0000,0.0000]],requires_grad=True)
可以看出填充詞元對(duì)應(yīng)的詞向量是零向量,并且在訓(xùn)練過程中填充詞元對(duì)應(yīng)的詞向量不會(huì)進(jìn)行更新(始終是零向量)。
padding_idx默認(rèn)為None,即不進(jìn)行填充。
max_norm
如果詞向量的范數(shù)超過了max_norm,則將其按范數(shù)歸一化至max_norm:
max_norm默認(rèn)為None,即不進(jìn)行歸一化。
norm_type
當(dāng)指定了max_norm時(shí),norm_type決定采用何種范數(shù)去計(jì)算。默認(rèn)是2-范數(shù)。
scale_grad_by_freq
若將該參數(shù)設(shè)置為True,則對(duì)詞向量w w w進(jìn)行更新時(shí),會(huì)根據(jù)它在一個(gè)batch中出現(xiàn)的頻率對(duì)相應(yīng)的梯度進(jìn)行縮放:
默認(rèn)為False。
sparse
若設(shè)置為True,則與Embedding.weight相關(guān)的梯度將變?yōu)橄∈鑿埩浚藭r(shí)優(yōu)化器只能選擇:SGD、SparseAdam和Adagrad。默認(rèn)為False。
3.2使用預(yù)訓(xùn)練的詞嵌入
有些情況下我們需要使用預(yù)訓(xùn)練的詞嵌入,這時(shí)候可以使用from_pretrained方法,如下:
torch.manual_seed(0) pretrained_embeddings=torch.randn(4,3) print(pretrained_embeddings) #tensor([[1.5410,-0.2934,-2.1788], #[0.5684,-1.0845,-1.3986], #[0.4033,0.8380,-0.7193], #[-0.4033,-0.5966,0.1820]]) emb=nn.Embedding(4,3).from_pretrained(pretrained_embeddings) print(emb.weight) #tensor([[1.5410,-0.2934,-2.1788], #[0.5684,-1.0845,-1.3986], #[0.4033,0.8380,-0.7193], #[-0.4033,-0.5966,0.1820]])
如果要避免預(yù)訓(xùn)練的詞嵌入在后續(xù)的訓(xùn)練過程中更新,可將freeze參數(shù)設(shè)置為True:
emb=nn.Embedding(4,3).from_pretrained(pretrained_embeddings,freeze=True)
綜上所述,這篇文章就給大家介紹到這里了,希望可以給大家?guī)韼椭?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/128362.html
摘要:截止到今天,已公開發(fā)行一周年。一年以來,社區(qū)中的用戶不斷做出貢獻(xiàn)和優(yōu)化,在此深表感謝。所以與衡量它的指標(biāo)包括在機(jī)器學(xué)習(xí)研究論文中的使用。來自香港科技大學(xué)的在上推出了面向普通觀眾的在線課程。 Yann LeCun Twitter截止到今天,PyTorch 已公開發(fā)行一周年。一年以來,我們致力于打造一個(gè)靈活的深度學(xué)習(xí)研究平臺(tái)。一年以來,PyTorch 社區(qū)中的用戶不斷做出貢獻(xiàn)和優(yōu)化,在此深表感謝...
摘要:經(jīng)過第一步的處理已經(jīng)把古詩(shī)詞詞語(yǔ)轉(zhuǎn)換為可以機(jī)器學(xué)習(xí)建模的數(shù)字形式,因?yàn)槲覀儾捎盟惴ㄟM(jìn)行古詩(shī)詞生成,所以還需要構(gòu)建輸入到輸出的映射處理。 LSTM 介紹 序列化數(shù)據(jù)即每個(gè)樣本和它之前的樣本存在關(guān)聯(lián),前一數(shù)據(jù)和后一個(gè)數(shù)據(jù)有順序關(guān)系。深度學(xué)習(xí)中有一個(gè)重要的分支是專門用來處理這樣的數(shù)據(jù)的——循環(huán)神經(jīng)網(wǎng)絡(luò)。循環(huán)神經(jīng)網(wǎng)絡(luò)廣泛應(yīng)用在自然語(yǔ)言處理領(lǐng)域(NLP),今天我們帶你從一個(gè)實(shí)際的例子出發(fā),介紹循...
摘要:本項(xiàng)目使用網(wǎng)絡(luò)上收集的對(duì)聯(lián)數(shù)據(jù)集地址作為訓(xùn)練數(shù)據(jù),運(yùn)用注意力機(jī)制網(wǎng)絡(luò)完成了根據(jù)上聯(lián)對(duì)下聯(lián)的任務(wù)。這種方式在一定程度上降低了輸出對(duì)位置的敏感性。而機(jī)制正是為了彌補(bǔ)這一缺陷而設(shè)計(jì)的。該類中有兩個(gè)方法,分別在訓(xùn)練和預(yù)測(cè)時(shí)應(yīng)用。 桃符早易朱紅紙,楊柳輕搖翡翠群 ——FlyAI Couplets 體驗(yàn)對(duì)對(duì)聯(lián)Demo: https://www.flyai.com/couplets s...
摘要:本項(xiàng)目使用網(wǎng)絡(luò)上收集的對(duì)聯(lián)數(shù)據(jù)集地址作為訓(xùn)練數(shù)據(jù),運(yùn)用注意力機(jī)制網(wǎng)絡(luò)完成了根據(jù)上聯(lián)對(duì)下聯(lián)的任務(wù)。這種方式在一定程度上降低了輸出對(duì)位置的敏感性。而機(jī)制正是為了彌補(bǔ)這一缺陷而設(shè)計(jì)的。該類中有兩個(gè)方法,分別在訓(xùn)練和預(yù)測(cè)時(shí)應(yīng)用。 桃符早易朱紅紙,楊柳輕搖翡翠群 ——FlyAI Couplets 體驗(yàn)對(duì)對(duì)聯(lián)Demo: https://www.flyai.com/couplets s...
閱讀 919·2023-01-14 11:38
閱讀 891·2023-01-14 11:04
閱讀 750·2023-01-14 10:48
閱讀 2039·2023-01-14 10:34
閱讀 956·2023-01-14 10:24
閱讀 835·2023-01-14 10:18
閱讀 506·2023-01-14 10:09
閱讀 583·2023-01-14 10:02