小編寫這篇文章的一個主要目的,主要介紹的內容是關于PyTorch的一些知識,主要是介紹PyTorch nn.Embedding的一些使用方法,就具體的使用方法詳細內容,下面給大家做一個詳細解答。
一、前置知識
1.1語料庫(Corpus)
太長不看版:NLP任務所依賴的語言數據稱為語料庫。
詳細介紹版:語料庫(Corpus,復數是Corpora)是組織成數據集的真實文本或音頻的集合。此處的真實是指由該語言的母語者制作的文本或音頻。語料庫可以由從報紙、小說、食譜、廣播到電視節目、電影和推文的所有內容組成。在自然語言處理中,語料庫包含可用于訓練AI的文本和語音數據。
1.2詞元(Token)
為簡便起見,假設我們的語料庫只有三個英文句子并且均已經過處理(全部小寫+去掉標點符號):
corpus=["he is an old worker","english is a useful tool","the cinema is far away"]
我們往往需要將其詞元化(tokenize)以成為一個序列,這里只需要簡單的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']]
這里我們是以單詞級別進行詞元化,還可以以字符級別進行詞元化。
1.3詞表(Vocabulary)
詞表不重復地包含了語料庫中的所有詞元,其實現方式十分容易:
vocab=set(sum(tokens,[])) print(vocab) #{'is','useful','an','old','far','the','away','a','he','tool','cinema','english','worker'}
詞表在NLP任務中往往并不是最重要的,我們需要為詞表中的每一個單詞分配唯一的索引并構建單詞到索引的映射:word2idx。這里我們按照單詞出現的頻率來構建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}
反過來,我們還可以構建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'}
對于1.2節中的tokens,也可以轉化為索引的表示:
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]]
這種表示方式將在后續講解nn.Embedding時提到。
二、nn.Embedding基礎
2.1為什么要embedding?
RNN無法直接處理單詞,因此需要通過某種方法把單詞變成數字形式的向量才能作為RNN的輸入。這種把單詞映射到向量空間中的一個向量的做法稱為詞嵌入(word embedding),對應的向量稱為詞向量(word vector)。
2.2基礎參數
我們首先講解nn.Embedding中的基礎參數,了解它的基本用法后,再講解它的全部參數。
基礎參數如下:
nn.Embedding(num_embeddings,embedding_dim)
其中num_embeddings是詞表的大小,即len(vocab);embedding_dim是詞向量的維度。
我們使用第一章節的例子,此時詞表大小為12 12 12,不妨設嵌入后詞向量的維度是3 3 3(即將單詞嵌入到三維向量空間中),則embedding層應該這樣創建:
torch.manual_seed(0)#為了復現性 emb=nn.Embedding(12,3)
embedding層中只有一個參數weight,在創建時它會從標準正態分布中進行初始化:
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當作embedding層的一個權重。
接下來再來看一下nn.Embedding的輸入。直觀來看,給定一個已經詞元化的句子,將其中的單詞輸入到embedding層應該得到相應的詞向量。事實上,nn.Embedding接受的輸入并不是詞元化后的句子,而是它的索引形式,即第一章節中提到的encoded_tokens。
nn.Embedding可以接受任何形狀的張量作為輸入,但因為傳入的是索引,所以張量中的每個數字都不應超過len(vocab)-1,否則就會報錯。接下來,nn.Embedding的作用就像一個查找表(Lookup Table)一樣,通過這些索引在weight中查找并返回相應的詞向量。
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])#一共有三個句子,這里只使用第一個句子 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的區別
細心的讀者可能已經看出nn.Embedding和nn.Linear似乎很像,那它們到底有什么區別呢?
回顧nn.Linear,若不開啟bias,設輸入向量為x,nn.Linear.weight對應的矩陣為A(形狀為hidden_size×input_size),則計算方式為:
y=xAT
其中x,y均為行向量。
假如x是one-hot向量,第i個位置是1 1 1,那么y就是A T的第i i行。
現給定一個單詞w,假設它在word2idx中的索引就是i,在nn.Embedding中,我們根據這個索引i去查找emb.weight的第i行。而在nn.Linear中,我們則是將這個索引i編碼成一個one-hot向量,再去乘上對應的權重矩陣得到矩陣的第i行。
請看下例:
torch.manual_seed(0) vocab_size=4#詞表大小為4 embedding_dim=3#詞向量維度為3 weight=torch.randn(4,3)#隨機初始化權重矩陣 #保持線性層和嵌入層具有相同的權重 linear_layer=nn.Linear(4,3,bias=False) linear_layer.weight.data=weight.T#注意轉置 emb_layer=nn.Embedding(4,3) emb_layer.weight.data=weight idx=torch.tensor(2)#假設某個單詞在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>)
從中我們可以總結出:
nn.Linear接受向量作為輸入,而nn.Embedding則是接受離散的索引作為輸入;
nn.Embedding實際上就是輸入為one-hot向量,且不帶bias的nn.Linear。
此外,nn.Linear在運算過程中做了矩陣乘法,而nn.Embedding是直接根據索引查表,因此在該情景下nn.Embedding的效率顯然更高。
????進一步閱讀:[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的一些帖子后,發現有不少人對nn.Embedding中的權重weight是怎么更新的感到非常困惑。
????nn.Embedding的權重實際上就是詞嵌入本身
事實上,nn.Embedding.weight在更新的過程中既沒有采用Skip-gram也沒有采用CBOW。回顧最簡單的多層感知機,其中的nn.Linear.weight會隨著反向傳播自動更新。當我們把nn.Embedding視為一個特殊的nn.Linear后,其更新機制就不難理解了,無非就是按照梯度進行更新罷了。
訓練結束后,得到的詞嵌入是最適合當前任務的詞嵌入,而非像word2vec,GloVe這種更為通用的詞嵌入。
當然我們也可以在訓練開始之前使用預訓練的詞嵌入,例如上述提到的word2vec,但此時應該考慮針對當前任務重新訓練或進行微調。
假如我們已經使用了預訓練的詞嵌入并且不想讓它在訓練過程中自我更新,那么可以嘗試凍結梯度,即:
emb.weight.requires_grad=False
三、nn.Embedding進階
在這一章節中,我們會講解nn.Embedding的所有參數并介紹如何使用預訓練的詞嵌入。
3.1全部參數
官方文檔:
padding_idx
我們知道,nn.Embedding雖然可以接受任意形狀的張量作為輸入,但絕大多數情況下,其輸入的形狀為batch_size×sequence_length,這要求同一個batch中的所有序列的長度相同。
回顧1.2節中的例子,語料庫中的三個句子的長度相同(擁有相同的單詞個數),但事實上這是博主特意選取的三個句子。現實任務中,很難保證同一個batch中的所有句子長度都相同,因此我們需要對那些長度較短的句子進行填充。因為輸入到nn.Embedding中的都是索引,所以我們也需要用索引進行填充,那使用哪個索引最好呢?
假設語料庫為:
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中新增一個詞元<pad>(代表填充詞元),并為其分配新的索引:
word2idx['<pad>']=12
對encoded_tokens進行填充:
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]]
創建embedding層并指定padding_idx:
emb=nn.Embedding(len(word2idx),3,padding_idx=12)#假設詞向量維度是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)
可以看出填充詞元對應的詞向量是零向量,并且在訓練過程中填充詞元對應的詞向量不會進行更新(始終是零向量)。
padding_idx默認為None,即不進行填充。
max_norm
如果詞向量的范數超過了max_norm,則將其按范數歸一化至max_norm:
max_norm默認為None,即不進行歸一化。
norm_type
當指定了max_norm時,norm_type決定采用何種范數去計算。默認是2-范數。
scale_grad_by_freq
若將該參數設置為True,則對詞向量w w w進行更新時,會根據它在一個batch中出現的頻率對相應的梯度進行縮放:
默認為False。
sparse
若設置為True,則與Embedding.weight相關的梯度將變為稀疏張量,此時優化器只能選擇:SGD、SparseAdam和Adagrad。默認為False。
3.2使用預訓練的詞嵌入
有些情況下我們需要使用預訓練的詞嵌入,這時候可以使用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]])
如果要避免預訓練的詞嵌入在后續的訓練過程中更新,可將freeze參數設置為True:
emb=nn.Embedding(4,3).from_pretrained(pretrained_embeddings,freeze=True)
綜上所述,這篇文章就給大家介紹到這里了,希望可以給大家帶來幫助。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/128362.html
摘要:截止到今天,已公開發行一周年。一年以來,社區中的用戶不斷做出貢獻和優化,在此深表感謝。所以與衡量它的指標包括在機器學習研究論文中的使用。來自香港科技大學的在上推出了面向普通觀眾的在線課程。 Yann LeCun Twitter截止到今天,PyTorch 已公開發行一周年。一年以來,我們致力于打造一個靈活的深度學習研究平臺。一年以來,PyTorch 社區中的用戶不斷做出貢獻和優化,在此深表感謝...
摘要:經過第一步的處理已經把古詩詞詞語轉換為可以機器學習建模的數字形式,因為我們采用算法進行古詩詞生成,所以還需要構建輸入到輸出的映射處理。 LSTM 介紹 序列化數據即每個樣本和它之前的樣本存在關聯,前一數據和后一個數據有順序關系。深度學習中有一個重要的分支是專門用來處理這樣的數據的——循環神經網絡。循環神經網絡廣泛應用在自然語言處理領域(NLP),今天我們帶你從一個實際的例子出發,介紹循...
摘要:本項目使用網絡上收集的對聯數據集地址作為訓練數據,運用注意力機制網絡完成了根據上聯對下聯的任務。這種方式在一定程度上降低了輸出對位置的敏感性。而機制正是為了彌補這一缺陷而設計的。該類中有兩個方法,分別在訓練和預測時應用。 桃符早易朱紅紙,楊柳輕搖翡翠群 ——FlyAI Couplets 體驗對對聯Demo: https://www.flyai.com/couplets s...
摘要:本項目使用網絡上收集的對聯數據集地址作為訓練數據,運用注意力機制網絡完成了根據上聯對下聯的任務。這種方式在一定程度上降低了輸出對位置的敏感性。而機制正是為了彌補這一缺陷而設計的。該類中有兩個方法,分別在訓練和預測時應用。 桃符早易朱紅紙,楊柳輕搖翡翠群 ——FlyAI Couplets 體驗對對聯Demo: https://www.flyai.com/couplets s...
閱讀 917·2023-01-14 11:38
閱讀 888·2023-01-14 11:04
閱讀 747·2023-01-14 10:48
閱讀 2025·2023-01-14 10:34
閱讀 954·2023-01-14 10:24
閱讀 829·2023-01-14 10:18
閱讀 504·2023-01-14 10:09
閱讀 581·2023-01-14 10:02