Stata文本分析:lsemantica-潜在语义分析的文本相似性判别

发布时间:2021-07-03 阅读 577

Stata连享会   主页 || 视频 || 推文 || 知乎 || Bilibili 站

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

New! lianxh 命令发布了:
随时搜索推文、Stata 资源。安装:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh
连享会新命令:cnssc, ihelp, rdbalance, gitee, installpkg

课程详情 https://gitee.com/lianxh/Course

课程主页 https://gitee.com/lianxh/Course

⛳ Stata 系列推文:

PDF下载 - 推文合集

作者: 赵汗青 (华南理工大学)
邮箱: hqingzh@gmail.com

编者按:本推文部分摘译自如下论文,特此致谢!
Source:Schwarz, C. (2019). lsemantica: A command for text similarity based on latent semantic analysis. The Stata Journal, 19(1), 129-142. -PDF-


目录


1. 潜在语义分析

在基于词频的文本相似度分析中,主要存在以下问题:

  • 同一个词汇在不同的语境中可能有不同的含义,即 “一词多义”;
  • 不同词汇在不同的语境中可能表达相同的含义,即 “多词一义”。

为了解决该问题,Deerwester 等 (1990) 提出了潜在语义分析 (latent semantic analysisa, LSA)。该方法的主要特征如下:

  • 它一种基于机器学习来比较单词或文本相似度的算法;
  • 由于理解单词的含义和上下文很重要,LSA 通过考虑单词之间的关系和潜在的多重含义来改进相似度计算结果。

这些属性使得 LSA 有着广泛的应用场景,包括:

  • 计算单词之间相似性
  • 计算文本之间相似性
  • 基于计算机辅助的摘要写作
  • 对论文进行自动评分
  • 评估语篇连贯性

虽然 Stata 中已经提供了 strdist 命令用于计算 Levenshtein 编辑距离、txttool 命令用于文本数据的清理和标记化、以及 ldagibbs 命令可以按相似主题对文本进行分类,但如果要计算文本之间的相似程度,我们还需要 lsemantica 命令。

2. lsemantica 命令

2.1 理论部分

2.1.1 建立 “文档-词汇” 矩阵

LSA 基本原理是通过计算每个文档中词汇分布情况的相似程度,来分析文档间的相似性,因此 lsemantica 命令首先要基于词袋模型创建一个 “文档-词汇” 矩阵 (document-term matrix)。

在矩阵 A 中,行数 D 代表需要分析的文档的数量,列数 V 代表分词后所有文档中的词汇数量。fd,v 代表词汇 v 在文档 d 中出现的次数。

2.1.2 对 “文档-词汇” 矩阵重新加权

接着,lsemantica 根据 TFIDF (termfrequency-inverse-document-frequency) 对矩阵 A 中的 fd,v 进行加权。其计算公式如下:

其中,dv 是第 v 个词在所有文档中出现的数量。TFIDF 的计算也可以通过 Stata 命令 textfind 实现。TFIDF 加权的意义在于降低那些同时出现在多个文档中的单词权重,因为这些单词对于文档的整体意义通常不够重要。TFIDF加权后,矩阵 A 变成如下矩阵:

2.1.3 对 “文档-词汇” 矩阵进行截断奇异值分解

当文档较多时,即 V 较大时,计算量会比较大。为了降低算力,lsemantica 命令会对矩阵 A 进行奇异值分解,并得到 D×R 正交矩阵 UR×R 方阵 Σ、以及 R×V 正交矩阵 WT

之后,lsemantica 对分解后的矩阵进行截断降维。矩阵 Σ 的奇异值从高到低排,去掉其中最小特征值相关的行和列,仅保留用户选择的 C×C,这使得矩阵的维度变为 C。因此矩阵 U 变成 UC,矩阵 Σ 变为 ΣC,矩阵 WT 变成 WCT (见下图的阴影部分)。

值得注意的是,C 的数量通常是根据词汇表的大小来选择的,Martin 和 Berry (2007) 认为 100~1000 通常会产生良好的效果。降维过程的最终结果是,产生矩阵 A 的低阶近似矩阵 AC。这一过程是 LSA 中最重要的步骤,因为它将原本的语义空间压缩到 C 这一最重要的部分。

2.1.4 用截断后的矩阵构造语义空间

lsemantica 基于 UC×ΣC 得到的 D×C “文档-成分” 矩阵来计算文档相似性,该矩阵是矩阵 A 的降维语义空间。通过 lsemantica cosine 对该矩阵的各个行向量计算余弦相似度,可以得到各个文档之间的相似度。除此之外,lsemantica 还可以保存 “单词-成分” 矩阵 WT,并通过命令 lsemantica_word_comp 读取。该矩阵主要用于判断单个单词之间的词义相似度,即用于识别多词同义的情形。

2.2 语法介绍

*命令安装
net install st0552.pkg, replace
*语法介绍
lsemantica varname, components(integer) tfidf min char(integer) ///
           stopwords(string) min freq(integer) max freq(real)   ///
           name new var(string) mat save path(string)
  • components(integer) 指定语义空间应该减少到的组件数量。组件的数量通常基于词汇表的大小来选择,默认值为 components(300)
  • tfidf 指定在应用截断奇异值分解之前是否应该使用 tfidf 重新加权。在大多数情况下,tfidf 重新加权将改善结果;
  • min_char(integer) 允许从文本中删除短词。字符少于最小字符的单词将从 LSA 中排除。默认值为 min_char(0)
  • stopwords(string) 指定要从 lsemantica 中排除停用词;
  • min_freq(integer) 允许删除出现在较少文档中的单词。默认值为min_freq(0)
  • max_freq(real) 允许删除文档中频繁出现的单词。默认值为 max_freq(1)
  • name_new_var(string) 指定 lsemantica 创建的输出变量的名称。这些变量包含每个文档的组件。默认情况下,变量的名称前缀是 component;
  • mat_save 指定是否应保存单词成分矩阵 WT。这个矩阵描述了单词之间的语义关系。默认情况下,不会保存矩阵;
  • path(string) 设置保存单词分量矩阵的路径。

3. Stata 实例

3.1 英文实例-计算论文标题相似度

本例子的数据集为 1980-2016 年在经济期刊上发表的 41349 篇文章的标题。在进行 LSA 之前,先删除标题中的非字母数字字符,并列出前三篇文章清洗后的标题。

