在传统的文本信息处理中,以单词向量表示文本的语义内容,以单词向量空间的度量来表示文本之间的语义近似度。这种方法不能准确表示语义。
潜在语义分析试图从大量的文本数据中发现潜在的话题,以话题向量来表示文本的语义内容,以话题向量的空间度量更准确地表示文本之间的语义相似度。
潜在语义分析使用的是非概率的话题分析模型,具体来说,就是将文本集合表示为单词-文本矩阵,对单词-文本矩阵进行奇异值分解,从而得到话题向量空间,以及文本在话题向量空间的表示。可采用的矩阵分解方法有:奇异值分解、非负矩阵分解。
给定一个含有个文本的集合,以及在所有文本中出现的个单词,则将单词在文本中出现的数据用一个单词-文本表示,记作。
其中,元素表示单词在文本中出现的频数或权值。该矩阵是一个稀疏矩阵。
权值通常用单词频率-逆文本频率(TF-IDF)表示,其定义是:
式中是单词出现在文本中的频数, 是文本中出现的所有单词的频数之和,是含有单词的文本数,是文本集合的全部文本数。直观的,一个单词在一个文本中出现的次数越高,这个单词在这个文本中的重要度就越高;一个单词在整个文本集合中出现的文本越少,这个单词就越能表示其所在文本的特点,重要程度就越高。
单词向量空间模型直接使用单词-文本矩阵信息。单词文本矩阵的第列向量表示文本:
其中是单词在文本的权值,,权值越大,该单词在该文本中的重要度就越高。
之后就可以采用两个单词向量的内积或标准化内积来表示对应的文本之间的语义相似度。与之间的相似度为:
这种简单的单词向量空间模型不能处理一词多义的问题,也无法处理同义的问题,需要更加精确的方法。
话题向量空间
假设所有文本共含有个话题,假设每个话题由一个定义在单词集合上的为向量表示,称为话题向量,即:
其中是单词在话题的权值,,权值越大,该单词在该话题中的重要度就越高。这个话题向量张成一个话题向量空间,维数为。
现在考虑将文本集合中的文本,在单词向量空间中由一个向量表示,将投影到话题向量空间,得到在话题向量空间的一个向量,是一个维向量,其表达式为:
其中是话题在文本的权值,权值越大,该话题在该文本中的重要度就越高。
矩阵表示话题在文本中出现的情况,称为话题-文本矩阵。
从单词向量空间到话题向量空间的线性变换
这样一来,在单词向量空间的文本向量可以通过它在话题空间的向量近似表示,具体的由个话题向量为系数的线性组合近似表示。
所以,单词文本矩阵可以近似的表示为单词-话题矩阵与话题-文本矩阵的乘积形式。这就是潜在语义分析。
要尽心潜在语义分析,需要同时决定两部分内容:话题向量T与文本在话题空间的表示Y,使两者的乘积近似等于原始矩阵。这些完全从话题-文本矩阵的信息中获得。
潜在语义分析算法
潜在语义分析利用矩阵奇异值分解,具体的,对单词-文本矩阵进行奇异值分解,将其左矩阵作为话题向量空间,将其对角矩阵与右矩阵的乘积作为文本在话题向量空间的表示。
算法:
1、给定文本集合和单词集合,首先构造一个文本-单词矩阵,矩阵中的每个元素表示单词在文本中出现的频数或权值。
2、截断奇异值分解
根据给定的话题数目k进行阶段奇异值分解:
3、话题向量空间
矩阵的每一列向量表示一个话题,称为话题向量。由这k话题张成一个子空间:
4、文本的话题向量表示
矩阵的每一个列向量是一个文本在话题向量空间的表示。
用代码表示:
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)
最终输出结果就是话题向量空间与文本在话题空间的线性表示。
非负矩阵分解算法
另外一种对单词文本矩阵分解的方法是非负矩阵分解。
若是非负的,记作,则分别找到两个非负矩阵和,使得
非负矩阵分解是为了用较少的基向量、系数向量来表示较大的数据矩阵。
我们采用最优化问题求解的方式来求
首先定义损失函数
1、平方损失
2、散度
因此,最优化问题转变为:
s.t.
或者为:
s.t.
算法实现
定理:
平方损失对下列乘法更新规则
是非增的,当且仅当和是平方损失函数的稳定点时函数的更新不变。
散度损失对下列乘法更新规则
是非增的,当且仅当和是散度损失函数的稳定点时函数的更新不变。
其实这种乘法更新是将梯度下降法的步长取特殊值,从而使收敛速率加快!
代码如下:
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步时已经收敛。
来源:CSDN
作者:xjtu_rzc
链接:https://blog.csdn.net/qq_39320588/article/details/104380940