从jieba分词到BERT-wwm——中文自然语言处理(NLP)基础分享系列(5)

使用LSI(潜在语义索引)进行维度归约

对于之前训练的TF-IDF向量空间表示,我们重新跑一遍代码,看一下它有些什么特点。

import pandas as pd 
import pickle 
from sklearn.feature_extraction.text import TfidfVectorizer 
 
pkl_file_rb = open(r'./save_file', 'rb') 
train =pickle.load(pkl_file_rb) 
 
corpus = pd.concat([train . title1_tokenized, train . title2_tokenized]) 
corpus = [c for c in corpus] 
 
tfidf_model = TfidfVectorizer().fit(corpus) 

matrix1= tfidf_model.transform(train['title1_tokenized']) 
matrix2= tfidf_model.transform(train['title2_tokenized']) 

matrix1
<320552x67243 sparse matrix of type ''
	with 2503463 stored elements in Compressed Sparse Row format>

有没有注意到——它被存成了一个 sparse matrix 的数据类型。
为什么会这样?
我们看到词库有67243个,这也是向量空间的维数,而一个新闻标题充其量也只由十几个词构成,因此绝大多数的维上取值都是0!
事实上,从内存供给能力看,也只能用这种稀疏矩阵的方法存储,否则就会OOM了……

对于这种高维稀疏的数据,使用机器学习(Machine Learning)直接用来训练模型是不合适的。
一方面,如此高的维度将导致低效的计算;另一方面,高维稀疏数据会增加从文本挖掘潜在模式的难度。

维度归约,也称降维,是指使用数据编码或变换,以便得到原数据的归约或“压缩”表示。
如果原数据可以由压缩数据重新构造而不丢失任何信息,则该数据归约是无损的;如果我们只能重新构造原数据的近似表示,则该数据归约是有损的。

LSI(潜在语义索引)

潜在语义索引(LSI)是一种常用的文档维度归约算法。LSI基于奇异值分解(Singular Value Decomposition,SVD)的方法实现,SVD是线性代数中一种重要的矩阵分解,是矩阵分析中正规矩阵对角化的推广。

LSI基本思想:提取最具代表性的特征,同时最小化同构错误。
奇异值分解(SVD):X=(Aij)=UΣV`,
其中,Xm*n, Um*r, Σr*r(对角阵), Vn*r, r<=MIN(m,n),
取Σ对角上的前k个元素,得Σk,
Xk= UkΣkVk`, Uk由U的前k列组成,Vk由V的前k列组成,
文档d在LSI对应的向量d’=dTUkΣ-1 ,由r维降至k维。
从jieba分词到BERT-wwm——中文自然语言处理(NLP)基础分享系列(5)_第1张图片

我们可以使用 sklearn 包的 TruncatedSVD 函数实现LSI方法。

from sklearn.decomposition import TruncatedSVD

svd_model = TruncatedSVD(n_components=64, algorithm='randomized', n_iter=100, random_state=122) #参数n_components即降维的目标维数r 
svd_model.fit(tfidf_model.transform(corpus))
TruncatedSVD(n_components=64, n_iter=100, random_state=122)

这里我们指定了降维的目标维数r为64,也就是说相对于原始的TF-IDF模型,我们把数据有损压缩了1000倍!

# 使用这个模型,对TF-IDF结果进行压缩。 
matrix1_sub  = svd_model.transform(matrix1) 
matrix2_sub  = svd_model.transform(matrix2) 
matrix1_sub.shape
(320552, 64)

压缩1000倍的数据还有用么?

接下来,我们使用这个压缩的“稠密”数据重复一下上一回的余弦相似度分布计算。

import numpy as np 
 
def cos_sim(a, b): 
    a_norm = np.linalg.norm(a) 
    b_norm = np.linalg.norm(b) 
    cos = np.dot(a,b)/(a_norm * b_norm) 
    return cos 

cosin_measure = [] 
for i in range(matrix1_sub.shape[0]): 
    cosin_measure.append(cos_sim(np.squeeze(matrix1_sub[i]),np.squeeze(matrix2_sub[i]))) 
c:\users\hp\appdata\local\programs\python\python37\lib\site-packages\ipykernel_launcher.py:6: RuntimeWarning: invalid value encountered in double_scalars
  
c:\users\hp\appdata\local\programs\python\python37\lib\site-packages\ipykernel_launcher.py:6: RuntimeWarning: divide by zero encountered in double_scalars
train['cosin_measure'] = cosin_measure 
print('Train columns with null values:\n', train.isnull().sum()) #检查各列的空值记录数 

train.loc[:,'cosin_measure'] = train.loc[:,'cosin_measure'].fillna(0) #余弦相似度空值填充 
Train columns with null values:
 title1_zh            0
title2_zh            7
label                0
title1_tokenized     0
title2_tokenized     0
cosin_measure       18
dtype: int64

简便起见,我们跳过4分位箱形图的展示,直接看一下不同label在百分位数分布点位[1,5,10,25,50,75,90,95,99]的余弦相似度取值。

for level in ['unrelated','agreed','disagreed']: 
    data = train[train.label == level].cosin_measure 
    print(level,np.percentile(data, [1,5,10,25,50,75,90,95,99], axis=0)) 
unrelated [-0.06352342 -0.01780777 -0.00228008  0.04328785  0.25206004  0.66588372
  0.88882218  0.96387027  0.99633213]
agreed [0.03268588 0.15247196 0.26748694 0.54153524 0.80297221 0.96769122
 0.99764079 0.99989671 1.        ]
disagreed [0.00746681 0.04532879 0.08774524 0.21708    0.43988834 0.70170525
 0.85775077 0.91423598 0.97046834]

我们看到,不同label类别的新闻标题A和B余弦相似度的取值分布,仍然保留了一定的差异。

对于新版的余弦相似度,我们选用0.888这个吉祥的数字作为分界阈值,即:新闻标题A和B余弦相似度小于0.888的判为’unrelated’,余弦相似度大于0.888的判为’agreed’。

重新计算一下准确率。

train_currect = train[(train['label'] == 'unrelated') & (train['cosin_measure'] < 0.888) \
                      | (train['label'] == 'agreed') & (train['cosin_measure'] >= 0.888)] 
accuracy = len(train_currect) / len(train) 
print('accuracy: {:.1%}'.format(accuracy)) 
accuracy: 72.9%

结果是72.9%,比之前的77.4%有所下降,但与基线模型的68%相比,依然有所提升!
这里仅是通过简单的余弦距离方法利用LSI向量数据,下一次我们将尝试使用这个64维的压缩数据建立一个机器学习(Machine Learning)模型用来识别新闻标题A和B的关系。


好了,就到这儿吧。

本系列共12期,将分6天发布。相关代码已全部分享在我的github项目(moronism189/chinese-nlp-stepbystep)下,急的可以先去那里下载。

你可能感兴趣的:(自然语言处理,机器学习,python)