. *数据获取
. cap mkdir tmp //建立临时文件夹
. cd "./tmp"
. net get st0552.pkg, replace ///
>     from(http://www.stata-journal.com/software/sj19-1/)
. use example_data.dta, clear
. gen text_strings = title
. *剔除非字母和数字字符
. replace text_strings=strlower(text_strings)
. replace text_strings =  ustrregexra(text_strings, "[^a-zA-Z ]", "")
. replace text_strings = stritrim(text_strings)
. list text_strings in 1/3
     +------------------------------------------------------------------+
     |                                                     text_strings |
     |------------------------------------------------------------------|
  1. |                      what is labor supply and do taxes affect it |
  2. |                tax rules and the mismanagment of monetary policy |
  3. | a consistent characterization of a nearcentury of price behavior |
     +------------------------------------------------------------------+

首先,lsemantica 生成 “文档-词汇” 矩阵,从数据中删除小于 4 个字符的单词和出现在少于 10 个文档或超过一半文档中的单词,并将定义的停止词列表 stopwords 中的单词从数据中删除。

然后,lsemantica 使用 TFIDF 对生成的 “文档-词汇” 矩阵进行重新加权。每当处理了 10% 的词汇表时,该命令都会报告。

. *自定义被排除的单词列表 (停止词)
. global stopwords "a able about across after all almost also 
> am among an and  any are as at be because been but by can 
> cannot could dear did do does either  else ever every for 
> from get got had has have he her hers him his how however  
> i if in into is it its just east let like likely may me 
> might most must my  neither no nor not of off often on 
> only or other our own rather said say says  she should 
> since so some than that the their them then there these 
> they this tis to too twas us wants was we were what when 
> where which while who whom why will with would yet you your"

. *进行潜在语义分析
. lsemantica text_strings, components(300) min_char(4)         ///
>     min_freq(10) max_freq(0.5) tfidf stopwords("$stopwords") ///
>     path("$path") mat_save

如果某些文档在文本清洗后没有剩下的单词文本,这些样本将被删除。因为它们干扰了截断奇异值分解。lsemantica 会报告哪些文档已经从数据中删除,以及词汇表的大小。在本示例中,167 个文档被删除。

需要注意的是,在 lsemantica 计算截断奇异值分解的过程中需要较多的算力,可能会运行较长时间,有时候 Stata 会变得无响应。其计算时长时间随着 “文档-词汇矩阵” 的大小而增加,即随着文档数量和词汇表的大小而增加。

lsemantica 运行完毕后,可以使用 lsemantica_cosine 命令计算分量向量之间的余弦相似度,即各文档间相似度。lsemantica_cosine 计算了与其他文档标题的平均相似度以及最大和最小相似度。

. lsemantica_cosine component_1-component_300, mean_cosine     ///
>     min_cosine max_cosine find_similar(10) find_similar_cosine(10)

. *查看平均相似度描述性统计
. summarize mean_similarity, detail
                       mean_similarity
-------------------------------------------------------------
      Percentiles      Smallest
 1%      .004445       .0019212
 5%     .0064652        .002039
10%     .0078351       .0022063       Obs              41,187
25%     .0106501       .0023181       Sum of Wgt.      41,187

50%     .0145226                      Mean           .0151535
                        Largest       Std. Dev.      .0060212
75%      .018957       .0410298
90%     .0232855       .0423272       Variance       .0000363
95%     .0260129       .0429791       Skewness        .573504
99%     .0315491       .0431718       Kurtosis       3.174497

. *查看最大相似度描述性统计
. summarize max_similarity, detail
                       max_similarity
-------------------------------------------------------------
      Percentiles      Smallest
 1%      .566003       .3102426
 5%     .6279272       .3372106
10%     .6633787       .3501289       Obs              41,187
25%     .7301425       .3673398       Sum of Wgt.      41,187

50%     .8161867                      Mean           .8236289
                        Largest       Std. Dev.      .1225668
75%       .94531              1
90%     .9939191              1       Variance       .0150226
95%     .9984688              1       Skewness      -.1232839
99%            1              1       Kurtosis        2.12262

. *查看最小相似度描述性统计
. summarize min_similarity, detail
                       min_similarity
-------------------------------------------------------------
      Percentiles      Smallest
 1%    -.2540156      -.4333508
 5%     -.192361      -.4333508
10%    -.1627325      -.4326484       Obs              41,187
25%    -.1244975      -.4326484       Sum of Wgt.      41,187

50%    -.0941637                      Mean          -.1053932
                        Largest       Std. Dev.      .0439186
75%    -.0745706      -.0296279
90%    -.0628191      -.0296218       Variance       .0019288
95%    -.0572771      -.0287106       Skewness      -1.664787
99%    -.0482183      -.0284858       Kurtosis       7.269679

由于余弦相似度矩阵的维数一般比较大 (DxD),因此将其存储在 Mata 中。如果需要,可以在 lsemantica_cosine 命令后使用 getmata (cosin_*) = cosine_sim 从 Mata 中检索完整的余弦相似矩阵。当然,也可以将其储存为 mata 文件,以便下次读取使用。

. *在 Stata 中运行 Mata 命令,以下代码需要同时运行
. *Stata 中运行 Mata,保存和读取余弦相似矩阵
. mata 
------------------- mata (type end to exit) ---------
: // 保存 Mata 文件
: mata matsave cosine cosine_sim, replace 
(saving cosine_sim[41187,41187])
file cosine.mmat saved

: // Mata 读取文件
: mata matuse cosine, replace 
(loading cosine_sim[41187,41187])
: end
-----------------------------------------------------

此外,lsemantica_cosine 可以为数据中的每一篇文章找到最相似的文章。在这个例子中,计算了 10 篇最相似的文章。然后,列出数据中第一篇文章的五个最相似的文章标题。可以看到,LSA 准确地识别了所有讨论劳动力供应问题的高度相似的文章。

. list most_similar_* cosine_most_similar_* if _n ==1

+---------------------------------------------------------------------------++
| 28468 | 513 | 6288 | 27267 | 34267 | 26707 | 38850 | 29322 | 29305 | 38932 |
+----------------------------------------------------------------------------+

. list title if _n==1 | _n==28468 | _n==513 | _n==6288 | _n==27267 | _n==34267

+----------------------------------------------------------------------+
| title                                                                  
| ---------------------------------------------------------------------- 
| 1.| What Is Labor Supply and Do Taxes Affect It?                         
| 513.| Family Labor Supply with Taxes                                      
| 6288. | The Effect of Taxes on Labor Supply in the Underground Economy       
| 27267.| Low-Skilled Immigration and the Labor Supply of Highly Skilled Women 
| 28468.| Labor Supply and Taxes: A Survey                                     
| 34267.| Worktime Regulations and Spousal Labor Supply                       
+----------------------------------------------------------------------+

lsemantica 可以计算与原始文章高度相似的文章数量。在这个例子中,选择了余弦相似度为 0.7 5的截止值,通过 Mata 代码生成一个名为 high_sim_articles 的新变量,即余弦相似度大于 0.75 的文档的数量。

. sort pub_year
. mata 
----------------- mata (type end to exit) ----------------
: pub_year = st_data(., "pub_year")

: high_sim_paper= J(0,1,.)

: for (y=1980 ; y<=2016 ; y++){
>   cosine_submat = select(cosine_sim, pub_year:==y)
>   cosine_submat = select(cosine_submat',pub_year:>=y)'
>	high_sim = rowsum(( cosine_submat:>=J(rows(cosine_submat), 
>   cols(cosine_submat), 0.75) ))
>   high_sim_paper = high_sim_paper \ high_sim

: var = st_addvar("double", "high_sim_paper")
: st_store(., "high_sim_paper", high_sim_paper)
: end
----------------------------------------------------------

对引用数量和高相似文章数量的回归分析表明,两个变量之间存在显著的正相关关系,即高相似文章数量越多的文章越多文章引用。

. reg citations high_sim_paper i.pub_year

      Source |       SS           df       MS      Number of obs   =    41,182
-------------+----------------------------------   F(37, 41144)    =     79.43
       Model |  34007739.4        37  919128.091   Prob > F        =    0.0000
    Residual |   476071505    41,144   11570.861   R-squared       =    0.0667
-------------+----------------------------------   Adj R-squared   =    0.0658
       Total |   510079244    41,181  12386.2763   Root MSE        =    107.57

--------------------------------------------------------------------------------
     citations |      Coef.   Std. Err.      t    P>|t|     [95% Conf. Interval]
---------------+----------------------------------------------------------------
high_sim_paper |  -.0244854   .4637143    -0.05   0.958    -.9333754    .8844046
      pub_year |
         1981  |   18.96915   15.03021     1.26   0.207    -10.49039     48.4287
        (output omitted)
         2016  |  -44.62202   10.75611    -4.15   0.000    -65.70423   -23.53981
         _cons |   48.41729   10.50983     4.61   0.000     27.81779     69.0168
--------------------------------------------------------------------------------

最后,lsemantica 可以比较词语的语义关系和相似度。使用lsemantica_word_comp,可以导入 lsemantica 存储的 “单词-成分” 矩阵。同样,lsemantica 余弦可以用于计算数据中单词之间的余弦相似度,以找到最相似的单词。例如,在下例中,lsemantica 识别出 “division”、“force”、“frictional”、“labor”、“monopsony” 和“segemented” 这几个词是相互关联的。

. lsemantica_word_comp using "word_comp.mata"
. lsemantica_cosine component_1-component_300, ///
>     find_similar(10) find_similar_cosine(10)
. list most_similar_*  if _n ==1516, noheader table

  +------------------------------+-----------------------------+
  | 1768   812  1144  2465  1111   291  1394  1309  2253  2232 |
  +------------------------------------------------------------+

. list word if _n==1516 | _n==812 | _n==1144 | _n==2465 | _n==1111 | _n==1768

   +--------------------+
   |       |    words   |
   | ----- |------------|
   | 812.  | division   |
   | 1111. | force      |
   | 1144. | frictional |
   | 1516. | labor      |
   | 1768. | monopsony  |
   | 2465. | segmented  |
   +--------------------+

3.2 中文实例-计算 MD&A 相似度

本实例仅分析平安银行 (证券代码 000001) 2001-2019 年的管理层讨论与分析的文本 (MD&A) 数据。同样,在 LSA 之前进行文字清洗,仅保留中文。

. use "https://gitee.com/arlionn/data/raw/master/
> data01/lsemantica/MD_A.dta", clear
. gen text_strings = mda
. *仅保留中文字符
. replace text_strings=ustrregexra(text_strings,"[^\u4e00-\u9fa5]+"," ") 
. *去除中文数字
. replace text_strings=ustrregexra(text_strings,"[一二三四五六七八九十]+"," ") 

由于中文和英文单词不同,没有空格用于区分各个单词,故这里需要在 Stata 中调用 Python 中的 jieba 库,对文本进行分词。

*调用python进行中文分词
python 
import jieba
from sfi import Data
import pandas as pd
dct = Data.getAsDict('Symbol text_strings')
txtdf =pd.DataFrame(dct) 
txtdf['text_strings']=txtdf['text_strings'].apply(lambda x:"  ".join(jieba.cut(x, cut_all=False)))
from sfi import Data 
Data.setObsTotal(len(txtdf))
Data.addVarStr("text",100)
Data.store("text",None,txtdf['text_strings'],None)
end

接着进行 LSA 分析,先计算 “文档-词汇” 矩阵,由于本示例中只有 19 条文本,词汇量较少,故只能提取出较少成分,否则在 SVD 分解过程中会报错,故这里 components(15)。同时纳入计算的词汇标准也降低,最小字符长度定义为 1,词频范围 0~1。

. lsemantica text, components(15) min_char(1) ///
>     min_freq(0) max_freq(1) 
*计算向量间余弦相似度
. lsemantica_cosine component_1-component_10,         ///
>   mean_cosine min_cosine max_cosine find_similar(5) ///
>   find_similar_cosine(5)
. list most_similar_* if _n ==1, noheader
     +------------------------------------------------------+
  1. |        2          3          4          8          9 |
     +------------------------------------------------------+

. list  Year if _n==1 | _n==2 |_n==3 |_n==4 |_n==2 |_n==8

     +-------------+
     |      | Year |
     | ---- |------|
     | 1.   | 2001 |
     | 2.   | 2002 |
     | 3.   | 2003 |
     | 4.   | 2004 |
     | 8.   | 2008 |
     +-------------+

从 Mata 中获取余弦相似度矩阵,生成变量 cosin_1-cosin_19,分别表示与 2001~2019 年管理层讨论与分析文本的相似度。通过循环构建变量 Similarity 表示与上一年年报中管理层讨论与分析文本的相似度,并查看其描述性统计。

. getmata (cosin_*) = cosine_sim
.	  gen Similarity=.
.	  foreach num of numlist 1/19 {
.        replace Similarity=cosin_`num' if _n==(`num'+1)
	}
	
. summarize Similarity,detail

                         Similarity
-------------------------------------------------------------
      Percentiles      Smallest
 1%     .0898155       .0898155
 5%     .0898155       .1218855
10%     .1218855       .2486546       Obs                  18
25%     .4395948       .3753533       Sum of Wgt.          18

50%     .7880501                      Mean            .689412
                        Largest       Std. Dev.      .3095546
75%     .9507129       .9534972
90%     .9966181        .995663       Variance        .095824
95%     .9967352       .9966181       Skewness      -.7702948
99%     .9967352       .9967352       Kurtosis       2.224078

4. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 爬虫 文本
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

免费公开课

最新课程-直播课

专题 嘉宾 直播/回看视频
最新专题 文本分析、机器学习、效率专题、生存分析等
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]
  • Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。

课程主页

课程主页

关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 连享会-主页知乎专栏,400+ 推文,实证分析不再抓狂。直播间 有很多视频课程,可以随时观看。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会-常见问题解答:
https://gitee.com/lianxh/Course/wikis

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh