继续更新出来本系列的代码:乱炖数据之2700余篇“简书交友”专题文章数据的花式玩法
在乱炖“简书交友”数据之代码(1)一文里,主要涉及结构化数据的分析,文本挖掘如词频统计、词云图等。本文继续用jieba库抽取文本关键词,并调用百度云NLP的API获取关键词的Word2Vec词向量,并用t-SNE可视化高维数据,之后用文本自己训练了Word2Vec词向量,效果稍好些,最后尝试了下LDA主题模型。
代码见于 GitHub - DesertsX / JianShuJiaoYou,下一篇也是本系列最后一篇会涉及文章的照片爬取、人脸识别及颜值打分和照片墙等更新后也会开源在此项目,欢迎star。
另外先预告下,之后打算开个“Kaggle Kernel 学习系列”,GitHub - DesertsX / Kaggle-Kernel-Learning,主要是翻译和学习下kaggle上优秀的kernels。其中第一篇非常粗糙,还没润色、修改排版布局的notebook可供浏览下,也欢迎关注、star和提供宝贵建议: https://desertsx.github.io/2018/06/09/1_1_Start_Here-A_Gentle_Introduction/ https://desertsx.github.io/2018/06/09/kaggle-Kernel-01/
import pandas as pd
import jieba
import jieba.analyse
df12 = pd.read_csv('JianShuJiaoYou-All-Data.csv', encoding='utf-8')
contents = " ".join(df12.Artical_Content.values.tolist())
Top200 基于 TF-IDF 算法抽取前200个关键词(普通名词和地点名词)。
textrank1 = " ".join(jieba.analyse.extract_tags(contents, topK=200, withWeight=False, allowPOS=('ns', 'n')))
print(textrank1)
简书 时候 朋友 简友 文章 交友 我会 文字 故事 投稿 爱情 感觉 大家 大学 时间 专题 事情 人生 老师 树洞 世界 东西 同学 照片 地方 女生 电影 情书 性格 昵称 有点 交流 学校 梦想 内心 经历 时光 男生 作者 读书 城市 女孩 姑娘 孩子 单身 毕业 感情 凡人 青春 样子 名字 摄影 男朋友 校园 灵魂 父母 日子 对方 问题 学生 手机 朋友圈 小说 年龄 美食 专业 职业 评论 哥哥 心情 星座 女朋友 跑步 账号 素材 体重 陌生人 小伙伴 音乐 唱歌 好友 文艺 自我介绍 见面 画画 室友 个人 姐姐 陪伴 风景 妈妈 想象 地点 缘分 闺蜜 社群 回家 习惯 篇文章 现实 记录 无法 图书馆 性别 公众 理想 原因 关系 平台 北京 兴趣 游戏 结果 粉丝 小时候 美丽 情感 礼物 男人 总会 女孩子 方式 机会 宿舍 程序员 长大 舍友 陌生 印象 文学 私信 妹子 嘉宾 校友 话题 男孩 学会 模样 交朋友 联系 声音 眼睛 家乡 记忆 编辑 小时 女人 颜值 无戒 世间 能力 缺点 写文章 脾气 先生 家庭 家人 写字 身体 大神 咖啡 读者 码字 上海 线下 晚安 热情 社会 幻想 衣服 群里 生气 教室 吉他 学历 成绩 笔名 味道 情绪 意义 状态 异地 民谣 励志 学姐 爸妈 天空 作品 书写 友情 妹妹 思想 心灵 成都 老爸 学长 文笔 婚姻 小学 目标
jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
算法基本思想:
Top200 基于 TextRank 算法抽取前200个关键词(allowPOS=('ns', 'n'),普通名词和地点名词),并返回关键词权重值(withWeight=True)。篇幅所限,仅罗列前几个词语。
textrank4 = jieba.analyse.textrank(contents, topK=200, withWeight=True, allowPOS=('ns', 'n'))
print(textrank4)
[('时候', 1.0), ('简书', 0.9286492277441794), ('朋友', 0.5604058706337074), ('文章', 0.5119472310224017), ('大家', 0.4435913680744001), ('交友', 0.4205067493937958), ('时间', 0.41736915018165616), ('大学', 0.4169278527224929), ('文字', 0.3963519612404718), ('故事', 0.3672191429321304), ('简友', 0.36709289668920975), ('感觉', 0.35411529061992564),]
抽取出这些关键词后,突然想到可以使用word2vec词向量,看看这些词语在向量空间中会是怎样分布的?哪些词语会在相似的区域?
原本想用gensim库
自己训练word2vec,但是没成功(后面重新研究了下,已经搞定了,后面再介绍),机缘巧合接触到百度云的产品,于是调用下看看效果如何。
词向量,就是将词语映射成一个固定维度的向量。词向量可能具备一定的语义信息,如相似的词语在相近的向量空间(如西瓜和苹果都属于水果,但苹果也存在歧义);可以学到词语之间的关系,如经典的“男人-女人=国王-王后”(King – Man + Woman = Queen)。
又比如,国家与首都之间的对应关系也能通过词向量反映出来。后文也调用百度云的api试了一下几组词,有类似效果。
相关原理可自行了解,文本不展开了:
再贴下官网的解释:
先官网注册下以便调用API,再按照Python库:pip install baidu-aip
。
# pip install baidu-aip
# https://cloud.baidu.com/doc/NLP/NLP-Python-SDK.html#.E6.96.B0.E5.BB.BAAipNlp
# 新建一个AipNlp
from aip import AipNlp
""" 你的 APP_ID/API_KEY/SECRET_KEY """
APP_ID = '你的 APP_ID'
API_KEY = '你的 API_KEY'
SECRET_KEY = 'SECRET_KEY'
client = AipNlp(APP_ID, API_KEY, SECRET_KEY)
官网示例:调用词向量表示 每个词语均用1024维的词向量表示,当然也有很多词不存在语料库里,所以也就无法用词向量表示。先看看官网给出的“俺也一样”的“张飞”的示例,一行代码就能获取相应词语的词向量。不过由于太占篇幅,文章里就不放了,非常详细的代码在: GitHub - DesertsX / JianShuJiaoYou。
word = "张飞"
""" 调用词向量表示 """
w = client.wordEmbedding(word);
print(w['word'], len(w['vec']) # 张飞 1024
print(w)
罗列出了词表中不存在的词语共13个,拿到187个词语的词向量。
import numpy as np
words_list = []
word_vectors = []
for word,_ in textrank4:
try:
data = client.wordEmbedding(word)
word_vector = data['vec']
#print(data['word'])
words_list.append(data['word'])
word_vectors.append(word_vector)
except:
print("No word:{}".format(word))
word_vectors = np.array(word_vectors)
# print("Total words:", len(words_list))
print(words_list)
print("Total words:", len(words_list), '\tWord Embedding shapes:', word_vectors.shape)
# Total words: 187
# Word Embedding shapes: (187, 1024)
No word:简书 No word:简友 No word:有点 No word:小时候 No word:图书馆 No word:男朋友 No word:线下 No word:陌生人 No word:朋友圈 No word:小伙伴 No word:大学生 No word:女孩子 No word:程序员 Total words: 187 Word Embedding shapes: (187, 1024)
使用 t-SNE 来查看可视化高维数据,分别在2维和3维下查看下词向量分布效果。 sklearn.manifold.TSNE
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
%matplotlib inline
#%config InlineBackend.figure_format='svg'
def plot_tsne_2D(word_vectors, words_list):
tsne = TSNE(n_components=2, random_state=0, n_iter=10000, perplexity=3)
np.set_printoptions(suppress=True)
T = tsne.fit_transform(word_vectors)
labels = words_list
plt.figure(figsize=(8, 6))
plt.scatter(T[:,0], T[:,1], c='steelblue', edgecolors='k')
for label, x, y in zip(labels, T[:,0], T[:,1]):
plt.annotate(label, xy=(x+1, y+1),xytext=(0,0), textcoords='offset points')
plot_tsne_2D(word_vectors, words_list)
效果不好,比较杂乱,相关相似的词语没有分布在相近区域,可能是百度使用的语料和本项目用的简书交友文章语料的不同导致的。
还是很杂乱,看到“哥哥”、“姐姐”、“女人”、“女孩”、“孩子”等词语分布的很分散,感觉一定是哪出了问题。
# https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
from mpl_toolkits.mplot3d import Axes3D
def plot_tsne_3D(word_vectors, words_list):
tsne = TSNE(n_components=3, random_state=0, n_iter=10000, perplexity=2)
np.set_printoptions(suppress=True)
T = tsne.fit_transform(word_vectors)
labels = words_list
plt.figure(figsize=(10, 6))
ax = plt.subplot(111,projection='3d')
for i in range(len(T)): # plot each point + it's index as text above
x = T[i,0]
y = T[i,1]
z = T[i,2]
label = labels[i]
ax.scatter(x, y, z, color='b');
ax.text(x, y, z, '%s' % (label), size=9, zorder=1, color='k');
ax.set_title('word2vec t-SNE 3D');
ax.set_xlabel('x');
ax.set_ylabel('y');
ax.set_zlabel('z');
plot_tsne_3D(word_vectors, words_list)
由于上述词向量可视化的结果不太理想,为了探索下哪出了问题,于是复现下官网配图的效果。当然一开始并不清楚它是举例用的、随便画的,还是实际通过计算后绘制的。个人倾向于后者,那么应该是能复现吧?!
将获取词向量,2维、3维 t-SNE 可视化均写成函数,方便重复使用。
words = ['姨夫', '老公', '妹妹', '奶奶', '表哥', '干爸', '外爷', '继父',
'性感', '沮丧', '真够', '悲愤', '探询', '怀想', '甜美',
'南宁', '合肥', '淮海', '珠江', '津蒙', '奎河', '邕江', '滦河']
def get_word2vec(words):
words_list = []
word_vectors = []
for word in words:
try:
data = client.wordEmbedding(word)
word_vector = data['vec']
#print(data['word'])
words_list.append(data['word'])
word_vectors.append(word_vector)
except:
print("No word:{}".format(word))
word_vectors = np.array(word_vectors)
print(words_list)
print("Total words:", len(words_list), '\tWord Embedding shapes:', word_vectors.shape)
return word_vectors, words_list
word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)
额...地名的大致在右下角,人物关系的在左上角,但还是区分度不够好。
plot_tsne_3D(word_vectors, words_list)
很差......完全摸不着头脑,可能是需要继续调参吧???
自己不死心的,又想了三类词语,这回的效果理想了很多。不过“腾 讯”一词有些突兀,于是去掉后再试试。
words = ['中国', '北京', '日本', '东京', '法国', '巴黎', '俄罗斯', '莫斯科',
'百度', '李彦宏', '京东', '刘强东', '腾讯', '马化腾', '阿里巴巴','马云',
'三国', '曹操', '刘备', '西游记', '唐僧', '悟空', '红楼', '宝玉', '黛玉','王熙凤']
word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)
去掉“腾讯”一词后,除了“京东”一词明显突兀,其他都还不错,相似相关的词分布在了一起
words = ['中国', '北京', '日本', '东京', '法国', '巴黎', '俄罗斯', '莫斯科',
'百度', '李彦宏', '京东', '刘强东', '马化腾', '阿里巴巴','马云',
'三国', '曹操', '刘备', '西游记', '唐僧', '悟空', '红楼', '宝玉', '黛玉','王熙凤']
word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)
plot_tsne_3D(word_vectors, words_list)
Gensim - models.word2vec – Deep learning with word2vec
读取数据,并去掉停用词,注意sentences
是列表嵌套列表的格式**
import pandas as pd
import jieba
df12 = pd.read_csv('JianShuJiaoYou-All-Data.csv', encoding='utf-8')
content = df12.Artical_Content.values.tolist()
stopwords1 = [line.rstrip() for line in open('./Stopwords/中文停用词库.txt', 'r', encoding='utf-8')]
stopwords2 = [line.rstrip() for line in open('./Stopwords/哈工大停用词表.txt', 'r', encoding='utf-8')]
stopwords3 = [line.rstrip() for line in open('./Stopwords/四川大学机器智能实验室停用词库.txt', 'r', encoding='utf-8')]
stopwords = stopwords1 + stopwords2 + stopwords3
print(stopwords[10:20])
sentences = []
for line in content:
try:
segs = jieba.lcut(line)
segs = filter(lambda x:len(x)>1, segs)
segs = filter(lambda x:x not in stopwords, segs)
# sentences.append(segs)
sentences.append(list(segs))
except Exception as e:
print(line)
continue
import multiprocessing
from gensim.models import Word2Vec
model_csv = Word2Vec(sentences, min_count=20, sg=0, workers=multiprocessing.cpu_count())
model_csv.wv.most_similar(['简书'], topn=15)
训练好后,找出与“简书”一词,最相近最相关的词语,罗列如下,效果还行:
[('平台', 0.9340553283691406), ('签约', 0.905404269695282), ('书上', 0.8926113843917847), ('一位', 0.8816096186637878), ('简书里', 0.8752850294113159), ('加入', 0.8719199895858765), ('创作', 0.8692747354507446), ('这篇', 0.8666418790817261), ('大神', 0.8640251755714417), ('下载', 0.8557288646697998), ('篇文章', 0.8523578643798828), ('第一篇', 0.8458684682846069), ('作者', 0.8452268838882446), ('专题', 0.8378645181655884), ('读者', 0.8378232717514038)]
查询前文抽取的关键词textrank4
的最相近词语,并拿到关键词的词向量。
import numpy as np
words_list = []
word_vectors = []
for word,_ in textrank4:
try:
result = model_csv.wv.most_similar(positive=[word],topn=15)
print("关键词: {}".format(word))
print(result)
print("+++++++++++++++++++++++++++++++++++")
w2v = model_csv.wv[word]
words_list.append(word)
word_vectors.append(w2v)
#for v in w2v:
# print(v[0],v[1])
except:
print("No word: {}".format(word))
word_vectors = np.array(word_vectors)
挑选了些查询的结果,供读者浏览,看看大家都在谈些什么,蛮有趣的:
关键词: 电影
[('拍照', 0.9252004623413086), ('爬山', 0.920052170753479), ('美食', 0.9192012548446655), ('音乐', 0.9168093204498291), ('看书', 0.9157875180244446), ('听歌', 0.9069955945014954), ('旅游', 0.9057952165603638), ('唱歌', 0.899057149887085), ('吉他', 0.8984708786010742), ('跑步', 0.8956234455108643), ('民谣', 0.8925215601921082), ('一部', 0.8880782127380371), ('运动', 0.8865675330162048), ('羽毛球', 0.8812819123268127), ('动漫', 0.8731318116188049)]
+++++++++++++++++++++++++++++++++++
关键词: 青春
[('记忆', 0.9561529755592346), ('回忆', 0.955527126789093), ('曾经', 0.9253653287887573), ('时光', 0.905220627784729), ('岁月', 0.896970272064209), ('最美', 0.8751366138458252), ('美丽', 0.8565613031387329), ('走过', 0.8368370532989502), ('春天', 0.8309674263000488), ('难忘', 0.8301017880439758), ('一片', 0.8259316682815552), ('美好', 0.8249017000198364), ('一段', 0.8218579292297363), ('依旧', 0.8214970231056213), ('心中', 0.8203814029693604)]
+++++++++++++++++++++++++++++++++++
关键词: 美丽
[('最美', 0.9421555995941162), ('天空', 0.9361464381217957), ('便是', 0.9163937568664551), ('流浪', 0.908709704875946), ('阳光', 0.907963752746582), ('春天', 0.9046747088432312), ('岁月', 0.9010506868362427), ('幻想', 0.8919180035591125), ('定格', 0.8899471163749695), ('纯真', 0.8851020932197571), ('年华', 0.8850700259208679), ('一片', 0.8834213614463806), ('难忘', 0.8828067183494568), ('远方', 0.8826968669891357), ('繁华', 0.8787059187889099)]
+++++++++++++++++++++++++++++++++++
关键词: 日子
[('遗憾', 0.9469071626663208), ('度过', 0.9386813640594482), ('多年', 0.9268732070922852), ('依旧', 0.9203112125396729), ('依然', 0.9201472401618958), ('变成', 0.9193578362464905), ('痛苦', 0.9121938943862915), ('后悔', 0.9080638885498047), ('过得', 0.9079610109329224), ('慢慢', 0.9073663949966431), ('珍惜', 0.9073460102081299), ('孤单', 0.9017938375473022), ('时光', 0.8997607231140137), ('过去', 0.8995781540870667), ('漫长', 0.8980476260185242)]
+++++++++++++++++++++++++++++++++++
关键词: 习惯
[('锻炼', 0.8789036870002747), ('享受', 0.8728272914886475), ('不爱', 0.8702492117881775), ('独处', 0.8683050870895386), ('发呆', 0.862934947013855), ('空闲', 0.856212854385376), ('人去', 0.8532602787017822), ('打篮球', 0.8526784777641296), ('没事', 0.8500849008560181), ('超级', 0.8474704027175903), ('放松', 0.844687819480896), ('热闹', 0.8433003425598145), ('走路', 0.8428263664245605), ('闲暇', 0.8407423496246338), ('养成', 0.8402824401855469)]
+++++++++++++++++++++++++++++++++++
关键词: 男朋友
[('女朋友', 0.9782456159591675), ('结婚', 0.9226555228233337), ('分手', 0.9135688543319702), ('距离', 0.880342423915863), ('男友', 0.8786644339561462), ('找个', 0.8754839897155762), ('担心', 0.8725529313087463), ('吵架', 0.8680383563041687), ('爸妈', 0.8625649213790894), ('两个', 0.8591646552085876), ('不想', 0.8558708429336548), ('我要', 0.853802502155304), ('我怕', 0.8512692451477051), ('分开', 0.8507095575332642), ('老婆', 0.844429075717926)]
+++++++++++++++++++++++++++++++++++
关键词: 家人
[('做好', 0.964654803276062), ('房子', 0.9459130167961121), ('挣钱', 0.9400242567062378), ('分开', 0.9261205196380615), ('爸妈', 0.9220873117446899), ('有钱', 0.9190271496772766), ('子女', 0.9171360731124878), ('压力', 0.9167039394378662), ('办法', 0.9156253337860107), ('懂事', 0.9149639010429382), ('打拼', 0.9143495559692383), ('谈过', 0.9131268858909607), ('依靠', 0.9117845892906189), ('谈恋爱', 0.9093905687332153), ('留在', 0.9089971780776978)]
+++++++++++++++++++++++++++++++++++
关键词: 长大
[('学会', 0.9459168910980225), ('身体', 0.9209215641021729), ('常常', 0.9176377058029175), ('懂事', 0.9148654937744141), ('适应', 0.9044305086135864), ('坚强', 0.9038822650909424), ('孩子', 0.9017991423606873), ('挣钱', 0.898858904838562), ('羡慕', 0.8973740935325623), ('年轻', 0.8969836235046387), ('房子', 0.8969626426696777), ('谈恋爱', 0.8935214877128601), ('模样', 0.8843299150466919), ('过得', 0.8807798624038696), ('保护', 0.8800650835037231)]
+++++++++++++++++++++++++++++++++++
关键词: 味道
[('房间', 0.9597378969192505), ('调皮', 0.957197904586792), ('嘴里', 0.9569690227508545), ('小孩子', 0.9566539525985718), ('阳台', 0.9550118446350098), ('空气', 0.9540750980377197), ('显得', 0.9536328315734863), ('零食', 0.9528669118881226), ('时常', 0.9519882202148438), ('多愁善感', 0.9519380331039429), ('伤感', 0.9509293437004089), ('有种', 0.9501902461051941), ('一双', 0.9479855298995972), ('韩剧', 0.9476990699768066), ('假装', 0.946105420589447)]
+++++++++++++++++++++++++++++++++++
关键词: 妹子
[('天秤座', 0.9551429748535156), ('典型', 0.9542213678359985), ('巨蟹座', 0.9520383477210999), ('处女座', 0.951753556728363), ('天蝎座', 0.9511485695838928), ('射手座', 0.9477106332778931), ('水瓶', 0.9455941915512085), ('水瓶座', 0.9437704682350159), ('160', 0.9407752156257629), ('白羊座', 0.937430202960968), ('双子座', 0.934114933013916), ('星座', 0.9333192110061646), ('摩羯座', 0.9331715703010559), ('双鱼座', 0.9310780167579651), ('湖北', 0.9304903745651245)]
+++++++++++++++++++++++++++++++++++
这回的 t-SNE 结果总算好了许多,看来训练语料还是影响很大的,具体图表里词语分布就不过多讲解了,可能图上传后也不太清晰,想看高清的SVG矢量图请到GitHub获取: GitHub - DesertsX / JianShuJiaoYou
plot_tsne_2D(word_vectors, words_list)
plot_tsne_3D(word_vectors, words_list)
本文先更新到此,主要涉及关键词的抽取、Word2Vec词向量的尝试和探索。后续LDA主题模型及主题的可视化、文章照片爬取、人脸识别及颜值打分和照片墙等等更新后也会开源在GitHub - DesertsX / JianShuJiaoYou,欢迎star与指正。
系列文章: 乱炖数据之2700余篇“简书交友”专题文章数据的花式玩法 乱炖“简书交友”数据之代码(1)
PS:预告下,即将开启“Kaggle Kernel 学习系列”,GitHub - DesertsX / Kaggle-Kernel-Learning,欢迎star。