輕松入門自然語(yǔ)言處理系列 07 文本表示
一、文本表示基礎(chǔ)
對(duì)于自然語(yǔ)言處理各類應(yīng)用,最基礎(chǔ)的任務(wù)是文本表示。因?yàn)橐粋€(gè)文本是不能直接作為模型的輸入的,所以必須要先把文本轉(zhuǎn)換成向量的形式之后,再導(dǎo)入到模型中訓(xùn)練。所謂的文本表示,其實(shí)就是研究如何把文本表示成向量或者矩陣的形式。
1.單詞的表示
任何機(jī)器學(xué)習(xí)模型的輸入一定是向量或矩陣的形式,所以在進(jìn)行文本分析時(shí),就需要用向量化的方式來(lái)表示單詞或句子。文本的最小單元為單詞,其次為短語(yǔ)、句子或者段落,要懂得如何將它們表示成向量的形式。其中,單詞的表示法是最基礎(chǔ)的。另外,對(duì)于句子或者更長(zhǎng)的文本來(lái)說(shuō),它們的表示依賴于單詞的表示法。
基于詞典使用向量表示單詞,如下:
這種方式是獨(dú)熱編碼(One-hot encoding),每個(gè)單詞對(duì)應(yīng)的向量大小與詞庫(kù)大小保持一致。同時(shí),單詞的表示法不止一種,包括獨(dú)熱編碼的表示法、詞向量的表示法等。
詞庫(kù)中所包含的單詞的先后順序不會(huì)對(duì)后續(xù)的任務(wù)產(chǎn)生不一樣的結(jié)果,即詞庫(kù)中的單詞順序是無(wú)關(guān)緊要的,雖然它會(huì)影響單詞編碼的順序,但由于都是跟詞庫(kù)里的單詞1對(duì)1對(duì)應(yīng)的,并不會(huì)對(duì)結(jié)果產(chǎn)生影響。
2.句子的表示
知道了如何表示一個(gè)單詞之后,很自然地就可以得到如何表示一個(gè)句子了。類似于單詞,句子也可以用向量表示,用1/0分別表示有或者沒(méi)有這個(gè)單詞。一個(gè)句子由多個(gè)單詞來(lái)組成,那實(shí)際上記錄一下哪些單詞出現(xiàn),哪些單詞沒(méi)有出現(xiàn)就可以了。當(dāng)然,很多時(shí)候我們也需要記錄一個(gè)單詞所出現(xiàn)的次數(shù),這就對(duì)應(yīng)著句子的兩種表示方式。
只記錄句子中是否出現(xiàn)了單詞,舉例如下:
可以看到,最后一個(gè)句子中出現(xiàn)了2次“又去”,但是上述的文本表示只記錄了一個(gè)單詞出現(xiàn)與否,而沒(méi)有包含單詞出現(xiàn)的次數(shù)信息,同時(shí)也沒(méi)有辦法記錄一個(gè)單詞的重要性。這種方式稱為Boolean Vector,只記錄句子中單詞是否出現(xiàn),不記錄出現(xiàn)的次數(shù)。
另一種方式考慮句子中單詞出現(xiàn)的次數(shù):
可以看到,現(xiàn)在每個(gè)位置記錄著對(duì)應(yīng)單詞在句子中出現(xiàn)的次數(shù),這稱為Count Vector。
現(xiàn)在使用sklearn庫(kù)的CountVectorizer,如下:
# -*- coding: utf-8 -*-
'''
@Author : Corley
@Time : 2022-03-08 12:36
@Project : NLPDevilCamp-setence_count_vector
'''
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
'I like this course.',
'I like this game.',
'I like this course, but I also like that game',
]
# 構(gòu)建count vectorizer object
vectorizer = CountVectorizer()
# 得到每個(gè)句子的count向量
X = vectorizer.fit_transform(corpus)
# 打印詞典
print(vectorizer.get_feature_names_out(), end='\n\n')
# 打印每個(gè)句子的向量
print(X.toarray(), end='\n\n')
print(X)
輸出:
['also' 'but' 'course' 'game' 'like' 'that' 'this']
[[0 0 1 0 1 0 1]
[0 0 0 1 1 0 1]
[1 1 1 1 2 1 1]]
(0, 4) 1
(0, 6) 1
(0, 2) 1
(1, 4) 1
(1, 6) 1
(1, 3) 1
(2, 4) 2
(2, 6) 1
(2, 2) 1
(2, 3) 1
(2, 1) 1
(2, 0) 1
(2, 5) 1
在打印矩陣時(shí),之所以在最后一行使用了toarray()
函數(shù),這是由于稀疏性的特點(diǎn):如果一個(gè)文本中沒(méi)有出現(xiàn)詞庫(kù)里的單詞,相應(yīng)的位置為0,這就導(dǎo)致文本向量中包含的大量的0。實(shí)際上,為了節(jié)省內(nèi)存空間沒(méi)必要把所有的0都保存下來(lái),所以sklearn在X的保存上默認(rèn)會(huì)使用稀疏矩陣的保存方法,只保存對(duì)應(yīng)的位置信息和相應(yīng)的值,因此在保存矩陣時(shí),只有非0的數(shù)才會(huì)占用內(nèi)存空間。當(dāng)使用toarray()
函數(shù)的時(shí)候,就會(huì)打印成完整的矩陣形態(tài)。
3.tf-idf向量
先舉例,使用Count Vector如下:
可以看到,Count Vector表示法是存在一定的問(wèn)題的,因?yàn)橐粋€(gè)句子中有的單詞出現(xiàn)的次數(shù)很多、有的單詞出現(xiàn)的次數(shù)很少,因此在Count Vector中數(shù)值越大,表明單詞出現(xiàn)的次數(shù)越多,包含的信息量也就越大。但是從文本的角度考慮,出現(xiàn)次數(shù)最多的單詞不一定的起的作用也大,例如he的重要性其實(shí)并不比denied的重要性更高。所以可以說(shuō),單詞并不是出現(xiàn)的越多就越重要,也不是出現(xiàn)的越少就越不重要。直觀地理解,一個(gè)單詞出現(xiàn)的比較少,但是實(shí)際上可能是更重要的,對(duì)于文本分類等需要體現(xiàn)句子間差異性的任務(wù)是很有用的;而一個(gè)單詞出現(xiàn)的次數(shù)很多,可能這個(gè)單詞基本在每個(gè)句子中都會(huì)出現(xiàn),說(shuō)明這個(gè)單詞對(duì)句子間的差異性的影響很小,這樣對(duì)文本分類等需要體現(xiàn)句子之間差異性的任務(wù)起不了太大作用。
所以,如果只記錄單詞的個(gè)數(shù)也是不夠的,我們還需要考慮單詞的權(quán)重,也可以認(rèn)為是質(zhì)量。這有點(diǎn)類似于,一個(gè)人有很多朋友不代表這個(gè)人有多厲害,還需要社交的質(zhì)量,其實(shí)是同一個(gè)道理。要把這種所謂的“質(zhì)量”引入到表示中,就需要用到單詞的一種重要表示法tf-idf,如下:
其中,tf相當(dāng)于前面的Count Vector,而idf是逆文本頻率,是單詞的重要性指標(biāo),兩者結(jié)合起來(lái)可以更好地表示一個(gè)詞。因此tf-idf一方面衡量了一個(gè)詞出現(xiàn)的頻率,另一方面也衡量了這個(gè)詞出現(xiàn)的權(quán)重。
tf-idf舉例如下:
tf-idf的應(yīng)用非常廣泛,即便放在當(dāng)前,也是表示文本的最核心的技術(shù)之一,同時(shí)也是文本表示領(lǐng)域的最有效的基準(zhǔn)。很多時(shí)候,基于深度學(xué)習(xí)的文本表示也未必要優(yōu)于tf-idf的表示。
二、文本相似度
語(yǔ)義理解是一個(gè)NLP任務(wù)的最終目標(biāo)之一:一種方式針對(duì)一個(gè)句子分析到底表達(dá)的是什么意思;另一種方式是相對(duì)的,已經(jīng)知道一個(gè)句子的語(yǔ)義,同時(shí)還有一些其他的不知道語(yǔ)義的句子,來(lái)分析這些句子的語(yǔ)義,此時(shí)就可以進(jìn)行相似度的比較,將每個(gè)句子與已知語(yǔ)義的句子進(jìn)行比較,相似度較近的句子,語(yǔ)義可能與已知的語(yǔ)義也較相近。因此,基于相似度的計(jì)算來(lái)理解語(yǔ)義,也是一種非常重要的方式。比如對(duì)于當(dāng)前主流的問(wèn)答系統(tǒng),用戶通過(guò)文本輸入提出一個(gè)問(wèn)題之后,去匹配與問(wèn)題文本相似的一些其他問(wèn)題,然后返回最匹配的其他問(wèn)題的答案。所以表示兩個(gè)文本之間的相似度極為重要。
因?yàn)槲谋究梢杂孟蛄縼?lái)表示,因此計(jì)算兩個(gè)文本之間的相似度,實(shí)際上可以認(rèn)為是計(jì)算兩個(gè)向量之間的相似度,也就是通過(guò)向量的相似度來(lái)表示句子的相似度。同時(shí)相似度計(jì)算公式適合任何向量化的場(chǎng)景,不僅僅局限于文本之間的相似度。有兩種常見(jiàn)的相似度計(jì)算方法,分別為基于歐式距離的計(jì)算,另外一種方式為基于余弦相似度的計(jì)算。
1.歐氏距離
文本相似度的一種計(jì)算方式是計(jì)算兩個(gè)文本向量之間的歐式距離,距離越大相似度越小、距離越小相似度越大:
但是需要注意,向量之間的相似度需要考慮到向量的方向,因?yàn)橄蛄孔钪匾奶匦跃褪撬?strong>方向性。如果兩個(gè)向量相似,那也需要它們的方向也比較相似。然而,計(jì)算歐式距離的過(guò)程并沒(méi)有把方向考慮進(jìn)去,這是歐式距離最大的問(wèn)題。
2.余弦相似度
為了彌補(bǔ)歐氏距離計(jì)算兩個(gè)向量的相似度時(shí)沒(méi)有考慮到方向的問(wèn)題,需要提出另外一種相似度計(jì)算方法,即余弦相似度,其在計(jì)算相似度時(shí)也考慮到了方向的相似性,這也是計(jì)算兩個(gè)向量之間的方向性最常用的方式。如下:
可以看到,在計(jì)算余弦相似度時(shí),一方面衡量了兩個(gè)向量的大小,具體是將向量的大小進(jìn)行歸一化;另一方面衡量了兩個(gè)向量之間的夾角:兩個(gè)向量之間的夾角越小,則兩個(gè)向量的方向上越一致,這兩個(gè)向量的相似度就越高;反之夾角越大,則方向越相離,相似度越低。兩者結(jié)合,得到的余弦相似度就可以更好地表征兩個(gè)向量之間的相似度。計(jì)算公式中的分子是兩個(gè)向量的內(nèi)積,也可以表示兩個(gè)向量之間的相似度,分母就是對(duì)向量長(zhǎng)度進(jìn)行歸一化,消除兩個(gè)向量大小所帶來(lái)的影響。同時(shí),計(jì)算得到的余弦相似度越大,則兩個(gè)文本之間的相似度越大,是同方向的。
現(xiàn)在實(shí)現(xiàn)給定兩個(gè)向量,計(jì)算兩個(gè)向量之間的余弦相似度:
# -*- coding: utf-8 -*-
'''
@Author : Corley
@Time : 2022-03-08 15:03
@Project : NLPDevilCamp-cosine_similarity
'''
import numpy as np
def cos_sim(a, b):
"""
給定兩個(gè)向量,a和b,計(jì)算它倆之間的余弦相似度
"""
dot_product = np.dot(a, b)
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
return dot_product / (norm_a * norm_b)
if __name__ == '__main__':
sentence_m = np.array([1, 1, 1, 1, 0, 0, 0, 0, 0])
sentence_h = np.array([0, 0, 1, 1, 1, 1, 0, 0, 0])
sentence_w = np.array([0, 0, 0, 1, 0, 0, 1, 1, 1])
print(cos_sim(sentence_m, sentence_h))
print(cos_sim(sentence_m, sentence_w))
輸出:
0.5
0.25
三、詞向量基礎(chǔ)
1.單詞之間的相似度
獨(dú)熱編碼的特點(diǎn):
- 每個(gè)單詞表示成長(zhǎng)度為|V|的向量,|V|是詞庫(kù)的大小
-
除了一個(gè)位置1,剩下的全是0,極度稀疏
-
無(wú)法比較語(yǔ)義相似度
除了需要得到兩個(gè)句子間的相似度,單詞作為文本的最基本的要素,如何表示單詞的含義以及兩個(gè)單詞之間的相似度也極其重要。計(jì)算兩個(gè)單詞之間的相似度有助于更好地理解文本,也可以嘗試使用歐氏距離和余弦相似度進(jìn)行計(jì)算:
可以看到, 通過(guò)歐氏距離計(jì)算得到的很多單詞之間的相似度都是一樣的、沒(méi)有區(qū)分度,因此通過(guò)歐氏距離計(jì)算兩個(gè)單詞之間的相似度是不夠合理的;使用余弦相似度時(shí),得到的余弦相似度也是相同的,也不能用來(lái)刻畫單詞之間的相似度。問(wèn)題可能存在于兩方面:?jiǎn)卧~的獨(dú)熱編碼的表示方式存在一定的問(wèn)題;計(jì)算相似度的兩種方式不太合理。同時(shí),計(jì)算相似度時(shí),需要保證相關(guān)的詞的近似度較高,與人的常識(shí)保持一致。
對(duì)于獨(dú)熱編碼,還有一個(gè)問(wèn)題,即稀疏性(Sparsity),即一個(gè)向量或矩陣中大部分都為0,只有少部分有意義的值??梢钥吹?,在獨(dú)熱編碼方式中,每一個(gè)向量都只有一個(gè)位置部位0、其余位置都為0。既然獨(dú)熱編碼表示不支持計(jì)算兩個(gè)單詞之間的相似度,就需要找到另外一種單詞的表示法。
2.詞向量基礎(chǔ)
獨(dú)熱編碼的方式存在一些問(wèn)題,分布式表示法可以解決這個(gè)問(wèn)題,詞向量就是分布式表示法的一種形式。舉例如下:
可以看到,詞向量表示法中不再具有稀疏性,而是變得很稠密;同時(shí)詞向量的大小可以通過(guò)超參數(shù)來(lái)控制,而與詞庫(kù)等沒(méi)有關(guān)系。同時(shí)需要注意,獨(dú)熱編碼和詞向量是兩套不同的詞和句子的表示方法,如果詞表示選擇獨(dú)熱編碼,句子表示也只能選擇對(duì)應(yīng)的Count Vector和tf-idf;如果詞表示選擇詞向量,句子表示也應(yīng)該選擇對(duì)應(yīng)的表示方式。
給定了詞向量后,可以計(jì)算兩個(gè)詞之間的相似度。以歐氏距離方法舉例如下:
可以看到,在分布式表示方法下,兩個(gè)單詞之間的相似度是可以算出來(lái)的。同時(shí),得到的相似度準(zhǔn)確與否取決于詞向量的質(zhì)量。
現(xiàn)在進(jìn)一步分析詞向量的得來(lái):
詞向量是由模型訓(xùn)練選擇出來(lái)的,給模型輸入大量的數(shù)據(jù),覆蓋了幾乎所有單詞,通過(guò)訓(xùn)練來(lái)得到每個(gè)詞的詞向量。常見(jiàn)的模型包括GloVe、SkipGram、ELMO、BERT等模型。模型越好,詞向量的質(zhì)量也更高;同時(shí)不同的模型訓(xùn)練出來(lái)的詞向量的使用場(chǎng)景也不相同。
現(xiàn)在進(jìn)一步定義詞向量的含義:
設(shè)計(jì)詞向量的目的是用向量來(lái)表示單詞的含義,也就是用數(shù)字化形式來(lái)表示抽象內(nèi)容。在進(jìn)行可視化時(shí),一個(gè)好的模型得到的詞向量的可視化效果也更好,也可以通過(guò)可視化的方式來(lái)判斷得到的詞向量的質(zhì)量,同時(shí)如果詞向量的維度很高時(shí),可以先把詞向量降維到低維的空間,再做可視化。
可視化舉例如下:
英文詞向量可視化
中文詞向量可視化
從上圖可以看到,語(yǔ)義上比較相似的單詞聚集在了一起,這其實(shí)變相地說(shuō)明,詞向量在某種意義上表達(dá)出了一個(gè)單詞的含義。為了可視化詞向量而使用的降維技術(shù)可以參考https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html,這也是一種常用的降維方法。
3.句子向量
在分布式系統(tǒng)中也可以表示句子的向量。在已經(jīng)有了訓(xùn)練好的詞向量的情況下,要通過(guò)這些詞向量來(lái)表示一個(gè)完整的文本或者一個(gè)句子,一種最簡(jiǎn)單且常用的方法就是進(jìn)行平均,如下:
可以看到,上面采用了最簡(jiǎn)單的方法——取平均值。同時(shí)句子向量的質(zhì)量也很大程度上依賴于組成句子的詞的詞向量的質(zhì)量。
有了文本表示之后,就可以開始對(duì)文本進(jìn)行建模了,比如計(jì)算兩個(gè)文本之間的相似度,或者對(duì)某個(gè)文本進(jìn)行分類。
小結(jié)如下:
單詞的獨(dú)熱編碼和分布式表示是兩種完全不一樣的編碼方式,這兩種不同的編碼方式是目前文本表示的兩個(gè)方向,有些時(shí)候傳統(tǒng)的獨(dú)熱編碼的方式可能更適合,有些時(shí)候分布式表示法更適合,具體還是要通過(guò)測(cè)試來(lái)獲得結(jié)論。獨(dú)熱編碼的最大問(wèn)題是不能表示一個(gè)單詞的含義;詞向量的質(zhì)量取決于詞向量訓(xùn)練模型,不同的模型給出的結(jié)果是不一樣的。
掃碼進(jìn)群: