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