潛在語義分析(LSA)的原理講解以及python實現

在傳統的文本信息處理中,以單詞向量表示文本的語義內容,以單詞向量空間的度量來表示文本之間的語義近似度。這種方法不能準確表示語義。
潛在語義分析試圖從大量的文本數據中發現潛在的話題,以話題向量來表示文本的語義內容,以話題向量的空間度量更準確地表示文本之間的語義相似度。
潛在語義分析使用的是非概率的話題分析模型,具體來說,就是將文本集合表示爲單詞-文本矩陣,對單詞-文本矩陣進行奇異值分解,從而得到話題向量空間,以及文本在話題向量空間的表示。可採用的矩陣分解方法有:奇異值分解、非負矩陣分解。
給定一個含有nn個文本的集合D={d1,d2,,dn}D=\{d_1,d_2,\cdots,d_n\},以及在所有文本中出現的mm個單詞W={w1,w2,,wm}W=\{w_1,w_2,\cdots,w_m\},則將單詞在文本中出現的數據用一個單詞-文本表示,記作XX
X=[xij]m×nX = [x_{ij}]_{m\times n}
其中,元素xijx_{ij}表示單詞wiw_i在文本djd_j中出現的頻數或權值。該矩陣是一個稀疏矩陣。
權值通常用單詞頻率-逆文本頻率(TF-IDF)表示,其定義是:
TFIDFij=tfijtfjlogdfdfi,i=1,2,,m;j=1,2,,nTFIDF_{ij}=\frac{tf_{ij}}{tf_{\cdot j}}\log\frac{df}{df_i},i=1,2,\cdots,m;j=1,2,\cdots,n
式中tfijtf_{ij}是單詞wiw_i出現在文本djd_j中的頻數, tfjtf_{\cdot j}是文本djd_j中出現的所有單詞的頻數之和,dfidf_i是含有單詞mim_i的文本數,dfdf是文本集合DD的全部文本數。直觀的,一個單詞在一個文本中出現的次數越高,這個單詞在這個文本中的重要度就越高;一個單詞在整個文本集合中出現的文本越少,這個單詞就越能表示其所在文本的特點,重要程度就越高。
單詞向量空間模型直接使用單詞-文本矩陣信息。單詞文本矩陣的第jj列向量xjx_j表示文本djd_j:
xj=[x1j,x2j,,xmj]T,j=1,2,,nx_j=[x_{1j},x_{2j},\cdots,x_{mj}]^T,j=1,2,\cdots,n
其中xijx_{ij}是單詞wiw_i在文本djd_j的權值,i=1,2,,mi=1,2,\cdots,m,權值越大,該單詞在該文本中的重要度就越高。
之後就可以採用兩個單詞向量的內積或標準化內積來表示對應的文本之間的語義相似度。did_idjd_j之間的相似度爲:
xixj,    xixjxixjx_i\cdot x_j,\,\,\,\,\frac{x_i\cdot x_j}{||x_i||\,||x_j||}
這種簡單的單詞向量空間模型不能處理一詞多義的問題,也無法處理同義的問題,需要更加精確的方法。

話題向量空間

假設所有文本共含有kk個話題,假設每個話題由一個定義在單詞集合WW上的mm爲向量表示,稱爲話題向量,即:
tl=[t1l,t2l,,tml]T,l=1,2,,kt_l=[t_{1l},t_{2l},\cdots,t_{ml}]^T,l=1,2,\cdots,k
其中tilt_{il}是單詞wiw_i在話題tlt_l的權值,i=1,2,,mi=1,2,\cdots,m,權值越大,該單詞在該話題中的重要度就越高。這kk個話題向量t1,t2,,tkt_1,t_2,\cdots,t_k張成一個話題向量空間,維數爲kk

現在考慮將文本集合DD中的文本djd_j,在單詞向量空間中由一個向量xjx_j表示,將xjx_j投影到話題向量空間TT,得到在話題向量空間的一個向量yjy_jyjy_j是一個kk維向量,其表達式爲:
yj=[y1j,y2j,,ykj],j=1,2,,ny_j=[y_{1j},y_{2j},\cdots,y_{kj}],j=1,2,\cdots,n
其中yljy_{lj}是話題tlt_l在文本djd_j的權值,權值越大,該話題在該文本中的重要度就越高。
矩陣YY表示話題在文本中出現的情況,稱爲話題-文本矩陣。
Y=[yij]k×nY=[y_{ij}]_{k\times n}

從單詞向量空間到話題向量空間的線性變換
這樣一來,在單詞向量空間的文本向量xjx_j可以通過它在話題空間的向量yjy_j近似表示,具體的由kk個話題向量yjy_j爲係數的線性組合近似表示。
xjy1jt1+y2jt2++ykjtk,j=1,2,,nx_j\approx y_{1j}t_1+y_{2j}t_2+\cdots+y_{kj}t_k, j=1,2,\cdots,n
所以,單詞文本矩陣XX可以近似的表示爲單詞-話題矩陣與話題-文本矩陣YY的乘積形式。這就是潛在語義分析。
XTYX\approx TY
要盡心潛在語義分析,需要同時決定兩部分內容:話題向量T與文本在話題空間的表示Y,使兩者的乘積近似等於原始矩陣。這些完全從話題-文本矩陣的信息中獲得。

潛在語義分析算法

