├── .gitignore ├── README.md ├── Word2Vec模型和Doc2Vec模型.md ├── code ├── clustering.py ├── cnn.py ├── fasttext.py ├── itchat.pkl ├── keywords.py ├── lda.py ├── mlp.py ├── ner.py ├── progressive-tweet-sentiment.csv ├── similarities.py ├── stopwords.txt ├── weixin.py ├── yelp-cnn-model-mlp.png ├── yelp-cnn-model-textcnn.png ├── yelp-cnn-model.png ├── yelp-mlp-model.png ├── yelp.py ├── yelp_sentiment_stars.png ├── yelp_stars.png └── 预测yelp评分-图6.png ├── paper └── Convolutional Neural Networks for Sentence Classification.pdf ├── picture ├── NLP之Word2Vec模型和Doc2Vec模型-图2.png ├── Word2Vec模型和Doc2Vec模型-图1.png ├── 使用CNN进行文档分类-图1.png ├── 使用CNN进行文档分类-图2.png ├── 使用CNN进行文档分类-图3.png ├── 使用CNN进行文档分类-图4.png ├── 使用LDA进行文档主题建模-图1.png ├── 使用LDA进行文档主题建模-图2.png ├── 使用LDA进行文档主题建模-图3.png ├── 使用LDA进行文档主题建模-图4.png ├── 使用TextRank和TFIDF进行文档关键字自动提取-图1.png ├── 使用TextRank和TFIDF进行文档关键字自动提取-图2.png ├── 使用fasttext进行文档分类-图1.png ├── 使用fasttext进行文档分类-图2.png ├── 使用fasttext进行文档分类-图3.png ├── 使用多层感知机进行文档分类-图1.png ├── 使用多层感知机进行文档分类-图2.png ├── 使用多层感知机进行文档分类-图3.png ├── 使用多层感知机进行文档分类-图4.png ├── 使用多层感知机进行文档分类-图5.png ├── 使用搜狗新闻预料生成word2vec-图1.png ├── 如何衡量机器学习分类器模型-图1.png ├── 如何衡量机器学习分类器模型-图2.png ├── 常用数据集-图4.png ├── 常用数据集简介-图1.png ├── 常用数据集简介-图3.png ├── 常用数据集简介-图5.png ├── 常用数据集简介-图8.png ├── 文档相似度-图1.png ├── 文档相似度-图2.png ├── 文档相似度-图3.png ├── 词袋模型和TFIDF模型-图1.png ├── 预测Yelp美食评分-图1.png ├── 预测Yelp美食评分-图2.png ├── 预测Yelp美食评分-图3.png ├── 预测Yelp美食评分-图4.png └── 预测Yelp美食评分-图6.png ├── 人家对话.png ├── 使用CNN进行文档分类.md ├── 使用Jieba进行中文词性标注.md ├── 使用LDA进行文档主题建模.md ├── 使用TextRank和TFIDF进行关键字自动提取.md ├── 使用fasttext进行文档分类.md ├── 使用多层感知机进行文档分类.md ├── 公众号.png ├── 如何衡量机器学习分类模型.md ├── 常用数据集简介.md ├── 打赏.png ├── 打造NLP工具箱.md ├── 推荐更新源.md ├── 文档相似度.md ├── 文档聚类.md ├── 知识星球.png ├── 自己动手训练word2vec模型.md ├── 词袋模型和TFIDF模型.md └── 预测Yelp美食评分.md /.gitignore: -------------------------------------------------------------------------------- 1 | data/* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NLP基础知识 2 | 3 | - [常用数据集简介](常用数据集简介.md) 4 | - [推荐更新源](推荐更新源.md) 5 | - [打造NLP工具箱](打造NLP工具箱.md) 6 | - [如何衡量机器学习分类模型](如何衡量机器学习分类模型.md) 7 | - [词袋模型和TFIDF模型](词袋模型和TFIDF模型.md) 8 | - [Word2Vec模型和Doc2Vec模型](Word2Vec模型和Doc2Vec模型.md) 9 | - [自己动手训练word2vec模型](自己动手训练word2vec模型.md) 10 | - [使用多层感知机进行文档分类](使用多层感知机进行文档分类.md) 11 | - [使用fasttext进行文档分类](使用fasttext进行文档分类.md) 12 | - [使用LDA进行文档主题建模](使用LDA进行文档主题建模.md) 13 | - [使用Jieba进行中文词性标注](使用Jieba进行中文词性标注.md) 14 | - [使用TextRank和TFIDF进行关键字自动提取](使用TextRank和TFIDF进行关键字自动提取.md) 15 | - [文档相似度](文档相似度.md) 16 | 17 | # NLP应用案例 18 | 19 | - [对美食评语进行情感分析](预测Yelp美食评分.md) 20 | 21 | # 让机器理解文字 22 | 23 | 图像、文字和声音是人类接触、理解外部世界最常见的三种形式,其中文字又是最容易保存和进行交换的形式。大量的人类文明,最终都是以文字的形式保留下来的;大量的信息交换,都是是文字的形式进行。如何能让机器可以与人交流,理解人类的思想,最终能像人类一样理解文字以及文字背后的各种想法、意图呢?自然语言处理,即所谓的NLP是重要的支撑技术。 24 | 25 | ![人机对话](人家对话.png) 26 | 27 | # NLP与安全 28 | 29 | 在传统的web攻防中,大家与http协议结下了不解之缘。但是在安全领域,web攻防只是很小一个分支。许多明显的与工作和生活无关的垃圾邮件,人一样就可以看出来,但是基于规则的垃圾邮件网关处理起来却总是差强人意。越来越多的电商、论坛甚至是视频网站的弹幕,总是可以看到明显的人身攻击或者违法违规信息,但是基于规则的过滤机制总是被绕过。人类可以很轻松的理解二十四口交换机,知道苹果是水果还是手机,但是机器如何做到呢?答案就是NLP。 30 | 31 | # 一本开源的NLP入门书籍 32 | 33 | 这可能是第一本用开源的思想写的NLP入门书籍,整个写作过程都在我的Github上。 34 | 35 | https://github.com/duoergun0729/nlp 36 | 37 | 之所以想用开源的思路去写,主要是因为NLP技术,尤其是基于机器学习的NLP技术发展非常快,比如目前已经广泛使用的fasttext技术,2016年发布论文,2017年已经进入大量生产领域,但是许多自然语言处理书籍还停留在大学课程的范围,甚至连词向量都很少涉及。相对周期繁琐的纸质书籍编写,在Github上我可以很方便的进行编写和更新,有勘误也可以很快修改。目前我已经完成了其中的三篇,后面我将不断更新内容,大家可以订阅我的Github,或者关注我的微信公众号《**兜哥带你学安全**》 38 | 39 | ![公众号](公众号.png) 40 | 41 | 42 | 43 | 44 | # License 45 | 46 | © 2018~2025 兜哥. 47 | 48 | 本作品采用[知识共享署名-非商业性使用 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc/4.0/)进行许可。没有我许可的任何使用该书进行的商业行为都是违法。 49 | 50 | -------------------------------------------------------------------------------- /Word2Vec模型和Doc2Vec模型.md: -------------------------------------------------------------------------------- 1 | # Word2Vec模型 2 | Word2Vec是Google在2013年开源的一款将词表征为实数值向量的高效工具,采用的模型有CBOW(Continuous Bag-Of-Words,即连续的词袋模型)和Skip-Gram 两种。Word2Vec通过训练,可以把对文本内容的处理简化为K维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度。因此,Word2Vec 输出的词向量可以被用来做很多NLP相关的工作,比如聚类、找同义词、词性分析等等。 3 | ![CBOW和Skip-gram原理图](picture/Word2Vec模型和Doc2Vec模型-图1.png) 4 | 5 | CBOW模型能够根据输入周围n-1个词来预测出这个词本身,而Skip-gram模型能够根据词本身来预测周围有哪些词。也就是说,CBOW模型的输入是某个词A周围的n个单词的词向量之和,输出是词A本身的词向量,而Skip-gram模型的输入是词A本身,输出是词A周围的n个单词的词向量。 6 | Word2Vec最常用的开源实现之一就是gensim,网址为: 7 | 8 | http://radimrehurek.com/gensim/ 9 | gensim的安装非常简单: 10 | 11 | pip install --upgrade gensim 12 | gensim的使用非常简洁,加载数据和训练数据可以合并,训练好模型后就可以按照单词获取对应的向量表示: 13 | 14 | sentences = [['first', 'sentence'], ['second', 'sentence']] 15 | model = gensim.models.Word2Vec(sentences, min_count=1) 16 | print model['first'] 17 | 其中Word2Vec有很多可以影响训练速度和质量的参数。第一个参数可以对字典做截断,少于min_count次数的单词会被丢弃掉, 默认值为5: 18 | 19 | model = Word2Vec(sentences, min_count=10) 20 | 另外一个是神经网络的隐藏层的单元数,推荐值为几十到几百。事实上Word2Vec参数的个数也与神经网络的隐藏层的单元数相同,比如size=200,那么训练得到的Word2Vec参数个数也是200: 21 | model = Word2Vec(sentences, size=200) 22 | 以处理IMDB数据集为例,初始化Word2Vec对象,设置神经网络的隐藏层的单元数为200,生成的词向量的维度也与神经网络的隐藏层的单元数相同。设置处理的窗口大小为8个单词,出现少于10次数的单词会被丢弃掉,迭代计算次数为10次,同时并发线程数与当前计算机的cpu个数相同: 23 | 24 | model=gensim.models.Word2Vec(size=200, window=8, min_count=10, iter=10, workers=cores) 25 | 26 | 其中当前计算机的cpu个数可以使用multiprocessing获取: 27 | 28 | cores=multiprocessing.cpu_count() 29 | 创建字典并开始训练获取Word2Vec。gensim的官方文档中强调增加训练次数可以提高生成的Word2Vec的质量,可以通过设置epochs参数来提高训练次数,默认的训练次数为5: 30 | 31 | x=x_train+x_test 32 | model.build_vocab(x) 33 | model.train(x, total_examples=model.corpus_count, epochs=model.iter) 34 | 35 | 经过训练后,Word2Vec会以字典的形式保存在model对象中,可以使用类似字典的方式直接访问获取,比如获取单词“love”的Word2Vec就可以使用如下形式: 36 | 37 | model[“love”] 38 | 39 | Word2Vec的维度与之前设置的神经网络的隐藏层的单元数相同为200,也就是说是一个长度为200的一维向量。通过遍历一段英文,逐次获取每个单词对应的Word2Vec,连接起来就可以获得该英文段落对应的Word2Vec: 40 | 41 | def getVecsByWord2Vec(model, corpus, size): 42 | x=[] 43 | for text in corpus: 44 | xx = [] 45 | for i, vv in enumerate(text): 46 | try: 47 | xx.append(model[vv].reshape((1,size))) 48 | except KeyError: 49 | continue 50 | x = np.concatenate(xx) 51 | x=np.array(x, dtype='float') 52 | return x 53 | 需要注意的是,出于性能的考虑,我们将出现少于10次数的单词会被丢弃掉,所以存在这种情况,就是一部分单词找不到对应的Word2Vec,所以需要捕捉这个异常,通常使用python的KeyError异常捕捉即可。 54 | 基于上述的Word2Vec的方法,Quoc Le 和Tomas Mikolov又给出了Doc2Vec的训练方法。如下图所示,其原理与Word2Vec相同,分为Distributed Memory (DM) 和Distributed Bag of Words (DBOW)。 55 | 56 | ![DM和DBOW原理图](picture/NLP之Word2Vec模型和Doc2Vec模型-图2.png) 57 | 58 | 以处理IMDB数据集为例,初始化Doc2Vec对象,设置神经网络的隐藏层的单元数为200,生成的词向量的维度也与神经网络的隐藏层的单元数相同。设置处理的窗口大小为8个单词,出现少于10次数的单词会被丢弃掉,迭代计算次数为10次,同时并发线程数与当前计算机的cpu个数相同: 59 | 60 | model=Doc2Vec(dm=0, dbow_words=1, size=max_features, window=8, min_count=10, iter=10, workers=cores) 61 | 62 | 其中需要强调的是,dm为使用的算法,默认为1,表明使用DM算法,设置为0表明使用DBOW算法,通常使用默认配置即可,比如: 63 | 64 | model = gensim.models.Doc2Vec.Doc2Vec(size=50, min_count=2, iter=10) 65 | 66 | # Doc2Vec 67 | 与Word2Vec不同的地方是,Doc2Vec处理的每个英文段落,需要使用一个唯一的标识标记,并且使用一种特殊定义的数据格式保存需要处理的英文段落,这种数据格式定义如下: 68 | 69 | SentimentDocument = namedtuple('SentimentDocument', 'words tags') 70 | 71 | 其中SentimentDocument可以理解为这种格式的名称,也可以理解为这种对象的名称,words会保存英文段落,并且是以单词和符合列表的形式保存,tags就是我们说的保存的唯一标识。最简单的一种实现就是依次给每个英文段落编号,训练数据集的标记为“TRAIN_数字”,训练数据集的标记为“TEST_数字”: 72 | 73 | def labelizeReviews(reviews, label_type): 74 | labelized = [] 75 | for i, v in enumerate(reviews): 76 | label = '%s_%s' % (label_type, i) 77 | labelized.append(SentimentDocument(v, [label])) 78 | return labelized 79 | 创建字典并开始训练获取Doc2Vec。与Word2Vec的情况一样,gensim的官方文档中强调增加训练次数可以提高生成的Doc2Vec的质量,可以通过设置epochs参数来提高训练次数,默认的训练次数为5: 80 | 81 | x=x_train+x_test 82 | model.build_vocab(x) 83 | model.train(x, total_examples=model.corpus_count, epochs=model.iter) 84 | 经过训练后,Doc2Vec会以字典的形式保存在model对象中,可以使用类似字典的方式直接访问获取,比如获取段落“I love tensorflow”的Doc2Vec就可以使用如下形式: 85 | 86 | model.docvecs[”I love tensorflow”] 87 | 88 | 一个典型的doc2ver展开为向量形式,内容如下所示,为了显示方便只展示了其中一部分维度的数据: 89 | 90 | array([ 0.02664499, 0.00475204, -0.03981256, 0.03796276, -0.03206162, 91 | 0.10963056, -0.04897128, 0.00151982, -0.03258783, 0.04711508, 92 | -0.00667155, -0.08523653, -0.02975186, 0.00166316, 0.01915652, 93 | -0.03415785, -0.05794788, 0.05110953, 0.01623618, -0.00512495, 94 | -0.06385455, -0.0151557 , 0.00365376, 0.03015811, 0.0229462 , 95 | 0.03176891, 0.01117626, -0.00743352, 0.02030453, -0.05072152, 96 | -0.00498496, 0.00151227, 0.06122205, -0.01811385, -0.01715777, 97 | 0.04883198, 0.03925886, -0.03568915, 0.00805744, 0.01654406, 98 | -0.05160677, 0.0119908 , -0.01527433, 0.02209963, -0.10316766, 99 | -0.01069367, -0.02432527, 0.00761799, 0.02763799, -0.04288232], dtype=float32) 100 | 101 | Doc2Vec的维度与之前设置的神经网络的隐藏层的单元数相同为200,也就是说是一个长度为200的一维向量。以英文段落为单位,通过遍历训练数据集和测试数据集,逐次获取每个英文段落对应的Doc2Vec,这里的英文段落就可以理解为数据集中针对电影的一段评价: 102 | 103 | def getVecs(model, corpus, size): 104 | vecs = [np.array(model.docvecs[z.tags[0]]).reshape((1, size)) for z in corpus] 105 | return np.array(np.concatenate(vecs),dtype='float') 106 | 107 | 训练Word2Vec和Doc2Vec是非常费时费力的过程,调试阶段会频繁更换分类算法以及修改分类算法参数调优,为了提高效率,可以把之前训练得到的Word2Vec和Doc2Vec模型保存成文件形式,以Doc2Vec为例,使用model.save函数把训练后的结果保存在本地硬盘上,运行程序时,在初始化Doc2Vec对象之前,可以先判断本地硬盘是否存在模型文件,如果存在就直接读取模型文件初始化Doc2Vec对象,反之则需要训练数据: 108 | 109 | if os.path.exists(doc2ver_bin): 110 | print "Find cache file %s" % doc2ver_bin 111 | model=Doc2Vec.load(doc2ver_bin) 112 | else: 113 | model=Doc2Vec(size=max_features, window=5, min_count=2, workers=cores,iter=40) 114 | model.build_vocab(x)) 115 | model.train(x, total_examples=model.corpus_count, epochs=model.iter) 116 | model.save(doc2ver_bin) 117 | 118 | -------------------------------------------------------------------------------- /code/clustering.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import re 3 | import os 4 | #from fastText import train_supervised 5 | 6 | import numpy as np 7 | import codecs 8 | 9 | from sklearn.model_selection import cross_val_score 10 | from sklearn.model_selection import train_test_split 11 | 12 | from sklearn.neural_network import MLPClassifier 13 | from sklearn import feature_extraction 14 | from sklearn.feature_extraction.text import TfidfTransformer 15 | from sklearn.feature_extraction.text import CountVectorizer 16 | 17 | #from keras.utils import to_categorical 18 | #from sklearn.preprocessing import OneHotEncoder 19 | 20 | from gensim import corpora, models 21 | 22 | from sklearn.cluster import DBSCAN 23 | from sklearn.preprocessing import StandardScaler 24 | 25 | import time 26 | from jieba import analyse 27 | 28 | from sklearn.metrics import silhouette_score 29 | 30 | #测试环境 31 | DEV_FILE="../data/news_sohusite_content_1000.txt" 32 | #生产环境 33 | PRO_FILE="/mnt/nlp/dataset/news_sohusite_content-50000.txt" 34 | 35 | 36 | def load_stopwords(): 37 | with open("stopwords.txt") as F: 38 | stopwords=F.readlines() 39 | F.close() 40 | return [word.strip() for word in stopwords] 41 | 42 | 43 | def load_sougou_content(): 44 | filename=PRO_FILE 45 | 46 | if os.path.exists(DEV_FILE): 47 | filename=DEV_FILE 48 | 49 | print "Open data file %s" % (filename) 50 | 51 | with open(filename) as F: 52 | content=F.readlines() 53 | F.close() 54 | 55 | 56 | return content 57 | """ 58 | [(0, 0.019423252), (1, 0.019521076), (2, 0.92217809), (3, 0.01954053), (4, 0.019337002)] 59 | 这里需要解释的是,无论是词袋模型还是LDA生成的结果,都可能存在大量的0,这会占用大量的内存空间。因此默认情况下, 60 | 词袋以及LDA计算的结果都以稀疏矩阵的形式保存。稀疏矩阵的最小单元定义为: 61 | 62 | (元素所在的位置,元素的值) 63 | 比如一个稀疏矩阵只有0号和2号元素不为0,分别为1和5,那么它的表示方法如下: 64 | 65 | [(0,1),(2,5)] 66 | """ 67 | def transformedCorpus2Vec(turples,num_topics): 68 | ret = [0] * num_topics 69 | for tuple in turples: 70 | index = tuple[0] 71 | weight = tuple[1] 72 | ret[index] = weight 73 | 74 | return ret 75 | 76 | 77 | def get_keywords(articles): 78 | 79 | 80 | text="" 81 | 82 | text=" ".join(articles) 83 | 84 | 85 | keywords_tfidf = analyse.extract_tags(text, topK=10, 86 | withWeight=False, allowPOS=('n', 'ns', 'vn', 'v', 'nz')) 87 | 88 | return keywords_tfidf 89 | 90 | 91 | def do_cluster(x,articles_content): 92 | start = time.clock() 93 | 94 | #使用DBSCAN进行聚类分类 95 | db = DBSCAN(eps=5.8, min_samples=2).fit(x) 96 | 97 | #获取每条记录对应的聚类标签 其中-1表示识别为噪音点 98 | labels = db.labels_ 99 | 100 | #统计聚类个数 101 | n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) 102 | 103 | print "n_clusters_:%d" % (n_clusters_) 104 | 105 | #计算耗时 106 | end = time.clock() 107 | print('[cluster]Running time: %s Seconds' % (end - start)) 108 | 109 | #把列表转成数组np结构 110 | articles_content=np.array(articles_content) 111 | 112 | for clusters_id in range(1,n_clusters_+1): 113 | print "/********************************************************************/" 114 | print "clusters_id %d" % (clusters_id) 115 | articles=articles_content[labels==clusters_id] 116 | 117 | #keywords=get_keywords(articles) 118 | 119 | #for keyword in keywords: 120 | # print keyword + "/" 121 | 122 | for article in articles: 123 | print article[:200] 124 | 125 | 126 | def do_cluster_dbscan(x,articles_content): 127 | 128 | 129 | for eps in np.arange(0.1,10.0,0.1): 130 | 131 | #使用DBSCAN进行聚类分类 132 | db = DBSCAN(eps=eps, min_samples=2).fit(x) 133 | silhouette=silhouette_score(x, db.labels_) 134 | 135 | print "eps=%.2f silhouette=%.2f" % (eps,silhouette) 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | def do_lda_usemulticore(): 147 | 148 | #获取当前时间 149 | start = time.clock() 150 | 151 | #加载搜狗新闻数据 152 | content=load_sougou_content() 153 | 154 | #备份原始数据 155 | articles_content=content 156 | 157 | #加载停用词 158 | stopwords=load_stopwords() 159 | 160 | #切割token 161 | content=[ [word for word in line.split() if word not in stopwords] for line in content] 162 | 163 | #计算耗时 164 | end = time.clock() 165 | print('[data clean]Running time: %s Seconds' % (end - start)) 166 | 167 | 168 | #获取当前时间 169 | start = time.clock() 170 | 171 | 172 | # 得到文档-单词矩阵 (直接利用统计词频得到特征) 173 | dictionary = corpora.Dictionary(content) 174 | 175 | # 将dictionary转化为一个词袋,得到文档-单词矩阵 176 | texts = [dictionary.doc2bow(text) for text in content] 177 | 178 | # 利用tf-idf来做为特征进行处理 179 | texts_tf_idf = models.TfidfModel(texts)[texts] 180 | 181 | #计算耗时 182 | end = time.clock() 183 | print('[get word bag]Running time: %s Seconds' % (end - start)) 184 | 185 | 186 | # 利用TFIDF&LDA做主题分类的情况 187 | print "TFIDF&LDA" 188 | 189 | num_topics=200 190 | 191 | start = time.clock() 192 | 193 | 194 | #workers指定使用的CPU个数 默认使用cpu_count()-1 即使用几乎全部CPU 仅保留一个CPU不参与LDA计算 195 | #https://radimrehurek.com/gensim/models/ldamulticore.html 196 | #Hoffman, Blei, Bach: Online Learning for Latent Dirichlet Allocation, NIPS 2010. 197 | lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 198 | #lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, 199 | # num_topics=num_topics,workers=12) 200 | 201 | 202 | #计算耗时 203 | end = time.clock() 204 | print('[lda]Running time: %s Seconds' % (end - start)) 205 | 206 | x=lda[texts_tf_idf] 207 | 208 | #格式转化 x是稀疏矩阵 需要转换成正常的格式 209 | x=[ transformedCorpus2Vec(x,num_topics) for x in x ] 210 | 211 | 212 | 213 | #标准化 214 | x = StandardScaler().fit_transform(x) 215 | 216 | #聚类分析 217 | #do_cluster_dbscan(x, articles_content) 218 | do_cluster(x, articles_content) 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | if __name__ == '__main__': 227 | 228 | #设置NTLK全局停用词 229 | analyse.set_stop_words("stopwords.txt") 230 | 231 | do_lda_usemulticore() 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /code/cnn.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import pandas as pd 3 | import numpy as np 4 | import sys 5 | 6 | #处理编码问题 7 | reload(sys) 8 | sys.setdefaultencoding('utf-8') 9 | 10 | from sklearn.model_selection import cross_val_score 11 | from sklearn.model_selection import train_test_split 12 | 13 | from sklearn.neural_network import MLPClassifier 14 | from sklearn import feature_extraction 15 | from sklearn.feature_extraction.text import TfidfTransformer 16 | from sklearn.feature_extraction.text import CountVectorizer 17 | 18 | from sklearn.preprocessing import LabelEncoder 19 | from keras.utils import np_utils 20 | 21 | from keras.models import Sequential 22 | from keras.layers import Dense, Dropout 23 | from keras.wrappers.scikit_learn import KerasClassifier 24 | from sklearn.model_selection import KFold,StratifiedKFold 25 | 26 | from keras import metrics 27 | 28 | from sklearn.svm import SVC 29 | 30 | from keras.layers import Embedding, LSTM 31 | 32 | import keras.preprocessing.text as T 33 | from keras.preprocessing.text import Tokenizer 34 | from keras.preprocessing.sequence import pad_sequences 35 | 36 | from nltk.corpus import stopwords 37 | from fastText import train_supervised 38 | from nltk.tokenize import sent_tokenize,word_tokenize 39 | from nltk.corpus import wordnet 40 | import enchant 41 | 42 | from keras.layers import Conv1D,GlobalMaxPooling1D,Activation,Input,MaxPooling1D,Flatten,concatenate,Embedding 43 | 44 | from keras.models import Model 45 | 46 | from gensim import corpora, models,similarities 47 | from gensim.models import word2vec,KeyedVectors 48 | 49 | 50 | 51 | 52 | 53 | 54 | #兼容在没有显示器的GPU服务器上运行该代码 55 | import matplotlib 56 | matplotlib.use('Agg') 57 | 58 | 59 | import matplotlib.pyplot as plt 60 | from keras.utils import plot_model 61 | 62 | 63 | 64 | #yelp评论文件路径 已经使用https://github.com/Yelp/dataset-examples处理成CSV格式 65 | #yelp_file="/Volumes/maidou/dataset/yelp/dataset/review.csv" 66 | yelp_file="/mnt/nlp/dataset/review.csv" 67 | #word2vec_file="/Volumes/maidou/dataset/gensim/GoogleNews-vectors-negative300.bin" 68 | word2vec_file="/mnt/nlp/dataset/GoogleNews-vectors-negative300.bin" 69 | 70 | #词袋模型的最大特征束 71 | max_features=5000 72 | 73 | 74 | def load_reviews(filename,nrows): 75 | #CSV格式表头内容: 76 | # funny,user_id,review_id,text,business_id,stars,date,useful,cool 77 | text=[] 78 | stars=[] 79 | 80 | #https://www.cnblogs.com/datablog/p/6127000.html 81 | #sep : str, default ‘,’指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘\s+’,将使用python的语法分析器。 82 | # 并且忽略数据中的逗号。正则表达式例子:'\r\t' 83 | #header: int or list of ints, default ‘infer’ 84 | # 指定行数用来作为列名,数据开始行数。如果文件中没有列名,设置为None。设置为0则认为第0行是列名 85 | #nrows : int, default None 需要读取的行数(从文件头开始算起)。 86 | #skiprows : list-like or integer, default None 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。 87 | #skip_blank_lines : boolean, default True如果为True,则跳过空行;否则记为NaN。 88 | 89 | ### 90 | #开发阶段读取前10000行 使用encoding='utf-8'参数非常重要 91 | df = pd.read_csv(filename,sep=',',header=0,encoding='utf-8',nrows=nrows) 92 | print df.head() 93 | 94 | #按照列名直接获取数据 把 list转换成list对象 95 | text=list(df['text']) 96 | stars=list(df['stars']) 97 | 98 | #显示各个评分的个数 99 | print df.describe() 100 | 101 | #绘图 102 | plt.figure() 103 | count_classes=pd.value_counts(df['stars'],sort=True) 104 | 105 | print "各个star的总数:" 106 | print count_classes 107 | count_classes.plot(kind='bar',rot=0) 108 | plt.xlabel('stars') 109 | plt.ylabel('stars counts') 110 | #plt.show() 111 | plt.savefig("yelp_stars.png") 112 | 113 | 114 | return text,stars 115 | 116 | def pad_sentences(data,maxlen=56,values=0.,vec_size = 300): 117 | """padding to max length 118 | :param data:要扩展的数据集 119 | :param maxlen:扩展的h长度 120 | :param values:默认的值 121 | """ 122 | length = len(data) 123 | if length < maxlen: 124 | for i in range(maxlen - length): 125 | data.append(np.array([values]*vec_size)) 126 | return data 127 | 128 | 129 | #使用词向量表征英语句子 130 | def get_vec_by_sentence_list(word_vecs,sentence_list,maxlen=56,vec_size = 300): 131 | data = [] 132 | values=0.0 133 | for sentence in sentence_list: 134 | # get a sentence 135 | sentence_vec = [] 136 | words = sentence.split() 137 | for word in words: 138 | 139 | try: 140 | sentence_vec.append(word_vecs[word].tolist()) 141 | except: 142 | print word 143 | 144 | # padding sentence vector to maxlen(w * h) 145 | sentence_vec = pad_sentences(sentence_vec,maxlen,values,vec_size) 146 | # add a sentence vector 147 | data.append(np.array(sentence_vec)) 148 | return data 149 | 150 | 151 | 152 | #使用keras的单层cnn 153 | def do_keras_cnn(text,stars): 154 | 155 | #转换成词袋序列 156 | max_document_length=200 157 | 158 | 159 | 160 | #设置分词最大个数 即词袋的单词个数 161 | tokenizer = Tokenizer(num_words=max_features,lower=True) 162 | tokenizer.fit_on_texts(text) 163 | sequences = tokenizer.texts_to_sequences(text) 164 | 165 | x=pad_sequences(sequences, maxlen=max_document_length) 166 | 167 | 168 | #print "加载GoogleNews-vectors-negative300.bin..." 169 | #model = KeyedVectors.load_word2vec_format(word2vec_file, binary=True) 170 | #print "加载完毕" 171 | 172 | #print model['boy'].shape 173 | 174 | #词向量的维数 GoogleNews-vectors-negative300.bin维数为300 175 | #max_features=300 176 | 177 | #x = np.concatenate([buildWordVector(model, z, 50) for z in text]) 178 | #x = get_vec_by_sentence_list(model,text,max_document_length,max_features) 179 | 180 | 181 | 182 | #我们可以使用从scikit-learn LabelEncoder类。 183 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 184 | encoder=LabelEncoder() 185 | encoder.fit(stars) 186 | encoded_y = encoder.transform(stars) 187 | 188 | 189 | 190 | #构造神经网络 191 | def baseline_model(): 192 | 193 | #CNN参数 194 | embedding_dims = 50 195 | filters = 250 196 | kernel_size = 3 197 | hidden_dims = 250 198 | 199 | model = Sequential() 200 | model.add(Embedding(max_features, embedding_dims)) 201 | 202 | model.add(Conv1D(filters, 203 | kernel_size, 204 | padding='valid', 205 | activation='relu', 206 | strides=1)) 207 | #池化 208 | model.add(GlobalMaxPooling1D()) 209 | 210 | model.add(Dense(2, activation='softmax')) 211 | 212 | model.compile(loss='categorical_crossentropy', 213 | optimizer='adam', 214 | metrics=['accuracy']) 215 | 216 | #可视化 217 | plot_model(model, to_file='yelp-cnn-model.png',show_shapes=True) 218 | 219 | model.summary() 220 | 221 | return model 222 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 223 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 224 | # 最新接口指定训练的次数为epochs 225 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=128, verbose=0) 226 | 227 | #使用5折交叉验证 228 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 229 | # print scores 230 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 231 | 232 | #scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='accuracy') 233 | # print scores 234 | #print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 235 | 236 | 237 | #使用keras的cnn+mlp 238 | def do_keras_cnn_mlp(text,stars): 239 | 240 | #转换成词袋序列 241 | max_document_length=200 242 | 243 | #设置分词最大个数 即词袋的单词个数 244 | tokenizer = Tokenizer(num_words=max_features,lower=True) 245 | tokenizer.fit_on_texts(text) 246 | sequences = tokenizer.texts_to_sequences(text) 247 | 248 | x=pad_sequences(sequences, maxlen=max_document_length) 249 | 250 | 251 | #我们可以使用从scikit-learn LabelEncoder类。 252 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 253 | encoder=LabelEncoder() 254 | encoder.fit(stars) 255 | encoded_y = encoder.transform(stars) 256 | 257 | 258 | 259 | #构造神经网络 260 | def baseline_model(): 261 | 262 | #CNN参数 263 | embedding_dims = 50 264 | filters = 250 265 | kernel_size = 3 266 | hidden_dims = 250 267 | 268 | model = Sequential() 269 | model.add(Embedding(max_features, embedding_dims)) 270 | 271 | model.add(Conv1D(filters, 272 | kernel_size, 273 | padding='valid', 274 | activation='relu', 275 | strides=1)) 276 | #池化 277 | model.add(GlobalMaxPooling1D()) 278 | 279 | 280 | #增加一个隐藏层 281 | model.add(Dense(hidden_dims)) 282 | model.add(Dropout(0.2)) 283 | model.add(Activation('relu')) 284 | 285 | #输出层 286 | 287 | model.add(Dense(2, activation='softmax')) 288 | 289 | model.compile(loss='categorical_crossentropy', 290 | optimizer='adam', 291 | metrics=['accuracy']) 292 | 293 | #可视化 294 | plot_model(model, to_file='yelp-cnn-model-mlp.png',show_shapes=True) 295 | 296 | model.summary() 297 | 298 | return model 299 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 300 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 301 | # 最新接口指定训练的次数为epochs 302 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=128, verbose=0) 303 | 304 | #使用5折交叉验证 305 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 306 | # print scores 307 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 308 | 309 | 310 | #使用keras的TextCNN 311 | 312 | def do_keras_textcnn(text,stars): 313 | 314 | #转换成词袋序列 315 | max_document_length=200 316 | 317 | #设置分词最大个数 即词袋的单词个数 318 | tokenizer = Tokenizer(num_words=max_features,lower=True) 319 | tokenizer.fit_on_texts(text) 320 | sequences = tokenizer.texts_to_sequences(text) 321 | 322 | x=pad_sequences(sequences, maxlen=max_document_length) 323 | 324 | 325 | #我们可以使用从scikit-learn LabelEncoder类。 326 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 327 | encoder=LabelEncoder() 328 | encoder.fit(stars) 329 | encoded_y = encoder.transform(stars) 330 | 331 | 332 | #论文中的参数: 333 | # Convolutional Neural Networks for Sentence Classification 334 | # Hyperparameters and Training 335 | #For all datasets we use: rectified linear units, filter 336 | #windows (h) of 3, 4, 5 with 100 feature maps each, 337 | #dropout rate (p) of 0.5, l2 constraint (s) of 3, and 338 | #mini-batch size of 50. These values were chosen 339 | #via a grid search on the SST-2 dev set. 340 | 341 | 342 | #构造神经网络 343 | def baseline_model(): 344 | 345 | #CNN参数 346 | embedding_dims = 50 347 | filters = 100 348 | 349 | # Inputs 350 | input = Input(shape=[max_document_length]) 351 | 352 | # Embeddings layers 353 | x = Embedding(max_features, embedding_dims)(input) 354 | 355 | # conv layers 356 | convs = [] 357 | for filter_size in [3,4,5]: 358 | l_conv = Conv1D(filters=filters, kernel_size=filter_size, activation='relu')(x) 359 | l_pool = MaxPooling1D()(l_conv) 360 | l_pool = Flatten()(l_pool) 361 | convs.append(l_pool) 362 | 363 | merge = concatenate(convs, axis=1) 364 | 365 | out = Dropout(0.2)(merge) 366 | 367 | output = Dense(32, activation='relu')(out) 368 | 369 | output = Dense(units=2, activation='softmax')(output) 370 | 371 | #输出层 372 | model = Model([input], output) 373 | 374 | model.compile(loss='categorical_crossentropy', 375 | optimizer='adam', 376 | metrics=['accuracy']) 377 | 378 | #可视化 379 | plot_model(model, to_file='yelp-cnn-model-textcnn.png',show_shapes=True) 380 | 381 | model.summary() 382 | 383 | return model 384 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 385 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 386 | # 最新接口指定训练的次数为epochs 387 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=50, verbose=1) 388 | 389 | #使用5折交叉验证 390 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 391 | # print scores 392 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 393 | 394 | 395 | #use pre train word2vec 396 | def do_keras_textcnn_w2v(text,stars,trainable): 397 | 398 | #转换成词袋序列 399 | max_document_length=200 400 | 401 | embedding_dims = 300 402 | 403 | 404 | #获取已经训练好的词向量 405 | model = KeyedVectors.load_word2vec_format(word2vec_file, binary=True) 406 | 407 | print model['word'].shape 408 | 409 | 410 | #设置分词最大个数 即词袋的单词个数 411 | tokenizer = Tokenizer(num_words=max_features,lower=True) 412 | tokenizer.fit_on_texts(text) 413 | sequences = tokenizer.texts_to_sequences(text) 414 | 415 | x=pad_sequences(sequences, maxlen=max_document_length) 416 | 417 | 418 | #我们可以使用从scikit-learn LabelEncoder类。 419 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 420 | encoder=LabelEncoder() 421 | encoder.fit(stars) 422 | encoded_y = encoder.transform(stars) 423 | 424 | #labels = to_categorical(np.asarray(labels))也可以进行数据处理 425 | 426 | #获取word到对应数字编号的映射关系 427 | word_index = tokenizer.word_index 428 | print('Found %s unique tokens.' % len(word_index)) 429 | 430 | 431 | #获取词向量的映射矩阵 432 | embedding_matrix = np.zeros((max_features + 1, embedding_dims)) 433 | 434 | for word, i in word_index.items(): 435 | 436 | #编号大于max_features的忽略 该字典是按照字典顺序 所以对应的id不一定是顺序的 437 | if i > max_features: 438 | continue 439 | 440 | try: 441 | embedding_matrix[i] = model[word].reshape(embedding_dims) 442 | 443 | except: 444 | print "%s not found!" % (word) 445 | 446 | 447 | #构造神经网络 448 | def baseline_model(): 449 | 450 | #CNN参数 451 | 452 | #filters个数通常与文本长度相当 便于提取特征 453 | filters = max_document_length 454 | 455 | # Inputs 456 | input = Input(shape=[max_document_length]) 457 | 458 | # 词向量层,本文使用了预训练word2vec词向量,把trainable设为False 459 | x = Embedding(max_features + 1, 460 | embedding_dims, 461 | weights=[embedding_matrix], 462 | trainable=trainable)(input) 463 | 464 | 465 | 466 | # conv layers 467 | convs = [] 468 | for filter_size in [3,4,5]: 469 | l_conv = Conv1D(filters=filters, kernel_size=filter_size, activation='relu')(x) 470 | l_pool = MaxPooling1D()(l_conv) 471 | l_pool = Flatten()(l_pool) 472 | convs.append(l_pool) 473 | 474 | merge = concatenate(convs, axis=1) 475 | 476 | out = Dropout(0.2)(merge) 477 | 478 | output = Dense(32, activation='relu')(out) 479 | 480 | output = Dense(units=2, activation='softmax')(output) 481 | 482 | #输出层 483 | model = Model([input], output) 484 | 485 | model.compile(loss='categorical_crossentropy', 486 | optimizer='adam', 487 | metrics=['accuracy']) 488 | 489 | #可视化 490 | plot_model(model, to_file='yelp-cnn-model-textcnn.png',show_shapes=True) 491 | 492 | model.summary() 493 | 494 | return model 495 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 496 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 497 | # 最新接口指定训练的次数为epochs 498 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=50, verbose=1) 499 | 500 | #使用5折交叉验证 501 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 502 | # print scores 503 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 504 | 505 | 506 | if __name__ == '__main__': 507 | 508 | 509 | text,stars=load_reviews(yelp_file,10000) 510 | 511 | stars=[ 0 if star < 3 else 1 for star in stars ] 512 | 513 | print "情感分类的总数:" 514 | count_classes = pd.value_counts(stars, sort=True) 515 | print count_classes 516 | 517 | #使用单层cnn文档分类 518 | #do_keras_cnn(text,stars) 519 | 520 | #使用cnn+mlp文档分类 521 | #do_keras_cnn_mlp(text,stars) 522 | 523 | #使用textCNN文档分类 524 | #do_keras_textcnn(text,stars) 525 | 526 | 527 | #使用textCNN文档分类 以及预计训练的词向量 528 | do_keras_textcnn_w2v(text,stars,True) -------------------------------------------------------------------------------- /code/fasttext.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import re 3 | from fastText import train_supervised 4 | 5 | import numpy as np 6 | import codecs 7 | 8 | from sklearn.model_selection import cross_val_score 9 | from sklearn.model_selection import train_test_split 10 | 11 | from sklearn.neural_network import MLPClassifier 12 | from sklearn import feature_extraction 13 | from sklearn.feature_extraction.text import TfidfTransformer 14 | from sklearn.feature_extraction.text import CountVectorizer 15 | 16 | from keras.utils import to_categorical 17 | from sklearn.preprocessing import OneHotEncoder 18 | 19 | 20 | def load_stopwords(): 21 | with open("stopwords.txt") as F: 22 | stopwords=F.readlines() 23 | F.close() 24 | return [word.strip() for word in stopwords] 25 | 26 | def load_SogouTCE(): 27 | SogouTCE=[] 28 | SogouTCE_kv = {} 29 | with open("../data/SogouTCE.txt") as F: 30 | for line in F: 31 | (url,channel)=line.split() 32 | SogouTCE.append(url) 33 | F.close() 34 | 35 | for index,url in enumerate(SogouTCE): 36 | #删除http前缀 37 | url=re.sub('http://','',url) 38 | print "k:%s v:%d" % (url,index) 39 | SogouTCE_kv[url]=index 40 | 41 | return SogouTCE_kv 42 | 43 | def load_url(SogouTCE_kv): 44 | labels=[] 45 | with open("../data/news_sohusite_url.txt") as F: 46 | #with codecs.open("../data/news_sohusite_url.txt","r",encoding='utf-8', errors='ignore') as F: 47 | for line in F: 48 | for k,v in SogouTCE_kv.items(): 49 | if re.search(k,line,re.IGNORECASE): 50 | #print "x:%s y:%d" % (line,v) 51 | print v 52 | labels.append(v) 53 | #else: 54 | # print "not found %s" %(line) 55 | 56 | F.close() 57 | return labels 58 | 59 | def load_selecteddata(SogouTCE_kv): 60 | x=[] 61 | y=[] 62 | 63 | #加载content列表 64 | #with codecs.open("../data/news_sohusite_content.txt", "r", encoding='utf-8', errors='ignore') as F: 65 | with open("../data/news_sohusite_content.txt") as F: 66 | content=F.readlines() 67 | F.close() 68 | 69 | # 加载url列表 70 | with open("../data/news_sohusite_url.txt") as F: 71 | url = F.readlines() 72 | F.close() 73 | 74 | for index,u in enumerate(url): 75 | for k, v in SogouTCE_kv.items(): 76 | # 只加载id为81,79和91的数据,同时注意要过滤掉内容为空的 77 | if re.search(k, u, re.IGNORECASE) and v in (81,79, 91) and len(content[index].strip()) > 1: 78 | #保存url对应的content内容 79 | x.append(content[index]) 80 | y.append(v) 81 | continue 82 | 83 | return x,y 84 | 85 | 86 | 87 | def dump_file(x,y,filename): 88 | with open(filename, 'w') as f: 89 | #f.write('Hello, world!') 90 | for i,v in enumerate(x): 91 | #f.write("%s __label__%d" % (v,y)) 92 | line="%s __label__%d\n" % (v,y[i]) 93 | #print line 94 | f.write(line) 95 | f.close() 96 | 97 | def print_results(N, p, r): 98 | print("N\t" + str(N)) 99 | print("P@{}\t{:.3f}".format(1, p)) 100 | print("R@{}\t{:.3f}".format(1, r)) 101 | 102 | def do_mlp(x,y): 103 | 104 | #mlp 105 | clf = MLPClassifier(solver='lbfgs', 106 | alpha=1e-5, 107 | hidden_layer_sizes=(5, 3), 108 | random_state=1) 109 | 110 | scores = cross_val_score(clf, x, y, cv = 5,scoring='f1_micro') 111 | #print scores 112 | print("f1: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 113 | 114 | scores = cross_val_score(clf, x, y, cv = 5,scoring='accuracy') 115 | #print scores 116 | print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 117 | 118 | if __name__ == '__main__': 119 | SogouTCE_kv=load_SogouTCE() 120 | 121 | #labels=load_url(SogouTCE_kv) 122 | 123 | x,y=load_selecteddata(SogouTCE_kv) 124 | 125 | stopwords=load_stopwords() 126 | 127 | #切割token 128 | x=[ [word for word in line.split() if word not in stopwords] for line in x] 129 | 130 | # 分割训练集和测试集 131 | x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) 132 | 133 | #按照fasttest的要求生成训练数据和测试数据 134 | dump_file(x_train,y_train,"../data/sougou_train.txt") 135 | dump_file(x_test, y_test, "../data/sougou_test.txt") 136 | 137 | # train_supervised uses the same arguments and defaults as the fastText cli 138 | model = train_supervised( 139 | input="../data/sougou_train.txt", 140 | epoch=25, lr=0.9, wordNgrams=2, verbose=2, minCount=1 141 | ) 142 | print_results(*model.test("../data/sougou_test.txt")) -------------------------------------------------------------------------------- /code/itchat.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/itchat.pkl -------------------------------------------------------------------------------- /code/keywords.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from jieba import analyse 4 | tfidf = analyse.extract_tags 5 | 6 | if __name__ == '__main__': 7 | 8 | text=""" 9 | 据半岛电视台援引叙利亚国家电视台称,叙利亚已经对美国、英国、法国的空袭进行了反击。据介绍,在叙军武器库中,对西方最具威慑力的当属各型战术地对地弹道导弹。 10 | 尽管美英法是利用巡航导弹等武器发动远程空袭,但叙军要对等还击却几乎是“不可能完成的任务”。目前叙军仍能作战的战机仍是老旧的苏制米格-29、米格-23、米格-21战斗机和苏-22、苏-24轰炸机,它们在现代化的西方空军面前难有自保之力,因此叙军的远程反击只能依靠另一个撒手锏——地对地战术弹道导弹。 11 | """ 12 | 13 | # 关键词提取所使用停用词文本语料库可以切换成自定义语料库的路径。 14 | analyse.set_stop_words("stopwords.txt") 15 | 16 | # 引入TextRank关键词抽取接口 17 | textrank = analyse.textrank 18 | 19 | # 基于TextRank算法进行关键词抽取 20 | keywords_textrank = textrank(text,topK = 10, withWeight = False, allowPOS = ('n','ns','vn','v','nz')) 21 | # 输出抽取出的关键词 22 | for keyword in keywords_textrank: 23 | print keyword + "/" 24 | 25 | 26 | print "TFIDF" 27 | 28 | # TFIDF 29 | keywords_tfidf = analyse.extract_tags(text,topK = 10, withWeight = False, allowPOS = ('n','ns','vn','v','nz')) 30 | 31 | # 输出抽取出的关键词 32 | for keyword in keywords_tfidf: 33 | print keyword + "/" -------------------------------------------------------------------------------- /code/lda.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import re 3 | import os 4 | #from fastText import train_supervised 5 | 6 | import numpy as np 7 | import codecs 8 | 9 | from sklearn.model_selection import cross_val_score 10 | from sklearn.model_selection import train_test_split 11 | 12 | from sklearn.neural_network import MLPClassifier 13 | from sklearn import feature_extraction 14 | from sklearn.feature_extraction.text import TfidfTransformer 15 | from sklearn.feature_extraction.text import CountVectorizer 16 | 17 | #from keras.utils import to_categorical 18 | #from sklearn.preprocessing import OneHotEncoder 19 | 20 | from gensim import corpora, models 21 | 22 | import time 23 | 24 | #测试环境 25 | DEV_FILE="../data/news_sohusite_content_10000.txt" 26 | #生产环境 27 | PRO_FILE="/mnt/nlp/dataset/news_sohusite_content-50000.txt" 28 | 29 | 30 | def load_stopwords(): 31 | with open("stopwords.txt") as F: 32 | stopwords=F.readlines() 33 | F.close() 34 | return [word.strip() for word in stopwords] 35 | 36 | 37 | def load_sougou_content(): 38 | filename=PRO_FILE 39 | 40 | if os.path.exists(DEV_FILE): 41 | filename=DEV_FILE 42 | 43 | print "Open data file %s" % (filename) 44 | 45 | with open(filename) as F: 46 | content=F.readlines() 47 | F.close() 48 | 49 | 50 | return content 51 | 52 | def test_bow(): 53 | content=[ 54 | ["你","爱","我"],["我","爱","她"] 55 | ] 56 | 57 | test=["你","爱","她"] 58 | 59 | # 得到文档-单词矩阵 (直接利用统计词频得到特征) 60 | dictionary = corpora.Dictionary(content) 61 | 62 | # 将dictionary转化为一个词袋,得到文档-单词矩阵 63 | texts = [dictionary.doc2bow(text) for text in content] 64 | 65 | texts=np.array(texts) 66 | 67 | print texts 68 | 69 | test=dictionary.doc2bow(text) 70 | 71 | print test 72 | 73 | 74 | def do_lda(): 75 | #加载搜狗新闻数据 76 | content=load_sougou_content() 77 | 78 | #加载停用词 79 | stopwords=load_stopwords() 80 | 81 | #切割token 82 | content=[ [word for word in line.split() if word not in stopwords] for line in content] 83 | 84 | 85 | # 得到文档-单词矩阵 (直接利用统计词频得到特征) 86 | dictionary = corpora.Dictionary(content) 87 | 88 | # 将dictionary转化为一个词袋,得到文档-单词矩阵 89 | texts = [dictionary.doc2bow(text) for text in content] 90 | 91 | # 利用tf-idf来做为特征进行处理 92 | texts_tf_idf = models.TfidfModel(texts)[texts] 93 | 94 | 95 | # 利用LDA做主题分类的情况 96 | #print "BOW&LDA" 97 | 98 | #num_topics=5 99 | 100 | #ldamulticore 101 | #lda = models.ldamodel.LdaModel(corpus=texts, id2word=dictionary, num_topics=num_topics) 102 | #lda = models.ldamodel.ldamulticore(corpus=texts, id2word=dictionary, num_topics=num_topics) 103 | 104 | #print lda.print_topics(num_topics=num_topics, num_words=4) 105 | 106 | #打印前5个主题 107 | #for index,topic in lda.print_topics(5): 108 | # print topic 109 | 110 | # 利用TFIDF&LDA做主题分类的情况 111 | print "TFIDF&LDA" 112 | 113 | num_topics=5 114 | 115 | lda = models.ldamodel.LdaModel(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 116 | 117 | #print lda.print_topics(num_topics=num_topics, num_words=4) 118 | 119 | #打印前10个主题 120 | for index,topic in lda.print_topics(5): 121 | print topic 122 | 123 | #获取预料对应的LDA特征 124 | corpus_lda = lda[texts_tf_idf] 125 | 126 | #for doc_tfidf in corpus_lda: 127 | # print(doc_tfidf) 128 | print corpus_lda[0] 129 | 130 | 131 | def do_lda_usemulticore(): 132 | 133 | #获取当前时间 134 | start = time.clock() 135 | 136 | #加载搜狗新闻数据 137 | content=load_sougou_content() 138 | 139 | #加载停用词 140 | stopwords=load_stopwords() 141 | 142 | #切割token 143 | content=[ [word for word in line.split() if word not in stopwords] for line in content] 144 | 145 | #计算耗时 146 | end = time.clock() 147 | print('[data clean]Running time: %s Seconds' % (end - start)) 148 | 149 | 150 | #获取当前时间 151 | start = time.clock() 152 | 153 | 154 | # 得到文档-单词矩阵 (直接利用统计词频得到特征) 155 | dictionary = corpora.Dictionary(content) 156 | 157 | # 将dictionary转化为一个词袋,得到文档-单词矩阵 158 | texts = [dictionary.doc2bow(text) for text in content] 159 | 160 | # 利用tf-idf来做为特征进行处理 161 | texts_tf_idf = models.TfidfModel(texts)[texts] 162 | 163 | #计算耗时 164 | end = time.clock() 165 | print('[get word bag]Running time: %s Seconds' % (end - start)) 166 | 167 | 168 | # 利用TFIDF&LDA做主题分类的情况 169 | print "TFIDF&LDA" 170 | 171 | num_topics=200 172 | 173 | start = time.clock() 174 | 175 | 176 | #workers指定使用的CPU个数 默认使用cpu_count()-1 即使用几乎全部CPU 仅保留一个CPU不参与LDA计算 177 | #https://radimrehurek.com/gensim/models/ldamulticore.html 178 | #Hoffman, Blei, Bach: Online Learning for Latent Dirichlet Allocation, NIPS 2010. 179 | lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 180 | #lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, 181 | # num_topics=num_topics,workers=12) 182 | 183 | # 打印前10个主题 184 | for index, topic in lda.print_topics(num_topics=10, num_words=10): 185 | print topic 186 | 187 | 188 | #计算耗时 189 | end = time.clock() 190 | print('[lda]Running time: %s Seconds' % (end - start)) 191 | 192 | if __name__ == '__main__': 193 | 194 | #test_bow() 195 | #do_lda() 196 | do_lda_usemulticore() 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /code/mlp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import re 3 | from fastText import train_supervised 4 | 5 | import numpy as np 6 | import codecs 7 | 8 | from sklearn.model_selection import cross_val_score 9 | from sklearn.model_selection import train_test_split 10 | 11 | from sklearn.neural_network import MLPClassifier 12 | from sklearn import feature_extraction 13 | from sklearn.feature_extraction.text import TfidfTransformer 14 | from sklearn.feature_extraction.text import CountVectorizer 15 | 16 | from keras.utils import to_categorical 17 | from sklearn.preprocessing import OneHotEncoder 18 | 19 | 20 | def load_SogouTCE(): 21 | SogouTCE=[] 22 | SogouTCE_kv = {} 23 | with open("../data/SogouTCE.txt") as F: 24 | for line in F: 25 | (url,channel)=line.split() 26 | SogouTCE.append(url) 27 | F.close() 28 | 29 | for index,url in enumerate(SogouTCE): 30 | #删除http前缀 31 | url=re.sub('http://','',url) 32 | print "k:%s v:%d" % (url,index) 33 | SogouTCE_kv[url]=index 34 | 35 | return SogouTCE_kv 36 | 37 | def load_url(SogouTCE_kv): 38 | labels=[] 39 | with open("../data/news_sohusite_url.txt") as F: 40 | #with codecs.open("../data/news_sohusite_url.txt","r",encoding='utf-8', errors='ignore') as F: 41 | for line in F: 42 | for k,v in SogouTCE_kv.items(): 43 | if re.search(k,line,re.IGNORECASE): 44 | #print "x:%s y:%d" % (line,v) 45 | print v 46 | labels.append(v) 47 | #else: 48 | # print "not found %s" %(line) 49 | 50 | F.close() 51 | return labels 52 | 53 | def load_selecteddata(SogouTCE_kv): 54 | x=[] 55 | y=[] 56 | 57 | #加载content列表 58 | #with codecs.open("../data/news_sohusite_content.txt", "r", encoding='utf-8', errors='ignore') as F: 59 | with open("../data/news_sohusite_content.txt") as F: 60 | content=F.readlines() 61 | F.close() 62 | 63 | # 加载url列表 64 | with open("../data/news_sohusite_url.txt") as F: 65 | url = F.readlines() 66 | F.close() 67 | 68 | for index,u in enumerate(url): 69 | for k, v in SogouTCE_kv.items(): 70 | # 只加载id为81,79和91的数据,同时注意要过滤掉内容为空的 71 | if re.search(k, u, re.IGNORECASE) and v in (81, 79, 91) and len(content[index].strip()) > 1: 72 | #保存url对应的content内容 73 | x.append(content[index]) 74 | y.append(v) 75 | 76 | return x,y 77 | 78 | 79 | 80 | def dump_file(x,y,filename): 81 | with open(filename, 'w') as f: 82 | #f.write('Hello, world!') 83 | for i,v in enumerate(x): 84 | #f.write("%s __label__%d" % (v,y)) 85 | line="%s __label__%d\n" % (v,y[i]) 86 | #print line 87 | f.write(line) 88 | f.close() 89 | 90 | def print_results(N, p, r): 91 | print("N\t" + str(N)) 92 | print("P@{}\t{:.3f}".format(1, p)) 93 | print("R@{}\t{:.3f}".format(1, r)) 94 | 95 | def do_mlp(x,y): 96 | 97 | #mlp 98 | clf = MLPClassifier(solver='lbfgs', 99 | alpha=1e-5, 100 | hidden_layer_sizes=(5, 3), 101 | random_state=1) 102 | 103 | scores = cross_val_score(clf, x, y, cv = 5,scoring='f1_micro') 104 | #print scores 105 | print("f1: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 106 | 107 | scores = cross_val_score(clf, x, y, cv = 5,scoring='accuracy') 108 | #print scores 109 | print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 110 | 111 | if __name__ == '__main__': 112 | SogouTCE_kv=load_SogouTCE() 113 | 114 | #labels=load_url(SogouTCE_kv) 115 | 116 | x,y=load_selecteddata(SogouTCE_kv) 117 | 118 | #切割词袋 119 | vectorizer = CountVectorizer(ngram_range=(2,2),max_features=5000) 120 | # 该类会统计每个词语的tf-idf权值 121 | transformer = TfidfTransformer() 122 | x = transformer.fit_transform(vectorizer.fit_transform(x)) 123 | 124 | #转换成one hot编码 125 | t=[] 126 | for i in y: 127 | if i == 79: 128 | t.append(0) 129 | 130 | if i == 81: 131 | t.append(1) 132 | 133 | if i == 91: 134 | t.append(2) 135 | 136 | y=to_categorical(t, num_classes=3) 137 | 138 | 139 | do_mlp(x,y) 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /code/ner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | 4 | import numpy as np 5 | import codecs 6 | 7 | from sklearn.model_selection import cross_val_score 8 | from sklearn.model_selection import train_test_split 9 | 10 | from sklearn.neural_network import MLPClassifier 11 | from sklearn import feature_extraction 12 | from sklearn.feature_extraction.text import TfidfTransformer 13 | from sklearn.feature_extraction.text import CountVectorizer 14 | 15 | from keras.utils import to_categorical 16 | from sklearn.preprocessing import OneHotEncoder 17 | 18 | 19 | import jieba 20 | import jieba.posseg 21 | 22 | 23 | def do_posseq(text): 24 | #seg_lig=jieba.cut(text,cut_all=False) 25 | 26 | seg_lig = jieba.posseg.cut(text) 27 | 28 | #for w,tag in seg_lig: 29 | # print "%s /%s" % (w,tag) 30 | 31 | print " ".join(["%s /%s" % (w,tag) for w,tag in seg_lig]) 32 | 33 | if __name__ == '__main__': 34 | 35 | #do_posseq("我爱北京天安门") 36 | 37 | text = """ 38 | 据半岛电视台援引叙利亚国家电视台称,叙利亚已经对美国、英国、法国的空袭进行了反击。据介绍,在叙军武器库中,对西方最具威慑力的当属各型战术地对地弹道导弹。 39 | 尽管美英法是利用巡航导弹等武器发动远程空袭,但叙军要对等还击却几乎是“不可能完成的任务”。目前叙军仍能作战的战机仍是老旧的苏制米格-29、米格-23、米格-21战斗机和苏-22、苏-24轰炸机,它们在现代化的西方空军面前难有自保之力,因此叙军的远程反击只能依靠另一个撒手锏——地对地战术弹道导弹。 40 | """ 41 | 42 | do_posseq(text) 43 | 44 | -------------------------------------------------------------------------------- /code/progressive-tweet-sentiment.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/progressive-tweet-sentiment.csv -------------------------------------------------------------------------------- /code/similarities.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import re 3 | from fastText import train_supervised 4 | 5 | import numpy as np 6 | import codecs 7 | import sys 8 | 9 | from sklearn.model_selection import cross_val_score 10 | from sklearn.model_selection import train_test_split 11 | 12 | from sklearn.neural_network import MLPClassifier 13 | from sklearn import feature_extraction 14 | from sklearn.feature_extraction.text import TfidfTransformer 15 | from sklearn.feature_extraction.text import CountVectorizer 16 | 17 | 18 | from gensim import corpora, models,similarities 19 | 20 | from simhash import Simhash 21 | import math 22 | 23 | from scipy import sparse 24 | 25 | 26 | def load_stopwords(): 27 | with open("stopwords.txt") as F: 28 | stopwords=F.readlines() 29 | F.close() 30 | return [word.strip() for word in stopwords] 31 | 32 | 33 | def load_sougou_content(): 34 | #with open("../data/news_sohusite_content.txt") as F: 35 | # 测试阶段仅加载前1w条记录 36 | with open("../data/news_sohusite_content_10000.txt") as F: 37 | content=F.readlines() 38 | F.close() 39 | return content 40 | 41 | #SparseMatrixSimilarity计算余弦距离 42 | def gensim_sim(content,test_news): 43 | # 加载积累的stopwords 44 | stopwords = load_stopwords() 45 | 46 | # 切割token并清除stopwords 47 | x = [[word for word in line.split() if word not in stopwords] for line in content] 48 | 49 | # 获取词袋 50 | dictionary = corpora.Dictionary(x) 51 | 52 | # 制作语料 53 | corpus = [dictionary.doc2bow(doc) for doc in x] 54 | 55 | # 进行TFIDF处理 56 | tfidf = models.TfidfModel(corpus) 57 | 58 | # 把测试文章转换成tfidf 59 | test_news_vec = [word for word in test_news.split() if word not in stopwords] 60 | 61 | test_news_vec = tfidf[dictionary.doc2bow(test_news_vec)] 62 | 63 | index = similarities.SparseMatrixSimilarity(tfidf[corpus], num_features=len(dictionary.keys())) 64 | sim = index[tfidf[test_news_vec]] 65 | 66 | # for index, similarities in sorted(enumerate(sim), key=lambda item: -item[1])[:4]: 67 | # print "index:%d similarities:%d content:%s" % ( index, similarities,content[index] ) 68 | # print "index:%d similarities:%d" % (index, similarities) 69 | for index, score in sorted(enumerate(sim), key=lambda item: -item[1])[:6]: 70 | #print "index:%d similarities:%f" % (index, score) 71 | print "index:%d similarities:%f content:%s" % (index, score, content[index]) 72 | 73 | 74 | def gensim_simhash(content,test_news): 75 | 76 | # 加载积累的stopwords 77 | stopwords = load_stopwords() 78 | 79 | # 切割token并清除stopwords 80 | x = [[word for word in line.split() if word not in stopwords] for line in content] 81 | 82 | # 切割token并清除stopwords 83 | test_news = [word for word in test_news.split() if word not in stopwords] 84 | 85 | # 计算simhash 86 | test_news_hash = Simhash(test_news) 87 | 88 | 89 | sim=[] 90 | # 遍历语料计算simhash值 91 | for news in x: 92 | hash = Simhash(news) 93 | score=test_news_hash.distance(hash) 94 | sim.append( score) 95 | #print "add %d %f" %(index,score) 96 | 97 | for index, score in sorted(enumerate(sim), key=lambda item: item[1])[:6]: 98 | # print "index:%d similarities:%f" % (index, score) 99 | print "index:%d similarities:%f content:%s" % (index, score, content[index]) 100 | 101 | 102 | def gensim_cos(content,test_news): 103 | # 加载积累的stopwords 104 | stopwords = load_stopwords() 105 | 106 | # 切割token并清除stopwords 107 | x = [[word for word in line.split() if word not in stopwords] for line in content] 108 | 109 | # 获取词袋 110 | dictionary = corpora.Dictionary(x) 111 | 112 | # 制作语料 113 | corpus = [dictionary.doc2bow(doc) for doc in x] 114 | 115 | # 把测试文章转换成tfidf 116 | test_news_vec = [word for word in test_news.split() if word not in stopwords] 117 | 118 | test_news_vec = dictionary.doc2bow(test_news_vec) 119 | 120 | 121 | #稀疏矩阵转正常矩阵 122 | #corpus=corpus.toarray() 123 | #test_news_vec=test_news_vec.toarray() 124 | 125 | cos=[] 126 | # 遍历语料计算余弦值值 127 | for news in corpus: 128 | a=np.array(news) 129 | b=np.array(test_news_vec) 130 | c=np.dot(a,b) 131 | score=c/(math.sqrt(a)*math.sqrt(b)) 132 | cos.append( score) 133 | 134 | for index, score in sorted(enumerate(cos), key=lambda item: item[1])[:6]: 135 | # print "index:%d similarities:%f" % (index, score) 136 | print "index:%d similarities:%f content:%s" % (index, score, content[index]) 137 | 138 | if __name__ == '__main__': 139 | reload(sys) 140 | sys.setdefaultencoding('utf8') 141 | 142 | 143 | #加载搜狐新闻语料 144 | content=load_sougou_content() 145 | 146 | 147 | #设置测试文章 148 | print "select test data:" 149 | test_news=content[88] 150 | print test_news 151 | 152 | print "使用余弦计算相似度" 153 | gensim_sim(content, test_news) 154 | 155 | #print "simhash" 156 | #gensim_simhash(content, test_news) 157 | 158 | 159 | -------------------------------------------------------------------------------- /code/stopwords.txt: -------------------------------------------------------------------------------- 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 | 0 27 | 1 28 | 2 29 | 3 30 | 4 31 | 5 32 | 6 33 | 7 34 | 8 35 | 9 36 | : 37 | :// 38 | :: 39 | ; 40 | < 41 | = 42 | > 43 | >> 44 | ? 45 | @ 46 | A 47 | Lex 48 | [ 49 | \ 50 | ] 51 | ^ 52 | _ 53 | ` 54 | exp 55 | sub 56 | sup 57 | | 58 | } 59 | ~ 60 | ~~~~ 61 | · 62 | × 63 | ××× 64 | Δ 65 | Ψ 66 | γ 67 | μ 68 | φ 69 | φ. 70 | В 71 | — 72 | —— 73 | ——— 74 | ‘ 75 | ’ 76 | ’‘ 77 | “ 78 | ” 79 | ”, 80 | … 81 | …… 82 | …………………………………………………③ 83 | ′∈ 84 | ′| 85 | ℃ 86 | Ⅲ 87 | ↑ 88 | → 89 | ∈[ 90 | ∪φ∈ 91 | ≈ 92 | ① 93 | ② 94 | ②c 95 | ③ 96 | ③] 97 | ④ 98 | ⑤ 99 | ⑥ 100 | ⑦ 101 | ⑧ 102 | ⑨ 103 | ⑩ 104 | ── 105 | ■ 106 | ▲ 107 |   108 | 、 109 | 。 110 | 〈 111 | 〉 112 | 《 113 | 》 114 | 》), 115 | 」 116 | 『 117 | 』 118 | 【 119 | 】 120 | 〔 121 | 〕 122 | 〕〔 123 | ㈧ 124 | 一 125 | 一. 126 | 一一 127 | 一下 128 | 一个 129 | 一些 130 | 一何 131 | 一切 132 | 一则 133 | 一则通过 134 | 一天 135 | 一定 136 | 一方面 137 | 一旦 138 | 一时 139 | 一来 140 | 一样 141 | 一次 142 | 一片 143 | 一番 144 | 一直 145 | 一致 146 | 一般 147 | 一起 148 | 一转眼 149 | 一边 150 | 一面 151 | 七 152 | 万一 153 | 三 154 | 三天两头 155 | 三番两次 156 | 三番五次 157 | 上 158 | 上下 159 | 上升 160 | 上去 161 | 上来 162 | 上述 163 | 上面 164 | 下 165 | 下列 166 | 下去 167 | 下来 168 | 下面 169 | 不 170 | 不一 171 | 不下 172 | 不久 173 | 不了 174 | 不亦乐乎 175 | 不仅 176 | 不仅...而且 177 | 不仅仅 178 | 不仅仅是 179 | 不会 180 | 不但 181 | 不但...而且 182 | 不光 183 | 不免 184 | 不再 185 | 不力 186 | 不单 187 | 不变 188 | 不只 189 | 不可 190 | 不可开交 191 | 不可抗拒 192 | 不同 193 | 不外 194 | 不外乎 195 | 不够 196 | 不大 197 | 不如 198 | 不妨 199 | 不定 200 | 不对 201 | 不少 202 | 不尽 203 | 不尽然 204 | 不巧 205 | 不已 206 | 不常 207 | 不得 208 | 不得不 209 | 不得了 210 | 不得已 211 | 不必 212 | 不怎么 213 | 不怕 214 | 不惟 215 | 不成 216 | 不拘 217 | 不择手段 218 | 不敢 219 | 不料 220 | 不断 221 | 不日 222 | 不时 223 | 不是 224 | 不曾 225 | 不止 226 | 不止一次 227 | 不比 228 | 不消 229 | 不满 230 | 不然 231 | 不然的话 232 | 不特 233 | 不独 234 | 不由得 235 | 不知不觉 236 | 不管 237 | 不管怎样 238 | 不经意 239 | 不胜 240 | 不能 241 | 不能不 242 | 不至于 243 | 不若 244 | 不要 245 | 不论 246 | 不起 247 | 不足 248 | 不过 249 | 不迭 250 | 不问 251 | 不限 252 | 与 253 | 与其 254 | 与其说 255 | 与否 256 | 与此同时 257 | 专门 258 | 且 259 | 且不说 260 | 且说 261 | 两者 262 | 严格 263 | 严重 264 | 个 265 | 个人 266 | 个别 267 | 中小 268 | 中间 269 | 丰富 270 | 串行 271 | 临 272 | 临到 273 | 为 274 | 为主 275 | 为了 276 | 为什么 277 | 为什麽 278 | 为何 279 | 为止 280 | 为此 281 | 为着 282 | 主张 283 | 主要 284 | 举凡 285 | 举行 286 | 乃 287 | 乃至 288 | 乃至于 289 | 么 290 | 之 291 | 之一 292 | 之前 293 | 之后 294 | 之後 295 | 之所以 296 | 之类 297 | 乌乎 298 | 乎 299 | 乒 300 | 乘 301 | 乘势 302 | 乘机 303 | 乘胜 304 | 乘虚 305 | 乘隙 306 | 九 307 | 也 308 | 也好 309 | 也就是说 310 | 也是 311 | 也罢 312 | 了 313 | 了解 314 | 争取 315 | 二 316 | 二来 317 | 二话不说 318 | 二话没说 319 | 于 320 | 于是 321 | 于是乎 322 | 云云 323 | 云尔 324 | 互 325 | 互相 326 | 五 327 | 些 328 | 交口 329 | 亦 330 | 产生 331 | 亲口 332 | 亲手 333 | 亲眼 334 | 亲自 335 | 亲身 336 | 人 337 | 人人 338 | 人们 339 | 人家 340 | 人民 341 | 什么 342 | 什么样 343 | 什麽 344 | 仅 345 | 仅仅 346 | 今 347 | 今后 348 | 今天 349 | 今年 350 | 今後 351 | 介于 352 | 仍 353 | 仍旧 354 | 仍然 355 | 从 356 | 从不 357 | 从严 358 | 从中 359 | 从事 360 | 从今以后 361 | 从优 362 | 从古到今 363 | 从古至今 364 | 从头 365 | 从宽 366 | 从小 367 | 从新 368 | 从无到有 369 | 从早到晚 370 | 从未 371 | 从来 372 | 从此 373 | 从此以后 374 | 从而 375 | 从轻 376 | 从速 377 | 从重 378 | 他 379 | 他人 380 | 他们 381 | 他是 382 | 他的 383 | 代替 384 | 以 385 | 以上 386 | 以下 387 | 以为 388 | 以便 389 | 以免 390 | 以前 391 | 以及 392 | 以后 393 | 以外 394 | 以後 395 | 以故 396 | 以期 397 | 以来 398 | 以至 399 | 以至于 400 | 以致 401 | 们 402 | 任 403 | 任何 404 | 任凭 405 | 任务 406 | 企图 407 | 伙同 408 | 会 409 | 伟大 410 | 传 411 | 传说 412 | 传闻 413 | 似乎 414 | 似的 415 | 但 416 | 但凡 417 | 但愿 418 | 但是 419 | 何 420 | 何乐而不为 421 | 何以 422 | 何况 423 | 何处 424 | 何妨 425 | 何尝 426 | 何必 427 | 何时 428 | 何止 429 | 何苦 430 | 何须 431 | 余外 432 | 作为 433 | 你 434 | 你们 435 | 你是 436 | 你的 437 | 使 438 | 使得 439 | 使用 440 | 例如 441 | 依 442 | 依据 443 | 依照 444 | 依靠 445 | 便 446 | 便于 447 | 促进 448 | 保持 449 | 保管 450 | 保险 451 | 俺 452 | 俺们 453 | 倍加 454 | 倍感 455 | 倒不如 456 | 倒不如说 457 | 倒是 458 | 倘 459 | 倘使 460 | 倘或 461 | 倘然 462 | 倘若 463 | 借 464 | 借以 465 | 借此 466 | 假使 467 | 假如 468 | 假若 469 | 偏偏 470 | 做到 471 | 偶尔 472 | 偶而 473 | 傥然 474 | 像 475 | 儿 476 | 允许 477 | 元/吨 478 | 充其极 479 | 充其量 480 | 充分 481 | 先不先 482 | 先后 483 | 先後 484 | 先生 485 | 光 486 | 光是 487 | 全体 488 | 全力 489 | 全年 490 | 全然 491 | 全身心 492 | 全部 493 | 全都 494 | 全面 495 | 八 496 | 八成 497 | 公然 498 | 六 499 | 兮 500 | 共 501 | 共同 502 | 共总 503 | 关于 504 | 其 505 | 其一 506 | 其中 507 | 其二 508 | 其他 509 | 其余 510 | 其后 511 | 其它 512 | 其实 513 | 其次 514 | 具体 515 | 具体地说 516 | 具体来说 517 | 具体说来 518 | 具有 519 | 兼之 520 | 内 521 | 再 522 | 再其次 523 | 再则 524 | 再有 525 | 再次 526 | 再者 527 | 再者说 528 | 再说 529 | 冒 530 | 冲 531 | 决不 532 | 决定 533 | 决非 534 | 况且 535 | 准备 536 | 凑巧 537 | 凝神 538 | 几 539 | 几乎 540 | 几度 541 | 几时 542 | 几番 543 | 几经 544 | 凡 545 | 凡是 546 | 凭 547 | 凭借 548 | 出 549 | 出于 550 | 出去 551 | 出来 552 | 出现 553 | 分别 554 | 分头 555 | 分期 556 | 分期分批 557 | 切 558 | 切不可 559 | 切切 560 | 切勿 561 | 切莫 562 | 则 563 | 则甚 564 | 刚 565 | 刚好 566 | 刚巧 567 | 刚才 568 | 初 569 | 别 570 | 别人 571 | 别处 572 | 别是 573 | 别的 574 | 别管 575 | 别说 576 | 到 577 | 到了儿 578 | 到处 579 | 到头 580 | 到头来 581 | 到底 582 | 到目前为止 583 | 前后 584 | 前此 585 | 前者 586 | 前进 587 | 前面 588 | 加上 589 | 加之 590 | 加以 591 | 加入 592 | 加强 593 | 动不动 594 | 动辄 595 | 勃然 596 | 匆匆 597 | 十分 598 | 千 599 | 千万 600 | 千万千万 601 | 半 602 | 单 603 | 单单 604 | 单纯 605 | 即 606 | 即令 607 | 即使 608 | 即便 609 | 即刻 610 | 即如 611 | 即将 612 | 即或 613 | 即是说 614 | 即若 615 | 却 616 | 却不 617 | 历 618 | 原来 619 | 去 620 | 又 621 | 又及 622 | 及 623 | 及其 624 | 及时 625 | 及至 626 | 双方 627 | 反之 628 | 反之亦然 629 | 反之则 630 | 反倒 631 | 反倒是 632 | 反应 633 | 反手 634 | 反映 635 | 反而 636 | 反过来 637 | 反过来说 638 | 取得 639 | 取道 640 | 受到 641 | 变成 642 | 古来 643 | 另 644 | 另一个 645 | 另一方面 646 | 另外 647 | 另悉 648 | 另方面 649 | 另行 650 | 只 651 | 只当 652 | 只怕 653 | 只是 654 | 只有 655 | 只消 656 | 只要 657 | 只限 658 | 叫 659 | 叫做 660 | 召开 661 | 叮咚 662 | 叮当 663 | 可 664 | 可以 665 | 可好 666 | 可是 667 | 可能 668 | 可见 669 | 各 670 | 各个 671 | 各人 672 | 各位 673 | 各地 674 | 各式 675 | 各种 676 | 各级 677 | 各自 678 | 合理 679 | 同 680 | 同一 681 | 同时 682 | 同样 683 | 后 684 | 后来 685 | 后者 686 | 后面 687 | 向 688 | 向使 689 | 向着 690 | 吓 691 | 吗 692 | 否则 693 | 吧 694 | 吧哒 695 | 吱 696 | 呀 697 | 呃 698 | 呆呆地 699 | 呐 700 | 呕 701 | 呗 702 | 呜 703 | 呜呼 704 | 呢 705 | 周围 706 | 呵 707 | 呵呵 708 | 呸 709 | 呼哧 710 | 呼啦 711 | 咋 712 | 和 713 | 咚 714 | 咦 715 | 咧 716 | 咱 717 | 咱们 718 | 咳 719 | 哇 720 | 哈 721 | 哈哈 722 | 哉 723 | 哎 724 | 哎呀 725 | 哎哟 726 | 哗 727 | 哗啦 728 | 哟 729 | 哦 730 | 哩 731 | 哪 732 | 哪个 733 | 哪些 734 | 哪儿 735 | 哪天 736 | 哪年 737 | 哪怕 738 | 哪样 739 | 哪边 740 | 哪里 741 | 哼 742 | 哼唷 743 | 唉 744 | 唯有 745 | 啊 746 | 啊呀 747 | 啊哈 748 | 啊哟 749 | 啐 750 | 啥 751 | 啦 752 | 啪达 753 | 啷当 754 | 喀 755 | 喂 756 | 喏 757 | 喔唷 758 | 喽 759 | 嗡 760 | 嗡嗡 761 | 嗬 762 | 嗯 763 | 嗳 764 | 嘎 765 | 嘎嘎 766 | 嘎登 767 | 嘘 768 | 嘛 769 | 嘻 770 | 嘿 771 | 嘿嘿 772 | 四 773 | 因 774 | 因为 775 | 因了 776 | 因此 777 | 因着 778 | 因而 779 | 固 780 | 固然 781 | 在 782 | 在下 783 | 在于 784 | 地 785 | 均 786 | 坚决 787 | 坚持 788 | 基于 789 | 基本 790 | 基本上 791 | 处在 792 | 处处 793 | 处理 794 | 复杂 795 | 多 796 | 多么 797 | 多亏 798 | 多多 799 | 多多少少 800 | 多多益善 801 | 多少 802 | 多年前 803 | 多年来 804 | 多数 805 | 多次 806 | 够瞧的 807 | 大 808 | 大不了 809 | 大举 810 | 大事 811 | 大体 812 | 大体上 813 | 大凡 814 | 大力 815 | 大多 816 | 大多数 817 | 大大 818 | 大家 819 | 大张旗鼓 820 | 大批 821 | 大抵 822 | 大概 823 | 大略 824 | 大约 825 | 大致 826 | 大都 827 | 大量 828 | 大面儿上 829 | 失去 830 | 奇 831 | 奈 832 | 奋勇 833 | 她 834 | 她们 835 | 她是 836 | 她的 837 | 好 838 | 好在 839 | 好的 840 | 好象 841 | 如 842 | 如上 843 | 如上所述 844 | 如下 845 | 如今 846 | 如何 847 | 如其 848 | 如前所述 849 | 如同 850 | 如常 851 | 如是 852 | 如期 853 | 如果 854 | 如次 855 | 如此 856 | 如此等等 857 | 如若 858 | 始而 859 | 姑且 860 | 存在 861 | 存心 862 | 孰料 863 | 孰知 864 | 宁 865 | 宁可 866 | 宁愿 867 | 宁肯 868 | 它 869 | 它们 870 | 它们的 871 | 它是 872 | 它的 873 | 安全 874 | 完全 875 | 完成 876 | 定 877 | 实现 878 | 实际 879 | 宣布 880 | 容易 881 | 密切 882 | 对 883 | 对于 884 | 对应 885 | 对待 886 | 对方 887 | 对比 888 | 将 889 | 将才 890 | 将要 891 | 将近 892 | 小 893 | 少数 894 | 尔 895 | 尔后 896 | 尔尔 897 | 尔等 898 | 尚且 899 | 尤其 900 | 就 901 | 就地 902 | 就是 903 | 就是了 904 | 就是说 905 | 就此 906 | 就算 907 | 就要 908 | 尽 909 | 尽可能 910 | 尽如人意 911 | 尽心尽力 912 | 尽心竭力 913 | 尽快 914 | 尽早 915 | 尽然 916 | 尽管 917 | 尽管如此 918 | 尽量 919 | 局外 920 | 居然 921 | 届时 922 | 属于 923 | 屡 924 | 屡屡 925 | 屡次 926 | 屡次三番 927 | 岂 928 | 岂但 929 | 岂止 930 | 岂非 931 | 川流不息 932 | 左右 933 | 巨大 934 | 巩固 935 | 差一点 936 | 差不多 937 | 己 938 | 已 939 | 已矣 940 | 已经 941 | 巴 942 | 巴巴 943 | 带 944 | 帮助 945 | 常 946 | 常常 947 | 常言说 948 | 常言说得好 949 | 常言道 950 | 平素 951 | 年复一年 952 | 并 953 | 并不 954 | 并不是 955 | 并且 956 | 并排 957 | 并无 958 | 并没 959 | 并没有 960 | 并肩 961 | 并非 962 | 广大 963 | 广泛 964 | 应当 965 | 应用 966 | 应该 967 | 庶乎 968 | 庶几 969 | 开外 970 | 开始 971 | 开展 972 | 引起 973 | 弗 974 | 弹指之间 975 | 强烈 976 | 强调 977 | 归 978 | 归根到底 979 | 归根结底 980 | 归齐 981 | 当 982 | 当下 983 | 当中 984 | 当儿 985 | 当前 986 | 当即 987 | 当口儿 988 | 当地 989 | 当场 990 | 当头 991 | 当庭 992 | 当时 993 | 当然 994 | 当真 995 | 当着 996 | 形成 997 | 彻夜 998 | 彻底 999 | 彼 1000 | 彼时 1001 | 彼此 1002 | 往 1003 | 往往 1004 | 待 1005 | 待到 1006 | 很 1007 | 很多 1008 | 很少 1009 | 後来 1010 | 後面 1011 | 得 1012 | 得了 1013 | 得出 1014 | 得到 1015 | 得天独厚 1016 | 得起 1017 | 心里 1018 | 必 1019 | 必定 1020 | 必将 1021 | 必然 1022 | 必要 1023 | 必须 1024 | 快 1025 | 快要 1026 | 忽地 1027 | 忽然 1028 | 怎 1029 | 怎么 1030 | 怎么办 1031 | 怎么样 1032 | 怎奈 1033 | 怎样 1034 | 怎麽 1035 | 怕 1036 | 急匆匆 1037 | 怪 1038 | 怪不得 1039 | 总之 1040 | 总是 1041 | 总的来看 1042 | 总的来说 1043 | 总的说来 1044 | 总结 1045 | 总而言之 1046 | 恍然 1047 | 恐怕 1048 | 恰似 1049 | 恰好 1050 | 恰如 1051 | 恰巧 1052 | 恰恰 1053 | 恰恰相反 1054 | 恰逢 1055 | 您 1056 | 您们 1057 | 您是 1058 | 惟其 1059 | 惯常 1060 | 意思 1061 | 愤然 1062 | 愿意 1063 | 慢说 1064 | 成为 1065 | 成年 1066 | 成年累月 1067 | 成心 1068 | 我 1069 | 我们 1070 | 我是 1071 | 我的 1072 | 或 1073 | 或则 1074 | 或多或少 1075 | 或是 1076 | 或曰 1077 | 或者 1078 | 或许 1079 | 战斗 1080 | 截然 1081 | 截至 1082 | 所 1083 | 所以 1084 | 所在 1085 | 所幸 1086 | 所有 1087 | 所谓 1088 | 才 1089 | 才能 1090 | 扑通 1091 | 打 1092 | 打从 1093 | 打开天窗说亮话 1094 | 扩大 1095 | 把 1096 | 抑或 1097 | 抽冷子 1098 | 拦腰 1099 | 拿 1100 | 按 1101 | 按时 1102 | 按期 1103 | 按照 1104 | 按理 1105 | 按说 1106 | 挨个 1107 | 挨家挨户 1108 | 挨次 1109 | 挨着 1110 | 挨门挨户 1111 | 挨门逐户 1112 | 换句话说 1113 | 换言之 1114 | 据 1115 | 据实 1116 | 据悉 1117 | 据我所知 1118 | 据此 1119 | 据称 1120 | 据说 1121 | 掌握 1122 | 接下来 1123 | 接着 1124 | 接著 1125 | 接连不断 1126 | 放量 1127 | 故 1128 | 故意 1129 | 故此 1130 | 故而 1131 | 敞开儿 1132 | 敢 1133 | 敢于 1134 | 敢情 1135 | 数/ 1136 | 整个 1137 | 断然 1138 | 方 1139 | 方便 1140 | 方才 1141 | 方能 1142 | 方面 1143 | 旁人 1144 | 无 1145 | 无宁 1146 | 无法 1147 | 无论 1148 | 既 1149 | 既...又 1150 | 既往 1151 | 既是 1152 | 既然 1153 | 日复一日 1154 | 日渐 1155 | 日益 1156 | 日臻 1157 | 日见 1158 | 时候 1159 | 昂然 1160 | 明显 1161 | 明确 1162 | 是 1163 | 是不是 1164 | 是以 1165 | 是否 1166 | 是的 1167 | 显然 1168 | 显著 1169 | 普通 1170 | 普遍 1171 | 暗中 1172 | 暗地里 1173 | 暗自 1174 | 更 1175 | 更为 1176 | 更加 1177 | 更进一步 1178 | 曾 1179 | 曾经 1180 | 替 1181 | 替代 1182 | 最 1183 | 最后 1184 | 最大 1185 | 最好 1186 | 最後 1187 | 最近 1188 | 最高 1189 | 有 1190 | 有些 1191 | 有关 1192 | 有利 1193 | 有力 1194 | 有及 1195 | 有所 1196 | 有效 1197 | 有时 1198 | 有点 1199 | 有的 1200 | 有的是 1201 | 有着 1202 | 有著 1203 | 望 1204 | 朝 1205 | 朝着 1206 | 末##末 1207 | 本 1208 | 本人 1209 | 本地 1210 | 本着 1211 | 本身 1212 | 权时 1213 | 来 1214 | 来不及 1215 | 来得及 1216 | 来看 1217 | 来着 1218 | 来自 1219 | 来讲 1220 | 来说 1221 | 极 1222 | 极为 1223 | 极了 1224 | 极其 1225 | 极力 1226 | 极大 1227 | 极度 1228 | 极端 1229 | 构成 1230 | 果然 1231 | 果真 1232 | 某 1233 | 某个 1234 | 某些 1235 | 某某 1236 | 根据 1237 | 根本 1238 | 格外 1239 | 梆 1240 | 概 1241 | 次第 1242 | 欢迎 1243 | 欤 1244 | 正值 1245 | 正在 1246 | 正如 1247 | 正巧 1248 | 正常 1249 | 正是 1250 | 此 1251 | 此中 1252 | 此后 1253 | 此地 1254 | 此处 1255 | 此外 1256 | 此时 1257 | 此次 1258 | 此间 1259 | 殆 1260 | 毋宁 1261 | 每 1262 | 每个 1263 | 每天 1264 | 每年 1265 | 每当 1266 | 每时每刻 1267 | 每每 1268 | 每逢 1269 | 比 1270 | 比及 1271 | 比如 1272 | 比如说 1273 | 比方 1274 | 比照 1275 | 比起 1276 | 比较 1277 | 毕竟 1278 | 毫不 1279 | 毫无 1280 | 毫无例外 1281 | 毫无保留地 1282 | 汝 1283 | 沙沙 1284 | 没 1285 | 没奈何 1286 | 没有 1287 | 沿 1288 | 沿着 1289 | 注意 1290 | 活 1291 | 深入 1292 | 清楚 1293 | 满 1294 | 满足 1295 | 漫说 1296 | 焉 1297 | 然 1298 | 然则 1299 | 然后 1300 | 然後 1301 | 然而 1302 | 照 1303 | 照着 1304 | 牢牢 1305 | 特别是 1306 | 特殊 1307 | 特点 1308 | 犹且 1309 | 犹自 1310 | 独 1311 | 独自 1312 | 猛然 1313 | 猛然间 1314 | 率尔 1315 | 率然 1316 | 现代 1317 | 现在 1318 | 理应 1319 | 理当 1320 | 理该 1321 | 瑟瑟 1322 | 甚且 1323 | 甚么 1324 | 甚或 1325 | 甚而 1326 | 甚至 1327 | 甚至于 1328 | 用 1329 | 用来 1330 | 甫 1331 | 甭 1332 | 由 1333 | 由于 1334 | 由是 1335 | 由此 1336 | 由此可见 1337 | 略 1338 | 略为 1339 | 略加 1340 | 略微 1341 | 白 1342 | 白白 1343 | 的 1344 | 的确 1345 | 的话 1346 | 皆可 1347 | 目前 1348 | 直到 1349 | 直接 1350 | 相似 1351 | 相信 1352 | 相反 1353 | 相同 1354 | 相对 1355 | 相对而言 1356 | 相应 1357 | 相当 1358 | 相等 1359 | 省得 1360 | 看 1361 | 看上去 1362 | 看出 1363 | 看到 1364 | 看来 1365 | 看样子 1366 | 看看 1367 | 看见 1368 | 看起来 1369 | 真是 1370 | 真正 1371 | 眨眼 1372 | 着 1373 | 着呢 1374 | 矣 1375 | 矣乎 1376 | 矣哉 1377 | 知道 1378 | 砰 1379 | 确定 1380 | 碰巧 1381 | 社会主义 1382 | 离 1383 | 种 1384 | 积极 1385 | 移动 1386 | 究竟 1387 | 穷年累月 1388 | 突出 1389 | 突然 1390 | 窃 1391 | 立 1392 | 立刻 1393 | 立即 1394 | 立地 1395 | 立时 1396 | 立马 1397 | 竟 1398 | 竟然 1399 | 竟而 1400 | 第 1401 | 第二 1402 | 等 1403 | 等到 1404 | 等等 1405 | 策略地 1406 | 简直 1407 | 简而言之 1408 | 简言之 1409 | 管 1410 | 类如 1411 | 粗 1412 | 精光 1413 | 紧接着 1414 | 累年 1415 | 累次 1416 | 纯 1417 | 纯粹 1418 | 纵 1419 | 纵令 1420 | 纵使 1421 | 纵然 1422 | 练习 1423 | 组成 1424 | 经 1425 | 经常 1426 | 经过 1427 | 结合 1428 | 结果 1429 | 给 1430 | 绝 1431 | 绝不 1432 | 绝对 1433 | 绝非 1434 | 绝顶 1435 | 继之 1436 | 继后 1437 | 继续 1438 | 继而 1439 | 维持 1440 | 综上所述 1441 | 缕缕 1442 | 罢了 1443 | 老 1444 | 老大 1445 | 老是 1446 | 老老实实 1447 | 考虑 1448 | 者 1449 | 而 1450 | 而且 1451 | 而况 1452 | 而又 1453 | 而后 1454 | 而外 1455 | 而已 1456 | 而是 1457 | 而言 1458 | 而论 1459 | 联系 1460 | 联袂 1461 | 背地里 1462 | 背靠背 1463 | 能 1464 | 能否 1465 | 能够 1466 | 腾 1467 | 自 1468 | 自个儿 1469 | 自从 1470 | 自各儿 1471 | 自后 1472 | 自家 1473 | 自己 1474 | 自打 1475 | 自身 1476 | 臭 1477 | 至 1478 | 至于 1479 | 至今 1480 | 至若 1481 | 致 1482 | 般的 1483 | 良好 1484 | 若 1485 | 若夫 1486 | 若是 1487 | 若果 1488 | 若非 1489 | 范围 1490 | 莫 1491 | 莫不 1492 | 莫不然 1493 | 莫如 1494 | 莫若 1495 | 莫非 1496 | 获得 1497 | 藉以 1498 | 虽 1499 | 虽则 1500 | 虽然 1501 | 虽说 1502 | 蛮 1503 | 行为 1504 | 行动 1505 | 表明 1506 | 表示 1507 | 被 1508 | 要 1509 | 要不 1510 | 要不是 1511 | 要不然 1512 | 要么 1513 | 要是 1514 | 要求 1515 | 见 1516 | 规定 1517 | 觉得 1518 | 譬喻 1519 | 譬如 1520 | 认为 1521 | 认真 1522 | 认识 1523 | 让 1524 | 许多 1525 | 论 1526 | 论说 1527 | 设使 1528 | 设或 1529 | 设若 1530 | 诚如 1531 | 诚然 1532 | 话说 1533 | 该 1534 | 该当 1535 | 说明 1536 | 说来 1537 | 说说 1538 | 请勿 1539 | 诸 1540 | 诸位 1541 | 诸如 1542 | 谁 1543 | 谁人 1544 | 谁料 1545 | 谁知 1546 | 谨 1547 | 豁然 1548 | 贼死 1549 | 赖以 1550 | 赶 1551 | 赶快 1552 | 赶早不赶晚 1553 | 起 1554 | 起先 1555 | 起初 1556 | 起头 1557 | 起来 1558 | 起见 1559 | 起首 1560 | 趁 1561 | 趁便 1562 | 趁势 1563 | 趁早 1564 | 趁机 1565 | 趁热 1566 | 趁着 1567 | 越是 1568 | 距 1569 | 跟 1570 | 路经 1571 | 转动 1572 | 转变 1573 | 转贴 1574 | 轰然 1575 | 较 1576 | 较为 1577 | 较之 1578 | 较比 1579 | 边 1580 | 达到 1581 | 达旦 1582 | 迄 1583 | 迅速 1584 | 过 1585 | 过于 1586 | 过去 1587 | 过来 1588 | 运用 1589 | 近 1590 | 近几年来 1591 | 近年来 1592 | 近来 1593 | 还 1594 | 还是 1595 | 还有 1596 | 还要 1597 | 这 1598 | 这一来 1599 | 这个 1600 | 这么 1601 | 这么些 1602 | 这么样 1603 | 这么点儿 1604 | 这些 1605 | 这会儿 1606 | 这儿 1607 | 这就是说 1608 | 这时 1609 | 这样 1610 | 这次 1611 | 这点 1612 | 这种 1613 | 这般 1614 | 这边 1615 | 这里 1616 | 这麽 1617 | 进入 1618 | 进去 1619 | 进来 1620 | 进步 1621 | 进而 1622 | 进行 1623 | 连 1624 | 连同 1625 | 连声 1626 | 连日 1627 | 连日来 1628 | 连袂 1629 | 连连 1630 | 迟早 1631 | 迫于 1632 | 适应 1633 | 适当 1634 | 适用 1635 | 逐步 1636 | 逐渐 1637 | 通常 1638 | 通过 1639 | 造成 1640 | 逢 1641 | 遇到 1642 | 遭到 1643 | 遵循 1644 | 遵照 1645 | 避免 1646 | 那 1647 | 那个 1648 | 那么 1649 | 那么些 1650 | 那么样 1651 | 那些 1652 | 那会儿 1653 | 那儿 1654 | 那时 1655 | 那末 1656 | 那样 1657 | 那般 1658 | 那边 1659 | 那里 1660 | 那麽 1661 | 部分 1662 | 都 1663 | 鄙人 1664 | 采取 1665 | 里面 1666 | 重大 1667 | 重新 1668 | 重要 1669 | 鉴于 1670 | 针对 1671 | 长期以来 1672 | 长此下去 1673 | 长线 1674 | 长话短说 1675 | 问题 1676 | 间或 1677 | 防止 1678 | 阿 1679 | 附近 1680 | 陈年 1681 | 限制 1682 | 陡然 1683 | 除 1684 | 除了 1685 | 除却 1686 | 除去 1687 | 除外 1688 | 除开 1689 | 除此 1690 | 除此之外 1691 | 除此以外 1692 | 除此而外 1693 | 除非 1694 | 随 1695 | 随后 1696 | 随时 1697 | 随着 1698 | 随著 1699 | 隔夜 1700 | 隔日 1701 | 难得 1702 | 难怪 1703 | 难说 1704 | 难道 1705 | 难道说 1706 | 集中 1707 | 零 1708 | 需要 1709 | 非但 1710 | 非常 1711 | 非徒 1712 | 非得 1713 | 非特 1714 | 非独 1715 | 靠 1716 | 顶多 1717 | 顷 1718 | 顷刻 1719 | 顷刻之间 1720 | 顷刻间 1721 | 顺 1722 | 顺着 1723 | 顿时 1724 | 颇 1725 | 风雨无阻 1726 | 饱 1727 | 首先 1728 | 马上 1729 | 高低 1730 | 高兴 1731 | 默然 1732 | 默默地 1733 | 齐 1734 | ︿ 1735 | ! 1736 | # 1737 | $ 1738 | % 1739 | & 1740 | ' 1741 | ( 1742 | ) 1743 | )÷(1- 1744 | )、 1745 | * 1746 | + 1747 | +ξ 1748 | ++ 1749 | , 1750 | ,也 1751 | - 1752 | -β 1753 | -- 1754 | -[*]- 1755 | . 1756 | / 1757 | 0 1758 | 0:2 1759 | 1 1760 | 1. 1761 | 12% 1762 | 2 1763 | 2.3% 1764 | 3 1765 | 4 1766 | 5 1767 | 5:0 1768 | 6 1769 | 7 1770 | 8 1771 | 9 1772 | : 1773 | ; 1774 | < 1775 | <± 1776 | <Δ 1777 | <λ 1778 | <φ 1779 | << 1780 | = 1781 | =″ 1782 | =☆ 1783 | =( 1784 | =- 1785 | =[ 1786 | ={ 1787 | > 1788 | >λ 1789 | ? 1790 | @ 1791 | A 1792 | LI 1793 | R.L. 1794 | ZXFITL 1795 | [ 1796 | [①①] 1797 | [①②] 1798 | [①③] 1799 | [①④] 1800 | [①⑤] 1801 | [①⑥] 1802 | [①⑦] 1803 | [①⑧] 1804 | [①⑨] 1805 | [①A] 1806 | [①B] 1807 | [①C] 1808 | [①D] 1809 | [①E] 1810 | [①] 1811 | [①a] 1812 | [①c] 1813 | [①d] 1814 | [①e] 1815 | [①f] 1816 | [①g] 1817 | [①h] 1818 | [①i] 1819 | [①o] 1820 | [② 1821 | [②①] 1822 | [②②] 1823 | [②③] 1824 | [②④ 1825 | [②⑤] 1826 | [②⑥] 1827 | [②⑦] 1828 | [②⑧] 1829 | [②⑩] 1830 | [②B] 1831 | [②G] 1832 | [②] 1833 | [②a] 1834 | [②b] 1835 | [②c] 1836 | [②d] 1837 | [②e] 1838 | [②f] 1839 | [②g] 1840 | [②h] 1841 | [②i] 1842 | [②j] 1843 | [③①] 1844 | [③⑩] 1845 | [③F] 1846 | [③] 1847 | [③a] 1848 | [③b] 1849 | [③c] 1850 | [③d] 1851 | [③e] 1852 | [③g] 1853 | [③h] 1854 | [④] 1855 | [④a] 1856 | [④b] 1857 | [④c] 1858 | [④d] 1859 | [④e] 1860 | [⑤] 1861 | [⑤]] 1862 | [⑤a] 1863 | [⑤b] 1864 | [⑤d] 1865 | [⑤e] 1866 | [⑤f] 1867 | [⑥] 1868 | [⑦] 1869 | [⑧] 1870 | [⑨] 1871 | [⑩] 1872 | [*] 1873 | [- 1874 | [] 1875 | ] 1876 | ]∧′=[ 1877 | ][ 1878 | _ 1879 | a] 1880 | b] 1881 | c] 1882 | e] 1883 | f] 1884 | ng昉 1885 | { 1886 | {- 1887 | | 1888 | } 1889 | }> 1890 | ~ 1891 | ~± 1892 | ~+ 1893 | ¥ 1894 | -------------------------------------------------------------------------------- /code/weixin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import itchat 3 | import time 4 | 5 | def demo(): 6 | # 可以保持一段时间登录状态,而不用每次运行代码都要扫码登录了 7 | itchat.auto_login(hotReload=True) 8 | itchat.dump_login_status() 9 | 10 | #遍历好友列表 11 | friends = itchat.get_friends(update=True)[:] 12 | 13 | for friend in friends: 14 | ''' 15 | 典型属性内容如下: 16 | {'MemberList': , 'VerifyFlag': 0, 'DisplayName': '', 'EncryChatRoomId': '', 'Alias': '', 17 | 'PYQuanPin': 'Evi1hui', 'PYInitial': 'EVI1HUI', 'RemarkName': '', 'AppAccountFlag': 0, 'City': '阳泉', 18 | 'ChatRoomId': 0, 'AttrStatus': 2147715109, 'UniFriend': 0, 'OwnerUin': 0, 'Statues': 0, 'StarFriend': 0, 19 | 'ContactFlag': 3, 20 | 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=654224580&username=@78606d673dcff895a468688273bb5e92862c15d9a0e8a574865785c8a0354660&skey=@crypt_cd4d9fd6_f8d94dff59afaff6b231f3f4b8aa1e15', 21 | 'Sex': 1, 'Uin': 0, 'HideInputBarFlag': 0, 'MemberCount': 0, 'Signature': '沉默的大多数', 22 | 'NickName': 'Evi1hui', 'RemarkPYQuanPin': '', 23 | 'UserName': '@78606d673dcff895a468688273bb5e92862c15d9a0e8a574865785c8a0354660', 24 | 'IsOwner': 0, 'RemarkPYInitial': '', 'KeyWord': 'wan', 'Province': '山西', 'SnsFlag': 177} 25 | ''' 26 | if friend['NickName'] == '兜哥的生活号': 27 | #print(friend['NickName']) 28 | print(friend) 29 | 30 | #发送的主键为UserName字段的值 31 | itchat.send_msg('Nice to meet you!','@1df002b437271f75f1afd07b937801b3a7211aad221e24115fb153674ca044b7') 32 | 33 | def sendall(msg): 34 | itchat.auto_login(hotReload=True) 35 | itchat.dump_login_status() 36 | 37 | # 遍历好友列表 38 | friends = itchat.get_friends(update=True)[:] 39 | 40 | for friend in friends: 41 | ''' 42 | 典型属性内容如下: 43 | {'MemberList': , 'VerifyFlag': 0, 'DisplayName': '', 'EncryChatRoomId': '', 'Alias': '', 44 | 'PYQuanPin': 'Evi1hui', 'PYInitial': 'EVI1HUI', 'RemarkName': '', 'AppAccountFlag': 0, 'City': '阳泉', 45 | 'ChatRoomId': 0, 'AttrStatus': 2147715109, 'UniFriend': 0, 'OwnerUin': 0, 'Statues': 0, 'StarFriend': 0, 46 | 'ContactFlag': 3, 47 | 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=654224580&username=@78606d673dcff895a468688273bb5e92862c15d9a0e8a574865785c8a0354660&skey=@crypt_cd4d9fd6_f8d94dff59afaff6b231f3f4b8aa1e15', 48 | 'Sex': 1, 'Uin': 0, 'HideInputBarFlag': 0, 'MemberCount': 0, 'Signature': '沉默的大多数', 49 | 'NickName': 'Evi1hui', 'RemarkPYQuanPin': '', 50 | 'UserName': '@78606d673dcff895a468688273bb5e92862c15d9a0e8a574865785c8a0354660', 51 | 'IsOwner': 0, 'RemarkPYInitial': '', 'KeyWord': 'wan', 'Province': '山西', 'SnsFlag': 177} 52 | ''' 53 | UserName=friend['UserName'] 54 | NickName=friend['NickName'] 55 | City=friend['City'] 56 | 57 | #print("UserName:%s NickName:%s City:%s" % (UserName,NickName,City)) 58 | # 发送的主键为UserName字段的值 59 | text="hi %s,%s" %(NickName,msg) 60 | 61 | # 休眠10秒 62 | time.sleep(10) 63 | print(text) 64 | itchat.send_msg(text, UserName) 65 | 66 | 67 | if __name__ == '__main__': 68 | #demo() 69 | sendall('今天高温,记得防暑降温喔:)') 70 | -------------------------------------------------------------------------------- /code/yelp-cnn-model-mlp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp-cnn-model-mlp.png -------------------------------------------------------------------------------- /code/yelp-cnn-model-textcnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp-cnn-model-textcnn.png -------------------------------------------------------------------------------- /code/yelp-cnn-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp-cnn-model.png -------------------------------------------------------------------------------- /code/yelp-mlp-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp-mlp-model.png -------------------------------------------------------------------------------- /code/yelp.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import pandas as pd 3 | import numpy as np 4 | import sys 5 | 6 | #处理编码问题 7 | reload(sys) 8 | sys.setdefaultencoding('utf-8') 9 | 10 | from sklearn.model_selection import cross_val_score 11 | from sklearn.model_selection import train_test_split 12 | 13 | from sklearn.neural_network import MLPClassifier 14 | from sklearn import feature_extraction 15 | from sklearn.feature_extraction.text import TfidfTransformer 16 | from sklearn.feature_extraction.text import CountVectorizer 17 | 18 | from sklearn.preprocessing import LabelEncoder 19 | from keras.utils import np_utils 20 | 21 | from keras.models import Sequential 22 | from keras.layers import Dense, Dropout 23 | from keras.wrappers.scikit_learn import KerasClassifier 24 | from sklearn.model_selection import KFold,StratifiedKFold 25 | 26 | from keras import metrics 27 | 28 | from sklearn.svm import SVC 29 | 30 | from keras.layers import Embedding, LSTM 31 | 32 | import keras.preprocessing.text as T 33 | from keras.preprocessing.text import Tokenizer 34 | from keras.preprocessing.sequence import pad_sequences 35 | 36 | from nltk.corpus import stopwords 37 | from fastText import train_supervised 38 | from nltk.tokenize import sent_tokenize,word_tokenize 39 | from nltk.corpus import wordnet 40 | import enchant 41 | 42 | from keras.layers import Conv1D, GlobalMaxPooling1D 43 | 44 | 45 | 46 | 47 | 48 | #兼容在没有显示器的GPU服务器上运行该代码 49 | import matplotlib 50 | matplotlib.use('Agg') 51 | 52 | 53 | import matplotlib.pyplot as plt 54 | from keras.utils import plot_model 55 | 56 | 57 | 58 | #yelp评论文件路径 已经使用https://github.com/Yelp/dataset-examples处理成CSV格式 59 | #yelp_file="/Volumes/maidou/dataset/yelp/dataset/review.csv" 60 | 61 | yelp_file="/mnt/nlp/dataset/review.csv" 62 | 63 | 64 | #词袋模型的最大特征束 65 | max_features=5000 66 | 67 | 68 | def load_reviews(filename,nrows): 69 | #CSV格式表头内容: 70 | # funny,user_id,review_id,text,business_id,stars,date,useful,cool 71 | text=[] 72 | stars=[] 73 | 74 | #https://www.cnblogs.com/datablog/p/6127000.html 75 | #sep : str, default ‘,’指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘\s+’,将使用python的语法分析器。 76 | # 并且忽略数据中的逗号。正则表达式例子:'\r\t' 77 | #header: int or list of ints, default ‘infer’ 78 | # 指定行数用来作为列名,数据开始行数。如果文件中没有列名,设置为None。设置为0则认为第0行是列名 79 | #nrows : int, default None 需要读取的行数(从文件头开始算起)。 80 | #skiprows : list-like or integer, default None 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。 81 | #skip_blank_lines : boolean, default True如果为True,则跳过空行;否则记为NaN。 82 | 83 | ### 84 | #开发阶段读取前10000行 使用encoding='utf-8'参数非常重要 85 | df = pd.read_csv(filename,sep=',',header=0,encoding='utf-8',nrows=nrows) 86 | print df.head() 87 | 88 | #按照列名直接获取数据 把 list转换成list对象 89 | text=list(df['text']) 90 | stars=list(df['stars']) 91 | 92 | #显示各个评分的个数 93 | print df.describe() 94 | 95 | #绘图 96 | plt.figure() 97 | count_classes=pd.value_counts(df['stars'],sort=True) 98 | 99 | print "各个star的总数:" 100 | print count_classes 101 | count_classes.plot(kind='bar',rot=0) 102 | plt.xlabel('stars') 103 | plt.ylabel('stars counts') 104 | #plt.show() 105 | plt.savefig("yelp_stars.png") 106 | 107 | 108 | return text,stars 109 | 110 | #实用SVM进行文档分类 111 | def do_svm(text,stars): 112 | # 切割词袋 删除英文停用词 113 | #vectorizer = CountVectorizer(ngram_range=(2, 2), max_features=max_features,stop_words='english',lowercase=True) 114 | vectorizer = CountVectorizer(ngram_range=(1, 1), max_features=max_features, stop_words='english', lowercase=True) 115 | #vectorizer = CountVectorizer(ngram_range=(1, 1), max_features=5000, stop_words=None, lowercase=True) 116 | 117 | print "vectorizer 参数:" 118 | print vectorizer 119 | # 该类会统计每个词语的tf-idf权值 120 | transformer = TfidfTransformer() 121 | # 使用2-gram和TFIDF处理 122 | x = transformer.fit_transform(vectorizer.fit_transform(text)) 123 | #x = vectorizer.fit_transform(text) 124 | 125 | #二分类 标签直接实用stars 126 | y=stars 127 | 128 | clf = SVC() 129 | 130 | # 使用5折交叉验证 131 | scores = cross_val_score(clf, x, y, cv=5, scoring='f1_micro') 132 | # print scores 133 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 134 | 135 | #使用keras的MLP 136 | def do_keras_mlp(text,stars): 137 | # 切割词袋 删除英文停用词 138 | #vectorizer = CountVectorizer(ngram_range=(2, 2), max_features=max_features,stop_words='english',lowercase=True) 139 | vectorizer = CountVectorizer(ngram_range=(1, 1), max_features=max_features, stop_words='english', lowercase=True) 140 | #vectorizer = CountVectorizer(ngram_range=(1, 1), max_features=5000, stop_words=None, lowercase=True) 141 | 142 | print "vectorizer 参数:" 143 | print vectorizer 144 | # 该类会统计每个词语的tf-idf权值 145 | transformer = TfidfTransformer() 146 | # 使用2-gram和TFIDF处理 147 | #x = transformer.fit_transform(vectorizer.fit_transform(text)) 148 | x = vectorizer.fit_transform(text) 149 | 150 | #我们可以使用从scikit-learn LabelEncoder类。 151 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 152 | encoder=LabelEncoder() 153 | encoder.fit(stars) 154 | encoded_y = encoder.transform(stars) 155 | 156 | #构造神经网络 157 | def baseline_model(): 158 | model = Sequential() 159 | model.add(Dense(5, input_dim=max_features, activation='relu')) 160 | model.add(Dropout(0.2)) 161 | model.add(Dense(2, activation='softmax')) 162 | # Compile model 163 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) 164 | 165 | #可视化 166 | #plot_model(model, to_file='yelp-mlp-model.png',show_shapes=True) 167 | 168 | #model.summary() 169 | 170 | return model 171 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 172 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 173 | # 最新接口指定训练的次数为epochs 174 | clf = KerasClassifier(build_fn=baseline_model, epochs=20, batch_size=128, verbose=1) 175 | 176 | #使用5折交叉验证 177 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 178 | # print scores 179 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 180 | 181 | #scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='accuracy') 182 | # print scores 183 | #print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 184 | 185 | 186 | #使用keras的LSTM 187 | def do_keras_lstm(text,stars): 188 | 189 | #转换成词袋序列 190 | max_document_length=200 191 | 192 | #删除通用词 193 | text_cleaned=[] 194 | 195 | list_stopWords = list(set(stopwords.words('english'))) 196 | english_punctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '&', '!', '*', '@', '#', '$', '%'] 197 | d = enchant.Dict("en_US") 198 | 199 | for line in text: 200 | 201 | # 分词 202 | list_words = word_tokenize(line.lower()) 203 | # 去掉标点符号 204 | list_words = [word for word in list_words if word not in english_punctuations] 205 | # 实用wordnet删除非常见英文单词 206 | #list_words = [word for word in list_words if wordnet.synsets(word) ] 207 | list_words = [word for word in list_words if d.check(word)] 208 | # 过滤停止词 209 | filtered_words = [w for w in list_words if not w in list_stopWords] 210 | text_cleaned.append( " ".join(filtered_words) ) 211 | 212 | 213 | text=text_cleaned 214 | 215 | #设置分词最大个数 即词袋的单词个数 216 | tokenizer = Tokenizer(num_words=max_features,lower=True) 217 | tokenizer.fit_on_texts(text) 218 | sequences = tokenizer.texts_to_sequences(text) 219 | 220 | x=pad_sequences(sequences, maxlen=max_document_length) 221 | 222 | 223 | #我们可以使用从scikit-learn LabelEncoder类。 224 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 225 | encoder=LabelEncoder() 226 | encoder.fit(stars) 227 | encoded_y = encoder.transform(stars) 228 | 229 | 230 | 231 | #构造神经网络 232 | def baseline_model(): 233 | model = Sequential() 234 | model.add(Embedding(max_features, 128)) 235 | model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2)) 236 | model.add(Dense(2, activation='softmax')) 237 | 238 | # try using different optimizers and different optimizer configs 239 | model.compile(loss='categorical_crossentropy', 240 | optimizer='adam', 241 | metrics=['accuracy']) 242 | 243 | #可视化 244 | plot_model(model, to_file='yelp-lstm-model.png',show_shapes=True) 245 | 246 | model.summary() 247 | 248 | return model 249 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 250 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 251 | # 最新接口指定训练的次数为epochs 252 | clf = KerasClassifier(build_fn=baseline_model, epochs=20, batch_size=128, verbose=0) 253 | 254 | #使用5折交叉验证 255 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 256 | # print scores 257 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 258 | 259 | #scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='accuracy') 260 | # print scores 261 | #print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 262 | 263 | 264 | def dump_file(x,y,filename): 265 | with open(filename, 'w') as f: 266 | for i,v in enumerate(x): 267 | line="%s __label__%d\n" % (v,y[i]) 268 | f.write(line) 269 | f.close() 270 | 271 | def do_fasttext(text,stars): 272 | #删除通用词 273 | text_cleaned=[] 274 | 275 | list_stopWords = list(set(stopwords.words('english'))) 276 | english_punctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '&', '!', '*', '@', '#', '$', '%'] 277 | d = enchant.Dict("en_US") 278 | 279 | for line in text: 280 | 281 | # 分词 282 | list_words = word_tokenize(line.lower()) 283 | # 去掉标点符号 284 | list_words = [word for word in list_words if word not in english_punctuations] 285 | # 实用wordnet删除非常见英文单词 286 | #list_words = [word for word in list_words if wordnet.synsets(word) ] 287 | list_words = [word for word in list_words if d.check(word)] 288 | # 过滤停止词 289 | filtered_words = [w for w in list_words if not w in list_stopWords] 290 | text_cleaned.append( " ".join(filtered_words) ) 291 | 292 | # 分割训练集和测试集 测试集占20% 293 | #x_train, x_test, y_train, y_test = train_test_split(text, stars, test_size=0.2) 294 | x_train, x_test, y_train, y_test = train_test_split(text_cleaned, stars, test_size=0.2) 295 | 296 | # 按照fasttest的要求生成训练数据和测试数据 297 | dump_file(x_train, y_train, "yelp_train.txt") 298 | dump_file(x_test, y_test, "yelp_test.txt") 299 | 300 | model = train_supervised( 301 | input="yelp_train.txt", epoch=20, lr=0.6, wordNgrams=2, verbose=2, minCount=1 302 | ) 303 | 304 | def print_results(N, p, r): 305 | print("N\t" + str(N)) 306 | print("P@{}\t{:.3f}".format(1, p)) 307 | print("R@{}\t{:.3f}".format(1, r)) 308 | 309 | print_results(*model.test("yelp_test.txt")) 310 | 311 | 312 | #使用keras的cnn 313 | def do_keras_cnn(text,stars): 314 | 315 | #转换成词袋序列 316 | max_document_length=200 317 | 318 | #删除通用词 319 | text_cleaned=[] 320 | 321 | list_stopWords = list(set(stopwords.words('english'))) 322 | english_punctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '&', '!', '*', '@', '#', '$', '%'] 323 | d = enchant.Dict("en_US") 324 | 325 | #for line in text: 326 | 327 | # 分词 328 | # list_words = word_tokenize(line.lower()) 329 | # 去掉标点符号 330 | # list_words = [word for word in list_words if word not in english_punctuations] 331 | # 实用wordnet删除非常见英文单词 332 | #list_words = [word for word in list_words if wordnet.synsets(word) ] 333 | # list_words = [word for word in list_words if d.check(word)] 334 | # 过滤停止词 335 | # filtered_words = [w for w in list_words if not w in list_stopWords] 336 | # text_cleaned.append( " ".join(filtered_words) ) 337 | 338 | 339 | #text=text_cleaned 340 | 341 | #设置分词最大个数 即词袋的单词个数 342 | tokenizer = Tokenizer(num_words=max_features,lower=True) 343 | tokenizer.fit_on_texts(text) 344 | sequences = tokenizer.texts_to_sequences(text) 345 | 346 | x=pad_sequences(sequences, maxlen=max_document_length) 347 | 348 | 349 | #我们可以使用从scikit-learn LabelEncoder类。 350 | # 这个类通过 fit() 函数获取整个数据集模型所需的编码,然后使用transform()函数应用编码来创建一个新的输出变量。 351 | encoder=LabelEncoder() 352 | encoder.fit(stars) 353 | encoded_y = encoder.transform(stars) 354 | 355 | 356 | 357 | #构造神经网络 358 | def baseline_model(): 359 | 360 | #CNN参数 361 | embedding_dims = 50 362 | filters = 250 363 | kernel_size = 3 364 | hidden_dims = 250 365 | 366 | model = Sequential() 367 | model.add(Embedding(max_features, embedding_dims)) 368 | 369 | model.add(Conv1D(filters, 370 | kernel_size, 371 | padding='valid', 372 | activation='relu', 373 | strides=1)) 374 | #池化 375 | model.add(GlobalMaxPooling1D()) 376 | 377 | model.add(Dense(2, activation='softmax')) 378 | 379 | model.compile(loss='categorical_crossentropy', 380 | optimizer='adam', 381 | metrics=['accuracy']) 382 | 383 | #可视化 384 | plot_model(model, to_file='yelp-cnn-model.png',show_shapes=True) 385 | 386 | model.summary() 387 | 388 | return model 389 | #在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。 390 | # 它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 391 | # 最新接口指定训练的次数为epochs 392 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=128, verbose=1) 393 | 394 | #使用5折交叉验证 395 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 396 | # print scores 397 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 398 | 399 | #scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='accuracy') 400 | # print scores 401 | #print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 402 | 403 | 404 | if __name__ == '__main__': 405 | 406 | 407 | text,stars=load_reviews(yelp_file,100000) 408 | 409 | stars=[ 0 if star < 3 else 1 for star in stars ] 410 | 411 | print "情感分类的总数:" 412 | count_classes = pd.value_counts(stars, sort=True) 413 | print count_classes 414 | count_classes.plot(kind='bar',rot=0) 415 | plt.xlabel('sentiment ') 416 | plt.ylabel('sentiment counts') 417 | #plt.show() 418 | plt.savefig("yelp_sentiment_stars.png") 419 | 420 | 421 | #使用MLP文档分类 422 | #do_keras_mlp(text,stars) 423 | #使用lstm文档分类 424 | do_keras_lstm(text,stars) 425 | #使用SVM文档分类 426 | #do_svm(text,stars) 427 | #使用fasttext文档分类 428 | #do_fasttext(text,stars) 429 | 430 | #使用cnn文档分类 431 | #do_keras_cnn(text,stars) -------------------------------------------------------------------------------- /code/yelp_sentiment_stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp_sentiment_stars.png -------------------------------------------------------------------------------- /code/yelp_stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/yelp_stars.png -------------------------------------------------------------------------------- /code/预测yelp评分-图6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/code/预测yelp评分-图6.png -------------------------------------------------------------------------------- /paper/Convolutional Neural Networks for Sentence Classification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/paper/Convolutional Neural Networks for Sentence Classification.pdf -------------------------------------------------------------------------------- /picture/NLP之Word2Vec模型和Doc2Vec模型-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/NLP之Word2Vec模型和Doc2Vec模型-图2.png -------------------------------------------------------------------------------- /picture/Word2Vec模型和Doc2Vec模型-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/Word2Vec模型和Doc2Vec模型-图1.png -------------------------------------------------------------------------------- /picture/使用CNN进行文档分类-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用CNN进行文档分类-图1.png -------------------------------------------------------------------------------- /picture/使用CNN进行文档分类-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用CNN进行文档分类-图2.png -------------------------------------------------------------------------------- /picture/使用CNN进行文档分类-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用CNN进行文档分类-图3.png -------------------------------------------------------------------------------- /picture/使用CNN进行文档分类-图4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用CNN进行文档分类-图4.png -------------------------------------------------------------------------------- /picture/使用LDA进行文档主题建模-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用LDA进行文档主题建模-图1.png -------------------------------------------------------------------------------- /picture/使用LDA进行文档主题建模-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用LDA进行文档主题建模-图2.png -------------------------------------------------------------------------------- /picture/使用LDA进行文档主题建模-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用LDA进行文档主题建模-图3.png -------------------------------------------------------------------------------- /picture/使用LDA进行文档主题建模-图4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用LDA进行文档主题建模-图4.png -------------------------------------------------------------------------------- /picture/使用TextRank和TFIDF进行文档关键字自动提取-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用TextRank和TFIDF进行文档关键字自动提取-图1.png -------------------------------------------------------------------------------- /picture/使用TextRank和TFIDF进行文档关键字自动提取-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用TextRank和TFIDF进行文档关键字自动提取-图2.png -------------------------------------------------------------------------------- /picture/使用fasttext进行文档分类-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用fasttext进行文档分类-图1.png -------------------------------------------------------------------------------- /picture/使用fasttext进行文档分类-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用fasttext进行文档分类-图2.png -------------------------------------------------------------------------------- /picture/使用fasttext进行文档分类-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用fasttext进行文档分类-图3.png -------------------------------------------------------------------------------- /picture/使用多层感知机进行文档分类-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用多层感知机进行文档分类-图1.png -------------------------------------------------------------------------------- /picture/使用多层感知机进行文档分类-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用多层感知机进行文档分类-图2.png -------------------------------------------------------------------------------- /picture/使用多层感知机进行文档分类-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用多层感知机进行文档分类-图3.png -------------------------------------------------------------------------------- /picture/使用多层感知机进行文档分类-图4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用多层感知机进行文档分类-图4.png -------------------------------------------------------------------------------- /picture/使用多层感知机进行文档分类-图5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用多层感知机进行文档分类-图5.png -------------------------------------------------------------------------------- /picture/使用搜狗新闻预料生成word2vec-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/使用搜狗新闻预料生成word2vec-图1.png -------------------------------------------------------------------------------- /picture/如何衡量机器学习分类器模型-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/如何衡量机器学习分类器模型-图1.png -------------------------------------------------------------------------------- /picture/如何衡量机器学习分类器模型-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/如何衡量机器学习分类器模型-图2.png -------------------------------------------------------------------------------- /picture/常用数据集-图4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/常用数据集-图4.png -------------------------------------------------------------------------------- /picture/常用数据集简介-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/常用数据集简介-图1.png -------------------------------------------------------------------------------- /picture/常用数据集简介-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/常用数据集简介-图3.png -------------------------------------------------------------------------------- /picture/常用数据集简介-图5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/常用数据集简介-图5.png -------------------------------------------------------------------------------- /picture/常用数据集简介-图8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/常用数据集简介-图8.png -------------------------------------------------------------------------------- /picture/文档相似度-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/文档相似度-图1.png -------------------------------------------------------------------------------- /picture/文档相似度-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/文档相似度-图2.png -------------------------------------------------------------------------------- /picture/文档相似度-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/文档相似度-图3.png -------------------------------------------------------------------------------- /picture/词袋模型和TFIDF模型-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/词袋模型和TFIDF模型-图1.png -------------------------------------------------------------------------------- /picture/预测Yelp美食评分-图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/预测Yelp美食评分-图1.png -------------------------------------------------------------------------------- /picture/预测Yelp美食评分-图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/预测Yelp美食评分-图2.png -------------------------------------------------------------------------------- /picture/预测Yelp美食评分-图3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/预测Yelp美食评分-图3.png -------------------------------------------------------------------------------- /picture/预测Yelp美食评分-图4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/预测Yelp美食评分-图4.png -------------------------------------------------------------------------------- /picture/预测Yelp美食评分-图6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/picture/预测Yelp美食评分-图6.png -------------------------------------------------------------------------------- /人家对话.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/人家对话.png -------------------------------------------------------------------------------- /使用CNN进行文档分类.md: -------------------------------------------------------------------------------- 1 | # 数据集 2 | Yelp Reviews是Yelp为了学习目的而发布的一个开源数据集。它包含了由数百万用户评论,商业属性和来自多个大都市地区的超过20万张照片。这是一个常用的全球NLP挑战数据集,包含5,200,000条评论,174,000条商业属性。 数据集下载地址为: 3 | 4 | https://www.yelp.com/dataset/download 5 | 6 | Yelp Reviews格式分为JSON和SQL两种,以JSON格式为例,其中最重要的review.json,包含评论数据。Yelp Reviews数据量巨大,非常适合验证CNN模型。 7 | 8 | # 特征提取 9 | 10 | 特征提取使用词袋序列模型和词向量。 11 | ## 词袋序列模型 12 | 词袋序列模型是在词袋模型的基础上发展而来的,相对于词袋模型,词袋序列模型可以反映出单词在句子中的前后关系。keras中通过Tokenizer类实现了词袋序列模型,这个类用来对文本中的词进行统计计数,生成文档词典,以支持基于词典位序生成文本的向量表示,创建该类时,需要设置词典的最大值。 13 | 14 | tokenizer = Tokenizer(num_words=None) 15 | 16 | Tokenizer类的示例代码如下: 17 | 18 | from keras.preprocessing.text import Tokenizer 19 | 20 | text1='some thing to eat' 21 | text2='some thing to drink' 22 | texts=[text1,text2] 23 | 24 | tokenizer = Tokenizer(num_words=None) 25 | #num_words:None或整数,处理的最大单词数量。少于此数的单词丢掉 26 | tokenizer.fit_on_texts(texts) 27 | 28 | # num_words=多少会影响下面的结果,行数=num_words 29 | print( tokenizer.texts_to_sequences(texts)) 30 | #得到词索引[[1, 2, 3, 4], [1, 2, 3, 5]] 31 | print( tokenizer.texts_to_matrix(texts)) 32 | # 矩阵化=one_hot 33 | [[ 0., 1., 1., 1., 1., 0., 0., 0., 0., 0.], 34 | [ 0., 1., 1., 1., 0., 1., 0., 0., 0., 0.]] 35 | 36 | 在处理Yelp数据集时,把每条评论看成一个词袋序列,且长度固定。超过固定长度的截断,不足的使用0补齐。 37 | 38 | #转换成词袋序列,max_document_length为序列的最大长度 39 | max_document_length=200 40 | 41 | #设置分词最大个数 即词袋的单词个数 42 | tokenizer = Tokenizer(num_words=max_features) 43 | tokenizer.fit_on_texts(text) 44 | sequences = tokenizer.texts_to_sequences(text) 45 | #截断补齐 46 | x=pad_sequences(sequences, maxlen=max_document_length) 47 | 48 | ## 词向量模型 49 | 词向量模型常见实现形式有word2Vec,fastText和GloVe,本章使用最基础的word2Vec,基于gensim库实现。为了提高性能,使用了预训练好的词向量,即使用Google News dataset数据集训练出的词向量。加载方式为: 50 | 51 | model = 52 | gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin',binary=True) 53 | 54 | GloVe预训练好的词向量可以从下列地址下载: 55 | 56 | http://nlp.stanford.edu/projects/glove/ 57 | 58 | 59 | # 衡量指标 60 | 使用5折交叉验证,并且考核F1的值,训练轮数为10轮,批处理大小为128。 61 | 62 | clf = KerasClassifier(build_fn=baseline_model, epochs=10, batch_size=128, verbose=0) 63 | 64 | #使用5折交叉验证 65 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 66 | # print scores 67 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 68 | 69 | # 单层CNN模型 70 | 我们尝试使用单层CNN结构来处理Yelp的分类问题。首先通过一个Embedding层,降维成为50位的向量,然后使用一个核数为250,步长为1的一维CNN层进行处理,接着连接一个池化层。为了防止过拟合,CNN层和全连接层之间随机丢失20%的数据进行训练。需要特别指出的是,Embedding层相当于是临时进行了词向量的计算,把原始的词序列转换成了指定维数的词向量序列。 71 | 72 | #CNN参数 73 | embedding_dims = 50 74 | filters = 250 75 | kernel_size = 3 76 | hidden_dims = 250 77 | 78 | model = Sequential() 79 | model.add(Embedding(max_features, embedding_dims)) 80 | 81 | model.add(Conv1D(filters, 82 | kernel_size, 83 | padding='valid', 84 | activation='relu', 85 | strides=1)) 86 | #池化 87 | model.add(GlobalMaxPooling1D()) 88 | 89 | model.add(Dense(2, activation='softmax')) 90 | 91 | model.compile(loss='categorical_crossentropy', 92 | optimizer='adam', 93 | metrics=['accuracy']) 94 | 95 | 损失函数使用交叉熵categorical_crossentropy,优化算法使用adsm,可视化结果如下。 96 | 97 | ![使用CNN进行文档分类-图1.png](picture/使用CNN进行文档分类-图1.png) 98 | 99 | 打印CNN的结构。 100 | 101 | model.summary() 102 | 103 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 104 | 105 | _________________________________________________________________ 106 | Layer (type) Output Shape Param # 107 | ================================================================= 108 | embedding_1 (Embedding) (None, None, 50) 250000 109 | _________________________________________________________________ 110 | conv1d_1 (Conv1D) (None, None, 250) 37750 111 | _________________________________________________________________ 112 | global_max_pooling1d_1 (Glob (None, 250) 0 113 | _________________________________________________________________ 114 | dense_1 (Dense) (None, 2) 502 115 | ================================================================= 116 | Total params: 288,252 117 | Trainable params: 288,252 118 | Non-trainable params: 0 119 | _________________________________________________________________ 120 | 121 | 当特征提取使用词袋序列,特征数取5000的前提下,结果如下。 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
数据量F1值
1w0.86
10w0.92
137 | 138 | # 单层CNN+MLP模型 139 | 在单层CNN的基础上增加一个隐藏层,便于更好的分析CNN层提取的高级特征,该隐藏层结点数为250。 140 | 141 | #CNN参数 142 | embedding_dims = 50 143 | filters = 250 144 | kernel_size = 3 145 | hidden_dims = 250 146 | 147 | model = Sequential() 148 | model.add(Embedding(max_features, embedding_dims)) 149 | 150 | model.add(Conv1D(filters, 151 | kernel_size, 152 | padding='valid', 153 | activation='relu', 154 | strides=1)) 155 | #池化 156 | model.add(GlobalMaxPooling1D()) 157 | 158 | 159 | #增加一个隐藏层 160 | model.add(Dense(hidden_dims)) 161 | model.add(Dropout(0.2)) 162 | model.add(Activation('relu')) 163 | 164 | #输出层 165 | 166 | model.add(Dense(2, activation='softmax')) 167 | 168 | model.compile(loss='categorical_crossentropy', 169 | optimizer='adam', 170 | metrics=['accuracy']) 171 | 172 | 可视化结果如下。 173 | 174 | ![使用CNN进行文档分类-图2.png](picture/使用CNN进行文档分类-图2.png) 175 | 176 | 打印CNN的结构。 177 | 178 | model.summary() 179 | 180 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 181 | 182 | _________________________________________________________________ 183 | Layer (type) Output Shape Param # 184 | ================================================================= 185 | embedding_1 (Embedding) (None, None, 50) 250000 186 | _________________________________________________________________ 187 | conv1d_1 (Conv1D) (None, None, 250) 37750 188 | _________________________________________________________________ 189 | global_max_pooling1d_1 (Glob (None, 250) 0 190 | _________________________________________________________________ 191 | dense_1 (Dense) (None, 250) 62750 192 | _________________________________________________________________ 193 | dropout_1 (Dropout) (None, 250) 0 194 | _________________________________________________________________ 195 | activation_1 (Activation) (None, 250) 0 196 | _________________________________________________________________ 197 | dense_2 (Dense) (None, 2) 502 198 | ================================================================= 199 | Total params: 351,002 200 | Trainable params: 351,002 201 | Non-trainable params: 0 202 | _________________________________________________________________ 203 | 204 | 当特征提取使用词袋序列,特征数取5000的前提下,结果如下,可以增加数据量可以提升性能。在数据量相同的情况下,比单层CNN效果略好。 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 |
数据量F1值
1w0.87
10w0.93
220 | 221 | # TextCNN 222 | TextCNN是利用卷积神经网络对文本进行分类的算法,由Yoon Kim中提出,本质上分别使用大小为3,4和5的一维卷积处理文本数据。这里的文本数据可以是定长的词袋序列模型,也可以使用词向量。 223 | 224 | ![使用CNN进行文档分类-图3](picture/使用CNN进行文档分类-图3.png) 225 | 226 | TextCNN的一种实现方式,就是分别使用大小为3,4和5的一维卷积处理输入,然后使用MaxPooling1D进行池化处理,并将处理的结果使用Flatten层压平并展开。把三个卷积层的结果合并,作为下一个隐藏层的输入,为了防止过拟合,丢失50%的数据进行训练。 227 | 228 | 229 | #CNN参数 230 | embedding_dims = 50 231 | filters = 100 232 | 233 | # Inputs 234 | input = Input(shape=[max_document_length]) 235 | 236 | # Embeddings layers 237 | x = Embedding(max_features, embedding_dims)(input) 238 | 239 | # conv layers 240 | convs = [] 241 | for filter_size in [3,4,5]: 242 | l_conv = Conv1D(filters=filters, kernel_size=filter_size, activation='relu')(x) 243 | l_pool = MaxPooling1D()(l_conv) 244 | l_pool = Flatten()(l_pool) 245 | convs.append(l_pool) 246 | 247 | merge = concatenate(convs, axis=1) 248 | 249 | out = Dropout(0.5)(merge) 250 | 251 | output = Dense(32, activation='relu')(out) 252 | 253 | output = Dense(units=2, activation='softmax')(output) 254 | 255 | #输出层 256 | model = Model([input], output) 257 | 258 | model.compile(loss='categorical_crossentropy', 259 | optimizer='adam', 260 | metrics=['accuracy']) 261 | 可视化结果如下。 262 | 263 | ![使用CNN进行文档分类-图4](picture/使用CNN进行文档分类-图4.png) 264 | 265 | 打印CNN的结构。 266 | 267 | model.summary() 268 | 269 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 270 | 271 | __________________________________________________________________________________________________ 272 | Layer (type) Output Shape Param # Connected to 273 | ================================================================================================== 274 | input_1 (InputLayer) (None, 200) 0 275 | __________________________________________________________________________________________________ 276 | embedding_1 (Embedding) (None, 200, 50) 250000 input_1[0][0] 277 | __________________________________________________________________________________________________ 278 | conv1d_1 (Conv1D) (None, 198, 100) 15100 embedding_1[0][0] 279 | __________________________________________________________________________________________________ 280 | conv1d_2 (Conv1D) (None, 197, 100) 20100 embedding_1[0][0] 281 | __________________________________________________________________________________________________ 282 | conv1d_3 (Conv1D) (None, 196, 100) 25100 embedding_1[0][0] 283 | __________________________________________________________________________________________________ 284 | max_pooling1d_1 (MaxPooling1D) (None, 99, 100) 0 conv1d_1[0][0] 285 | __________________________________________________________________________________________________ 286 | max_pooling1d_2 (MaxPooling1D) (None, 98, 100) 0 conv1d_2[0][0] 287 | __________________________________________________________________________________________________ 288 | max_pooling1d_3 (MaxPooling1D) (None, 98, 100) 0 conv1d_3[0][0] 289 | __________________________________________________________________________________________________ 290 | flatten_1 (Flatten) (None, 9900) 0 max_pooling1d_1[0][0] 291 | __________________________________________________________________________________________________ 292 | flatten_2 (Flatten) (None, 9800) 0 max_pooling1d_2[0][0] 293 | __________________________________________________________________________________________________ 294 | flatten_3 (Flatten) (None, 9800) 0 max_pooling1d_3[0][0] 295 | __________________________________________________________________________________________________ 296 | concatenate_1 (Concatenate) (None, 29500) 0 flatten_1[0][0] 297 | flatten_2[0][0] 298 | flatten_3[0][0] 299 | __________________________________________________________________________________________________ 300 | dropout_1 (Dropout) (None, 29500) 0 concatenate_1[0][0] 301 | __________________________________________________________________________________________________ 302 | dense_1 (Dense) (None, 32) 944032 dropout_1[0][0] 303 | __________________________________________________________________________________________________ 304 | dense_2 (Dense) (None, 2) 66 dense_1[0][0] 305 | ================================================================================================== 306 | Total params: 1,254,398 307 | Trainable params: 1,254,398 308 | Non-trainable params: 0 309 | __________________________________________________________________________________________________ 310 | 311 | 当特征提取使用词袋序列,特征数取5000的前提下,结果如下,可以看出增加数据量可以提升性能。在数据量相同的情况下,比单层CNN效果略好,与CNN+MLP效果相当。 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 |
数据量F1值
1w0.88
10w0.92
327 | 328 | # TextCNN变种 329 | - CNN-rand:设计好 embedding_size 这个超参数后,对不同单词的向量作随机初始化, 后续BP的时候作调整. 330 | - static:拿 pre-trained vectors from word2vec,FastText or GloVe 直接用, 训练过程中不再调整词向量. 这也算是迁移学习的一种思想. 331 | - non-static:pre-trained vectors + fine tuning,即拿word2vec训练好的词向量初始化, 训练过程中再对它们微调. 332 | - multiple channel:类比于图像中的RGB通道, 这里也可以用static与non-static 搭两个通道来搞. 333 | 334 | ## static版本TextCNN 335 | 可以通过使用预先训练的词向量,训练过程中不再调整词向量。最简单的一种实现方式就是使用Word2Vec训练好的词向量。在gensim库中,在Google News dataset数据集训练出的词向量。 336 | 337 | model = KeyedVectors.load_word2vec_format(word2vec_file, binary=True) 338 | print model['word'].shape 339 | 340 | 通过打印某个单词的词向量形状获取预先训练的词向量的维数,本例中为300。设置需要处理的单词的最大个数max_features,然后获取对应单词序列。 341 | 342 | #设置分词最大个数 即词袋的单词个数 343 | tokenizer = Tokenizer(num_words=max_features,lower=True) 344 | tokenizer.fit_on_texts(text) 345 | sequences = tokenizer.texts_to_sequences(text) 346 | 347 | x=pad_sequences(sequences, maxlen=max_document_length) 348 | 349 | 通过tokenizer对象获取word到对应数字编号的映射关系表。 350 | 351 | #获取word到对应数字编号的映射关系 352 | word_index = tokenizer.word_index 353 | print('Found %s unique tokens.' % len(word_index)) 354 | 355 | 枚举映射关系表,生成嵌入层的参数矩阵。虽然是在很大数据集上进行了训练,理论上还是存在单词无法查找到对应的词向量的可能,所以需要捕捉这种异常情况。 356 | 357 | #获取词向量的映射矩阵 358 | embedding_matrix = np.zeros((max_features + 1, embedding_dims)) 359 | for word, i in word_index.items(): 360 | #编号大于max_features的忽略 该字典是按照字典顺序 所以对应的id不一定是顺序的 361 | if i > max_features: 362 | continue 363 | try: 364 | embedding_matrix[i] = model[word].reshape(embedding_dims) 365 | except: 366 | print "%s not found!" % (word) 367 | 368 | 生成了嵌入层的参数矩阵矩阵后,可以使用该矩阵创建对应的嵌入层。在本例中,预先训练的词向量,训练过程中不再调整词向量,所以需要把trainable设置为False。 369 | 370 | # Inputs 371 | input = Input(shape=[max_document_length]) 372 | 373 | # 词向量层,本文使用了预训练word2vec词向量,把trainable设为False 374 | x = Embedding(max_features + 1, 375 | embedding_dims, 376 | weights=[embedding_matrix], 377 | trainable=False)(input) 378 | 379 | 打印CNN的结构。 380 | 381 | model.summary() 382 | 383 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。其中1,500,300个参数不可训练,这里指的就是固定的词向量。 384 | 385 | _____________________ 386 | Layer (type) Output Shape Param # Connected to 387 | ================================================================================================== 388 | input_5 (InputLayer) (None, 200) 0 389 | __________________________________________________________________________________________________ 390 | embedding_5 (Embedding) (None, 200, 300) 1500300 input_5[0][0] 391 | __________________________________________________________________________________________________ 392 | conv1d_13 (Conv1D) (None, 198, 200) 180200 embedding_5[0][0] 393 | __________________________________________________________________________________________________ 394 | conv1d_14 (Conv1D) (None, 197, 200) 240200 embedding_5[0][0] 395 | __________________________________________________________________________________________________ 396 | conv1d_15 (Conv1D) (None, 196, 200) 300200 embedding_5[0][0] 397 | __________________________________________________________________________________________________ 398 | max_pooling1d_13 (MaxPooling1D) (None, 99, 200) 0 conv1d_13[0][0] 399 | __________________________________________________________________________________________________ 400 | max_pooling1d_14 (MaxPooling1D) (None, 98, 200) 0 conv1d_14[0][0] 401 | __________________________________________________________________________________________________ 402 | max_pooling1d_15 (MaxPooling1D) (None, 98, 200) 0 conv1d_15[0][0] 403 | __________________________________________________________________________________________________ 404 | flatten_13 (Flatten) (None, 19800) 0 max_pooling1d_13[0][0] 405 | __________________________________________________________________________________________________ 406 | flatten_14 (Flatten) (None, 19600) 0 max_pooling1d_14[0][0] 407 | __________________________________________________________________________________________________ 408 | flatten_15 (Flatten) (None, 19600) 0 max_pooling1d_15[0][0] 409 | __________________________________________________________________________________________________ 410 | concatenate_5 (Concatenate) (None, 59000) 0 flatten_13[0][0] 411 | flatten_14[0][0] 412 | flatten_15[0][0] 413 | __________________________________________________________________________________________________ 414 | dropout_5 (Dropout) (None, 59000) 0 concatenate_5[0][0] 415 | __________________________________________________________________________________________________ 416 | dense_9 (Dense) (None, 32) 1888032 dropout_5[0][0] 417 | __________________________________________________________________________________________________ 418 | dense_10 (Dense) (None, 2) 66 dense_9[0][0] 419 | ================================================================================================== 420 | Total params: 4,108,998 421 | Trainable params: 2,608,698 422 | Non-trainable params: 1,500,300 423 | __________________________________________________________________________________________________ 424 | 425 | 当特征提取使用词向量,且使用预训练好的词向量,特征数取5000的前提下,训练过程中词向量相关参数不可改变,结果如下,可以看出增加数据量可以提升性能。在数据量相同的情况下,比单层CNN效果略好,与CNN+MLP效果相当。 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 |
数据量F1值
1w0.88
10w0.91
441 | 442 | ## fine tuning版本的TextCNN 443 | fine tuning版本的TextCNN的最大特点是使用预先训练的词向量,训练过程中词向量的参数参与整个反向传递过程,接受训练和调整。具体实现时设置trainable为True即可。 444 | 445 | # 词向量层,本文使用了预训练word2vec词向量并接受调整,把trainable设为True 446 | x = Embedding(max_features + 1, 447 | embedding_dims, 448 | weights=[embedding_matrix], 449 | trainable=True)(input) 450 | 451 | 452 | 查看需要训练的参数信息,发现所有参数均可以参与训练过程。 453 | 454 | ================================================================================================== 455 | Total params: 4,108,998 456 | Trainable params: 4,108,998 457 | Non-trainable params: 0 458 | __________________________________________________________________________________________________ 459 | 460 | 当特征提取使用词向量,且使用预训练好的词向量,特征数取5000的前提下,训练过程中词向量相关参数可改变,结果如下,可以看出增加数据量可以提升性能。在数据量相同的情况下,比单层CNN效果略好,与CNN+MLP效果相当,比使用静态词向量的效果略好。 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 |
数据量F1值
1w0.89
10w0.92
476 | 477 | 478 | # 参考文献 479 | 480 | - Convolutional Neural Networks for Sentence Classification 481 | - http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/ -------------------------------------------------------------------------------- /使用Jieba进行中文词性标注.md: -------------------------------------------------------------------------------- 1 | # 词性 2 | 词性指以词的特点作为划分词类的根据。现代汉语的词可以分为两类14种词性。 3 | # 常见词性分类 4 | 词性分类又叫词性标注(Part-Of-Speech tag, POS-tag),常见的词性标准类型如下: 5 | 6 | 1. 名词 7 | - n 名词 8 | - nr 人名 9 | - nr1 汉语姓氏 10 | - nr2 汉语名字 11 | - nrj 日语人名 12 | - nrf 音译人名 13 | - ns 地名 14 | - nsf 音译地名 15 | - nt 机构团体名 16 | - nz 其它专名 17 | - nl 名词性惯用语 18 | - ng 名词性语素 19 | 20 | 2. 时间词 21 | - t 时间词 22 | - tg 时间词性语素 23 | 24 | 3. 处所词 25 | - s 处所词 (在公司,在学校) 26 | 27 | 4. 方位词 28 | - f 方位词 29 | 30 | 5. 动词 31 | - v 动词 32 | - vd 副动词 33 | - vn 名动词 34 | - vshi 动词“是” 35 | - vyou 动词“有” 36 | - vf 趋向动词 37 | - vx 形式动词 38 | - vi 不及物动词(内动词) 39 | - vl 动词性惯用语 40 | - vg 动词性语素 41 | 42 | 6. 形容词 43 | - a 形容词 44 | - ad 副形词 45 | - an 名形词 46 | - ag 形容词性语素 47 | - al 形容词性惯用语 48 | 49 | 7. 区别词 50 | - b 区别词 51 | - bl 区别词性惯用语 52 | 53 | 8. 状态词 54 | - z 状态词 55 | 56 | 9. 代词 57 | - r 代词 58 | - rr 人称代词 59 | - rz 指示代词 60 | - rzt 时间指示代词 61 | - rzs 处所指示代词 62 | - rzv 谓词性指示代词 63 | - ry 疑问代词 64 | - ryt 时间疑问代词 65 | - rys 处所疑问代词 66 | - ryv 谓词性疑问代词 67 | - rg 代词性语素 68 | 69 | 10. 数词 70 | - m 数词 71 | - mq 数量词 72 | 73 | 11. 量词 74 | - q 量词 75 | - qv 动量词 76 | - qt 时量词 77 | 78 | 12. 副词 79 | - d 副词 80 | 81 | 13. 介词 82 | - p 介词 83 | - pba 介词“把” 84 | - pbei 介词“被” 85 | 86 | 14. 连词 87 | - c 连词 88 | - cc 并列连词 89 | 90 | # 使用Jieba词性分类 91 | Jieba下进行词性分类非常简便。 92 | 93 | seg_lig = jieba.posseg.cut(text) 94 | for w,tag in seg_lig: 95 | print "%s /%s" % (w,tag) 96 | 97 | 以经典句子为例,“我爱北京天安门“,词性分类的结果为: 98 | 99 | 我 /r 100 | 爱 /v 101 | 北京 /ns 102 | 天安门 /ns 103 | 104 | 使用一个稍微复杂的例子。 105 | 106 | > 107 | 据半岛电视台援引叙利亚国家电视台称,叙利亚已经对美国、英国、法国的空袭进行了反击。据介绍,在叙军武器库中,对西方最具威慑力的当属各型战术地对地弹道导弹。尽管美英法是利用巡航导弹等武器发动远程空袭,但叙军要对等还击却几乎是“不可能完成的任务”。目前叙军仍能作战的战机仍是老旧的苏制米格-29、米格-23、米格-21战斗机和苏-22、苏-24轰炸机 108 | 109 | 110 | 由于文字较多,使用分行显示会十分乱,我们稍微修改代码,让分词后的词性标注结果紧跟着原单词。 111 | 112 | seg_lig = jieba.posseg.cut(text) 113 | print " ".join(["%s /%s" % (w,tag) for w,tag in seg_lig]) 114 | 115 | 分词的结果如下所示。 116 | 117 | > 118 | >据 /p 半岛 /n 电视台 /n 援引 /vn 叙利亚 /ns 国家 /n 电视台 /n 称 /v , /x 叙利亚 /ns 已经 /d 对 /p 美国 /ns 、 /x 英国 /ns 、 /x 法国 /ns 的 /uj 空袭 /v 进行 /v 了 /ul 反击 /v 。 /x 据介绍 /n , /x 在 /p 叙军 /n 武器库 /n 中 /f , /x 对 /p 西方 /s 最 /d 具 /v 威慑力 /n 的 /uj 当属 /n 各型 /r 战术 /n 地对地 /n 弹道导弹 /n 。 119 | 尽管 /c 美英 /nz 法 /j 是 /v 利用 /n 巡航导弹 /n 等 /u 武器 /n 发动 /vn 远程 /n 空袭 /v , /x 但 /c 叙军 /n 要 /v 对 /p 等 /u 还击 /v 却 /d 几乎 /d 是 /v “ /x 不 /d 可能 /v 完成 /v 的 /uj 任务 /n ” /x 。 /x 目前 /t 叙军 /n 仍 /d 能 /v 作战 /v 的 /uj 战机 /n 仍 /d 是 /v 老 /a 旧 /a 的 /uj 苏制 /n 米格 /nrt - /x 29 /m 、 /x 米格 /nrt - /x 23 /m 、 /x 米格 /nrt - /x 21 /m 战斗机 /n 和 /c 苏 /ns - /x 22 /m 、 /x 苏 /j - /x 24 /m 轰炸机 /n , /x 它们 /r 在 /p 现代化 /vn 的 /uj 西方 /s 空军 /n 面前 /f 难 /a 有 /v 自保 /vn 之 /u 力 /n , /x 因此 /c 叙军 /n 的 /uj 远程 /n 反击 /v 只能 /v 依靠 /v 另 /r 一个 /m 撒手锏 /n — /x — /x 地对地 /n 战术 /n 弹道导弹 /n 。 120 | 121 | 122 | # 参考文献 123 | 124 | - https://www.cnblogs.com/csj007523/p/7773027.html 125 | -------------------------------------------------------------------------------- /使用LDA进行文档主题建模.md: -------------------------------------------------------------------------------- 1 | # LDA简介 2 | LDA(Latent Dirichlet Allocation)是一种文档主题模型,包含词、主题和文档三层结构。 3 | 4 | ![使用LDA进行文档主题建模-图1](picture/使用LDA进行文档主题建模-图1.png) 5 | 6 | LDA认为一篇文档由一些主题按照一定概率组成,一个主题又由一些词语按照一定概率组成。早期人们用词袋模型对一篇文章进行建模,把一篇文档表示为若干单词的计数。无论是中文还是英文,都由大量单词组成,这就造成词袋向量的维数巨大,少则几千多则上万,在使用分类模型进行训练时,非常容易造成训练缓慢以及过拟合。LDA本质上把词袋模型进行了降维,把一篇文档以主题的形式进行了表示。主题的个数通常为几百,这就把文档使用了维数为几百的向量进行了表示,大大加快了训练速度,并且相对不容易造成过拟合。从某种程度上来说,主题是对若干词语的抽象表示。 7 | 8 | ![使用LDA进行文档主题建模-图3](picture/使用LDA进行文档主题建模-图3.png) 9 | 10 | 以最近一部电视剧《南方有乔木》为例。假设一篇文章介绍了这部电视剧的主要内容。我们可以把这篇文章表示为: 11 | 12 | 0.30*"创业"+0.3*"三角恋"+0.2*"无人机" 13 | 14 | 然后我们可以把三角恋表示为: 15 | 16 | 0.4*"南乔"+0.3*"时樾"+0.3*"安宁" 17 | 18 | 需要指出的是,计算出文档、主题以及词语之间的表示关系,需要基于大量的文档,这样才具有普遍的意义。LDA正是提供了这种算法,自动从训练文档中计算出这种对应关系。 19 | 20 | # 数据集 21 | 本文演示用的数据集,依然使用搜狗新闻数据集SogouCS。我们从SogouCS中提取正文内容,每个URL对应的正文当做一篇文档,并且使用jieba进行了分词。演示期间我们提取SogouCS的前10000条数据用于计算LDA。 22 | 23 | def load_sougou_content(): 24 | with open("../data/news_sohusite_content_10000.txt") as F: 25 | content=F.readlines() 26 | F.close() 27 | return content 28 | 29 | 计算LDA时,需要删除停用词,加载我们之前保存的停用词。 30 | 31 | def load_stopwords(): 32 | with open("stopwords.txt") as F: 33 | stopwords=F.readlines() 34 | F.close() 35 | return [word.strip() for word in stopwords] 36 | 37 | # 计算主题 38 | 39 | 我们使用gensim提供的API进行LDA计算。首先从语料中提取字典,并用该字典把预料转换成词袋。 40 | 41 | # 得到文档-单词矩阵 (直接利用统计词频得到特征) 42 | dictionary = corpora.Dictionary(content) 43 | # 将dictionary转化为一个词袋,得到文档-单词矩阵 44 | texts = [dictionary.doc2bow(text) for text in content] 45 | 46 | 然后进行LDA计算,演示期间设置只计算5个主题,通常生产环境经验值为200。 47 | 48 | num_topics=5 49 | lda = models.ldamodel.LdaModel(corpus=texts, id2word=dictionary, num_topics=num_topics) 50 | 51 | 其中比较重要的几个参数含义如下: 52 | 53 | - corpus,计算LDA的语料 54 | - id2word,语料对应的字典 55 | - num_topics,计算的主题的数量 56 | 57 | 我们打印前5个主题。 58 | 59 | for index,topic in lda.print_topics(5): 60 | print topic 61 | 62 | 主题内容如下所示。 63 | 64 | 0.007*"月" + 0.006*"中" + 0.005*"年" + 0.005*"日" + 0.004*"公司" + 0.004*"时间" + 0.003*"北京" + 0.003*"比赛" + 0.002*"中国" + 0.002*"记者" 65 | 66 | 0.006*"月" + 0.006*"发展" + 0.005*"年" + 0.005*"日" + 0.005*"中" + 0.005*"中国" + 0.004*"工作" + 0.004*"说" + 0.003*"记者" + 0.003*"建设" 67 | 68 | 0.008*"月" + 0.007*"市场" + 0.006*"经济" + 0.005*"增长" + 0.004*"元" + 0.004*"中国" + 0.004*"企业" + 0.004*"产品" + 0.004*"年" + 0.004*"记者" 69 | 70 | 0.011*"日" + 0.011*"月" + 0.007*"记者" + 0.005*"时" + 0.005*"年" + 0.004*"公司" + 0.004*"说" + 0.004*"中" + 0.003*"发现" + 0.002*"亿元" 71 | 72 | 0.006*"o" + 0.006*"i" + 0.006*"月" + 0.005*"日" + 0.005*"e" + 0.005*"说" + 0.005*"n" + 0.005*"中" + 0.004*"中国" + 0.004*"a" 73 | 74 | 如果需要设置每个话题对应的关键字的个数,可以通过参数num_words设置,默认为10,这里的num_topics参数容易导致误解,它的含义是显示排名前几个话题,类似topN参数。 75 | 76 | print_topics(num_topics=20, num_words=10) 77 | 78 | 词袋处理后的结果,使用TFIDF算法处理后,可以进一步提升LDA的效果。 79 | 80 | # 利用tf-idf来做为特征进行处理 81 | texts_tf_idf = models.TfidfModel(texts)[texts] 82 | lda = models.ldamodel.LdaModel(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 83 | 84 | 运行的效果如下所示。 85 | 86 | 0.001*"比赛" + 0.001*"联赛" + 0.001*"意甲" + 0.001*"主场" + 0.001*"轮" + 0.001*"赛季" + 0.001*"时间" + 0.001*"孩子" + 0.001*"北京" + 0.000*"航天员" 87 | 88 | 0.001*"叙利亚" + 0.001*"奥运会" + 0.001*"伦敦" + 0.000*"选手" + 0.000*"中国" + 0.000*"说" + 0.000*"男子" + 0.000*"米" + 0.000*"北京" + 0.000*"日" 89 | 90 | 0.000*"梅西" + 0.000*"林书豪" + 0.000*"鲁尼" + 0.000*"稀土" + 0.000*"钓鱼岛" + 0.000*"巴萨" + 0.000*"试用" + 0.000*"常规赛" + 0.000*"蛋黄派" + 0.000*"尼克斯" 91 | 92 | 0.002*"体育" + 0.001*"搜狐" + 0.001*"北京" + 0.001*"o" + 0.001*"时间" + 0.001*"n" + 0.001*"i" + 0.001*"日" + 0.001*"C" + 0.001*"e" 93 | 94 | 0.001*"市场" + 0.001*"经济" + 0.001*"增长" + 0.001*"投资" + 0.001*"亿元" + 0.001*"基金" + 0.001*"公司" + 0.001*"银行" + 0.001*"企业" + 0.001*"同比" 95 | 96 | # 使用LDA提取文档特征 97 | 通常LDA的结果可以作为进一步文档分类、文档相似度计算以及文档聚类的依据,可以把LDA当做一种特征提取方法。 98 | 99 | #获取语料对应的LDA特征 100 | corpus_lda = lda[texts_tf_idf] 101 | #打印0号文档对应的LDA值 102 | print corpus_lda[0] 103 | 104 | 输出0号文档对应的LDA值如下,即把0号文档以5个话题形式表示。 105 | 106 | [(0, 0.019423252), (1, 0.019521076), (2, 0.92217809), (3, 0.01954053), (4, 0.019337002)] 107 | 108 | 这里需要解释的是,无论是词袋模型还是LDA生成的结果,都可能存在大量的0,这会占用大量的内存空间。因此默认情况下,词袋以及LDA计算的结果都以稀疏矩阵的形式保存。稀疏矩阵的最小单元定义为: 109 | 110 | (元素所在的位置,元素的值) 111 | 112 | 比如一个稀疏矩阵只有0号和2号元素不为0,分别为1和5,那么它的表示方法如下: 113 | 114 | [(0,1),(2,5)] 115 | 116 | 117 | # 使用多核计算 118 | 119 | LDA在生产环境中运行遇到的最大问题就是默认只能使用单核资源,运行速度过慢。gensim针对这一情况也提供了多核版本。 120 | 121 | lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 122 | 123 | 该版本默认情况默认使用cpu_count()-1 即使用几乎全部CPU,仅保留一个CPU不参与LDA计算,也可以通过参数workers指定使用的CPU个数,这里的CPU指的物理CPU,不是超线程的CPU数。一般认为: 124 | 125 | CPU总核数 = 物理CPU个数 * 每颗物理CPU的核数 126 | 总逻辑CPU数 = 物理CPU个数 * 每颗物理CPU的核数 * 超线程数 127 | 128 | 可以尝试使用如下命令查看服务器的CPU信息。 129 | 130 | # 查看CPU信息(型号) 131 | cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 132 | 24 Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz 133 | 134 | # 查看物理CPU个数 135 | cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l 136 | 2 137 | 138 | # 查看每个物理CPU中core的个数(即核数) 139 | cat /proc/cpuinfo| grep "cpu cores"| uniq 140 | cpu cores : 6 141 | 142 | # 查看逻辑CPU的个数 143 | cat /proc/cpuinfo| grep "processor"| wc -l 144 | 24 145 | 146 | gensim官网上公布了一组测试数据,数据集为Wikipedia英文版数据集,该数据集有350万个文档,10万个特征,话题数设置为100的情况下运行LDA算法。硬件环境为一台拥有4个物理i7 CPU的服务器。使用单核接口需要使用3小时44分,当使用多核接口且使用3个物理CPU仅需要1小时6分钟。我们延续上面的例子,继续使用搜狗的数据集,为了要效果更加明显,我们使用SogouCS的前50000的数据进行LDA运算,话题数设置为100,并使用time.clock()获取当前时间,便于计算各个环节消耗的时间,比如计算LDA消耗的时间的方法如下所示。 147 | 148 | #获取当前时间 149 | start = time.clock() 150 | #workers指定使用的CPU个数 默认使用cpu_count()-1 即使用几乎全部CPU 仅保留一个CPU不参与LDA计算 151 | lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 152 | #计算耗时 153 | end = time.clock() 154 | print('[lda]Running time: %s Seconds' % (end - start)) 155 | 156 | 分别计算使用1,2,4等不同CPU的情况,统计了预处理环节、LDA环节的耗时,其中预处理环节主要进行了词袋处理和TFIDF处理,测试用的服务器一共有12个物理CPU。 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 |
CPU数预处理环节耗时(单位:秒) LDA环节耗时(单位:秒)
默认24169
125437
224296
425215
623189
825186
1023171
1223160
205 | 206 | 测试结果表明,使用多CPU资源可以提升LDA的计算效率,尤其对于CPU使用个数较少时,几乎成线性下降,当使用CPU个数较多时,性能改善不明显。在本例中默认参数将使用11个CPU,因此默认参数下计算速率略低于使用12个CPU。 207 | 208 | ![使用LDA进行文档主题建模-图4.png](picture/使用LDA进行文档主题建模-图4.png) 209 | 210 | # 在线学习 211 | LDA可以进行在线学习,动态更新模型。 212 | 213 | lda = LdaMulticore(corpus, num_topics=10) 214 | lda.update(other_corpus) 215 | 216 | 217 | 218 | # 参考文档 219 | 220 | - http://www.52nlp.cn/lda-math-文本建模 221 | - https://radimrehurek.com/gensim/models/ldamulticore.html 222 | - Hoffman, Blei, Bach: Online Learning for Latent Dirichlet Allocation, NIPS 2010. -------------------------------------------------------------------------------- /使用TextRank和TFIDF进行关键字自动提取.md: -------------------------------------------------------------------------------- 1 | # 文档关键字 2 | 早期的信息检索平台并没有如今这么发达,大量的类似论坛、wiki的信息发布网站都依赖文档的关键字进行文档检索。即使是在如今的互联网世界,大量的信息检索还要依赖关键字。依靠人工归纳文章的关键字虽然相对准确,但是面对信息爆炸的互联网,这几乎成为了不可能的任务。本章将介绍如何使用TextRank和TFIDF算法自动提取文档的关键字。 3 | # TextRank 4 | TextRank的思路来自于PageRank。PageRank最开始用来计算网页的重要性。整个互联网可以看作一张有向图,节点是网页。如果网页A存在到网页B的链接,那么有一条从网页A指向网页B的有向边,指向同一个网页的链接越多,该网页的重要性或者说PageRank值更大。综合考虑Title和Keywords等其它因素之后,Google通过PageRank来调整结果,使那些更重要的网页在搜索结果排名更靠前。 5 | 6 | ![使用TextRank和TFIDF进行文档关键字自动提取-图2](picture/使用TextRank和TFIDF进行文档关键字自动提取-图2.png) 7 | 8 | TextRank的原理和PageRank类似,一篇文档去掉停用词以后,每个单词相当于一个网页,有时候也会指定某些词性的单词才参与计算TextRank,比如名词和动词。网页有明显的指向关系,但是文档的单词之间只有前后关系,所以要指定一个滑动的窗口大小,比如前后5个单词。在滑动窗口内的单词之间的前后关系当做网页之间的指向关系,参与TextRank的计算。 9 | ![使用TextRank和TFIDF进行文档关键字自动提取-图1](picture/使用TextRank和TFIDF进行文档关键字自动提取-图1.png) 10 | # TFIDF 11 | TFIDF在本书中多次提到了,它同样可以用于提取关键字。TFIDF的一个基本假设是,一个单词的重要性由词频决定,如果一个单词在一句话里出现频率高,同时在其他句子里出现频率低,那么这个单词对这句话就非常重要,对于一个文档也是如此。 12 | # 测试数据 13 | 测试数据挑选了写本章时的一条新闻,新闻内容如下: 14 | 15 | > 16 | 据半岛电视台援引叙利亚国家电视台称,叙利亚已经对美国、英国、法国的空袭进行了反击。据介绍,在叙军武器库中,对西方最具威慑力的当属各型战术地对地弹道导弹。尽管美英法是利用巡航导弹等武器发动远程空袭,但叙军要对等还击却几乎是“不可能完成的任务”。目前叙军仍能作战的战机仍是老旧的苏制米格-29、米格-23、米格-21战斗机和苏-22、苏-24轰炸机,它们在现代化的西方空军面前难有自保之力,因此叙军的远程反击只能依靠另一个撒手锏——地对地战术弹道导弹. 17 | 18 | # 提取关键字 19 | ## 使用TextRank提取关键字 20 | Jieba提供了TextRank实现,直接使用默认参数就可以完成关键字的提取。 21 | 22 | # 引入TextRank关键词抽取接口 23 | textrank = analyse.textrank 24 | 25 | # 基于TextRank算法进行关键词抽取 26 | keywords = textrank(text) 27 | # 输出抽取出的关键词 28 | for keyword in keywords: 29 | print keyword + "/" 30 | 31 | 提取的结果如下所示,自动化提取关键字的结果差强人意,其中"只能"、"据介绍"这些完全可以省略,另外通常关键字个数需要控制在10个以内。 32 | 33 | 叙军/ 34 | 远程/ 35 | 空袭/ 36 | 电视台/ 37 | 战术/ 38 | 反击/ 39 | 空军/ 40 | 现代化/ 41 | 叙利亚/ 42 | 地对地/ 43 | 只能/ 44 | 武器/ 45 | 发动/ 46 | 弹道导弹/ 47 | 任务/ 48 | 国家/ 49 | 据介绍/ 50 | 法国/ 51 | 进行/ 52 | 当属/ 53 | 54 | Jieba提供了接口,设置关键字的个数以及提取的关键字的词性,比如: 55 | 56 | - topK,指定关键字的个数 57 | - allowPOS,指定关键字的词性,常见的词性包括: 58 | - n 名词 59 | - nr 人名 60 | - ns 地名 61 | - nz 其它专名 62 | - t 时间词 63 | - v 动词 64 | - vd 副动词 65 | - vn 名动词 66 | 67 | 我们只提取10个关键字,且只关注名词和动词以及名动词。 68 | 69 | # 基于TextRank算法进行关键词抽取 70 | keywords = textrank(text,topK = 10, withWeight = False, allowPOS = ('n','ns','vn','v')) 71 | 72 | 生成的结果如下所示,基本满足我们的需求了。 73 | 74 | 叙军/ 75 | 远程/ 76 | 空袭/ 77 | 电视台/ 78 | 战术/ 79 | 反击/ 80 | 空军/ 81 | 现代化/ 82 | 叙利亚/ 83 | 地对地/ 84 | 85 | ## 使用TFIDF提取关键字 86 | 使用TFIDF提取关键字的方法和TextRank类似。 87 | 88 | # TFIDF 89 | keywords_tfidf = analyse.extract_tags(text,topK = 10, withWeight = False, allowPOS = ('n','ns','vn','v','nz')) 90 | 91 | # 输出抽取出的关键词 92 | for keyword in keywords_tfidf: 93 | print keyword + "/" 94 | 95 | 生成的结果如下所示,与TextRank相比差别不大。 96 | 97 | 叙军/ 98 | 地对地/ 99 | 空袭/ 100 | 弹道导弹/ 101 | 远程/ 102 | 叙利亚/ 103 | 电视台/ 104 | 反击/ 105 | 战术/ 106 | 撒手锏/ 107 | 108 | # 参考资料 109 | - Rada Mihalcea and Paul Tarau,TextRank: Bringing Order into Texts 110 | - The PageRank Citation Ranking:Bringing Order to the Web -------------------------------------------------------------------------------- /使用fasttext进行文档分类.md: -------------------------------------------------------------------------------- 1 | # fasttext原理 2 | fasttext提供了一种有效且快速的方式生成词向量以及进行文档分类。 3 | fasttext模型输入一个词的序列,输出这个词序列属于不同类别的概率。fasttext模型架构和Word2Vec中的CBOW模型很类似。不同之处在于,fasttext预测标签,而CBOW模型预测中间词。fasttext设计的初衷就是为了作为一个文档分类器,副产品是也生成了词向量。 4 | ![使用fasttext进行文档分类-图2](picture/使用fasttext进行文档分类-图2.png) 5 | 6 | # fasttext特性 7 | ## n-gram 8 | 在词袋模型中,把单词当做独立的个体,没有考虑词前后的关系。比如"我打你"和“你打我“,使用词袋模型的话,这两句话是完全一样的。 9 | 词袋的特征为: 10 | 11 | ["我",“打“,”你”] 12 | 13 | "我打你"和“你打我“对应的特征向量均为: 14 | 15 | [1,1,1] 16 | 17 | n-gram是对词袋模型的一种改善,它会关注一个单词的前后关系,比如n-gram中最常见的2-gram,就关注单词的前一个词,比如"我打你",就可以拆分为"我打"和"打你"。这两句话一起建模的话,2-gram对应的特征为: 18 | 19 | ["我打","打你","你打","打我"] 20 | 21 | "我打你"对应的特征向量为: 22 | 23 | [1,1,0,0] 24 | 25 | "你打我"对应的特征向量为: 26 | 27 | [0,0,1,1] 28 | 29 | 与Word2Vec使用词袋模型不同,fasttext使用了n-gram模型,因此fasttext可以更有效的表达词前后的之间的关系。 30 | ## 高效率 31 | fasttext在使用标准多核CPU的情况下10分钟内处理超过10亿个词汇,特别是与深度模型对比,fastText能将训练时间由数天缩短到几秒钟。使用一个标准多核CPU,得到了在10分钟内训练完超过10亿词汇量模型的结果。 32 | 33 | # 安装fasttext 34 | fasttext的安装非常简便,直接从github上同步最新的代码并进行安装即可。 35 | 36 | $ git clone https://github.com/facebookresearch/fastText.git 37 | $ cd fastText 38 | $ pip install . 39 | 40 | # 预训练模型 41 | facebook已经基于其收集的海量语料,训练好了fasttext的词向量模型,目前已经支持了150多种语言。有需要的读者可以直接下载并使用,对应的链接为: 42 | 43 | https://github.com/facebookresearch/fastText/blob/master/docs/crawl-vectors.md 44 | 45 | ![使用fasttext进行文档分类](picture/使用fasttext进行文档分类-图1.png) 46 | 47 | # 数据集 48 | 数据集依然使用搜狗实验室提供的"搜狐新闻数据",该数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息。 49 | 对应的网址为: 50 | 51 | http://www.sogou.com/labs/resource/cs.php 52 | 53 | 数据文件的格式为: 54 | 55 | 56 | 页面URL 57 | 58 | 页面ID 59 | 60 | 页面标题 61 | 62 | 页面内容 63 | 64 | 65 | 66 | 我们可以看到数据文件中并没有标记页面内容属于哪个频道,如果需要做文档分类,搜狗提供了页面URL和频道之间的映射关系。 67 | 68 | ![使用fasttext进行文档分类-图3](picture/使用fasttext进行文档分类-图3.png) 69 | 70 | 下载SogouTCE文件,可以看到具体的映射关系举例如下: 71 | 72 | http://www.xinhuanet.com/auto/ 汽车 73 | http://www.xinhuanet.com/fortune 财经 74 | http://www.xinhuanet.com/internet/ IT 75 | http://www.xinhuanet.com/health/ 健康 76 | http://www.xinhuanet.com/sports 体育 77 | http://www.xinhuanet.com/travel 旅游 78 | http://www.xinhuanet.com/edu 教育 79 | http://www.xinhuanet.com/employment 招聘 80 | http://www.xinhuanet.com/life 文化 81 | http://www.xinhuanet.com/mil 军事 82 | http://www.xinhuanet.com/olympics/ 奥运 83 | http://www.xinhuanet.com/society 社会 84 | 85 | # 数据清洗 86 | 搜狐新闻数据的文件默认编码格式为gb18030,因此解压缩后要线转换成utf-8格式。 87 | 88 | tar -zxvf news_sohusite_xml.full.tar.gz 89 | cat news_sohusite_xml.dat | iconv -f gb18030 -t utf-8 > news_sohusite_xml-utf8.txt 90 | 91 | 转换完格式后查看文件内容,文件以xml形式记录,举例如下: 92 | 93 | 94 | http://gongyi.sohu.com/s2008/sourceoflife/ 95 | f2467af22cd2f0ea-34913306c0bb3300 96 | 中国西部是地球上主要干旱带之一,妇女是当地劳动力... 97 | 同心县地处宁夏中部干旱带的核心区, 冬寒长,春暖迟,夏热短,秋凉早,干旱少雨,蒸发强烈,风大沙多。主要自然灾害有沙尘暴、干热风、霜冻、冰雹等,其中以干旱危害最为严重。由于生态环境的极度恶劣,导致农村经济发展缓慢,人民群众生产、生活水平低下,靠天吃饭的被动局 98 | 面依然存在,同心,又是国家级老、少、边、穷县之一…[详细] 99 | 100 | 101 | 但是数据文件并不是标准的xml格式,如下所示,该文件相对标准的xml格式缺少了根元素。 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 所有的doc节点都直接是最顶层,没有根节点。因此要添加根节点使该文本文件符合xml文件的规范,最简单的一种形式就是在文件的开始和结尾添加根元素标签。 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 可以直接使用文本编辑工具在数据文件的开始和结尾进行修改,但是这有可能导致你的终端因为内存使用过大而崩溃。一种比较稳妥的做法是使用程序完成。 135 | 136 | def make_xml(): 137 | print "" 138 | print "" 139 | with open("data/news_sohusite_xml-utf8.txt") as F: 140 | for line in F: 141 | print line 142 | F.close() 143 | print "" 144 | 145 | 在终端执行该程序,并将标准输出的结果保存即可,剩下的操作只要解析xml文件即可。下面我们介绍另一种方法,观察可以发现,url和content是成对出现的,并且一一对应。我们可以过滤这两个字段的内容,分别保存成content文件和url文件。 146 | 首先过滤出url字段的内容,并且删除掉url标签。 147 | 148 | cat news_sohusite_xml-utf8.txt | grep '' | sed 's///g' | sed 's/<\/url>//g' > news_sohusite_url.txt 149 | 150 | 然后过滤出content字段的内容,并且删除掉content标签。 151 | 152 | cat news_sohusite_xml-utf8.txt | grep '' | sed 's///g' | sed 's/<\/content>//g' > news_sohusite_content.txt 153 | 154 | content是中文内容,需要使用jieba进行切词,可以把切词的动作也放到上面的命令里面。 155 | 156 | cat news_sohusite_xml-utf8.txt | grep '' | sed 's///g' | sed 's/<\/content>//g' | python -m jieba -d ' ' > news_sohusite_content.txt 157 | 158 | 加载url和对应领域的映射关系的文件,以哈希的形式保存对应的映射关系。 159 | 160 | def load_SogouTCE(): 161 | SogouTCE=[] 162 | SogouTCE_kv = {} 163 | with open("../data/SogouTCE.txt") as F: 164 | for line in F: 165 | (url,channel)=line.split() 166 | SogouTCE.append(url) 167 | F.close() 168 | for index,url in enumerate(SogouTCE): 169 | #删除http前缀 170 | url=re.sub('http://','',url) 171 | print "k:%s v:%d" % (url,index) 172 | SogouTCE_kv[url]=index 173 | return SogouTCE_kv 174 | 175 | 我们分析下各个领域的数据分布情况,把匹配上的url对应的标记打印出来。 176 | 177 | def load_url(SogouTCE_kv): 178 | labels=[] 179 | with open("../data/news_sohusite_url.txt") as F: 180 | for line in F: 181 | for k,v in SogouTCE_kv.items(): 182 | if re.search(k,line,re.IGNORECASE): 183 | #print "x:%s y:%d" % (line,v) 184 | print v 185 | labels.append(v) 186 | F.close() 187 | return labels 188 | 189 | 运行程序,分析各个领域对应的url数量。 190 | 191 | python fasttext.py > v.txt 192 | cat v.txt | sort -n | uniq -c 193 | 194 | 每行的第一个字段是数量,第二个字段是对应的领域的id,结果表明搜狐新闻数据集中在某几个领域,并且分布不均匀。为了避免样本不均衡导致的误判,我们选择数量上占前三的领域作为后继分析的数据,id分别为81,79和91。 195 | 196 | 138576 79 197 | 27489 80 198 | 199871 81 199 | 23409 82 200 | 44537 83 201 | 2179 84 202 | 13012 85 203 | 1924 87 204 | 3294 88 205 | 842 89 206 | 50138 91 207 | 5882 92 208 | 209 | 反查对应的url为: 210 | 211 | kit.sohu.com/ id:81 212 | auto.sohu.com/ id:79 213 | yule.sohu.com/ id:91 214 | 215 | 过滤我们关注的领域的内容,将content保存在x列表里,对应的领域的id保存在y列表里,作为标签使用,至此我们完成了数据清洗的工作。 216 | 217 | def load_selecteddata(SogouTCE_kv): 218 | x=[] 219 | y=[] 220 | 221 | #加载content列表 222 | with open("../data/news_sohusite_content.txt") as F: 223 | content=F.readlines() 224 | F.close() 225 | 226 | # 加载url列表 227 | with open("../data/news_sohusite_url.txt") as F: 228 | url = F.readlines() 229 | F.close() 230 | 231 | for index,u in enumerate(url): 232 | for k, v in SogouTCE_kv.items(): 233 | # 只加载id为81,79和91的数据 234 | if re.search(k, u, re.IGNORECASE) and v in (81, 79, 91): 235 | #保存url对应的content内容 236 | x.append(content[index]) 237 | y.append(v) 238 | 239 | return x,y 240 | 241 | ## 删除停用词 242 | 在处理中文语料时,需要删除停用词。所谓停用词就是对理解中文含义没有明显作用的哪些单词,常见的停用词举例如下: 243 | 244 | 一一 245 | 一下 246 | 一个 247 | 一些 248 | 一何 249 | 一切 250 | 一则 251 | 一则通过 252 | 一天 253 | 一定 254 | 一方面 255 | 一旦 256 | 一时 257 | 258 | 另外所有的字母和数字还有标点符号也可以作为停用词。我们把停用词保存在一个文本文件里面便于配置使用。 259 | 定义加载停用词的函数。 260 | 261 | def load_stopwords(): 262 | with open("stopwords.txt") as F: 263 | stopwords=F.readlines() 264 | F.close() 265 | return [word.strip() for word in stopwords] 266 | 267 | 使用停用词过滤之前提取的文本内容。 268 | 269 | 270 | stopwords=load_stopwords() 271 | 272 | #切割token 273 | x=[ [word for word in line.split() if word not in stopwords] for line in x] 274 | 275 | # 文档分类 276 | ## 数据文件格式 277 | fasttext对训练和测试的数据格式有一定的要求,数据文件和标签文件要合并到一个文件里面。文件中的每一行代表一条记录,同时每条记录的最后标记对应的标签。默认情况下标签要以\_\_label\_\_开头,比如: 278 | 279 | 这是一条测试数据 __label__1 280 | python下实现合并数据文件和标签文件的功能非常简单。 281 | 282 | def dump_file(x,y,filename): 283 | with open(filename, 'w') as f: 284 | for i,v in enumerate(x): 285 | line="%s __label__%d\n" % (v,y[i]) 286 | f.write(line) 287 | f.close() 288 | 289 | 加载数据清洗后的数据和标签,随机划分成训练数据和测试数据,其中测试数据占20%。 290 | 291 | SogouTCE_kv=load_SogouTCE() 292 | x,y=load_selecteddata(SogouTCE_kv) 293 | # 分割训练集和测试集 294 | x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) 295 | 296 | 按照fasttext的格式要求保存成训练数据和测试数据。 297 | 298 | #按照fasttest的要求生成训练数据和测试数据 299 | dump_file(x_train,y_train,"../data/sougou_train.txt") 300 | dump_file(x_test, y_test, "../data/sougou_test.txt") 301 | 302 | 查看训练数据文件的内容,举例如下: 303 | 304 | 2 0 1 2 款 长安 标致 雪铁龙 D S 4 / D S 5 九寨沟 试驾 __label__79 305 | 306 | ## 训练模型 307 | 下面开始训练fasttext模型。 308 | 309 | 310 | # train_supervised uses the same arguments and defaults as the fastText cli 311 | model = train_supervised( 312 | input="../data/sougou_train.txt", epoch=25, lr=0.6, wordNgrams=2, verbose=2, minCount=1 313 | ) 314 | 315 | 其中比较重要的几个参数的含义为: 316 | 317 | - input;表示训练数据文件的路径 318 | - epoch:表示训练的次数 319 | - lr:表示初始的学习速率 320 | - wordNgrams:表示n-gram的值,一般使用2,表示2-gram 321 | - minCount:表示参与计算的单词的最小出现次数。 322 | 323 | ## 验证效果 324 | fasttext默认情况下会计算对应的准确率和召回率。 325 | 326 | def print_results(N, p, r): 327 | print("N\t" + str(N)) 328 | print("P@{}\t{:.3f}".format(1, p)) 329 | print("R@{}\t{:.3f}".format(1, r)) 330 | 331 | 使用测试数据文件进行校验。 332 | 333 | print_results(*model.test("../data/sougou_test.txt")) 334 | 335 | 运行程序,显示加载了36M的单词,其中包含288770的单词组合,标记类型一共3种。 336 | 337 | Read 36M words 338 | Number of words: 288770 339 | Number of labels: 3 340 | 341 | 验证效果如下所示,准确率为99.0%,召回率为99.0%,对应的F1计算为99.0%,效果非常不错。 342 | 343 | Progress: 100.0% words/sec/thread: 626183 lr: 0.000000 loss: 0.005640 ETA: 0h 0m 344 | N 71107 345 | P@1 0.990 346 | R@1 0.990 347 | -------------------------------------------------------------------------------- /使用多层感知机进行文档分类.md: -------------------------------------------------------------------------------- 1 | # 多层感知机概述 2 | ## 生活中的神经网络 3 | ![使用多层感知机进行文档分类-图1](picture/使用多层感知机进行文档分类-图1.png) 4 | 5 | 人的大脑是由无数的神经元组成的复杂网络。神经元是具有长突起的细胞,它由细胞体和轴突、树突组成。 6 | 7 | ![使用多层感知机进行文档分类-图2](picture/使用多层感知机进行文档分类-图2.png) 8 | 9 | 每个神经元可以有一或多个树突,可以接受刺激并将兴奋传入细胞体。每个神经元只有一个轴突,可以把兴奋从胞体传送到另一个神经元或其他组织,如肌肉或腺体。 10 | 11 | ![使用多层感知机进行文档分类-图3](picture/使用多层感知机进行文档分类-图3.png) 12 | 13 | ## 神经网络算法概述 14 | 神经网络算法就是模拟了人体神经元的工作原理,多个输入参数,分别具有各自的权重,经过激励函数的处理后,得到输出。输出可以再对接下一级的神经网络的输入,从而组成更加复杂的神经网络。 15 | 16 | ![使用多层感知机进行文档分类-图4](picture/使用多层感知机进行文档分类-图4.png) 17 | 大脑里的生物神经细胞和其他的神经细胞是相互连接在一起的。为了创建一个人工神经网络,人工神经细胞也要以同样方式相互连接在一起。为此可以有许多不同的连接方式,其中最容易理解并且也是最广泛地使用的,就是把神经细胞一层一层地连结在一起。这一种类型的神经网络就叫前馈网络。 18 | 19 | ![使用多层感知机进行文档分类-图5](picture/使用多层感知机进行文档分类-图5.png) 20 | 21 | 如果对预测错误的神经元施加惩罚,从输出层开始层层向上查找预测错误的神经元,微调这些神经元对应的权重,达到修复错误的目的,这样的算法就叫做反向传播算法。Scikit-learn中的神经网络实现都是使用反向传播算法。本文重点介绍算法在安全领域的应用,神经网络的公式推导请参考其他机器学习专业书籍。本书介绍的是神经网络中最简单的一种形式,即多层感知机。 22 | 23 | # 数据集 24 | 数据集依然使用搜狗实验室提供的"搜狐新闻数据",该数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息。 对应的网址为: 25 | 26 | http://www.sogou.com/labs/resource/cs.php 27 | 28 | 我们选择其中数量最大的三个频道的数据进行分析,数据清洗过程请参考我之前的文章《使用fasttext进行文档分类》 29 | 30 | # 特征提取 31 | ## 词袋&TFIDF 32 | 特征提取的方式采用词袋结合TFIDF的方式。 33 | 34 | #切割词袋 35 | vectorizer = CountVectorizer() 36 | # 该类会统计每个词语的tf-idf权值 37 | transformer = TfidfTransformer() 38 | x = transformer.fit_transform(vectorizer.fit_transform(x)) 39 | 40 | ## n-gram&TFIDF 41 | 特征提取还可以使用词袋模型的加强版n-gram,比如最常见的2-gram,这样可以更好的提取单词前后之间的关系。 42 | 43 | #切割词袋 44 | vectorizer = CountVectorizer(ngram_range=(2,2)) 45 | # 该类会统计每个词语的tf-idf权值 46 | transformer = TfidfTransformer() 47 | x = transformer.fit_transform(vectorizer.fit_transform(x)) 48 | 49 | ## one-hot编码 50 | 由于这次需要区分的标签一共有三种,不能直接使用过去二分类问题的方式进行处理了。针对多分类问题,需要使用one-hot编码处理标签。所谓的one-hot编码,又称为一位有效编码,主要是采用位状态寄存器来对个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。比如我们有三类标签,就可以编码为: 51 | 52 | [1,0,0] 53 | [0,1,0] 54 | [0,0,1] 55 | 在keras中可以非常方便的进行转换。 56 | 57 | #转换成one hot编码 58 | y=to_categorical(t, num_classes=3) 59 | 60 | # 训练与效果验证 61 | 在scikit-learn中构造多层感知机的分类器非常方便,通常只需要设计神经网络的结构即可。本例中设计的隐藏层有两层,结点数分为为5和3,通常最后一层的结点数与标签类型数相同。 62 | 63 | #mlp 64 | clf = MLPClassifier(solver='lbfgs', 65 | alpha=1e-5, 66 | hidden_layer_sizes=(5, 3), 67 | random_state=1) 68 | 69 | 其中比较重要的几个参数的含义为: 70 | 71 | - hidden_layer_sizes,表示隐藏层的结构 72 | - activation,激活函数,{‘identity’, ‘logistic’, ‘tanh’, ‘relu’}, 默认relu 73 | - solver,优化方式,{‘lbfgs’, ‘sgd’, ‘adam’}, 默认adam。lbfgs使用quasi-Newton方法的优化器,sgd使用随机梯度下降,adam也是一种随机梯度的优化器 74 | - alpha,可选的,默认0.0001,正则化项参数 75 | 76 | 效果验证使用5折交叉验证,考核的指标是f1和accuracy。使用cross_val_score函数可以非常方便的实现交叉验证的功能,其中cv参数指定交叉验证的方式,比如5代表5折交叉验证。 77 | 78 | scores = cross_val_score(clf, x, y, cv = 5,scoring='f1_micro') 79 | print("f1: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 80 | 81 | scores = cross_val_score(clf, x, y, cv = 5,scoring='accuracy') 82 | print("accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 83 | 84 | 这里需要特别说明的是,在二分类问题中,我们考核f1即可,但是在多分类问题中,f1以微平均和宏平均两个指标的形式存在,分别为f1\_micro和f1\_macro。f1\_micro对数据集中的每一个实例不分类别进行统计建立全局混淆矩阵,然后计算相应指标。f1\_macro是先对每一个类统计指标值,然后在对所有类求算术平均值。通常使用其中任何一个即可。 85 | 运行程序,当使用词袋&TFIDF模型时,效果如下: 86 | 87 | f1: 0.98 (+/- 0.04) 88 | accuracy: 0.97 (+/- 0.05) 89 | 90 | 当使用n-gram&TFIDF模型的效果,有兴趣的读者可以自行验证。 -------------------------------------------------------------------------------- /公众号.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/公众号.png -------------------------------------------------------------------------------- /如何衡量机器学习分类模型.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 在NLP中我们经常需要使用机器学习的分类器。如何衡量一个分类器的好坏呢?最常见的指标包括准确率与召回率,准确度与F1-Score以及ROC与AUC。 3 | # 测试数据 4 | 我们以Scikit-Learn环境介绍常见的性能衡量指标。为了演示方便,我们创建测试数据,测试数据一共1000条记录,每条记录100个特征,内容随机生成。 5 | 6 | x, y = datasets.make_classification(n_samples=1000, n_features=100, 7 | n_redundant=0, random_state = 1) 8 | 把数据集随机划分成训练集和测试集,其中测试集占40%。 9 | train_X, test_X, train_y, test_y = train_test_split(x,
 y,
 test_size=0.2,
 random_state=66) 10 | 11 | 使用KNN算法进行训练和预测。 12 | 13 | knn = KNeighborsClassifier(n_neighbors=5) 14 | knn.fit(train_X, train_Y) 15 | pred_Y = knn.predict(test_X) 16 | 17 | # 混淆矩阵 18 | 混淆矩阵,即Confusion Matrix,是将分类问题按照真实情况与判别情况两个维度进行归类的一个矩阵,在二分类问题中,可以用一个2乘以2的矩阵表示。如图1-1 所示,TP表示实际为真预测为真,TN表示实际为假预测为假,FN表示实际为真预测为假,通俗讲就是漏报了,FP表示实际为假预测为真,通俗讲就是误报了。 19 | 20 | ![如何衡量机器学习分类器模型](picture/如何衡量机器学习分类器模型-图1.png "二分类问题的混淆矩阵") 21 | 22 | 在Scikit-Learn中,使用metrics.confusion_matrix输出混淆矩阵。 23 | 24 | print "confusion_matrix:" 25 | print metrics.confusion_matrix(test_Y, pred_Y) 26 | 27 | 输出结果如下,其中漏报 36个,误报了25个。 28 | 29 | confusion_matrix: 30 | [[70 25] 31 |  [36 69]] 32 |   33 | # 准确率与召回率 34 | 机器学习中最基本指标是召回率(Recall Rate)和准确率(Precision Rate),召回率也叫查全率,准确率也叫查准率。 35 | 36 | 召回率=TP/(TP+FN) 37 | 准确率=TP/(TP+FP) 38 | 39 | 用一个吃货都可以理解的例子来解释这两个枯燥的概念。一个池塘有10条鱼和20只小龙虾,渔夫撒网打鱼,结果捞上来8条鱼12只小龙虾,那么准确率为8/(8+12)=40%,召回率为8/10=80%。 40 | 在Scikit-Learn中,可以如下获得准确率和召回率。 41 | 42 | print "recall_score:" 43 | print metrics.recall_score(test_Y, pred_Y) 44 | print "precision_score:" 45 | print metrics.precision_score(test_Y, pred_Y) 46 | 47 | 输出结果如下,其中召回率为65.71%,准确率为73.40%。 48 | 49 | recall_score: 50 | 0.657142857143 51 | precision_score: 52 | 0.734042553191 53 | 54 | # 准确度与F1-Score 55 | 准确度(Accuracy)是对检测结果一个均衡的评价,表现的是全体预测正确占全部样本的比例。F1-Score也是对准确率和召回率的一个均衡评价,国内外不少数据挖掘比赛都是重点关注F1-Score的值。在Scikit-Learn中,可以如下获得准确度和F1-Score。 56 | 57 | print "accuracy_score:" 58 | print metrics.accuracy_score(test_Y, pred_Y) 59 | print "f1_score:" 60 | print metrics.f1_score(test_Y, pred_Y) 61 | 62 | 输出结果如下,其中准确度为69.5%和F1-Score为69.34%。 63 | 64 | accuracy_score: 65 | 0.695 66 | f1_score: 67 | 0.693467336683 68 | 69 | # ROC与AUC 70 | ROC(Receiver Operating Characteristic Curve)受试者工作特征曲线,以真阳性率为纵坐标,假阳性率为横坐标绘制的曲线,是反映灵敏性和特效性连续变量的综合指标。一般认为ROC越光滑说明分类算法过拟合的概率越低,越接近左上角说明分类性能越好。AUC(Area Under the Receiver Operating Characteristic Curve)就是量化衡量ROC分类性能的指标,如图1-2 所示,物理含义是ROC曲线的面积,AUC越大越好。 71 | 72 | ![如何衡量机器学习分类器模型-图2](picture/如何衡量机器学习分类器模型-图2.png "ROC曲线示例") 73 | 74 | 绘制ROC曲线的方法如下: 75 | 76 | f_pos, t_pos, thresh = metrics.roc_curve(test_Y, pred_Y) 77 | auc_area = metrics.auc(f_pos, t_pos)
 plt.plot(f_pos, t_pos, 'darkorange', lw=2, label='AUC = %.2f' % auc_area) 78 | plt.legend(loc='lower right') 79 | plt.plot([0, 1], [0, 1], color='navy', linestyle='--') 80 | plt.title('ROC') 81 | plt.ylabel('True Pos Rate') 82 | plt.xlabel('False Pos Rate') 83 | plt.show() 84 | 85 | 在Scikit-Learn中,可以如下获得AUC值。 86 | 87 | print "AUC:" 88 | print metrics.roc_auc_score(test_Y, pred_Y) 89 | 90 | 计算获得的AUC值为0.70。 91 | 92 | AUC: 93 | 0.696992481203 94 | -------------------------------------------------------------------------------- /常用数据集简介.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 数据和算法在NLP中都非常重要,使用公开的数据集可以帮助我们快速学习NLP相关知识并实践,下面我们将介绍常用的几个数据集。 3 | 4 | # 搜狗实验室数据 5 | 6 | ![常用数据集简介-图1](picture/常用数据集简介-图1.png "搜狗实验室数据") 7 | 8 | 搜狗实验室(Sogo Labs)是搜狗搜索核心研发团队对外交流的窗口,包含数据资源、数据挖掘云、研究合作等几个栏目。数据资源包括评测集合、语料数据、新闻数据、图片数据和自然语言处理相关数据,网址为: 9 | > http://www.sogou.com/labs/resource/list_pingce.php 10 | 11 | ## 互联网语料库(SogouT) 12 | 13 | SogouT来自互联网各种类型的1.3亿个原始网页, 压缩前的大小超过了5TB,格式如下: 14 | 15 | 16 | 17 | 页面ID 18 | 19 | 页面URL 20 | 21 | 页面原始内容 22 | 23 | 24 | 25 | 为了满足不同需求,SogouT分为了不同的版本,差别体现在数据量上: 26 | 27 | - 迷你版(样例数据, 61KB):tar.gz格式,zip格式 28 | - 完整版(1TB):(硬盘拷贝) 29 | - 历史版本(130GB):V2.0(硬盘拷贝) 30 | 31 | 32 | ## 全网新闻数据(SogouCA) 33 | 34 | SogouCA来自若干新闻站点2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息,格式如下: 35 | 36 | 37 | 38 | 页面URL 39 | 40 | 页面ID 41 | 42 | 页面标题 43 | 44 | 页面内容 45 | 46 | 47 | 48 | 为了满足不同需求,SogouCA分为了不同的版本,差别体现在数据量上: 49 | 50 | - 迷你版(样例数据, 101KB):tar.gz格式,zip格式 51 | - 完整版(711MB):tar.gz格式,zip格式 52 | - 历史版本: 53 | - 完整版(同时提供硬盘拷贝,1.02GB):tar.gz格式 54 | - 迷你版(样例数据, 3KB):tar.gz格式 55 | - 精简版(一个月数据, 437MB):tar.gz格式 56 | 57 | ## 搜狐新闻数据(SogouCS) 58 | 59 | SogouCS来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息,格式如下: 60 | 61 | 62 | 63 | 页面URL 64 | 65 | 页面ID 66 | 67 | 页面标题 68 | 69 | 页面内容 70 | 71 | 72 | 73 | 为了满足不同需求,SogouCS分为了不同的版本,差别体现在数据量上: 74 | 75 | - 迷你版(样例数据, 110KB):tar.gz格式,zip格式 76 | - 完整版(648MB):tar.gz格式,zip格式 77 | - 历史版本: 78 | - 完整版(同时提供硬盘拷贝,65GB):tar.gz格式 79 | - 迷你版(样例数据, 1KB):tar.gz格式 80 | - 精简版(一个月数据, 347MB):tar.gz格式 81 | - 特别版(王灿辉WWW08论文数据, 647KB):tar.gz格式 82 | 83 | ## 文本分类评价(SogouTCE) 84 | 85 | SogouTCE用以评估文本分类结果的正确性,语料来自搜狐等多个新闻网站近20个频道,格式如下: 86 | 87 | URL前缀\t对应类别标记 88 | 89 | SogouTCE只包含URL前缀和对应类别标记的数据,原始的文本数据可以使用SogouCA和SogouCS。 90 | 91 | ## 互联网词库(SogouW) 92 | 93 | SogouW来自于对SOGOU搜索引擎所索引到的中文互联网语料的统计分析,统计所进行的时间是2006年10月,涉及到的互联网语料规模在1亿页面以上。统计出的词条数约为15万条高频词,除标出这部分词条的词频信息之外,还标出了常用的词性信息,格式如下: 94 | 95 | 词A 词频 词性1 词性2 … 词性N 96 | 97 | 词B 词频 词性1 词性2 … 词性N 98 | 99 | 词C 词频 词性1 词性2 … 词性N 100 | 101 | # IMDB Reviews 102 | 103 | 互联网电影资料库(Internet Movie Database,简称IMDB)是一个关于电影演员、电影、电视节目、电视明星和电影制作的在线数据库。IMDB Reviews是记录了观众对IMDB中作品的评价。除了训练和测试评估示例之外,还有更多未标记的数据可供使用,包括文本和预处理的词袋格式。IMDB Reviews包含25,000个高度差异化的电影评论用于训练,25,000个测试,通常用于英文的情感理解。 104 | 105 | # Sentiment140 106 | Sentiment140是一个可用于情感分析的数据集,包含160,000条推文。一个流行的数据集,非常适合开始你的NLP旅程。情绪已经从数据中预先移除。最终的数据集具有以下6个特征: 107 | 108 | - 推文的极性 109 | - 推文的ID 110 | - 推文的日期 111 | - 问题 112 | - 推文的用户名 113 | - 推文的文本 114 | 115 | # Yelp Reviews 116 | Yelp Reviews是Yelp为了学习目的而发布的一个开源数据集。它包含了由数百万用户评论,商业属性和来自多个大都市地区的超过20万张照片。这是一个常用的全球NLP挑战数据集,包含5,200,000条评论,174,000条商业属性。 117 | 数据集下载地址为: 118 | 119 | 120 | >https://www.yelp.com/dataset/download 121 | 122 | ![常用数据集简介-图5](picture/常用数据集简介-图5.png) 123 | 124 | 数据集格式分为JSON和SQL两种,以JSON格式为例,其中最重要的review.json,包含评论数据,格式如下: 125 | 126 | 127 | { 128 | // string, 22 character unique review id 129 | "review_id": "zdSx_SD6obEhz9VrW9uAWA", 130 | 131 | // string, 22 character unique user id, maps to the user in user.json 132 | "user_id": "Ha3iJu77CxlrFm-vQRs_8g", 133 | 134 | // string, 22 character business id, maps to business in business.json 135 | "business_id": "tnhfDv5Il8EaGSXZGiuQGg", 136 | 137 | // integer, star rating 138 | "stars": 4, 139 | 140 | // string, date formatted YYYY-MM-DD 141 | "date": "2016-03-09", 142 | 143 | // string, the review itself 144 | "text": "Great place to hang out after work: the prices are decent, and the ambience is fun. It's a bit loud, but very lively. The staff is friendly, and the food is good. They have a good selection of drinks.", 145 | 146 | // integer, number of useful votes received 147 | "useful": 0, 148 | 149 | // integer, number of funny votes received 150 | "funny": 0, 151 | 152 | // integer, number of cool votes received 153 | "cool": 0} 154 | 155 | 另外tip.json文件记录了短评数据,格式如下: 156 | 157 | { 158 | // string, text of the tip 159 | "text": "Secret menu - fried chicken sando is da bombbbbbb Their zapatos are good too.", 160 | 161 | // string, when the tip was written, formatted like YYYY-MM-DD 162 | "date": "2013-09-20", 163 | 164 | // integer, how many likes it has 165 | "likes": 172, 166 | 167 | // string, 22 character business id, maps to business in business.json 168 | "business_id": "tnhfDv5Il8EaGSXZGiuQGg", 169 | 170 | // string, 22 character unique user id, maps to the user in user.json 171 | "user_id": "49JhAJh8vSQ-vM4Aourl0g" 172 | } 173 | 174 | 专门有个开源项目用于解析该JSON文件: 175 | 176 | >https://github.com/Yelp/dataset-examples 177 | 178 | # Enron-Spam 179 | Enron-Spam数据集是目前在电子邮件相关研究中使用最多的公开数据集,其邮件数据是安然公司(Enron Corporation, 原是世界上最大的综合性天然气和电力公司之一,在北美地区是头号天然气和电力批发销售商)150位高级管理人员的往来邮件。这些邮件在安然公司接受美国联邦能源监管委员会调查时被其公布到网上。机器学习领域使用Enron-Spam数据集来研究文档分类、词性标注、垃圾邮件识别等,由于Enron-Spam数据集都是真实环境下的真实邮件,非常具有实际意义。 180 | Enron-Spam数据集合如下图所示,使用不同文件夹区分正常邮件和垃圾邮件。 ![常用数据集简介-图3.png](picture/常用数据集简介-图3.png "Enron-Spam数据集文件夹结构") 正常邮件内容举例如下: 181 | 182 | > Subject: christmas baskets the christmas baskets have been ordered . we have ordered several baskets . individual earth - sat freeze - notis smith barney group baskets rodney keys matt rodgers charlie notis jon davis move team phillip randle chris hyde harvey freese faclities 183 | 垃圾邮件内容举例如下: 184 | 185 | > Subject: fw : this is the solution i mentioned lscoo 186 | thank you , 187 | your email address was obtained from a purchased list ,reference # 2020 mid = 3300 . if you wish to unsubscribe 188 | from this list , please click here and enter 189 | your name into the remove box . if you have previously unsubscribed and are still receiving this message , you may email our abuse control center , or call 1 - 888 - 763 - 2497 , or write us at : nospam , 6484 coral way , miami , fl , 33155 " . 2002 web credit inc . all rights reserved . 190 | 191 | Enron-Spam数据集对应的网址为: 192 | 193 | > http://www2.aueb.gr/users/ion/data/enron-spam/ # babi阅读理解数据集 194 | 195 | babi是来自FAIR(Facebook AI Research)的合成式阅读理解与问答数据集,是个入门级的阅读理解数据集,其训练集以对话集合[2] + 问题[1] + 回答[1]的形式组成: 196 | 197 | 1 Mary moved to the bathroom. 198 | 2 John went to the hallway. 199 | 3 Where is Mary? bathroom 1 200 | 4 Daniel went back to the hallway. 201 | 5 Sandra moved to the garden. 202 | 6 Where is Daniel? hallway 4 203 | 7 John moved to the office. 204 | 8 Sandra journeyed to the bathroom. 205 | 9 Where is Daniel? hallway 4 206 | 10 Mary moved to the hallway. 207 | 11 Daniel travelled to the office. 208 | 12 Where is Daniel? office 11 209 | 13 John went back to the garden. 210 | 14 John moved to the bedroom. 211 | 15 Where is Sandra? bathroom 8 212 | 1 Sandra travelled to the office. 213 | 2 Sandra went to the bathroom. 214 | 3 Where is Sandra? bathroom 2 215 | 4 Mary went to the bedroom. 216 | 5 Daniel moved to the hallway. 217 | 6 Where is Sandra? bathroom 2 218 | 7 John went to the garden. 219 | 8 John travelled to the office. 220 | 9 Where is Sandra? bathroom 2 221 | 10 Daniel journeyed to the bedroom. 222 | 11 Daniel travelled to the hallway. 223 | 12 Where is John? office 8 224 | 225 | 抽象表示格式为: 226 | 227 | ID text 228 | ID text 229 | ID text 230 | ID question[tab]answer[tab]supporting fact IDS. 231 | 232 | 项目主页地址为: 233 | 234 | > https://research.fb.com/downloads/babi/ 235 | 236 | 数据下载地址为: 237 | 238 | > http://www.thespermwhale.com/jaseweston/babi/tasks_1-20_v1.tar.gz 239 | 240 | 下载压缩包,解压后,全部文件保存在en文件下,文件夹下包含以下文件: 241 | 242 | qa10_indefinite-knowledge_test.txt 243 | qa1_single-supporting-fact_test.txt 244 | qa10_indefinite-knowledge_train.txt 245 | qa1_single-supporting-fact_train.txt 246 | qa11_basic-coreference_test.txt 247 | qa20_agents-motivations_test.txt 248 | qa11_basic-coreference_train.txt 249 | qa20_agents-motivations_train.txt 250 | qa12_conjunction_test.txt 251 | qa2_two-supporting-facts_test.txt 252 | qa12_conjunction_train.txt 253 | qa2_two-supporting-facts_train.txt 254 | qa13_compound-coreference_test.txt 255 | qa3_three-supporting-facts_test.txt 256 | qa13_compound-coreference_train.txt 257 | qa3_three-supporting-facts_train.txt 258 | qa14_time-reasoning_test.txt 259 | qa4_two-arg-relations_test.txt 260 | qa14_time-reasoning_train.txt 261 | qa4_two-arg-relations_train.txt 262 | qa15_basic-deduction_test.txt 263 | qa5_three-arg-relations_test.txt 264 | qa15_basic-deduction_train.txt 265 | qa5_three-arg-relations_train.txt 266 | qa16_basic-induction_test.txt 267 | qa6_yes-no-questions_test.txt 268 | qa16_basic-induction_train.txt 269 | qa6_yes-no-questions_train.txt 270 | qa17_positional-reasoning_test.txt 271 | qa7_counting_test.txt 272 | qa17_positional-reasoning_train.txt 273 | qa7_counting_train.txt 274 | qa18_size-reasoning_test.txt 275 | qa8_lists-sets_test.txt 276 | qa18_size-reasoning_train.txt 277 | qa8_lists-sets_train.txt 278 | qa19_path-finding_test.txt 279 | qa9_simple-negation_test.txt 280 | qa19_path-finding_train.txt 281 | qa9_simple-negation_train.txt 282 | 283 | # SMS Spam Collection 284 | SMS Spam Collection是用于骚扰短信识别的经典数据集,完全来自真实短信内容,包括4831条正常短信和747条骚扰短信。从官网下载数据集压缩包,解压,正常短信和骚扰短信保存在一个文本文件中。 285 | 每行完整记录一条短信内容,每行开头通过ham和spam标识正常短信和骚扰短信,数据集文件内容举例如下: 286 | > ham What you doing?how are you? ham Ok lar... Joking wif u oni... ham dun say so early hor... U c already then say... ham MY NO. IN LUTON 0125698789 RING ME IF UR AROUND! H* ham Siva is in hostel aha:-. ham Cos i was out shopping wif darren jus now n i called him 2 ask wat present he wan lor. Then he started guessing who i was wif n he finally guessed darren lor. spam FreeMsg: Txt: CALL to No: 86888 & claim your reward of 3 hours talk time to use from your phone now! ubscribe6GBP/ mnth inc 3hrs 16 stop?txtStop spam Sunshine Quiz! Win a super Sony DVD recorder if you canname the capital of Australia? Text MQUIZ to 82277. B spam URGENT! Your Mobile No 07808726822 was awarded a L2,000 Bonus Caller Prize on 02/09/03! This is our 2nd attempt to contact YOU! Call 0871-872-9758 BOX95QU SMS Spam Collection数据集主页地址为: >http://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection ![常用数据集](picture/常用数据集-图4.png) 287 | 288 | # UBUNTU DIALOG CORPUS 289 | UBUNTU DIALOG CORPUS(UDC)是可用的最大的公共对话数据集之一,下载地址为: 290 | 291 | >cs.mcgill.ca/~jpineau/datasets/ubuntu-corpus-1.0/ubuntu_dialogs.tgz 292 | 293 | 它基于公共IRC网络上的Ubuntu频道的聊天记录。训练数据包括100万个样例,50%的正样例(标签1)和50%的负样例(标签0)。每个样例都包含一个上下文,即直到这一点的谈话记录,以及一个话语,即对上下文的回应。一个正标签意味着话语是对当前语境上下文的实际响应,一个负标签意味着这个话语不是真实的响应 ,它是从语料库的某个地方随机挑选出来的。这是一些示例数据: 294 | 295 | ![常用数据集简介-图8](picture/常用数据集简介-图8.png) 296 | 297 | UDC相关的经典论文地址为: 298 | 299 | >https://arxiv.org/abs/1506.08909 300 | 301 | UDC相关的Github地址为: 302 | 303 | >https://github.com/rkadlec/ubuntu-ranking-dataset-creator 304 | 305 | # Hate speech identification 306 | Hate speech identification由ICWSM 2017论文“自动仇恨语音检测和无礼语言问题”的作者提供。包含3类短文本: 307 | 308 | - 包含仇恨言论; 309 | - 是冒犯性的,但没有仇恨言论; 310 | - 根本没有冒犯性。 311 | 312 | 由15,000行文本构成,每个字符串都经过3人判断。 313 | 314 | 下载链接为: 315 | 316 | >https://github.com/t-davidson/hate-speech-and-offensive-language 317 | 318 | # Twitter Progressive issues sentiment analysis 319 | 320 | Twitter Progressive issues sentiment analysis是关于诸如堕胎合法化、女权主义、希拉里·克林顿等各种左倾问题的推文,分为赞成、反对或保持中立的三种类别。 321 | 322 | 下载链接为: 323 | 324 | >https://www.figure-eight.com/data-for-everyone/ 325 | 326 | # 今日头条新闻文本分类数据集 327 | 今日头条新闻文本分类数据集共382688条,分布于15个分类中,分类code与名称: 328 | 329 | - 100 民生 故事 news_story 330 | - 101 文化 文化 news_culture 331 | - 102 娱乐 娱乐 news_entertainment 332 | - 103 体育 体育 news_sports 333 | - 104 财经 财经 news_finance 334 | - 106 房产 房产 news_house 335 | - 107 汽车 汽车 news_car 336 | - 108 教育 教育 news_edu 337 | - 109 科技 科技 news_tech 338 | - 110 军事 军事 news_military 339 | - 112 旅游 旅游 news_travel 340 | - 113 国际 国际 news_world 341 | - 114 证券 股票 stock 342 | - 115 农业 三农 news_agriculture 343 | - 116 电竞 游戏 news_game 344 | 345 | 数据格式为: 346 | 347 | 6552431613437805063_!_102_!_news_entertainment_!_谢娜为李浩菲澄清网络谣言, 348 | 349 | 之后她的两个行为给自己加分_!_佟丽娅,网络谣言,快乐大本营,李浩菲,谢娜,观众们 350 | 351 | 每行为一条数据,以_\!\_分割的个字段,从前往后分别是 新闻ID,分类code,分类名称,新闻字符串(仅含标题),新闻关键词. 352 | 353 | 项目主页在github上,运行get_data.py即可获取实时获取对应的数据。 354 | 355 | https://github.com/fateleak/toutiao-text-classfication-dataset 356 | 357 | 也可以直接使用github上的历史数据进行分析。 358 | 359 | https://github.com/fateleak/toutiao-text-classfication-dataset/raw/master/toutiao_cat_data.txt.zip -------------------------------------------------------------------------------- /打赏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/打赏.png -------------------------------------------------------------------------------- /打造NLP工具箱.md: -------------------------------------------------------------------------------- 1 | # Scikit-learn 2 | Scikit-learn是广受欢迎的入门级机器学习库,包含大量的机器学习算法和特征提取实现,使用非常简便。Scikit-learn实现的是浅层学习算法,神经网络仅实现了多层感知机。 3 | Scikit-learn的安装方式如下: 4 | 5 | pip install scikit-learn 6 | 7 | # TensorFlow 8 | TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,可被用于语音识别或图像识别等多项机器学习和深度学习领域.它可在小到一部智能手机、大到数千台数据中心服务器的各种设备上运行。TensorFlow的安装方式如下: 9 | 10 | pip install tensorflow 11 | 12 | # Keras 13 | Keras是一个高级别的Python神经网络框架,能在TensorFlow或者 Theano 上运行。Keras的作者、谷歌AI研究员Francois Chollet宣布了一条激动人心的消息,Keras将会成为第一个被添加到TensorFlow核心中的高级别框架,这将会让Keras变成Tensorflow的默认API。 14 | Keras的安装非常简便,使用pip工具即可。 15 | 16 | pip install keras 17 | 18 | 如果需要使用源码安装,可以直接从GitHub上下载对应源码。 19 | 20 | >https://github.com/fchollet/keras 21 | 22 | 23 | 然后进入Keras目录安装即可。 24 | 25 | python setup.py install 26 | 27 | # Anaconda 28 | Anaconda是一个用于科学计算的Python开发平台,支持 Linux,Mac和Windows系统,提供了包管理与环境管理的功能,可以很方便地解决多版本Python并存、切换以及各种第三方包安装问题。Anaconda利用conda命令来进行package和environment的管理,并且已经包含了Python和相关的配套工具。Anaconda集成了大量的机器学习库以及数据处理必不可少的第三方库,比如NumPy,SciPy,Scikit-Learn以及TensorFlow等。 29 | Anaconda的安装非常方便,从其官网的下载页面选择对应的安装包即可。 30 | 以我的Mac本为例,安装对应Anaconda安装包后,使用如下命令查看当前用户的profile文件的内容。 31 | 32 | cat ~/.bash_profile 33 | 34 | 我们可以发现在当前用户的profile文件的最后增加了如下内容,表明已经将Anaconda的bin目录下的命令添加到了PATH变量中,可以像使用系统命令一样直接使用Anaconda的命令行工具了。 35 | 36 | # added by Anaconda2 5.0.0 installer 37 | export PATH="/anaconda2/bin:$PATH" 38 | 39 | Anaconda强大的包管理以及多种Python环境并存使用主要以来于conda命令,常用的conda命令列举如下。 40 | 41 | # 创建一个名为python27的环境,指定Python版本是2.7 42 | conda create --name python27 python=2.7 43 | # 查看当前环境下已安装的包 44 | conda list 45 | # 查看某个指定环境的已安装包 46 | conda list -n python27 47 | # 查找package信息 48 | conda search numpy 49 | # 安装package 50 | conda install -n python27 numpy 51 | # 更新package 52 | conda update -n python27 numpy 53 | # 删除package 54 | conda remove -n python27 numpy 55 | 56 | 假设我们已经创建一个名为python27的环境,指定Python版本是2.7,激活该环境的方法如下。 57 | 58 | source activate python27 59 | 60 | 如果要退出该环境,命令如下所示。 61 | 62 | source deactivate 63 | 64 | 在python27的环境下查看Python版本,果然是2.7版本。 65 | 66 | maidou:3book liu.yan$ source activate python27 67 | (python27) maidou:3book liu.yan$ 68 | (python27) maidou:3book liu.yan$ python 69 | Python 2.7.14 |Anaconda, Inc.| (default, Oct 5 2017, 02:28:52) 70 | [GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin 71 | Type "help", "copyright", "credits" or "license" for more information. 72 | >>> 73 | 74 | 查看python27环境下默认安装了哪些包,为了避免显示内容过多,过滤前6行查看。 75 | 76 | conda list | head -6 77 | # packages in environment at /anaconda2/envs/python27: 78 | # 79 | ca-certificates 2017.08.26 ha1e5d58_0 80 | certifi 2017.7.27.1 py27h482ffc0_0 81 | libcxx 4.0.1 h579ed51_0 82 | libcxxabi 4.0.1 hebd6815_0 83 | 84 | 统计包的个数,除去2行的无关内容,当前环境下有16个包。 85 | 86 | conda list | wc -l 87 | 18 88 | 89 | 查看目前一共具有几个环境,发现除了系统默认的root环境,又多出了我们创建的python27环境。 90 | 91 | conda info --envs 92 | # conda environments: 93 | # 94 | python27 /anaconda2/envs/python27 95 | root * /anaconda2 96 | 97 | 在python27环境下安装Anaconda默认的全部安装包,整个安装过程会比较漫长,速度取决于你的网速。 98 | 99 | conda install anaconda 100 | Fetching package metadata ........... 101 | Solving package specifications: . 102 | Package plan for installation in environment /anaconda2/envs/python27: 103 | 104 | 继续统计包的个数,除去2行的无关内容,当前环境下已经有238个包了。 105 | 106 | conda list | wc -l 107 | 240 108 | 109 | Anaconda默认安装的第三方包里没有包含TensorFlow和Keras,需要使用命令手工安装,以TensorFlow为例,可以使用conda命令直接安装。 110 | 111 | conda install tensorflow 112 | 113 | 同时也可以使用pip命令直接安装。 114 | 115 | pip install tensorflow 116 | 117 | 本书一共创建了两个环境,分别是python27和python36,顾名思义对应的Python版本分别为2.7和3.6,用于满足不同案例对python版本的不同要求。 118 | 119 | # Gensim 120 | Gensim是一款开源的第三方Python工具包,用于从原始的非结构化的文本中,无监督地学习到文本隐层的主题向量表达。它支持包括TF-IDF,LSA,LDA,和word2vec在内的多种主题模型算法,支持流式训练,并提供了诸如相似度计算,信息检索等一些常用任务的API接口。 121 | Gensim的安装方式如下: 122 | 123 | pip install gensim 124 | 125 | # NTLK 126 | NLTK由Steven Bird和Edward Loper在宾夕法尼亚大学计算机和信息科学系开发,在NLP领域中,最常使用的一个Python库。 127 | NTLK的安装方式如下: 128 | 129 | pip install ntlk 130 | 131 | NTLK分为模型和数据两部分,其中数据需要单独下载。 132 | 133 | >>>import nltk 134 | >>>nltk.download() 135 | 136 | 推荐选择all,设置好下载路径,然后点击Download,系统就开始下载。NLTK的数据包了,下载的时间比较漫长,大家要耐心等待。如果有个别数据包无法下载,可以切换到All Packages标签页,双击指定的包来进行下载。 137 | 138 | # Jieba 139 | Jieba,经常被人昵称为结巴,是最受欢迎的中文分词工具,安装方式如下: 140 | 141 | pip install jieba 142 | 143 | 144 | # Jupyter notebook 145 | Jupyter notebook中使用Anaconda中的环境需要单独配置,默认情况下使用的是系统默认的Python环境,以使用advbox环境为例。 首先在默认系统环境下执行以下命令,安装ipykernel。 146 | 147 | conda install ipykernel 148 | conda install -n advbox ipykernel 149 | 150 | 在advbox环境下激活,这样启动后就可以在界面上看到advbox了。 151 | 152 | python -m ipykernel install --user --name advbox --display-name advbox 153 | 154 | 155 | 远程访问jupyter notebook 156 | ipython notebook是一个基于浏览器的python数据分析工具,使用起来非常方便,具有极强的交互方式和富文本的展示效果。jupyter是它的升级版,它的安装也非常方便,一般Anaconda安装包中会自带。安装好以后直接输入jupyter notebook便可以在浏览器中使用。但是它默认只能在本地访问,如果想把它安装在服务器上,然后在本地远程访问,则需要进行如下配置: 157 | 158 | 1. 登陆远程服务器 159 | 2. 生成配置文件 160 | 161 | $jupyter notebook --generate-config 162 | 163 | 164 | 3. 生成密码 165 | 打开ipython,创建一个密文的密码: 166 | 167 | In [1]: from notebook.auth import passwd 168 | In [2]: passwd() 169 | Enter password: 170 | Verify password: 171 | Out[2]: 'sha1:ce23d945972f:34769685a7ccd3d08c84a18c63968a41f1140274' 172 | 173 | 174 | 把生成的密文‘sha:ce…’复制下来 175 | 176 | 4. 修改默认配置文件 177 | 178 | $vim ~/.jupyter/jupyter_notebook_config.py 179 | 180 | 进行如下修改: 181 | 182 | c.NotebookApp.ip='*' 183 | c.NotebookApp.password = u'sha:ce...刚才复制的那个密文' 184 | c.NotebookApp.open_browser = False 185 | c.NotebookApp.port =8888 #随便指定一个端口 186 | 187 | 188 | 5. 启动jupyter notebook: 189 | 190 | $jupyter notebook 191 | 192 | 193 | # GPU服务器 194 | 当数据量大或者计算量大时,GPU几乎成为必选,尤其是使用CNN和RNN时,几乎就是CPU杀手。目前主流的云上都提供了GPU服务器。以百度云为例,默认支持的tensorflow的GPU版本是1.4。 195 | 当你习惯使用python2.*时,推荐使用的组合为: 196 | 197 | - tensorflow-gpu==1.4 198 | - keras==2.1.5 199 | - python==2.7 200 | 201 | 当你习惯使用python5.*时,推荐使用的组合为: 202 | 203 | - tensorflow-gpu==1.4 204 | - keras==2.1.5 205 | - python==3.5 206 | 207 | 手工安装深度学习库 208 | 209 | 有时候需要根据软硬件环境自己选择安装对应的深度学习库。其中最重要的是看cuDNN和CUDA的版本,查看服务器的cuDNN和CUDA版本的方法为: 210 | 211 | #cuda 版本 212 | cat /usr/local/cuda/version.txt 213 | #cudnn 版本 214 | cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 215 | #或者 216 | cat /usr/include/cudnn.h | grep CUDNN_MAJOR -A 2 217 | -------------------------------------------------------------------------------- /推荐更新源.md: -------------------------------------------------------------------------------- 1 | # apt更新源 2 | 编辑/etc/apt/sources-list 3 | 4 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted 5 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates main restricted 6 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial universe 7 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates universe 8 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial multiverse 9 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates multiverse 10 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse 11 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security main restricted 12 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security universe 13 | deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security multiverse 14 | 15 | 16 | # pip更新源 17 | 在用户目录下创建.pip文件夹,并创建pip.conf文件 18 | 19 | [global] 20 | trusted-host = pypi.mirrors.ustc.edu.cn 21 | index-url = https://pypi.mirrors.ustc.edu.cn/simple 22 | 23 | 或者 24 | 25 | [global] 26 | index-url = http://mirrors.aliyun.com/pypi/simple 27 | [install] 28 | trusted-host=mirrors.aliyun.com 29 | -------------------------------------------------------------------------------- /文档相似度.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 每年毕业季,各位学子都会使用一个叫做论文查重系统,来检测是否毕业论文存在抄袭嫌疑。如何衡量两篇论文之间的相似度呢?面对类似的问题,人们提出了文档相似度的概念。文档相似度指的是两篇文档之间的相似程度,也被称为文档距离。文档相似度通常是文本聚类、信息检索等NLP任务的基础,常见的计算文档距离的方法包括simhash和余弦距离。 3 | # simhash算法 4 | simhash是由Charikar在2002年提出来的,论文名为《Similarity estimation techniques from rounding algorithms》。Google基于simhash在海量网页中进行相似度计算并去重。通常对比两个文档是否相同时,会计算对应的hash值,常见的算法包括md5和sha256。实际使用中,对于检测文档是否被篡改时,使用hash值具有不错的表现。但是当文档内容因为修改少许文字,插入广告甚至只是修改了标点符合和错别字,都会导致hash值改变,可是文档的核心内容并未发生改变。如何使用数学的方法表征这种文档相似性呢?simhash的设计初衷就是使用一种所谓局部hash的方法,可以既可以敏感的识别文档的少许修改又可以识别出文档的大多数内容相同。 5 | 6 | simhash的一种典型实现就是将一个文档最后转换成一个64位的字节的特征字或者说simhash值,然后判断重复只需要判断他们的特征字的距离是不是小于3,就可以判断两个文档是否相似。这个距离使用海明距离,即两个simhash值取异或后二进制中1的个数。大家可以结合自身业务特点修改simhash值的位数以及判断文档相似性的海明距离的值。 7 | 8 | ![文档相似度-图1](picture/文档相似度-图1.png) 9 | 10 | 如图所示,计算6位simhash值典型的实现算法为: 11 | 12 | - 将Doc分词和计算权重,抽取出n个(关键词,权重)对,即图中的(feature, weight) 13 | - 计算关键词的hash,生成图中的(hash,weight),并将hash和weight相乘,这一过程是对hash值加权 14 | - 将hash和weight相乘的值相加,比如图中的[13, 108, -22, -5, -32, 55],并最终转换成simhash值110001,转换的规则为正数为1负数为0 15 | 16 | ## simhash库 17 | simhash具有多种实现,常用的一种已经部署在pip源上了,直接安装即可。 18 | 19 | pip install simhash 20 | 21 | 有兴趣的读者也可以使用源码安装。 22 | 23 | git clone https://github.com/leonsim/simhash 24 | cd simhash/ 25 | python setup.py 26 | python setup.py install 27 | 28 | ## 数据集 29 | 数据集依然使用搜狗实验室提供的"搜狐新闻数据",该数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息。 对应的网址为: 30 | 31 | > http://www.sogou.com/labs/resource/cs.php 32 | 33 | 为了处理方便,我们提取其中前1万条的正文信息,保存到如下文件中: 34 | 35 | > ../data/news_sohusite_content_10000.txt 36 | 37 | ## 数据清洗 38 | 为了避免开发环境的编码方式对结果的影响,设置环境的默认编码方式为utf8. 39 | 40 | reload(sys) 41 | sys.setdefaultencoding('utf8') 42 | 43 | 加载加载搜狐新闻语料. 44 | 45 | def load_sougou_content(): 46 | #with open("../data/news_sohusite_content.txt") as F: 47 | # 测试阶段仅加载前1w条记录 48 | with open("../data/news_sohusite_content_10000.txt") as F: 49 | content=F.readlines() 50 | F.close() 51 | return content 52 | 53 | 选择其第88篇文章为测试文章,在剩下的语料中寻找与其相似的文档。 54 | 55 | #加载搜狐新闻语料 56 | content=load_sougou_content() 57 | 58 | #设置测试文章 59 | print "select test data:" 60 | test_news=content[88] 61 | print test_news 62 | 63 | 测试文档的内容如下: 64 | 65 | > 南方日报 讯   ( 记者 / 黄少宏   实习生 / 朱子 煜   通讯员 / 成广伟 ) “ 学习 台北 垃圾 分类 模式 ” 要 全面铺开 , 部分 小区 试行 垃圾 费 “ 按袋 计量 征收 ” , 厨余 垃圾 专袋 收运 . . . . . . 这是 记者 昨日 从 广州市 城 管委 庆祝 建党 9 1 周年 暨 争先 创优 表彰大会 上 获悉 的 。 据 广州市 城 管委 主任 李廷贵 透露 , 7 月 1 0 日 将 召开 垃圾 分类 全市 动员大会 , 并 推出 一系列 政策 。 在 昨日 的 大会 上 , 针对 垃圾 围城 难题 , 李廷贵 透露 , 广州市委 、 市政府 已经 形成 决议 , 初步 决定 在 7 月 1 0 日 , 在 中山纪念堂 召开 动员大会 , 进行 全面 部署 , 全面 动员 , 正式 启动 垃圾 分类 , 并 将 形成 一整套 法律法规 和 规范 文件 、 配套 实施方案 。 李廷贵 提到 , 要 采取 5 种 垃圾 分类 技术 路线 , 其中 包括 按袋 计量 。 据介绍 , 广州 将 于 今年年底 前 选择 1 - 2 个 生活 小区 试点 “ 垃圾 费 按袋 计量 收费 ” 模式 , 厨余 垃圾 排放 免费 , 其他 垃圾 排放 计量 收费 。 至于 “ 垃圾 费 按袋 计量 收费 ” 政策 , 将 以 专用 垃圾袋 作为 收费 的 工具 , 市民 丢弃 垃圾 , 必须 购买 政府 制作 、 在 指定 地点 发售 的 专用 垃圾袋 盛装 , 再交 垃圾车 收运 。 产生 多少 垃圾 付 多少 钱 , 垃圾 越 少 , 缴费 就 越少 。 除 台北 “ 垃圾 不 落地 ” 模式 将 在 广州 进一步 推广 外 , “ 垃圾袋 实名制 、 垃圾 费 随袋 征收 ” 政策 也 将 在 广州 试点 运行 。 那么 , 垃圾袋 实名制 将 如何 推行 呢 ? 据介绍 , 广州 将 在 政府 特制 的 分类 垃圾袋 上 , 印 上 居民 的 住宅 地段 号 和 房号 , 以此 作为 识别 垃圾袋 出自 那家 哪户 的 标志 , 根据 该 标志 , 可以 追查 垃圾 投放 的 源头 。 如果 居民 没有 按照 规定 对 垃圾 分类 , 将 根据 标记 信息 追查 到 居民 个人 。 66 | 67 | 68 | 数据清洗过程,加载我们积累的中文停用词。 69 | 70 | def load_stopwords(): 71 | with open("stopwords.txt") as F: 72 | stopwords=F.readlines() 73 | F.close() 74 | return [word.strip() for word in stopwords] 75 | 76 | 为了避免停用词的影响,清洗阶段我们从数据集中删除停用词。 77 | 78 | # 加载积累的stopwords 79 | stopwords = load_stopwords() 80 | 81 | # 切割token并清除stopwords 82 | x = [[word for word in line.split() if word not in stopwords] for line in content] 83 | 84 | # 切割token并清除stopwords 85 | test_news = [word for word in test_news.split() if word not in stopwords] 86 | 87 | # 计算simhash 88 | test_news_hash = Simhash(test_news) 89 | 90 | ## 计算simhash值 91 | 92 | 依次计算语料库中每条记录的simhash,并记录下与测试数据的距离。 93 | 94 | sim=[] 95 | # 遍历语料计算simhash值 96 | for news in x: 97 | hash = Simhash(news) 98 | score=test_news_hash.distance(hash) 99 | sim.append( score) 100 | 101 | 选择距离最短的6的文档和序号并打印,因为需要按照score正序排列,需要设置key参数。 102 | 103 | for index, score in sorted(enumerate(sim), key=lambda item: item[1])[:6]: 104 | print "index:%d similarities:%f content:%s" % (index, score, content[index]) 105 | 106 | 排名第一的是距离为0的第88号文档,正好就是原文,排名第二的是序号为5644的文档,距离为4,可以发现讲的也是垃圾回收的内容。 107 | 108 | > 垃圾袋 印 房号 可追溯 源头 , 按袋 计量 收费 广州 将 试点 垃圾袋 实名制 垃圾袋 印 房号 可追溯 源头 , 按袋 计量 收费 “ 学习 台北 垃圾 分类 模式 ” 要 全面铺开 , 部分 小区 试行 垃圾 费 “ 按袋 计量 征收 ” , 厨余 垃圾 专袋 收运 … … 这是 记者 近日 从 广州市 城 管委 庆祝 建党 9 1 周年 暨 争先 创优 表彰大会 上 获悉 的 。 据 广州市 城 管委 主任 李廷贵 透露 , 7 月 1 0 日 将 召开 垃圾 分类 全市 动员大会 , 并 推出 一系列 政策 。 针对 垃圾 围城 难题 , 李廷贵 透露 , 广州市委 、 市政府 已经 形成 决议 , 初步 决定 在 7 月 1 0 日 , 在 中山纪念堂 召开 动员大会 , 进行 全面 部署 , 全面 动员 , 正式 启动 垃圾 分类 , 并 将 形成 一整套 法律法规 和 规范 文件 、 配套 实施方案 。 李廷贵 提到 , 要 采取 5 种 垃圾 分类 技术 路线 , 其中 包括 按袋 计量 。 据介绍 , 广州 将 于 今年年底 前 选择 1 - 2 个 生活 小区 试点 “ 垃圾 费 按袋 计量 收费 ” 模式 , 厨余 垃圾 排放 免费 , 其他 垃圾 排放 计量 收费 。 至于 “ 垃圾 费 按袋 计量 收费 ” 政策 , 将 以 专用 垃圾袋 作为 收费 的 工具 , 市民 丢弃 垃圾 , 必须 购买 政府 制作 、 在 指定 地点 发售 的 专用 垃圾袋 盛装 , 再交 垃圾车 收运 。 产生 多少 垃圾 付 多少 钱 , 垃圾 越 少 , 缴费 就 越少 。 109 | 110 | 111 | 112 | # 余弦距离 113 | 114 | 余弦距离,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。假设向量a、b的坐标分别为(x1,y1)、(x2,y2) ,则对应的余弦距离为: 115 | 116 | ![文档相似度-图2](picture/文档相似度-图2.png) 117 | 118 | 设向量 A = (A1,A2,...,An),B = (B1,B2,...,Bn) 。推广到多维: 119 | 120 | ![文档相似度-图3](picture/文档相似度-图3.png) 121 | 122 | 夹角越小,余弦值越接近于1,它们的方向更加吻合,则越相似。可见余弦距离在0和1之间且约接近1说明越两者越相似。 123 | 124 | ## 数据集 125 | 数据集也和simhash使用相同的数据集。 126 | 127 | ## 数据清洗 128 | 数据清洗方式与simhash类似,只不过多了一个TFIDF处理。 129 | 130 | # 加载积累的stopwords 131 | stopwords = load_stopwords() 132 | 133 | # 切割token并清除stopwords 134 | x = [[word for word in line.split() if word not in stopwords] for line in content] 135 | 136 | # 获取词袋 137 | dictionary = corpora.Dictionary(x) 138 | 139 | # 制作语料 140 | corpus = [dictionary.doc2bow(doc) for doc in x] 141 | 142 | # 进行TFIDF处理 143 | tfidf = models.TfidfModel(corpus) 144 | 145 | # 把测试文章转换成tfidf 146 | test_news_vec = [word for word in test_news.split() if word not in stopwords] 147 | 148 | test_news_vec = tfidf[dictionary.doc2bow(test_news_vec)] 149 | 150 | 词袋提取使用的是gensim的库,生成的矩阵为稀疏矩阵。 151 | 152 | ## 计算余弦距离 153 | gensim库封装实现了针对稀疏矩阵计算余弦距离的类,直接调用即可。由于需要按照score的倒序排列,所以使用key参数。 154 | 155 | index = similarities.SparseMatrixSimilarity(tfidf[corpus], num_features=len(dictionary.keys())) 156 | sim = index[tfidf[test_news_vec]] 157 | 158 | for index, score in sorted(enumerate(sim), key=lambda item: -item[1])[:6]: 159 | print "index:%d similarities:%f content:%s" % (index, score, content[index]) 160 | 161 | 排名第一的是距离为0.965616的第88号文档,正好就是原文,排名第二的是序号为5644的文档,距离为40.907202,可以发现讲的也是垃圾回收的内容。 162 | 163 | # 参考文献 164 | - https://yanyiwu.com/work/2014/01/30/simhash-shi-xian-xiang-jie.html 165 | -------------------------------------------------------------------------------- /文档聚类.md: -------------------------------------------------------------------------------- 1 | # 概念 2 | 文本聚类(Text clustering)文档聚类主要是依据著名的聚类假设:同类的文档相似度较大,而不同类的文档相似度较小。作为一种无监督的机器学习方法,聚类由于不需要训练过程,以及不需要预先对文档手工标注类别,因此具有一定的灵活性和较高的自动化处理能力,已经成为对文本信息进行有效地组织、摘要和导航的重要手段,为越来越多的研究人员所关注。 3 | 4 | # 数据集 5 | 本文演示用的数据集,依然使用搜狗新闻数据集SogouCS。我们从SogouCS中提取正文内容,每个URL对应的正文当做一篇文档,并且使用jieba进行了分词。演示期间我们提取SogouCS的前10000条数据用于计算LDA。 6 | # 特征提取 7 | 通常会在LDA的基础上进行文本聚类,即把文本内容删除停用词、提取词袋并进行TFIDF后再使用LDA算法转换成若干主题的概率分布,然后使用聚类算法分析。关于LDA的内容请参考本书中关于使用LDA进行文档主题建模的章节。 8 | 9 | lda = models.ldamulticore.LdaMulticore(corpus=texts_tf_idf, id2word=dictionary, num_topics=num_topics) 10 | 11 | # 衡量指标 12 | 无监督学习的性能衡量方式不像监督学习那样直观,目前常见的衡量方式包括一下几种: 13 | ## 轮廓系数 14 | 聚类中还有一种评估方法,叫轮廓系数。轮廓系数综合了聚类后的两项因素:内聚度和分离度。内聚度就指一个样本在簇内的不相似度,而分离度就指一个样本在簇间的不相似度。在scikit-learn中,同样也提供了直接计算轮廓系数的方法。 15 | 16 | from sklearn.metrics import silhouette_score 17 | 18 | # 聚类算法 19 | 20 | 常用于文本聚类的算法包括kmeans、dbscan等。 21 | 22 | ## dbscan 23 | dbscan(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据库中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。该算法利用基于密度的聚类的概念,即要求聚类空间中的一定区域内所包含对象(点或其他空间对象)的数目不小于某一给定阈值。dbscan算法的显著优点是聚类速度快且能够有效处理噪声点和发现任意形状的空间聚类。 24 | 25 | # 参考文献 -------------------------------------------------------------------------------- /知识星球.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duoergun0729/nlp/b6d2ecf5545d8c8d203ff03ef8b74c9d436f8f82/知识星球.png -------------------------------------------------------------------------------- /自己动手训练word2vec模型.md: -------------------------------------------------------------------------------- 1 | # 训练语料 2 | word2vec的算法是公开的,word2vec模型的质量完全取决于训练语料的质量。目前免费开放的预料不多,中文语料更是凤毛麟角。 3 | 4 | ![使用搜狗新闻预料生成word2vec-图1](picture/使用搜狗新闻预料生成word2vec-图1.png) 5 | 6 | 这里推荐使用搜狗实验室的中文语料,对应的网址为: 7 | 8 | http://www.sogou.com/labs/resource/cs.php 9 | 10 | 通常使用"搜狐新闻数据"即可,该数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据,提供URL和正文信息。 11 | 12 | ## 数据格式 13 | 14 | 15 | 16 | 页面URL 17 | 18 | 页面ID 19 | 20 | 页面标题 21 | 22 | 页面内容 23 | 24 | 25 | 26 | 注意:content字段去除了HTML标签,保存的是新闻正文文本 27 | 28 | ## 数据文件 29 | 搜狐新闻数据区根据文件格式和数据规模细分为以下几种: 30 | 31 | - 迷你版(样例数据, 110KB):tar.gz格式,zip格式 32 | 33 | - 完整版(648MB):tar.gz格式,zip格式 34 | 35 | - 历史版本:2008版(6KB):完整版(同时提供硬盘拷贝,65GB):tar.gz格式 36 | 37 | # 数据预处理 38 | ## 提取中文内容 39 | 原始数据中包含完整的html文件,所以需要提取其中的中文内容,通常提取其中``标签包含的内容即可。 40 | 41 | tar -zxvf news_sohusite_xml.full.tar.gz 42 | cat news_sohusite_xml.dat | iconv -f gb18030 -t utf-8 | grep "" > news_sohusite.txt 43 | sed -i "" 's///g' news_sohusite.txt 44 | sed -i "" 's/<\/content>//g' news_sohusite.txt 45 | 46 | 其中iconv命令的格式为: 47 | 48 | iconv -f encoding [-t encoding] [inputfile]... 49 | 50 | 参数含义为: 51 | 52 | - -f encoding :把字符从encoding编码开始转换。 53 | - -t encoding :把字符转换到encoding编码。 54 | - -l :列出已知的编码字符集合 55 | - -o file :指定输出文件 56 | - -c :忽略输出的非法字符 57 | - -s :禁止警告信息,但不是错误信息 58 | - --verbose :显示进度信息 59 | - -f和-t所能指定的合法字符在-l选项的命令里面都列出来了 60 | 61 | ## 中文切词 62 | 与处理英文不同,中文没有切词,需要使用jieba进行切词处理。 63 | 64 | python -m jieba -d ' ' news_sohusite.txt > news_sohusite_cutword.txt 65 | 66 | # 训练word2vec 67 | 完成预处理后,即可以利用gensim库进行训练。 68 | 69 | def train_word2vec(filename): 70 | #模型文件不存在才处理 71 | if not os.path.exists(word2vec_file): 72 | sentences = LineSentence(filename) 73 | #sg=0 使用cbow训练, sg=1对低频词较为敏感 74 | model = Word2Vec(sentences, 75 | size=n_dim, window=5, min_count=2, sg=1, workers=2) 76 | model.save(word2vec_file) 77 | 78 | Word2Vec函数常见的几个参数含义如下: 79 | 80 | - sentences表示需要处理的语料 81 | - size表示word2vec的维数,一般50-300 82 | - window表示处理word时的窗口长度 83 | - min_count表示处理分析的word出现的最小次数 84 | - sg为1表示使用skip-gram算法,为0为cbow 85 | - workers表示计算使用的线程数 86 | - iter表示迭代计算的次数 87 | 88 | # 使用word2vec处理中文 89 | 把一个中文句子使用词向量表示的方法。对于类似短信、微博、标题这些长度较短的文字,可以使用各个word的word2vec相加取平均来表示。对训练数据集创建词向量,接着进行比例缩放(scale)。 90 | 91 | def buildWordVector(imdb_w2v,text, size): 92 | vec = np.zeros(size).reshape((1, size)) 93 | count = 0. 94 | #print text 95 | for word in text.split(): 96 | #print word 97 | try: 98 | vec += imdb_w2v[word].reshape((1, size)) 99 | count += 1. 100 | except KeyError: 101 | print word 102 | continue 103 | if count != 0: 104 | vec /= count 105 | return vec 106 | 107 | 当需要把中文数据集X转换成word2vec,可以使用如下方式。 108 | 109 | #加载训练好的词向量模型 110 | model = Word2Vec.load(word2vec_file) 111 | 112 | x_vecs = np.concatenate([buildWordVector(model,z, n_dim) for z in x]) 113 | x_vecs = scale(x_vecs) 114 | 115 | # 测试效果 116 | 下面我们测试生成的word2vec模型的质量。 117 | ## 寻找近义词 118 | 寻找近义词是word2vec的一个应用场景。 119 | ### 百度的近义词 120 | print pd.Series(model.most_similar(u'百度')) 121 | 0 (网易, 0.844283640385) 122 | 1 (搜索引擎, 0.822018146515) 123 | 2 (腾讯, 0.774820387363) 124 | 3 (搜狗, 0.76777946949) 125 | 4 (新浪, 0.760137319565) 126 | 5 (奇虎, 0.745484173298) 127 | 6 (文库, 0.725166857243) 128 | 7 (手机软件, 0.717750906944) 129 | 8 (优酷, 0.70574760437) 130 | 9 (客户端, 0.70448333025) 131 | ### 微信的近义词 132 | print pd.Series(model.most_similar(u'微信')) 133 | 0 (摇一摇, 0.768034994602) 134 | 1 (陌陌, 0.763847649097) 135 | 2 (网上聊天, 0.751431167126) 136 | 3 (聊天工具, 0.731707036495) 137 | 4 (盗号, 0.722806692123) 138 | 5 (飞聊, 0.715048789978) 139 | 6 (手机, 0.706719994545) 140 | 7 (发短信, 0.704942345619) 141 | 8 (聊天, 0.691777765751) 142 | 9 (账号密码, 0.679741084576) 143 | 144 | ## 单词运算 145 | word2vec的一个神奇之处就是把文字转换成了数字,数字之间的加减运算,同样适用于word2vec。 146 | ### 足球+明星 147 | print pd.Series(model.most_similar(positive=[u'足球'+u'明星'])) 148 | 0 (巨星, 0.741350233555) 149 | 1 (光芒万丈, 0.727712750435) 150 | 2 (和亨利, 0.722848057747) 151 | 3 (球星, 0.722578346729) 152 | 4 (已贵, 0.71345859766) 153 | 5 (格米利, 0.694822609425) 154 | 6 (支斯篮, 0.690492749214) 155 | 7 (田坛, 0.689639627934) 156 | 8 (体坛, 0.689606904984) 157 | 9 (竞神锋, 0.684816122055) 158 | 159 | 160 | ### 球星-明星 161 | 162 | print pd.Series(model.most_similar(positive=[u'球星'],negative=[u'明星'])) 163 | dtype: object 164 | 0 (国际米兰, 0.492849290371) 165 | 1 (中锋, 0.480526059866) 166 | 2 (球员, 0.479797780514) 167 | 3 (上赛季, 0.479528963566) 168 | 4 (主帅, 0.479275196791) 169 | 5 (球队, 0.477513790131) 170 | 6 (德里奇, 0.474446773529) 171 | 7 (热那亚, 0.472252100706) 172 | 8 (中场, 0.459134191275) 173 | 9 (巴萨, 0.45858669281) 174 | 175 | ## 比较单词的相似度 176 | 177 | ### 比较微信和陌陌 178 | 179 | print model.wv.similarity(u'微信', u'陌陌') 180 | 0.763847656891 181 | 182 | ### 比较男人和坏人 183 | 184 | print model.wv.similarity(u'男人', u'坏人') 185 | 0.617036796702 -------------------------------------------------------------------------------- /词袋模型和TFIDF模型.md: -------------------------------------------------------------------------------- 1 | # 词袋模型 2 | 文本特征提取有两个非常重要的模型: 3 | 4 | - 词集模型:单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个。 5 | 6 | - 词袋模型:在词集的基础上如果一个单词在文档中出现不止一次,统计其出现的次数(频数)。 7 | 8 | 两者本质上的区别,词袋是在词集的基础上增加了频率的维度,词集只关注有和没有,词袋还要关注有几个。 9 | 假设我们要对一篇文章进行特征化,最常见的方式就是词袋。 10 | 导入相关的函数库: 11 | 12 | >>> from sklearn.feature_extraction.text import CountVectorizer 13 | 14 | 实例化分词对象: 15 | 16 | >>> vectorizer = CountVectorizer(min_df=1) 17 | >>> vectorizer 18 | CountVectorizer(analyzer=...'word', binary=False, decode_error=...'strict', 19 | dtype=<... 'numpy.int64'>, encoding=...'utf-8', input=...'content', 20 | lowercase=True, max_df=1.0, max_features=None, min_df=1, 21 | ngram_range=(1, 1), preprocessor=None, stop_words=None, 22 | strip_accents=None, token_pattern=...'(?u)\\b\\w\\w+\\b', 23 | tokenizer=None, vocabulary=None) 24 | 25 | 将文本进行词袋处理: 26 | 27 | >>> corpus = [ 28 | ... 'This is the first document.', 29 | ... 'This is the second second document.', 30 | ... 'And the third one.', 31 | ... 'Is this the first document?', 32 | ... ] 33 | >>> X = vectorizer.fit_transform(corpus) 34 | >>> X 35 | <4x9 sparse matrix of type '<... 'numpy.int64'>' 36 | with 19 stored elements in Compressed Sparse ... format> 37 | 38 | 获取对应的特征名称: 39 | 40 | >>> vectorizer.get_feature_names() == ( 41 | ... ['and', 'document', 'first', 'is', 'one', 42 | ... 'second', 'the', 'third', 'this']) 43 | True 44 | 45 | 获取词袋数据,至此我们已经完成了词袋化: 46 | 47 | >>> X.toarray() 48 | array([[0, 1, 1, 1, 0, 0, 1, 0, 1], 49 | [0, 1, 0, 1, 0, 2, 1, 0, 1], 50 | [1, 0, 0, 0, 1, 0, 1, 1, 0], 51 | [0, 1, 1, 1, 0, 0, 1, 0, 1]]...) 52 | 53 | 但是如何可以使用现有的词袋的特征,对其他文本进行特征提取呢?我们定义词袋的特征空间叫做词汇表vocabulary: 54 | 55 | vocabulary=vectorizer.vocabulary_ 56 | 57 | 针对其他文本进行词袋处理时,可以直接使用现有的词汇表: 58 | 59 | >>> new_vectorizer = CountVectorizer(min_df=1, vocabulary=vocabulary) 60 | 61 | CountVectorize函数比较重要的几个参数为: 62 | 63 | - decode_error,处理解码失败的方式,分为‘strict’、‘ignore’、‘replace’三种方式。 64 | - strip_accents,在预处理步骤中移除重音的方式。 65 | - max_features,词袋特征个数的最大值。 66 | - stop_words,判断word结束的方式。 67 | - max_df,df最大值。 68 | - min_df,df最小值 。 69 | - binary,默认为False,当与TF-IDF结合使用时需要设置为True。 70 | 本例中处理的数据集均为英文,所以针对解码失败直接忽略,使用ignore方式,stop_words的方式使用english,strip_accents方式为ascii方式。 71 | 72 | # TF-IDF模型 73 | 文本处理领域还有一种特征提取方法,叫做TF-IDF模型(term frequency–inverse document frequency,词频与逆向文件频率)。TF-IDF是一种统计方法,用以评估某一字词对于一个文件集或一个语料库的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。 74 | TF-IDF的主要思想是,如果某个词或短语在一篇文章中出现的频率TF(Term Frequency,词频),词频高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF。TF表示词条在文档d中出现的频率。IDF(inverse document frequency,逆向文件频率)的主要思想是:如果包含词条t的文档越少,也就是n越小,IDF越大,则说明词条t具有很好的类别区分能力。如果某一类文档C中包含词条t的文档数为m,而其他类包含t的文档总数为k,显然所有包含t的文档数n=m+k,当m大的时候,n也大,按照IDF公式得到的IDF的值会小,就说明该词条t类别区分能力不强。但是实际上,如果一个词条在一个类的文档中频繁出现,则说明该词条能够很好代表这个类的文本的特征,这样的词条应该给它们赋予较高的权重,并选来作为该类文本的特征词以区别与其他类文档。 75 | 在Scikit-Learn中实现了TF-IDF算法,实例化TfidfTransformer即可: 76 | 77 | >>> from sklearn.feature_extraction.text import TfidfTransformer 78 | >>> transformer = TfidfTransformer(smooth_idf=False) 79 | >>> transformer 80 | TfidfTransformer(norm=...'l2', smooth_idf=False, sublinear_tf=False, use_idf=True) 81 | TF-IDF模型通常和词袋模型配合使用,对词袋模型生成的数组进一步处理: 82 | 83 | >>> counts = [[3, 0, 1], 84 | ... [2, 0, 0], 85 | ... [3, 0, 0], 86 | ... [4, 0, 0], 87 | ... [3, 2, 0], 88 | ... [3, 0, 2]] 89 | ... 90 | >>> tfidf = transformer.fit_transform(counts) 91 | >>> tfidf 92 | <6x3 sparse matrix of type '<... 'numpy.float64'>' with 9 stored elements in Compressed Sparse ... format> 93 | >>> tfidf.toarray() 94 | array([[ 0.81940995, 0. , 0.57320793], 95 | [ 1. , 0. , 0. ], 96 | [ 1. , 0. , 0. ], 97 | [ 1. , 0. , 0. ], 98 | [ 0.47330339, 0.88089948, 0. ], 99 | [ 0.58149261, 0. , 0.81355169]]) 100 | 101 | # 词汇表模型 102 | 词袋模型可以很好的表现文本由哪些单词组成,但是却无法表达出单词之间的前后关系,于是人们借鉴了词袋模型的思想,使用生成的词汇表对原有句子按照单词逐个进行编码。TensorFlow默认支持了这种模型: 103 | 104 | tf.contrib.learn.preprocessing.VocabularyProcessor ( 105 | max_document_length, 106 | min_frequency=0, 107 | vocabulary=None, 108 | tokenizer_fn=None) 109 | 其中各个参数的含义为: 110 | 111 | - max_document_length:,文档的最大长度。如果文本的长度大于最大长度,那么它会被剪切,反之则用0填充。 112 | - min_frequency,词频的最小值,出现次数小于最小词频则不会被收录到词表中。 113 | - vocabulary,CategoricalVocabulary 对象。 114 | - tokenizer_fn,分词函数。 115 | 116 | 假设有如下句子需要处理: 117 | 118 | x_text =[ 119 | 'i love you', 120 | 'me too' 121 | ] 122 | 123 | 基于以上句子生成词汇表,并对'i me too'这句话进行编码: 124 | 125 | vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length) 126 | vocab_processor.fit(x_text) 127 | print next(vocab_processor.transform(['i me too'])).tolist() 128 | x = np.array(list(vocab_processor.fit_transform(x_text))) 129 | print x 130 | 131 | 运行程序,x_text使用词汇表编码后的数据为: 132 | [[1 2 3 0] 133 | [4 5 0 0]] 134 | 'i me too'这句话编码的结果为: 135 | [1, 4, 5, 0] 136 | 整个过程如下图所示。 137 | 138 | ![image](picture/词袋模型和TFIDF模型-图1.png) 139 | -------------------------------------------------------------------------------- /预测Yelp美食评分.md: -------------------------------------------------------------------------------- 1 | # Yelp简介 2 | Yelp是美国著名商户点评网站,创立于2004年,囊括各地餐馆、购物中心、酒店、旅游等领域的商户,用户可以在Yelp网站中给商户打分,提交评论,交流购物体验等。在Yelp中搜索一个餐厅或者旅馆,能看到它的简要介绍以及网友的点论,点评者还会给出多少星级的评价,通常点评者都是亲身体验过该商户服务的消费者,评论大多形象细致。 3 | # Yelp Reviews 4 | Yelp Reviews是Yelp为了学习目的而发布的一个开源数据集。它包含了由数百万用户评论,商业属性和来自多个大都市地区的超过20万张照片。这是一个常用的全球NLP挑战数据集,包含5,200,000条评论,174,000条商业属性。 数据集下载地址为: 5 | 6 | > https://www.yelp.com/dataset/download 7 | 8 | Yelp Reviews格式分为JSON和SQL两种,以JSON格式为例,其中最重要的review.json,包含评论数据,格式如下: 9 | 10 | { 11 | // string, 22 character unique review id 12 | "review_id": "zdSx_SD6obEhz9VrW9uAWA", 13 | 14 | // string, 22 character unique user id, maps to the user in user.json 15 | "user_id": "Ha3iJu77CxlrFm-vQRs_8g", 16 | 17 | // string, 22 character business id, maps to business in business.json 18 | "business_id": "tnhfDv5Il8EaGSXZGiuQGg", 19 | 20 | // integer, star rating 21 | "stars": 4, 22 | 23 | // string, date formatted YYYY-MM-DD 24 | "date": "2016-03-09", 25 | 26 | // string, the review itself 27 | "text": "Great place to hang out after work: the prices are decent, and the ambience is fun. It's a bit loud, but very lively. The staff is friendly, and the food is good. They have a good selection of drinks.", 28 | 29 | // integer, number of useful votes received 30 | "useful": 0, 31 | 32 | // integer, number of funny votes received 33 | "funny": 0, 34 | 35 | // integer, number of cool votes received 36 | "cool": 0} 37 | 38 | # 数据清洗 39 | Yelp Reviews文件格式为JSON和SQL,使用起来并不是十分方便。专门有个开源项目用于解析该JSON文件: 40 | 41 | > https://github.com/Yelp/dataset-examples 42 | 43 | 该项目可以将Yelp Reviews的Yelp Reviews转换成CSV格式,便于进一步处理,该项目的安装非常简便,同步完项目后直接安装即可。 44 | 45 | git clone https://github.com/Yelp/dataset-examples 46 | python setup.py install 47 | 48 | 假如需要把review.json转换成CSV格式,命令如下: 49 | 50 | python json_to_csv_converter.py /dataset/yelp/dataset/review.json 51 | 52 | 命令执行完以后,就会在review.json相同目录下生成对应的CSV文件review.csv。查看该CSV文件的表头,内容如下,其中最重要的两个字段就是text和stars,分别代表评语和打分。 53 | 54 | #CSV格式表头内容: 55 | #funny,user_id,review_id,text,business_id,stars,date,useful,cool 56 | 57 | 使用pandas读取该CSV文件,开发阶段可以指定仅读取前10000行。 58 | 59 | #开发阶段读取前10000行 60 | df = pd.read_csv(filename,sep=',',header=0,nrows=10000) 61 | 62 | pandas的可以配置的参数非常多,其中比较重要的几个含义如下: 63 | 64 | - sep : str, default ‘,’。指定分隔符。 65 | - header: int or list of ints, default ‘infer’。指定行数用来作为列名,数据开始行数。如果文件中没有列名,设置为None。设置为0则认为第0行是列名 66 | - nrows : int, default None 需要读取的行数(从文件头开始算起) 67 | - skiprows : list-like or integer, default None。需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。 68 | - skip\_blank\_lines : boolean, default True。如果为True,则跳过空行;否则记为NaN 69 | 70 | 按照列名直接获取数据,读取评论内容和打分结果,使用list转换成list对象。 71 | 72 | text=list(df['text']) 73 | stars=list(df['stars']) 74 | 75 | 查看打分结果的分布。 76 | 77 | #显示各个评分的个数 78 | print df.describe() 79 | 80 | 分布结果如下,一共有10000个评分,最高分5分,最低1分,平均得分为3.74。 81 | 82 | 83 | funny stars useful cool 84 | count 10000.000000 10000.000000 10000.000000 10000.000000 85 | mean 0.649800 3.743800 1.669500 0.777800 86 | std 1.840679 1.266381 3.059511 1.958625 87 | min 0.000000 1.000000 0.000000 0.000000 88 | 25% 0.000000 3.000000 0.000000 0.000000 89 | 50% 0.000000 4.000000 1.000000 0.000000 90 | 75% 1.000000 5.000000 2.000000 1.000000 91 | max 46.000000 5.000000 95.000000 43.000000 92 | 93 | 94 | pandas下面分析数据的分布非常方便,而且可以支持可视化。以分析stars评分的分布为例,首先按照stars评分统计各个评分的个数。 95 | 96 | #绘图 97 | plt.figure() 98 | count_classes=pd.value_counts(df['stars'],sort=True) 99 | 100 | 然后使用pandas的内置函数进行绘图,横轴是stars评分,纵轴是对应的计数。 101 | 102 | print "各个star的总数:" 103 | print count_classes 104 | count_classes.plot(kind='bar',rot=0) 105 | plt.xlabel('stars') 106 | plt.ylabel('stars counts') 107 | plt.savefig("yelp_stars.png") 108 | 109 | 110 | 在Mac系统下运行可能会有如下报错。 111 | 112 | > 113 | RuntimeError: Python is not installed as a framework. The Mac OS X backend will not be able to function correctly if Python is not installed as a framework. See the Python documentation for more information on installing Python as a framework on Mac OS X. Please either reinstall Python as a framework, or try one of the other backends. If you are using (Ana)Conda please install python.app and replace the use of ‘python‘ with ‘pythonw‘. See ‘Working with Matplotlib on OSX‘ in the Matplotlib FAQ for more information. 114 | 115 | 处理方式为: 116 | 117 | - 打开终端,输入cd ~/.matplotlib 118 | - 新建文件vi matplotlibrc 119 | - 文件中添加内容 backend: TkAgg 120 | 121 | 再次运行程序,得到可视化的图表,可以发现大多数人倾向打4-5分。 122 | 123 | ![预测Yelp美食评分-图1.png](picture/预测Yelp美食评分-图1.png) 124 | 125 | 各个评分的具体计数分别为: 126 | 127 | 各个star的总数: 128 | 5 3555 129 | 4 2965 130 | 3 1716 131 | 2 891 132 | 1 873 133 | 134 | 135 | 在进行情感分析的时候,通常可以把3分及其以上的认为是积极,标记为1,1-2分认为是消极,标记为0。 136 | 137 | stars=[ 0 if star < 3 else 1 for star in stars ] 138 | 139 | 经过处理后,统计情感分类的结果。 140 | 141 | print "情感分类的总数:" 142 | count_classes = pd.value_counts(stars, sort=True) 143 | print count_classes 144 | 145 | 结果显示,积极的占将近80%。 146 | 147 | 情感分类的总数: 148 | 1 8236 149 | 0 1764 150 | 151 | 可视化情感分类的数据,结果如下。 152 | 153 | ![预测Yelp美食评分-图3.png](picture/预测Yelp美食评分-图3.png) 154 | 155 | 156 | 另外需要主要的是,如果数据清洗过程中出现编码错误,示例如下: 157 | 158 | UnicodeDecodeError: 'utf8' codec can't decode byte 0xc3 in position 18: unexpected end of data 159 | 160 | 遇到类似问题的解决方法是,解析CSV文件时强制使用utf8格式。 161 | 162 | df = pd.read_csv(filename,sep=',',header=0,encoding='utf-8',nrows=200000) 163 | 164 | 另外在代码文件的前几行中显示设置编码格式为utf8。 165 | 166 | #coding=utf-8 167 | import sys 168 | #处理编码问题 169 | reload(sys) 170 | sys.setdefaultencoding('utf-8') 171 | 172 | # 特征提取 173 | 174 | ## 词袋模型 175 | 176 | 最简单的一种特征提取方式就是词袋模型,scikit-learn下有完整的封装。 177 | 178 | # 切割词袋 删除英文停用词 179 | vectorizer = CountVectorizer(ngram_range=(1, 1), max_features=max_features,stop_words='english',lowercase=True) 180 | 181 | 词袋模型的一种变形就是ngram,提取的特征是相邻的若干单词,最常见的就是2-gram,表示前后两个单词,在scikit-learn的实现为: 182 | 183 | # 切割词袋 删除英文停用词 184 | vectorizer = CountVectorizer(ngram_range=(2, 2), max_features=max_features,stop_words='english',lowercase=True) 185 | 186 | ## 词袋模型结合TF-IDF模型 187 | 188 | 词袋模型通常可以和TF-IDF模型一起使用,用于提升分类效果。 189 | 190 | # 该类会统计每个词语的tf-idf权值 191 | transformer = TfidfTransformer() 192 | # 使用2-gram和TFIDF处理 193 | x = transformer.fit_transform(vectorizer.fit_transform(text)) 194 | 195 | ## 词袋序列模型 196 | 词袋序列模型是在词袋模型的基础上发展而来的,相对于词袋模型,词袋序列模型可以反映出单词在句子中的前后关系。keras中通过Tokenizer类实现了词袋序列模型,这个类用来对文本中的词进行统计计数,生成文档词典,以支持基于词典位序生成文本的向量表示,创建该类时,需要设置词典的最大值。 197 | 198 | tokenizer = Tokenizer(num_words=None) 199 | 200 | Tokenizer类的成员函数为: 201 | 202 | - fit_on_text(texts) 使用一系列文档来生成token词典,texts为list类,每个元素为一个文档。 203 | - texts_to_sequences(texts) 将多个文档转换为word下标的向量形式,shape为[len(texts),len(text)] -- (文档数,每条文档的长度) 204 | - texts_to_matrix(texts) 将多个文档转换为矩阵表示,shape为[len(texts),num_words] 205 | 206 | Tokenizer类的示例代码如下: 207 | 208 | 209 | from keras.preprocessing.text import Tokenizer 210 | 211 | text1='some thing to eat' 212 | text2='some thing to drink' 213 | texts=[text1,text2] 214 | 215 | tokenizer = Tokenizer(num_words=None) 216 | #num_words:None或整数,处理的最大单词数量。少于此数的单词丢掉 217 | tokenizer.fit_on_texts(texts) 218 | 219 | # num_words=多少会影响下面的结果,行数=num_words 220 | print( tokenizer.texts_to_sequences(texts)) 221 | #得到词索引[[1, 2, 3, 4], [1, 2, 3, 5]] 222 | print( tokenizer.texts_to_matrix(texts)) 223 | # 矩阵化=one_hot 224 | [[ 0., 1., 1., 1., 1., 0., 0., 0., 0., 0.], 225 | [ 0., 1., 1., 1., 0., 1., 0., 0., 0., 0.]] 226 | 227 | 在处理Yelp数据集时,把每条评论看成一个词袋序列,且长度固定。超过固定长度的截断,不足的使用0补齐。 228 | 229 | #转换成词袋序列,max_document_length为序列的最大长度 230 | max_document_length=200 231 | 232 | #设置分词最大个数 即词袋的单词个数 233 | tokenizer = Tokenizer(num_words=max_features) 234 | tokenizer.fit_on_texts(text) 235 | sequences = tokenizer.texts_to_sequences(text) 236 | #截断补齐 237 | x=pad_sequences(sequences, maxlen=max_document_length) 238 | 239 | 第一次使用nltk资源时,需要进行下载。 240 | 241 | Python 2.7.14 |Anaconda, Inc.| (default, Mar 27 2018, 17:29:31) 242 | [GCC 7.2.0] on linux2 243 | Type "help", "copyright", "credits" or "license" for more information. 244 | >>> import nltk 245 | >>> nltk.download('stopwords') 246 | [nltk_data] Downloading package stopwords to /root/nltk_data... 247 | [nltk_data] Unzipping corpora/stopwords.zip. 248 | True 249 | >>> 250 | 251 | # 使用MLP进行情感分析 252 | 253 | MLP是多层感知机的简写,是最简单的深度神经网络结构。我们构造一个双层的MLP,第一层隐藏层的结点数为5,第二层为2. 254 | 255 | #构造神经网络 256 | def baseline_model(): 257 | model = Sequential() 258 | model.add(Dense(5, input_dim=max_features, activation='relu')) 259 | model.add(Dropout(0.2)) 260 | model.add(Dense(2, activation='softmax')) 261 | # Compile model 262 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) 263 | 264 | 可视化MLP模型,keras下将模型可视化非常容易,内置函数即可图片化展现。 265 | 266 | from keras.utils import plot_model 267 | plot_model(model, to_file='model.png') 268 | 269 | plot_model接收两个可选参数: 270 | 271 | - show_shapes:指定是否显示输出数据的形状,默认为False 272 | - show\_layer\_names:指定是否显示层名称,默认为True 273 | 274 | 第一次运行,可能会报错。 275 | 276 | ImportError: Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work. 277 | 278 | 这是因为相关库没有及时安装,解决方法如下: 279 | 280 | pip install pydot-ng 281 | brew install graphviz 282 | 283 | 再次运行程序,可视化结果如下。 284 | 285 | ![预测Yelp美食评分-图2.png](picture/预测Yelp美食评分-图2.png) 286 | 287 | keras也支持打印模型。 288 | 289 | model.summary() 290 | 291 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 292 | 293 | _________________________________________________________________ 294 | Layer (type) Output Shape Param # 295 | ================================================================= 296 | dense_3 (Dense) (None, 5) 25005 297 | _________________________________________________________________ 298 | dropout_2 (Dropout) (None, 5) 0 299 | _________________________________________________________________ 300 | dense_4 (Dense) (None, 2) 12 301 | ================================================================= 302 | 303 | 为了让验证的效果更加可信,我们使用5折交叉验证,考核分类器的F1值,训练的轮数为20。在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 304 | 305 | # 最新接口指定训练的次数为epochs 306 | clf = KerasClassifier(build_fn=baseline_model, epochs=20, batch_size=128, verbose=0) 307 | #使用5折交叉验证 308 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 309 | # print scores 310 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 311 | 312 | 在样本数为10000,特征数取5000的前提下,结果如下所示,可以看出2-gram在本数据集下没有对分类效果有改善。 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 |
特征提取方式F1值
词袋0.89
2-gram0.84
词袋&TF-IDF0.89
2-gram&TF-IDF0.84
336 | 337 | 适当增加训练数据量,特征数取5000的前提下,结果如下所示,可见在该测试集合下,增加数据量对F1的影响有限。 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 |
样本总量F1值
1w0.89
10w0.91
20w0.91
357 | 358 | # 使用LSTM进行情感分析 359 | LSTM特别适合处理具有序列化数据,并且可以很好的自动化提炼序列前后的特征关系。当我们把Yelp数据集转换成词袋序列后,就可以尝试使用LSTM来进行处理。我们构造一个简单的LSTM结构,首先通过一个Embedding层进行降维成为128位的向量,然后使用一个核数为128的LSTM进行处理。为了防止过拟合,LSTM层和全连接层之间随机丢失20%的数据进行训练。 360 | 361 | #构造神经网络 362 | def baseline_model(): 363 | model = Sequential() 364 | model.add(Embedding(max_features, 128)) 365 | model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2)) 366 | model.add(Dense(2, activation='softmax')) 367 | 368 | # try using different optimizers and different optimizer configs 369 | model.compile(loss='categorical_crossentropy', 370 | optimizer='adam', 371 | metrics=['accuracy']) 372 | 373 | #可视化 374 | plot_model(model, to_file='yelp-lstm-model.png',show_shapes=True) 375 | 376 | model.summary() 377 | 378 | return model 379 | 380 | 再次运行程序,可视化结果如下。 381 | 382 | ![预测Yelp美食评分-图4.png](picture/预测Yelp美食评分-图4.png) 383 | 384 | 打印LSTM的结构。 385 | 386 | model.summary() 387 | 388 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 389 | 390 | ================================================================= 391 | Layer (type) Output Shape Param # 392 | ================================================================= 393 | embedding_1 (Embedding) (None, None, 128) 640000 394 | _________________________________________________________________ 395 | lstm_1 (LSTM) (None, 128) 131584 396 | _________________________________________________________________ 397 | dense_1 (Dense) (None, 2) 258 398 | ================================================================= 399 | Total params: 771,842 400 | Trainable params: 771,842 401 | Non-trainable params: 0 402 | _________________________________________________________________ 403 | 404 | 为了让验证的效果更加可信,我们使用5折交叉验证,考核分类器的F1值,训练的轮数为20。在 scikit-learn 中使用 Keras 的模型,我们必须使用 KerasClassifier 进行包装。这个类起到创建并返回我们的神经网络模型的作用。它需要传入调用 fit()所需要的参数,比如迭代次数和批处理大小。 405 | 406 | # 最新接口指定训练的次数为epochs 407 | clf = KerasClassifier(build_fn=baseline_model, epochs=20, batch_size=128, verbose=0) 408 | #使用5折交叉验证 409 | scores = cross_val_score(clf, x, encoded_y, cv=5, scoring='f1_micro') 410 | # print scores 411 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 412 | 413 | LSTM和CNN都是计算密集型的模型,在CPU上运行的速度几乎难以接受。强烈建议实用GPU服务器进行加速,下面为使用一块Tesla P4进行加速的显示内容。 414 | 415 | name: Tesla P4 416 | major: 6 minor: 1 memoryClockRate (GHz) 1.1135 417 | pciBusID 0000:00:06.0 418 | Total memory: 7.43GiB 419 | Free memory: 7.32GiB 420 | 2018-05-05 11:20:53.108858: I tensorflow/core/common_runtime/gpu/gpu_device.cc:976] DMA: 0 421 | 2018-05-05 11:20:53.108869: I tensorflow/core/common_runtime/gpu/gpu_device.cc:986] 0: Y 422 | 2018-05-05 11:20:53.108882: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla P4, pci bus id: 0000:00:06.0) 423 | 424 | 在样本数为10000,特征数取5000的前提下,结果如下所示。 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 |
特征提取方式F1值
原始词袋序列0.84
词袋序列+全部转换成小写0.85
441 | 442 | 适当增加训练数据量,特征数取5000,词袋序列+全部转换成小写的前提下,结果如下所示,可见在该测试集合下,增加数据量可以提升F1性能,但是效果有限。 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 |
样本总量F1值
1w0.85
10w0.90
458 | 459 | 460 | # 使用CNN进行情感分析 461 | 462 | 近几年使用CNN处理文本分类问题也逐渐成为主流。我们尝试使用简单的CNN结构来处理Yelp的分类问题。首先通过一个Embedding层进行降维成为50位的向量,然后使用一个核数为250,步长为1的一维CNN层进行处理,接着连接一个池化层。为了防止过拟合,CNN层和全连接层之间随机丢失20%的数据进行训练。 463 | 464 | #CNN参数 465 | embedding_dims = 50 466 | filters = 250 467 | kernel_size = 3 468 | hidden_dims = 250 469 | 470 | model = Sequential() 471 | model.add(Embedding(max_features, embedding_dims)) 472 | 473 | model.add(Conv1D(filters, 474 | kernel_size, 475 | padding='valid', 476 | activation='relu', 477 | strides=1)) 478 | #池化 479 | model.add(GlobalMaxPooling1D()) 480 | 481 | model.add(Dense(2, activation='softmax')) 482 | 483 | model.compile(loss='categorical_crossentropy', 484 | optimizer='adam', 485 | metrics=['accuracy']) 486 | 487 | 可视化结果如下。 488 | 489 | ![预测Yelp美食评分-图6.png](picture/预测Yelp美食评分-图6.png) 490 | 491 | 打印CNN的结构。 492 | 493 | model.summary() 494 | 495 | 输出的结果如下所示,除了显示模型的结构,还可以显示需要训练的参数信息。 496 | 497 | _________________________________________________________________ 498 | Layer (type) Output Shape Param # 499 | ================================================================= 500 | embedding_1 (Embedding) (None, None, 50) 250000 501 | _________________________________________________________________ 502 | conv1d_1 (Conv1D) (None, None, 250) 37750 503 | _________________________________________________________________ 504 | global_max_pooling1d_1 (Glob (None, 250) 0 505 | _________________________________________________________________ 506 | dense_1 (Dense) (None, 2) 502 507 | ================================================================= 508 | Total params: 288,252 509 | Trainable params: 288,252 510 | Non-trainable params: 0 511 | _________________________________________________________________ 512 | 513 | 在样本数为10000,特征数取5000的前提下,结果如下所示,。 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 |
特征提取方式F1值
词袋序列0.88
词袋序列+全部转换成小写+删除停用词0.86
530 | 531 | 适当增加训练数据量,特征数取5000,词袋序列+全部转换成小写的前提下,结果如下所示,可见在该测试集合下,增加数据量对F1的影响有限。 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 |
样本总量F1值
1w0.86
10w0.90
20w0.91
551 | 552 | 553 | # 使用fasttext进行情感分析 554 | fasttext对训练和测试的数据格式有一定的要求,数据文件和标签文件要合并到一个文件里面。文件中的每一行代表一条记录,同时每条记录的最后标记对应的标签。默认情况下标签要以__label__开头,比如: 555 | 556 | 这是一条测试数据 __label__1 557 | 558 | python下实现合并数据文件和标签文件的功能非常简单。 559 | 560 | def dump_file(x,y,filename): 561 | with open(filename, 'w') as f: 562 | for i,v in enumerate(x): 563 | line="%s __label__%d\n" % (v,y[i]) 564 | f.write(line) 565 | f.close() 566 | 567 | 加载数据清洗后的数据和标签,随机划分成训练数据和测试数据,其中测试数据占20%。 568 | 569 | 按照fasttest的要求生成训练数据和测试数据。 570 | 571 | dump_file(x_train, y_train, "yelp_train.txt") 572 | dump_file(x_test, y_test, "yelp_test.txt") 573 | 574 | 使用训练数据集对应的文件进行训练。 575 | 576 | model = train_supervised( 577 | input="yelp_train.txt", epoch=20, lr=0.6, wordNgrams=2, verbose=2, minCount=1 578 | ) 579 | 580 | 在测试数据集对应的文件上进行预测。 581 | 582 | def print_results(N, p, r): 583 | print("N\t" + str(N)) 584 | print("P@{}\t{:.3f}".format(1, p)) 585 | print("R@{}\t{:.3f}".format(1, r)) 586 | print_results(*model.test("yelp_test.txt")) 587 | 588 | 在样本数为10000的前提下,结果如下所示,F1为0.866。 589 | 590 | Read 1M words 591 | Number of words: 71445 592 | Number of labels: 2 593 | Progress: 100.0% words/sec/thread: 1744947 lr: 0.000000 loss: 0.066168 ETA: 0h 0m 594 | N 2000 595 | P@1 0.866 596 | R@1 0.866 597 | 598 | 通过优化特征提取方式,可以进一步提升fasttext的分类性能,结果如下所示。 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 |
特征提取方式F1值
2-gram0.866
2-gram+删除停用词0.902
2-gram+删除停用词+转小写0.908
619 | 620 | 另外,通过增加训练数据量也可以提升分类效果,在使用2-gram+删除停用词+转小写的前提下,结果如下。 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 |
样本总量F1值
1w0.908
10w0.921
20w0.923
641 | 642 | # 使用SVM进行情感分析 643 | 在深度学习出现之前,SVM和朴素贝叶斯经常用于文本分类领域,我们以SVM为例。实例化SVM分类器,并使用5折验证法,考核F1值。 644 | 645 | clf = SVC() 646 | # 使用5折交叉验证 647 | scores = cross_val_score(clf, x, y, cv=5, scoring='f1_micro') 648 | # print scores 649 | print("f1_micro: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2)) 650 | 651 | 在样本数为10000,特征数取5000的前提下,结果如下所示,可见在本数据集下特征提取方式对结果影响不大。 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 |
特征提取方式F1值
词袋0.82
2-gram0.82
词袋&TF-IDF0.82
2-gram&TF-IDF0.82
675 | 676 | # 参考文献 677 | 678 | - https://www.cnblogs.com/datablog/p/6127000.html 679 | - https://blog.csdn.net/jiaach/article/details/79403352 680 | - https://blog.csdn.net/wyty88/article/details/51201766 --------------------------------------------------------------------------------