欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解PyTorch中的nn.Embedding的使用

 更新時(shí)間:2022年07月04日 11:44:36   作者:aelum  
本文主要介紹了PyTorch中的nn.Embedding的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(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)紙、小說(shuō)、食譜、廣播到電視節(jié)目、電影和推文的所有內(nèi)容組成。 在自然語(yǔ)言處理中,語(yǔ)料庫(kù)包含可用于訓(xùn)練 AI 的文本和語(yǔ)音數(shù)據(jù)。

1.2 詞元(Token)

為簡(jiǎn)便起見,假設(shè)我們的語(yǔ)料庫(kù)只有三個(gè)英文句子并且均已經(jīng)過(guò)處理(全部小寫+去掉標(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)的頻率來(lái)構(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}

反過(guò)來(lái),我們還可以構(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無(wú)法直接處理單詞,因此需要通過(guò)某種方法把單詞變成數(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)重。

接下來(lái)再來(lái)看一下 nn.Embedding 的輸入。直觀來(lái)看,給定一個(gè)已經(jīng)詞元化的句子,將其中的單詞輸入到 embedding 層應(yīng)該得到相應(yīng)的詞向量。事實(shí)上,nn.Embedding 接受的輸入并不是詞元化后的句子,而是它的索引形式,即第一章節(jié)中提到的 encoded_tokens。

nn.Embedding 可以接受任何形狀的張量作為輸入,但因?yàn)閭魅氲氖撬饕詮埩恐械拿總€(gè)數(shù)字都不應(yīng)超過(guò) len(vocab) - 1,否則就會(huì)報(bào)錯(cuò)。接下來(lái),nn.Embedding 的作用就像一個(gè)查找表(Lookup Table)一樣,通過(guò)這些索引在 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.Embeddingnn.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)算過(guò)程中做了矩陣乘法,而 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 的更新問(wèn)題

在查閱了PyTorch官方論壇和Stack Overflow的一些帖子后,發(fā)現(xiàn)有不少人對(duì) nn.Embedding 中的權(quán)重 weight 是怎么更新的感到非常困惑。

?? nn.Embedding 的權(quán)重實(shí)際上就是詞嵌入本身

事實(shí)上,nn.Embedding.weight 在更新的過(guò)程中既沒(méi)有采用 Skip-gram 也沒(méi)有采用 CBOW?;仡欁詈?jiǎn)單的多層感知機(jī),其中的 nn.Linear.weight 會(huì)隨著反向傳播自動(dòng)更新。當(dāng)我們把 nn.Embedding 視為一個(gè)特殊的 nn.Linear 后,其更新機(jī)制就不難理解了,無(wú)非就是按照梯度進(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)練過(guò)程中自我更新,那么可以嘗試凍結(jié)梯度,即:

emb.weight.requires_grad = False

?? 進(jìn)一步閱讀:

[PyTorch Forums] How nn.Embedding trained?
[PyTorch Forums] How does nn.Embedding work?
[Stack Overflow] Embedding in pytorch
[Stack Overflow] What “exactly” happens inside embedding layer in pytorch?

三、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è)句子?,F(xiàn)實(shí)任務(wù)中,很難保證同一個(gè) batch 中的所有句子長(zhǎng)度都相同,因此我們需要對(duì)那些長(zhǎng)度較短的句子進(jìn)行填充。因?yàn)檩斎氲?nn.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)練過(guò)程中填充詞元對(duì)應(yīng)的詞向量不會(huì)進(jìn)行更新(始終是零向量)。

padding_idx 默認(rèn)為 None,即不進(jìn)行填充。

max_norm

如果詞向量的范數(shù)超過(guò)了 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)橄∈鑿埩?,此時(shí)優(yōu)化器只能選擇:SGDSparseAdamAdagrad。默認(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)練過(guò)程中更新,可將 freeze 參數(shù)設(shè)置為 True

emb = nn.Embedding(4, 3).from_pretrained(pretrained_embeddings, freeze=True)

四、最后

到此這篇關(guān)于深入理解PyTorch中的nn.Embedding的使用的文章就介紹到這了,更多相關(guān)PyTorch nn.Embedding內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python使用xlrd模塊實(shí)現(xiàn)操作Excel讀寫的方法匯總

    Python使用xlrd模塊實(shí)現(xiàn)操作Excel讀寫的方法匯總

    本文介紹Python中使用xlrd、xlwt、xlutils模塊操作Excel文件的方法,xlrd用于讀取Excel文件,但2.0.0版本后不支持.xlsx格式,xlwt用于創(chuàng)建和寫入Excel文件,而xlutils主要用于復(fù)制和處理Excel文件,詳細(xì)介紹了如何打開文件、獲取工作表信息、操作行列數(shù)據(jù)和處理日期格式數(shù)據(jù)
    2024-10-10
  • 解決python執(zhí)行較大excel文件openpyxl慢問(wèn)題

    解決python執(zhí)行較大excel文件openpyxl慢問(wèn)題

    這篇文章主要介紹了解決python執(zhí)行較大excel文件openpyxl慢問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-05-05
  • Dataframe的行名及列名排序問(wèn)題

    Dataframe的行名及列名排序問(wèn)題

    這篇文章主要介紹了Dataframe的行名及列名排序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python NumPy實(shí)現(xiàn)數(shù)組排序與過(guò)濾示例分析講解

    Python NumPy實(shí)現(xiàn)數(shù)組排序與過(guò)濾示例分析講解

    NumPy是Python的一種開源的數(shù)值計(jì)算擴(kuò)展,它支持大量的維度數(shù)組與矩陣運(yùn)算,這篇文章主要介紹了使用NumPy實(shí)現(xiàn)數(shù)組排序與過(guò)濾的方法,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-05-05
  • pytorch 加載(.pth)格式的模型實(shí)例

    pytorch 加載(.pth)格式的模型實(shí)例

    今天小編就為大家分享一篇pytorch 加載(.pth)格式的模型實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-08-08
  • python實(shí)現(xiàn)跨進(jìn)程(跨py文件)通信示例

    python實(shí)現(xiàn)跨進(jìn)程(跨py文件)通信示例

    本文主要介紹了python實(shí)現(xiàn)跨進(jìn)程(跨py文件)通信示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Python實(shí)現(xiàn)句子翻譯功能

    Python實(shí)現(xiàn)句子翻譯功能

    這篇文章主要介紹了Python實(shí)現(xiàn)句子翻譯功能,涉及urllib庫(kù)的使用等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • 用60行代碼實(shí)現(xiàn)Python自動(dòng)搶微信紅包

    用60行代碼實(shí)現(xiàn)Python自動(dòng)搶微信紅包

    這篇文章主要介紹了用60行代碼實(shí)現(xiàn)Python自動(dòng)搶微信紅包,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 深入解讀python字符串函數(shù)

    深入解讀python字符串函數(shù)

    這篇文章主要為大家介紹了python字符串函數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-12-12
  • 使用python模塊plotdigitizer摳取論文圖片中的數(shù)據(jù)實(shí)例詳解

    使用python模塊plotdigitizer摳取論文圖片中的數(shù)據(jù)實(shí)例詳解

    這篇文章主要介紹了使用python模塊plotdigitizer摳取論文圖片中的數(shù)據(jù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03

最新評(píng)論