潛在語義分析利用矩陣奇異值分解,具體的,對單詞-文本矩陣進行奇異值分解,將其左矩陣作爲話題向量空間,將其對角矩陣與右矩陣的乘積作爲文本在話題向量空間的表示。
算法:
1、給定文本集合和單詞集合,首先構造一個文本-單詞矩陣,矩陣中的每個元素表示單詞在文本中出現的頻數或權值。
2、截斷奇異值分解
根據給定的話題數目k進行階段奇異值分解:
XUkΣkVkX\approx U_k\Sigma_kV_k
3、話題向量空間
矩陣UkU_k的每一列向量表示一個話題,稱爲話題向量。由這k話題張成一個子空間:
Uk=[u1   u2      uk]U_k=[u_1\,\,\,u_2\,\,\,\cdots\,\,\,u_k]
4、文本的話題向量表示
矩陣(ΣkVkT)(\Sigma_kV_k^T)的每一個列向量是一個文本在話題向量空間的表示。
用代碼表示:

import numpy as np
import pandas as pd
import jieba


df = pd.read_excel(r'D:\BaiduNetdiskDownload\最終data.xlsx')
data = np.array(df['abstract'])
dic = []
for i in data:
    cutdata = jieba.cut(i)
    for j in cutdata:
        if j not in dic and len(j)>=2:
            dic.append(j)
a = len(dic)
X = np.zeros((a, len(data)))

for i in range(len(data)):
    cutdata = jieba.cut(data[i])
    p = []
    for j in cutdata:
        p.append(j)
    for k in range(len(p)):
        if p[k] in dic:
            index = dic.index(p[k])
            X[index][i] += 1

U, O, V = np.linalg.svd(X)

print(U[:, :3])
a = np.diag(O[:3])
b = V[:3, :]
c = np.dot(a, b)
print(c.shape)

最終輸出結果就是話題向量空間與文本在話題空間的線性表示。

非負矩陣分解算法

另外一種對單詞文本矩陣分解的方法是非負矩陣分解。
XX是非負的,記作X0X\geqslant0,則分別找到兩個非負矩陣W0W\geqslant0H0H\geqslant0,使得
XWHX\approx WH
非負矩陣分解是爲了用較少的基向量、係數向量來表示較大的數據矩陣。
我們採用最優化問題求解的方式來求W,HW,H
首先定義損失函數
1、平方損失
AB=i,j(aijbij)2||A-B||=\sum_{i,j}(a_{ij}-b_{ij})^2
2、散度
D(AB)=i,j(aijlogaijbijaij+bij)D(A||B)=\sum_{i,j}(a_{ij}\log\frac{a_{ij}}{b_{ij}}-a_{ij}+b_{ij})
因此,最優化問題轉變爲:
minW,H     XWH2\min_{W,H}\,\,\,\,\,||X-WH||^2
s.t.W,H0W,H\geqslant0
或者爲:
minW,H     D(XWH)min_{W,H}\,\,\,\,\,D(X||WH)
s.t.W,H0W,H\geqslant0

算法實現

定理:
平方損失XWH2||X-WH||^2對下列乘法更新規則
HljHlj(WTX)lj(WHHT)ilH_{lj}\leftarrow H_{lj}\frac{(W^TX)_{lj}}{(WHH^T)_{il}}
WilWil(XHT)il(WHHT)ilW_{il}\leftarrow W_{il}\frac{(XH^T)_{il}}{(WHH^T)_{il}}
非增的,當且僅當WWHH是平方損失函數的穩定點時函數的更新不變。

散度損失D(XWH)D(X-WH)對下列乘法更新規則
HljHlji[WilXij/(WH)ij]iWilH_{lj}\leftarrow H_{lj}\frac{\sum_i[W_{il}X_{ij}/(WH)_{ij}]}{\sum_iW_{il}}
WilWilj[HljXij/(WH)ij]jHljW_{il}\leftarrow W_{il}\frac{\sum_j[H_{lj}X_{ij}/(WH)_{ij}]}{\sum_jH_{lj}}
非增的,當且僅當WWHH是散度損失函數的穩定點時函數的更新不變。
其實這種乘法更新是將梯度下降法的步長取特殊值,從而使收斂速率加快!
代碼如下:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed()
X = np.random.randint(0, 3, (5, 4))

class LSA:
    def __init__(self, x, k):  # k爲話題個數
        self.k = k
        self.X = x
        self.m = x.shape[0]
        self.n = x.shape[1]
        self.W = np.random.uniform(0, 1, (self.m, k))
        self.H = np.random.uniform(0, 1, (k, self.n))

    def standard(self):
        t = self.W**2
        T = np.sqrt(np.sum(t, axis=0))
        for i in range(self.W.shape[0]):
            self.W[i] = self.W[i]/T

    def update(self):
        up1 = np.dot(self.X, self.H.T)
        t1 = np.dot(self.W, self.H)
        down1 = np.dot(t1, self.H.T)
        up2 = np.dot(self.W.T, self.X)
        t2 = np.dot(self.W.T, self.W)
        down2 = np.dot(t2, self.H)
        for i in range(self.m):
            for l in range(self.k):
                self.W[i][l] = self.W[i][l]*(up1[i][l]/down1[i][l])
        for l in range(self.k):
            for j in range(self.n):
                self.H[l][j] = self.H[l][j]*(up2[l][j]/down2[l][j])

    def cost(self):
        X = np.dot(self.W, self.H)
        S = (self.X - X)**2
        score = np.sum(S)
        return score

L = LSA(X, 3)
x = []
score = []
for i in range(50):
    L.standard()
    L.update()
    x.append(i)
    score.append(L.cost())
print(X)
print(np.dot(L.W, L.H))
plt.scatter(x, score)
plt.show()

最終的運行結果爲:
在這裏插入圖片描述
原始矩陣與近似矩陣,可見差異已經相當小。
平方損失的圖像:
在這裏插入圖片描述
在迭代到第50步時已經收斂。

發佈了16 篇原創文章 · 獲贊 14 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章