LSI和LDA模型初试验

从文本到向量

import csv

# 出于试验目的,只读取前5条评论
with open('comments.csv', 'r', newline='') as f:
    comments_reader = csv.reader(f, dialect='excel')
    corpus = [comment[1] for comment in comments_reader][1:6]

for i, comment in enumerate(corpus):
    print(i + 1, comment)
1 京东速度杠杠的!信任京东!昨晚11:30付的尾款!本来显示的是预计12号送达的!结果下午还是送来了!效率没得说!要的就是这速度!东西看着还不错吧!小巧玲珑的很好看!目前没发现什么问题!我个人觉得这屏幕分辨率太低了点!!不知道这个电子狗功能是不是鸡肋!看上的就是这个功能,希望好用!因为我原车本来就自带记录仪的!虽然还没有上车使用!但因为信任京东,信任360就先好评了!具体表现要明天使用后才知道!后续再补充!
2 喜欢这款行车记录仪,大家认真看看评价吧,差不多花了不到一半的价格买到它的,卫︿新,去∧艘~东家羊毛~你懂得哦,这个宫宗皓可以享半价哦!多少年了一直买京东,从来不知道还有补贴,不过知道它的人不多,经常省个百八十不成问题,不光这一个,所有商品基本都有。商家发货快,问了客服很多问题,照着提示已经安装好了,安装也方便,装上去也不占地方,很上档次,中间遇上了一点小麻烦,咨询了客服,态度不错很快就帮我解决了,今天开出去溜了一下,看起来也很上档次,图像拍摄的很清晰,正品,效果非常棒了,但是基本能满足,也对得起这个价格。物有所值,买到合心意质量好的东东,质量好杠杠滴,赞一个!而且价格超级划算,我就是关住了 ~ 东 家 羊 毛 ~,这个威威宫宗号。半价买的,排在第一个才是哦。一如既往的支持京东。
3 生产日期很美丽!2017-11,东西还没用上去,但是从体积上肯定不错,毕竟不会被遮挡。隐藏的很好,而且参数我看过,和后视镜版本M302几乎一样,胜在不占空间。东西也比较简约,使用过一段日子再来评价。外观还不错!
4 这是我精挑细选的一款记录仪,不用支架,小巧玲珑,携带方便,摄像高清,还可以无线联网,非常棒
5 看个一分钟录像,卡了一个小时,下载四五次,还下不下来,真**上火,APP也卸载了重新安装,行车记录仪也恢复出厂设置,鼓捣一晚上还是这样,气死我了
# 分别对各文本进行预处理,流程为:1、分词,2、去除只在整个语料库中出现1次的词,
# 3、去除停用词

# 分词
import jieba

texts = [[w for w in jieba.cut(text)] for text in corpus]

# 去除停用词
with open('stoplist_ch.txt', 'r', encoding='utf-8') as f:
    stoplist = [w.rstrip('\n') for w in f.readlines()]
texts = [[w for w in text if w not in stoplist] 
         for text in texts]

# 去除只在整个语料库中出现1次的词
fdist = {}
for text in texts:
    for w in text:
        fdist[w] = fdist.get(w, 0) + 1

texts = [[w for w in text if fdist[w] > 1]
         for text in texts]

print(texts)
[['京东', '速度', '杠杠', '信任', '京东', '11', '本来', '速度', '东西', '不错', '小巧玲珑', '问题', '知道', '功能', '功能', '本来', '记录仪', '使用', '信任', '京东', '信任', '使用', '知道', '再'], ['行车', '记录仪', '评价', '价格', '买到', '半价', '买', '京东', '知道', '知道', '一个', '客服', '问题', '安装', '好', '安装', '占', '上档次', '客服', '不错', '上档次', '棒', '价格', '买到', '质量', '好', '质量', '好', '杠杠', '一个', '价格', '半价', '买', '京东'], ['11', '东西', '不错', '好', '占', '东西', '使用', '再', '评价', '不错'], ['记录仪', '小巧玲珑', '棒'], ['一个', '行车', '记录仪']]
# 将这些词编号,以词典键值对的方式表示
from gensim import corpora

dictionary = corpora.Dictionary(texts)
print(dictionary)
Dictionary(29 unique tokens: ['11', '不错', '东西', '京东', '使用']...)
# 以"词:编号"的方式shi显示
print(dictionary.token2id)
{'11': 0, '不错': 1, '东西': 2, '京东': 3, '使用': 4, '信任': 5, '再': 6, '功能': 7, '小巧玲珑': 8, '本来': 9, '杠杠': 10, '知道': 11, '记录仪': 12, '速度': 13, '问题': 14, '一个': 15, '上档次': 16, '买': 17, '买到': 18, '价格': 19, '半价': 20, '占': 21, '好': 22, '安装': 23, '客服': 24, '棒': 25, '行车': 26, '评价': 27, '质量': 28}
# 用词袋的方法将各词在各文本的出现次数,即词频表示出来
corpus_mm = [dictionary.doc2bow(text) for text in texts]
print(corpus_mm)
[[(0, 1), (1, 1), (2, 1), (3, 3), (4, 2), (5, 3), (6, 1), (7, 2), (8, 1), (9, 2), (10, 1), (11, 2), (12, 1), (13, 2), (14, 1)], [(1, 1), (3, 2), (10, 1), (11, 2), (12, 1), (14, 1), (15, 2), (16, 2), (17, 2), (18, 2), (19, 3), (20, 2), (21, 1), (22, 3), (23, 2), (24, 2), (25, 1), (26, 1), (27, 1), (28, 2)], [(0, 1), (1, 2), (2, 2), (4, 1), (6, 1), (21, 1), (22, 1), (27, 1)], [(8, 1), (12, 1), (25, 1)], [(12, 1), (15, 1), (26, 1)]]

这样一来,语料库中各文本就表示成了文本中各词的出现次数了,比如,第1个文本的词向量即为:1, 1, 1, 3, 2, 3, 1, 2, 1, 2, 1, 2, 1, 2, 1。值得注意的是,没有出现的词不会在打印中表示。

向量转换

上述步骤得到的是以词频作为向量各纬度的值,但并没有表现出词与词之间的关系,这是词袋的不足之一。

因此,在词袋向量的基础上,应进一步提取更多信息。为了达到这个目的,可以将向量各纬度的值转换成另外一种值。比如下面介绍的TF-IDF。

# 转换初始化,指定要转换为TF-IDF向量
import gensim

tfidf_trans = gensim.models.TfidfModel(corpus_mm)
# 转换为TF-IDF向量
corpus_tfidf = tfidf_trans[corpus_mm]
for doc in corpus_tfidf:
    print(doc)
[(0, 0.10650413173766084), (1, 0.05937530265887042), (2, 0.10650413173766084), (3, 0.31951239521298247), (4, 0.21300826347532167), (5, 0.5612141915880775), (6, 0.10650413173766084), (7, 0.3741427943920516), (8, 0.10650413173766084), (9, 0.3741427943920516), (10, 0.10650413173766084), (11, 0.21300826347532167), (12, 0.025936866279295836), (13, 0.3741427943920516), (14, 0.10650413173766084)]
[(1, 0.04686268994890363), (3, 0.1681193991574051), (10, 0.08405969957870255), (11, 0.1681193991574051), (12, 0.020470991612052493), (14, 0.08405969957870255), (15, 0.1681193991574051), (16, 0.29529681509070516), (17, 0.29529681509070516), (18, 0.29529681509070516), (19, 0.4429452226360578), (20, 0.29529681509070516), (21, 0.08405969957870255), (22, 0.2521790987361076), (23, 0.29529681509070516), (24, 0.29529681509070516), (25, 0.08405969957870255), (26, 0.08405969957870255), (27, 0.08405969957870255), (28, 0.29529681509070516)]
[(0, 0.29823262853716886), (1, 0.3325251761267265), (2, 0.5964652570743377), (4, 0.29823262853716886), (6, 0.29823262853716886), (21, 0.29823262853716886), (22, 0.29823262853716886), (27, 0.29823262853716886)]
[(8, 0.6968503263117378), (12, 0.1697034043219193), (25, 0.6968503263117378)]
[(12, 0.1697034043219193), (15, 0.6968503263117378), (26, 0.6968503263117378)]
# 更进一步,探究各文本在语义上倾向于哪个主题
# 先划分主题,使用LSI模型
lsi_trans = gensim.models.LsiModel(corpus_tfidf, id2word=dictionary,
                                   num_topics=4) # 转换初始化,指定划分4个主题
corpus_lsi = lsi_trans[corpus_tfidf] # 词袋->TF-IDF->LSI
# 看看各个主题都有什么主题词
lsi_trans.print_topics(4)
[(0,
  '0.315*"东西" + 0.258*"一个" + 0.254*"信任" + 0.253*"好" + 0.230*"使用" + 0.224*"京东" + 0.218*"行车" + 0.210*"价格" + 0.198*"不错" + 0.187*"小巧玲珑"'),
 (1,
  '0.537*"一个" + 0.509*"行车" + -0.281*"东西" + -0.222*"信任" + -0.204*"使用" + -0.162*"11" + -0.162*"再" + 0.149*"价格" + -0.148*"速度" + -0.148*"功能"'),
 (2,
  '0.662*"小巧玲珑" + 0.638*"棒" + -0.150*"东西" + -0.144*"一个" + -0.131*"行车" + 0.129*"记录仪" + -0.118*"好" + -0.093*"评价" + -0.093*"占" + -0.091*"不错"'),
 (3,
  '-0.387*"信任" + -0.329*"行车" + -0.278*"一个" + 0.268*"价格" + -0.258*"速度" + -0.258*"本来" + -0.258*"功能" + 0.241*"好" + 0.179*"质量" + 0.179*"安装"')]
# 看看各文本在各主题上的分布
for doc in corpus_lsi:
    print(doc)
[(0, 0.6151990923970965), (1, -0.43114870816315265), (2, 0.10554078138694145), (3, -0.5507585471447924)]
[(0, 0.6452603721867233), (1, 0.36676022694119653), (2, -0.14588731937618363), (3, 0.48322823901839956)]
[(0, 0.6091519197175418), (1, -0.43608218559430395), (2, -0.2693719615275453), (3, 0.23616797769920406)]
[(0, 0.2721984576989459), (1, 0.11753367175861239), (2, 0.9274598337066065), (3, 0.12780692177247774)]
[(0, 0.34886155667911756), (1, 0.7516857754005692), (2, -0.16957436157580397), (3, -0.43465064036045764)]

可以看出,从语义上说,第1、2、3个文本偏向第1个主题,第4个文本偏向第3个主题,第5个文本偏向第2个主题。

LDA模型

上面的LSI模型得到的可以看作是"语义向量",可用于计算文本之间的相似性,而LDA模型则侧重于计算某个主题所对应词在该主题下的权重,也可以看作是某主题下词的概率分布。

# LDA模型由词袋向量转换而来
lda_trans = gensim.models.LdaModel(corpus_mm, id2word=dictionary,
                                   num_topics=4) # 转换初始化,指定划分4个主题
corpus_lda = lda_trans[corpus_mm] # 词袋->LDA
# 看看各主题都有什么主题词
lda_trans.print_topics(4)
[(0,
  '0.092*"京东" + 0.072*"知道" + 0.050*"信任" + 0.047*"好" + 0.041*"价格" + 0.038*"功能" + 0.037*"速度" + 0.037*"不错" + 0.036*"使用" + 0.036*"杠杠"'),
 (1,
  '0.055*"信任" + 0.053*"本来" + 0.052*"使用" + 0.051*"京东" + 0.043*"功能" + 0.043*"速度" + 0.042*"知道" + 0.038*"记录仪" + 0.037*"小巧玲珑" + 0.037*"问题"'),
 (2,
  '0.069*"信任" + 0.060*"不错" + 0.060*"使用" + 0.054*"东西" + 0.049*"本来" + 0.048*"速度" + 0.045*"功能" + 0.044*"知道" + 0.043*"京东" + 0.043*"再"'),
 (3,
  '0.075*"记录仪" + 0.061*"不错" + 0.060*"一个" + 0.060*"好" + 0.052*"东西" + 0.048*"价格" + 0.047*"评价" + 0.046*"棒" + 0.045*"行车" + 0.040*"质量"')]
# 看看各文本在各主题上的分布
for doc in corpus_lda:
    print(doc)
[(0, 0.9686841), (1, 0.010232531), (2, 0.0106450515), (3, 0.0104383305)]
[(0, 0.42112163), (3, 0.56442404)]
[(0, 0.02371528), (1, 0.023032475), (2, 0.02412851), (3, 0.92912376)]
[(0, 0.06447045), (1, 0.06305108), (2, 0.06310642), (3, 0.809372)]
[(0, 0.06418085), (1, 0.06276632), (2, 0.06285436), (3, 0.8101985)]

计算相似性

# TODO: 现在有一个新文本,需要分析与其相似的文本有哪些
上一篇:如何使用gensim使用训练有素的LDA模型预测新查询的主题?


下一篇:lda 小说分析