在英語(yǔ)中,諸如“helps”、“helped”和“helping”之類的詞是同一個(gè)詞“help”的變形形式。“dog”與“dogs”的關(guān)系與“cat”與“cats”的關(guān)系相同,“boy”與“boyfriend”的關(guān)系與“girl”與“girlfriend”的關(guān)系相同。在法語(yǔ)和西班牙語(yǔ)等其他語(yǔ)言中,許多動(dòng)詞有超過 40 種變形形式,而在芬蘭語(yǔ)中,一個(gè)名詞可能有多達(dá) 15 種格。在語(yǔ)言學(xué)中,形態(tài)學(xué)研究詞的形成和詞的關(guān)系。然而,word2vec 和 GloVe 都沒有探索單詞的內(nèi)部結(jié)構(gòu)。
15.6.1。fastText 模型
回想一下單詞在 word2vec 中是如何表示的。在skip-gram模型和連續(xù)詞袋模型中,同一個(gè)詞的不同變形形式直接由不同的向量表示,沒有共享參數(shù)。為了使用形態(tài)信息,fastText 模型提出了一種子詞嵌入方法,其中一個(gè)子詞是一個(gè)字符n-gram (Bojanowski等人,2017 年)。與學(xué)習(xí)詞級(jí)向量表示不同,fastText 可以被視為子詞級(jí) skip-gram,其中每個(gè)中心詞由其子詞向量的總和表示。
讓我們舉例說明如何使用單詞“where”為 fastText 中的每個(gè)中心詞獲取子詞。首先,在單詞的首尾添加特殊字符“<”和“>”,以區(qū)別于其他子詞的前綴和后綴。然后,提取字符n-克從字。例如,當(dāng)n=3,我們得到所有長(zhǎng)度為 3 的子詞:“”,以及特殊子詞“”。
在 fastText 中,對(duì)于任何單詞w, 表示為Gw其所有長(zhǎng)度在 3 到 6 之間的子字及其特殊子字的并集。詞匯表是所有詞的子詞的并集。出租zg是子詞的向量g在字典中,向量vw為詞w作為 skip-gram 模型中的中心詞的是其子詞向量的總和:
(15.6.1)vw=∑g∈Gwzg.
fastText 的其余部分與 skip-gram 模型相同。與skip-gram模型相比,fastText中的詞匯量更大,導(dǎo)致模型參數(shù)更多。此外,為了計(jì)算一個(gè)詞的表示,必須將其所有子詞向量相加,從而導(dǎo)致更高的計(jì)算復(fù)雜度。然而,由于具有相似結(jié)構(gòu)的詞之間的子詞共享參數(shù),稀有詞甚至詞匯表外的詞可能會(huì)在 fastText 中獲得更好的向量表示。
15.6.2。字節(jié)對(duì)編碼
在 fastText 中,所有提取的子詞都必須具有指定的長(zhǎng)度,例如3到6,因此無(wú)法預(yù)定義詞匯量大小。為了允許在固定大小的詞匯表中使用可變長(zhǎng)度的子詞,我們可以應(yīng)用一種稱為字節(jié)對(duì)編碼(BPE) 的壓縮算法來提取子詞 ( Sennrich et al. , 2015 )。
字節(jié)對(duì)編碼對(duì)訓(xùn)練數(shù)據(jù)集進(jìn)行統(tǒng)計(jì)分析,以發(fā)現(xiàn)單詞中的常見符號(hào),例如任意長(zhǎng)度的連續(xù)字符。從長(zhǎng)度為 1 的符號(hào)開始,字節(jié)對(duì)編碼迭代地合并最頻繁的一對(duì)連續(xù)符號(hào)以產(chǎn)生新的更長(zhǎng)的符號(hào)。請(qǐng)注意,為了提高效率,不考慮跨越單詞邊界的對(duì)。最后,我們可以使用子詞這樣的符號(hào)來分詞。字節(jié)對(duì)編碼及其變體已用于流行的自然語(yǔ)言處理預(yù)訓(xùn)練模型中的輸入表示,例如 GPT-2 (Radford等人,2019 年)和 RoBERTa (Liu等人,2019 年). 下面,我們將說明字節(jié)對(duì)編碼的工作原理。
首先,我們將符號(hào)詞匯表初始化為所有英文小寫字符、一個(gè)特殊的詞尾符號(hào)'_'和一個(gè)特殊的未知符號(hào)'[UNK]'。
import collections symbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]']
import collections symbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]']
由于我們不考慮跨越單詞邊界的符號(hào)對(duì),我們只需要一個(gè)字典raw_token_freqs將單詞映射到它們?cè)跀?shù)據(jù)集中的頻率(出現(xiàn)次數(shù))。請(qǐng)注意,特殊符號(hào)'_'附加到每個(gè)單詞,以便我們可以輕松地從輸出符號(hào)序列(例如,“a_ tall er_ man”)中恢復(fù)單詞序列(例如,“a taller man”)。由于我們從僅包含單個(gè)字符和特殊符號(hào)的詞匯表開始合并過程,因此在每個(gè)單詞中的每對(duì)連續(xù)字符之間插入空格(字典的鍵token_freqs)。換句話說,空格是單詞中符號(hào)之間的分隔符。
raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4} token_freqs = {} for token, freq in raw_token_freqs.items(): token_freqs[' '.join(list(token))] = raw_token_freqs[token] token_freqs
{'f a s t _': 4, 'f a s t e r _': 3, 't a l l _': 5, 't a l l e r _': 4}
raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4} token_freqs = {} for token, freq in raw_token_freqs.items(): token_freqs[' '.join(list(token))] = raw_token_freqs[token] token_freqs
{'f a s t _': 4, 'f a s t e r _': 3, 't a l l _': 5, 't a l l e r _': 4}
我們定義了以下get_max_freq_pair函數(shù),該函數(shù)返回單詞中出現(xiàn)頻率最高的一對(duì)連續(xù)符號(hào),其中單詞來自輸入字典的鍵token_freqs。
def get_max_freq_pair(token_freqs): pairs = collections.defaultdict(int) for token, freq in token_freqs.items(): symbols = token.split() for i in range(len(symbols) - 1): # Key of `pairs` is a tuple of two consecutive symbols pairs[symbols[i], symbols[i + 1]] += freq return max(pairs, key=pairs.get) # Key of `pairs` with the max value
def get_max_freq_pair(token_freqs): pairs = collections.defaultdict(int) for token, freq in token_freqs.items(): symbols = token.split() for i in range(len(symbols) - 1): # Key of `pairs` is a tuple of two consecutive symbols pairs[symbols[i], symbols[i + 1]] += freq return max(pairs, key=pairs.get) # Key of `pairs` with the max value
作為一種基于連續(xù)符號(hào)頻率的貪婪方法,字節(jié)對(duì)編碼將使用以下merge_symbols函數(shù)合并最頻繁的一對(duì)連續(xù)符號(hào)以產(chǎn)生新的符號(hào)。
def merge_symbols(max_freq_pair, token_freqs, symbols): symbols.append(''.join(max_freq_pair)) new_token_freqs = dict() for token, freq in token_freqs.items(): new_token = token.replace(' '.join(max_freq_pair), ''.join(max_freq_pair)) new_token_freqs[new_token] = token_freqs[token] return new_token_freqs
def merge_symbols(max_freq_pair, token_freqs, symbols): symbols.append(''.join(max_freq_pair)) new_token_freqs = dict() for token, freq in token_freqs.items(): new_token = token.replace(' '.join(max_freq_pair), ''.join(max_freq_pair)) new_token_freqs[new_token] = token_freqs[token] return new_token_freqs
現(xiàn)在我們?cè)谧值涞逆I上迭代執(zhí)行字節(jié)對(duì)編碼算法token_freqs。在第一次迭代中,出現(xiàn)頻率最高的一對(duì)連續(xù)符號(hào)是't'和'a',因此字節(jié)對(duì)編碼將它們合并以產(chǎn)生新的符號(hào)'ta'。在第二次迭代中,字節(jié)對(duì)編碼繼續(xù)合并'ta'并 'l'產(chǎn)生另一個(gè)新符號(hào)'tal'。
num_merges = 10 for i in range(num_merges): max_freq_pair = get_max_freq_pair(token_freqs) token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols) print(f'merge #{i + 1}:', max_freq_pair)
merge #1: ('t', 'a') merge #2: ('ta', 'l') merge #3: ('tal', 'l') merge #4: ('f', 'a') merge #5: ('fa', 's') merge #6: ('fas', 't') merge #7: ('e', 'r') merge #8: ('er', '_') merge #9: ('tall', '_') merge #10: ('fast', '_')
num_merges = 10 for i in range(num_merges): max_freq_pair = get_max_freq_pair(token_freqs) token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols) print(f'merge #{i + 1}:', max_freq_pair)
merge #1: ('t', 'a') merge #2: ('ta', 'l') merge #3: ('tal', 'l') merge #4: ('f', 'a') merge #5: ('fa', 's') merge #6: ('fas', 't') merge #7: ('e', 'r') merge #8: ('er', '_') merge #9: ('tall', '_') merge #10: ('fast', '_')
在字節(jié)對(duì)編碼的 10 次迭代之后,我們可以看到該列表 symbols現(xiàn)在包含 10 個(gè)以上的符號(hào),這些符號(hào)是從其他符號(hào)迭代合并而來的。
print(symbols)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]', 'ta', 'tal', 'tall', 'fa', 'fas', 'fast', 'er', 'er_', 'tall_', 'fast_']
print(symbols)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]', 'ta', 'tal', 'tall', 'fa', 'fas', 'fast', 'er', 'er_', 'tall_', 'fast_']
對(duì)于在字典的鍵中指定的相同數(shù)據(jù)集 raw_token_freqs,數(shù)據(jù)集中的每個(gè)單詞現(xiàn)在都被子詞“fast_”、“fast”、“er_”、“tall_”和“tall”分割為字節(jié)對(duì)編碼的結(jié)果算法。例如,單詞“faster_”和“taller_”分別被分割為“fast er_”和“tall er_”。
print(list(token_freqs.keys()))
['fast_', 'fast er_', 'tall_', 'tall er_']
print(list(token_freqs.keys()))
['fast_', 'fast er_', 'tall_', 'tall er_']
請(qǐng)注意,字節(jié)對(duì)編碼的結(jié)果取決于所使用的數(shù)據(jù)集。我們還可以使用從一個(gè)數(shù)據(jù)集學(xué)習(xí)的子詞來分割另一個(gè)數(shù)據(jù)集的詞。作為一種貪婪的方法,以下 segment_BPE函數(shù)嘗試將輸入?yún)?shù)中的單詞分解為最長(zhǎng)可能的子詞symbols。
def segment_BPE(tokens, symbols): outputs = [] for token in tokens: start, end = 0, len(token) cur_output = [] # Segment token with the longest possible subwords from symbols while start < len(token) and start < end: if token[start: end] in symbols: cur_output.append(token[start: end]) start = end end = len(token) else: end -= 1 if start < len(token): cur_output.append('[UNK]') outputs.append(' '.join(cur_output)) return outputs
def segment_BPE(tokens, symbols): outputs = [] for token in tokens: start, end = 0, len(token) cur_output = [] # Segment token with the longest possible subwords from symbols while start < len(token) and start < end: if token[start: end] in symbols: cur_output.append(token[start: end]) start = end end = len(token) else: end -= 1 if start < len(token): cur_output.append('[UNK]') outputs.append(' '.join(cur_output)) return outputs
在下文中,我們使用symbols從上述數(shù)據(jù)集中學(xué)習(xí)的列表中的子詞來分割tokens代表另一個(gè)數(shù)據(jù)集。
tokens = ['tallest_', 'fatter_'] print(segment_BPE(tokens, symbols))
['tall e s t _', 'fa t t er_']
tokens = ['tallest_', 'fatter_'] print(segment_BPE(tokens, symbols))
['tall e s t _', 'fa t t er_']
15.6.3。概括
fastText 模型提出了一種子詞嵌入方法?;?word2vec 中的 skip-gram 模型,它將中心詞表示為其子詞向量的總和。
字節(jié)對(duì)編碼對(duì)訓(xùn)練數(shù)據(jù)集進(jìn)行統(tǒng)計(jì)分析,以發(fā)現(xiàn)單詞中的常見符號(hào)。作為一種貪婪的方法,字節(jié)對(duì)編碼迭代地合并最頻繁的一對(duì)連續(xù)符號(hào)。
子詞嵌入可以提高罕見詞和詞典外詞的表示質(zhì)量。
15.6.4。練習(xí)
例如,大約有3×108可能的 6-英語(yǔ)克。當(dāng)子詞太多時(shí)會(huì)出現(xiàn)什么問題?如何解決這個(gè)問題?提示:參考 fastText 論文(Bojanowski et al. , 2017)第 3.2 節(jié)的結(jié)尾。
如何設(shè)計(jì)基于連續(xù)詞袋模型的子詞嵌入模型?
獲得尺寸詞匯表m, 當(dāng)初始符號(hào)詞匯量為n?
如何擴(kuò)展字節(jié)對(duì)編碼的思想來提取短語(yǔ)?
-
pytorch
+關(guān)注
關(guān)注
2文章
809瀏覽量
13960
發(fā)布評(píng)論請(qǐng)先 登錄
Pytorch代碼移植嵌入式開發(fā)筆記,錯(cuò)過絕對(duì)后悔
ch582做的藍(lán)牙鍵盤,無(wú)法連iphone手機(jī)怎么解決?
通過Cortex來非常方便的部署PyTorch模型
如何往星光2板子里裝pytorch?
子詞駐留特征在電話語(yǔ)音確認(rèn)中的應(yīng)用
關(guān)于GN-GloVe的詞嵌入技術(shù)詳解
基于PyTorch的深度學(xué)習(xí)入門教程之PyTorch簡(jiǎn)單知識(shí)
PyTorch教程15.5之帶全局向量的詞嵌入(GloVe)

評(píng)論