前言
这次的信息内容安全课程期末大作业选题定为了微博热点爬取+情感分析。整合起来就有点像一个舆论分析玩具。具体要涉及到的东西也蛮多,这里我负责情感分析的部分,所以将用到的东西整理到这里。
主要参考到的是一个 GitHub 的 Repo : https://github.com/BUPTLdy/Sentiment-Analysis 。作者做了 SVM 和 LSTM 的情感极性分析。而我们的大作业选题中就包括使用 LSTM 进行情感分析,所以这里我主要以他的代码作为参考。
环境要求
- Unix/Linux系统
- python 2.7
- python包安装: keras,sklearn,gensim,jieba,h5py,numpy,pandas
然后作者使用的是 python 2.7 + Linux 。看来搞这些还是 Linux 稳啊,但是因为打算现在 Windows 上试试,(最后搞不出来就得不要脸地和实验室借 Linux 服务器了),所以现在自己的 Windows 主机上搞一搞。
Windows
环境准备
首先就是安各种库了,这里我用的是前不久安得 Python 3.8.2,已经安装了部分的包,基本就是无脑 pip 即可。
值得一提的是 Keras , 去官网简单看了看貌似被集成在了 TensorFlow,于是又去先安 TensorFlow。 TensorFlow 也是蛮大的包,最新的是 2.2,然后默认就是 GPU 版本,我的电脑上安装的是 NVIDIA 的 GeForce 1050,应该是要比 CPU 好使的,所以就研究了一下 GPU 的使用。
之前由于使用 PyTorch 已经安装了最新的 CUDA 10.2,结果发现最新的 TensorFlow 适配的 CUDA 居然是 10.1 。太难过了,又要 C 盘再安几个 G。而且看了看貌似 Keras 和 TensorFlow 官网上声明支持的python版本最高也是 3.6, 3.7 。用 3.8 可能还会有各种风险,太难了。不过先往后做吧,出问题再说。
安装完各种东西以后测试一下 TensorFlow 对于 GPU 的支持性。
1 | import tensorflow as tf |
如果遇到了 W 开头的输出,也就是 Warning,就要自己复制到百度搜一搜解决方案了。
代码阅读
好像安了大部分了,但是还有一些等一会儿出问题了再安,先过一遍代码,看看能不能屡清楚思路,(当然肯定是不能的),然后遇到哪里,学哪里。(时间有限迫不得已的办法,有时间的童鞋还是应该系统学习。)
1 | import yaml |
要注意一点, yaml 要使用 pip install pyyaml
进行安装。
然后有一个 reload(sys) 的操作报错了。参考这篇文章:https://blog.csdn.net/mighty13/article/details/98670394。这里我干脆先注释掉了,应该就是 Python2 编码相关的问题。https://www.cnblogs.com/bestween/p/11186988.html
1 | from sklearn.cross_validation import train_test_split |
这条语句继续报错,改成下面的代码即可。
1 | from sklearn.model_selection import train_test_split |
https://www.cnblogs.com/Yanjy-OnlyOne/p/11288098.html 是 train_test_split 的教程。大致上是给定数据集划分测试集与训练集的工具。
1 | import multiprocessing |
这几句 import 不再解释, gensim 是 NLP 的库。
1 | from keras.preprocessing import sequence |
这一段就是 import 了 很多 keras 的库,这里 PyCharm 智能地提醒了我这里需要用 Keras 1.1.1,而我的 Keras 是2.3.1。先无视吧,有兼容性问题再上网找。
1 | np.random.seed(1337) # For Reproducibility |
然后为了可以复现,设定随机数种子 1337 。引入了结巴分词,pandas。设定了递归上限。
1 | # set parameters: |
这段是一些函数参数变量的声明,在后面用到了再回头分析。
1 | #加载训练文件 |
首先加载训练文件,作者的数据集都是 Excel 存储的,已经存储在了 Github 的 Repo 里。然后将积极的与消极的评论连接起来,并且整合出一个 y ,所有积极的对应 1, 消极的对应 0 。
1 | #对句子经行分词,并去掉换行符 |
这里就是调用结巴分词的API了。遍历 text 中的 每个 document ,这里把所有的换行换成空格。然后使用 lcut,jieba.lcut 直接生成的就是一个list。
1 | #创建词语字典,并返回每个词语的索引,词向量,以及每个句子所对应的词语索引 |
这里最后一行的 print 改成了 Python3 的语法,后面不再重复。首先做了个条件判断看看有没有输入。然后使用了 gensim 库 的 Dictionary 类。这里扔上来 Dictionary 类的 官方API 。doc2bow 函数用来将 文档转换为 BOW即 Bag of words ,词袋。词袋的内容就是一个 dict ,key 是 token 的 id,value 是 token 的count。
至于 model 传入的是什么类我们还不清楚,往后看。 gensim_dict.items 就像是 python自己的dict,返回的是键值对。但是还看不懂哪里做了注释中提到的筛选,说是只有频数超过10的词语。pad_sequences 函数用来将序列补全,一般补0, https://blog.csdn.net/wcy23580/article/details/84957471。
1 | #创建词语字典,并返回每个词语的索引,词向量,以及每个句子所对应的词语索引 |
不过还好作者似乎在不同函数中的局部变量都使用的是一样的名字。这里我们就看到了 model 是使用 Word2Vec 类实例化的变量。 Word2Vec是一个 gensim的库,找一找 API。https://radimrehurek.com/gensim/models/word2vec.html#gensim.models.word2vec.Word2Vec,参数的 min_count = n_exposures ,也就是 10,意思表示出现频率低于 10 的词会被忽视,怪不得上一个函数的注释说道只有频数超过10的词语。然后进行训练。最后将模型保存起来,然后调用前面的函数。
1 | def get_data(index_dict,word_vectors,combined,y): |
这里就是获取数据的函数(顾名思义了hhh),然后这里的代码好像也没有特别的API需要查,大致就是构建了一个词向量的矩阵,然后分割了训练集与测试集,最后返回了所有单词的索引加上0,词向量的矩阵,以及训练集、测试集。
1 | ##定义网络结构 |
这里就到了定义神经网络的部分了,感觉要开始硬核了。什么理论知识都没得,只好看见什么查什么了。首先model是一个序列模型。然后是 Embedding。
https://keras.io/zh/layers/embeddings/
https://arxiv.org/pdf/1512.05287.pdf
https://blog.csdn.net/jiangpeng59/article/details/77533309 看这篇
https://github.com/MoyanZitto/keras-cn/blob/master/docs/legacy/blog/word_embedding.md
理解了一下大概就是,这个层只可以做输入层,然后可以用来获取词向量。把输入的词的索引输出成词向量。那个mask_zero 参数的意思可以参照文档,我暂时还理解的比较模糊就不乱说了……
然后下一层 LSTM,太硬了,一会儿回头再理解吧,大概就是核心了。
然后下一层 Dropout。
http://www.jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf Dropout 的 paper
这个 Dropout 有种陌生又熟悉的感觉……可能之前查文献查到过这个,大致的思想就是一个神经元的输入可以随机扔掉几个输入,按照比例,然后这样可以防止过拟合。(太感人了,和最近在刷的吴恩达老师的机器学习联系上了,过拟合一般的原因就是参数过多,样本较少,较好的解决办法是正则化)
然后 Dense 层就是全连接层。
Activation 是激活函数,这里用的 sigmoid 函数。
然后编译模型,目标函数用了 交叉熵,又遇到了盲点。
https://blog.csdn.net/Julialove102123/article/details/80236180 损失函数:sigmoid与binary_crossentropy,以及softmax与categorical_crossentropy的关系
https://blog.csdn.net/wtq1993/article/details/51741471 交叉熵代价函数(cross-entropy cost function)
具体的交叉熵函数如下,其中 x 是 输入, y 是期望输出, a是 实际输出。
关于优化方法,这里使用的是 Adam,再次盲点,扔连接。似乎常用的都是 Adam。
https://www.cnblogs.com/GeekDanny/p/9655597.html 介绍各类优化方法
https://keras.io/api/optimizers/adam/
关于 fit 和 evaluate,参见官方文档。https://keras.io/zh/models/sequential/。
最后将模型输出为了 yaml,然后输出了一下 evaluate 的分数。将模型的参数(权重)保存。
1 | #训练模型,并保存 |
这个函数应该就是一个整合函数了,调用一次就可以完成所有的准备以及训练。
首先加载 xls 表格的数据,然后进行 Tokenize 分词,然后将分词送入 word2vec,训练词向量模型。然后将词向量整理成矩阵,然后分割测试集训练集,送入 lstm 模型训练。
1 | def input_transform(string): |
这里应该是一个实际应用时的 输入转化,用来将输入的字符串分词,然后转化成向量,然后加载 Word2Vec 模型,最后用模型获取输入的字符串的向量表示(可以送入lstm的向量)。
1 | def lstm_predict(string): |
然后这里就是加载模型及其参数,然后将输入的字符串用上一个函数转化一下,然后送入模型。判断结果是1或者0.
运行测试
终于分析完了,太感人了,虽然LSTM最关键的地方没有分析,但是对于整个代码的结构已经有了一个概念。赶紧跑一下这个代码试试,不过由于他的代码环境和我的完全不一样,不敢保证出现其他的问题。
一测试就出了 Bug。
1 | ImportError: Missing optional dependency 'xlrd'. Install xlrd >= 1.0.0 for Excel support Use pip or conda to install xlrd. |
还算好,能直接告诉怎么解决。
1 | pip install xlrd |
继续下一个 bug。
1 | index_dict, word_vectors,combined=word2vec_train(combined) |
按照提示,将 简单的 train 改成如下代码。
1 | model.train(combined, total_examples=model.corpus_count) |
再次报错
1 | ValueError: You must specify an explict epochs count. The usual value is epochs=model.epochs. |
https://www.cnblogs.com/chenlove/p/9911762.html : 在 model 后面加上 mv 就好了。
1 | gensim_dict.doc2bow(model.mv.vocab.keys(), allow_update = True) |
再次报错
1 | File "F:\InfoContentSecurityProject\Sentiment-Analysis\Sentiment_lstm.py", line 135, in train_lstm |
https://stackoverflow.com/questions/46115403/typeerror-unrecognized-keyword-arguments-show-accuracy-true-yelp-challen 大致就是老版本的 Keras 可以用. Accuracy 应该放在 compile 的参数里。用 metrics,这里前面的代码已经放了这个参数,所以我的解决方案就是先上吊 show_accuracy 试试。
插播一个吐槽,这个测试要十几秒才能跑到下一个 bug ,还是相对有一些麻烦的。
哦豁,跑起来了,debug到这里居然跑起来了,我都不知道现在是跑的哪一个的训练,预计时间貌似还走的挺准的,估计了4分钟多一点。要是跑完了再出 bug 我就太难过了。我滴个鬼鬼,一个 epoch 要跑4分钟,设置了 epoch 是 4,总共要 16分钟…… 这要是一会儿出 bug 了就得改代码直接读 model 了,训练属实搞不起啊。
终于跑完了,后面居然没有bug,太感人了。就 pyyaml 库报了一个 warning。
https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
1 | yaml_string = yaml.load(f, Loader=yaml.FullLoader) |
上面的 load 函数如果没有指定 Loader 就会报 warning 的,不过不影响,看了一下上面的网站,大概就是直接用 FullLoader还是安全的。就OK了,主要是担心加载 yaml 文件是恶意的。
MISC
基本上流程跑完一遍了,也将 GitHub 上的代码的兼容性问题解决。这里记录一些乱七八糟的。
Word2Vec Model
Word2Vec 看了 Stanford 的 CS224n,当看到用词向量做类推时震惊了,国王减男人加女人等于皇后。居然能用向量存储这些隐含信息,太有意思了。
然后由于语料和算力有限,玩了玩人家公开的model就没再看,今天做起来这个作业想起来这个模型,上网搜了一下中文的model。 https://www.jianshu.com/p/ae5b45e96dbf,结果看到了这个,文中有下载链接可以玩。
现在还在下,百度云120KB的速度属实地道。
Pandas 合并2个 Dataframe
在整理数据时,需要将多个数据来源的数据整理起来用来训练。
搜了一下有蛮多函数可以干这个事情,最后使用 pandas.concat 函数来做这个事情。
但是合并以后输出的 Dataframe是两列,原来都是1列,现在变成2列。
最后发现是因为读取 Dataframe 的时候,第一行数据被当做了 Header,两个 Dataframe 的 Header 不一样,所以变成了两个列,第一个 Dataframe 的第二个列补零,第二个 Dataframe 的第一个列补零。
只需要在读取 Dataframe 时,使用 Header = None 即可。
1 | import pandas as pd |
Keras 模型可视化
使用 pydot 库,样例代码如下。
1 | # encoding=utf-8 |
这时候几乎一定会报错,然后就是安装各种包。
1 | pip install pydot-ng |
安装完后又要安装一个库,http://www.graphviz.org/download/。直接下载 msi 安装程序,安装。
安装完成后会提示你把安装目录的 bin 目录加入 PATH。然后这个时候你就会发现各种各样的问题。
这里拿出我的解决方案。不会改很多的源码。其实PATH也不用添加。
点击报错时的路径,比如 C:\Users\xxx\AppData\Local\Programs\Python\Python38\Lib\site-packages\pydot.py。然后修改这个文件,
1 | 'plain-ext', 'png', 'ps', 'ps2', |
找到类似上面的代码片段,将 self.prog 后面的赋值更改C:\Program Files (x86)\Graphviz2.38\bin\dot.exe。记得反斜杠要2个转义。
1 | 'plain-ext', 'png', 'ps', 'ps2', |
改成这样就OK了。
PS:学会一个不重启更新 Windows 环境变量的方法,打开任务管理器,找到 Windows 资源管理器,选中,重启,就可以不重启更新环境变量了。