文本挖掘学习笔记(三):文档相似度、文档分类和情感分析

全文基于《射雕英雄传》语料库,下面是读入数据的一个基于Pandas的通用操作框架。


import pandas as pd

raw = pd.read_table("金庸-射雕英雄传txt精校版.txt",
                  names = ['txt'],  encoding ="GBK")

def m_head(tmpstr):
    return tmpstr[:1]

def m_mid(tmpstr):
    return tmpstr.find("回 ")

raw['head'] = raw.txt.apply(m_head)
raw['mid'] = raw.txt.apply(m_mid)
raw['len'] = raw.txt.apply(len)
chapnum = 0
for i in range(len(raw)):
    if raw['head'][i] == "第" and raw['mid'][i] > 0 and raw['len'][i] < 30 :
        chapnum += 1
    if chapnum >= 40 and raw['txt'][i] == "附录一:成吉思汗家族" :
        chapnum = 0
    raw.loc[i, 'chap'] = chapnum

del raw['head']
del raw['mid']
del raw['len']
rawgrp = raw.groupby('chap')
chapter = rawgrp.agg(sum)
chapter = chapter[chapter.index != 0]

计算两个词相似度的原理:简单的说,就是将每个词的向量在空间上进行余弦运算,当cos越接近0时候,两者越相似。

1.1词条相似度:word2vec

词袋模型不考虑词条之间的相关性,因此无法用于计算词条相似度。

分布式表达会考虑词条的上下文关联,因此能够提取出词条上下文中的相关性信息,而词条之间的相似度就可以直接利用此类信息加以计算。

目前主要使用gensim实现相应的算法。
gensim 通用格式list of list格式

gensim也提供了sklearn的API接口:sklearn_api.w2vmodel,可以在sklearn中直接使用。

class gensim.models.word2vec.Word2Vec(

sentences = None : &#x7C7B;&#x4F3C;list of list&#x7684;&#x683C;&#x5F0F;&#xFF0C;&#x5BF9;&#x4E8E;&#x7279;&#x522B;&#x5927;&#x7684;&#x6587;&#x672C;&#xFF0C;&#x5C3D;&#x91CF;&#x8003;&#x8651;&#x6D41;&#x5F0F;&#x5904;&#x7406;
size = 100 : &#x8BCD;&#x6761;&#x5411;&#x91CF;&#x7684;&#x7EF4;&#x5EA6;&#xFF0C;&#x6570;&#x636E;&#x91CF;&#x5145;&#x8DB3;&#x65F6;&#xFF0C;300/500&#x7684;&#x6548;&#x679C;&#x4F1A;&#x66F4;&#x597D;
window = 5 : &#x4E0A;&#x4E0B;&#x6587;&#x7A97;&#x53E3;&#x5927;&#x5C0F;
workers = 3 : &#x540C;&#x65F6;&#x8FD0;&#x884C;&#x7684;&#x7EBF;&#x7A0B;&#x6570;&#xFF0C;&#x591A;&#x6838;&#x7CFB;&#x7EDF;&#x53EF;&#x660E;&#x663E;&#x52A0;&#x901F;&#x8BA1;&#x7B97;

其余细节参数设定:

min_count = 5 : &#x4F4E;&#x9891;&#x8BCD;&#x8FC7;&#x6EE4;&#x9608;&#x503C;&#xFF0C;&#x4F4E;&#x4E8E;&#x8BE5;&#x8BCD;&#x9891;&#x7684;&#x4E0D;&#x7EB3;&#x5165;&#x6A21;&#x578B;
max_vocab_size = None : &#x6BCF;1&#x5343;&#x4E07;&#x8BCD;&#x6761;&#x9700;&#x8981;1G&#x5185;&#x5B58;&#xFF0C;&#x5FC5;&#x8981;&#x65F6;&#x8BBE;&#x5B9A;&#x8BE5;&#x53C2;&#x6570;&#x4EE5;&#x8282;&#x7EA6;&#x5185;&#x5B58;
sample=0.001 : &#x8D1F;&#x4F8B;&#x91C7;&#x6837;&#x7684;&#x6BD4;&#x4F8B;&#x8BBE;&#x5B9A;
negative=5 : &#x4E00;&#x822C;&#x4E3A;5-20&#xFF0C;&#x8BBE;&#x4E3A;0&#x65F6;&#x4E0D;&#x8FDB;&#x884C;&#x8D1F;&#x4F8B;&#x91C7;&#x6837;
iter = 5 : &#x6A21;&#x578B;&#x5728;&#x8BED;&#x6599;&#x5E93;&#x4E0A;&#x7684;&#x8FED;&#x4EE3;&#x6B21;&#x6570;&#xFF0C;&#x8BE5;&#x53C2;&#x6570;&#x5C06;&#x88AB;&#x53D6;&#x6D88;

与神经网络模型有关的参数设定:

seed=1, alpha=0.025, min_alpha=0.0001, sg=0, hs=0
chapter.head()

txtchap1.0第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…2.0第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…3.0第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…4.0第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…5.0第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…

生成list of list格式,注意方法,后面要用到


import jieba
chapter['cut'] = chapter.txt.apply(jieba.lcut)
chapter.head()

txtcutchap1.0第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…[第一回, , 风雪, 惊变, , , , , , , 那, 说话, 人, 五…2.0第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…[第二回, , 江南七怪, , , , , 颜烈, 跨出, 房门, ,, 过道, …3.0第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…[第三回, , 黄沙, 莽莽, , , , , 寺里, 僧众, 见, 焦木, 圆寂…4.0第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…[第四回, , 黑风双, 煞, , , , , 完颜洪熙, 笑, 道, :, “,…5.0第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…[第五回, , 弯弓, 射雕, , , , , 一行, 人下, 得, 山来, ,,…


from gensim.models.word2vec import Word2Vec
n_dim = 300
w2vmodel = Word2Vec(size = n_dim, min_count = 10)
w2vmodel.build_vocab(chapter.cut)
w2vmodel
<gensim.models.word2vec.word2vec at 0x230bb928da0>
</gensim.models.word2vec.word2vec>

word2vecmodel.train(

sentences : iterable of iterables&#x683C;&#x5F0F;&#xFF0C;&#x5BF9;&#x4E8E;&#x7279;&#x522B;&#x5927;&#x91CF;&#x7684;&#x6587;&#x672C;&#xFF0C;&#x5C3D;&#x91CF;&#x8003;&#x8651;&#x6D41;&#x5F0F;&#x5904;&#x7406;
total_examples = None : &#x53E5;&#x5B50;&#x603B;&#x6570;&#xFF0C;int&#xFF0C;&#x53EF;&#x76F4;&#x63A5;&#x4F7F;&#x7528;model.corpus_count&#x6307;&#x5B9A;
total_words = None : &#x53E5;&#x4E2D;&#x8BCD;&#x6761;&#x603B;&#x6570;&#xFF0C;int&#xFF0C;&#x8BE5;&#x53C2;&#x6570;&#x548C;total_examples&#x81F3;&#x5C11;&#x8981;&#x6307;&#x5B9A;&#x4E00;&#x4E2A;
epochs = None : &#x6A21;&#x578B;&#x8FED;&#x4EE3;&#x6B21;&#x6570;&#xFF0C;&#x9700;&#x8981;&#x6307;&#x5B9A;

其他带默认值的参数设定:

start_alpha=None, end_alpha=None, word_count=0, queue_factor=2,
report_delay=1.0, compute_loss=False, callbacks=()

w2vmodel.train(chapter.cut, \
               total_examples = w2vmodel.corpus_count, epochs = 10)
(3444321, 6113050)

print(w2vmodel.wv["郭靖"].shape)
w2vmodel.wv["郭靖"]
(300,)
array([-5.71486771e-01, -6.11459553e-01,  9.88356650e-01, -2.19969201e+00,
       -6.24286681e-02, -2.08215073e-01, -4.94846284e-01,  4.11184639e-01,
        3.51217866e-01, -7.16320872e-01,  2.41676435e-01, -5.17797768e-01,
        ......

       -5.74352980e-01,  3.34270656e-01, -8.82370621e-02, -2.53521979e-01,
        6.62185490e-01, -1.17333210e+00, -5.62149405e-01, -1.21558267e-04,
       -1.54235452e-01,  4.67433631e-01, -3.35694969e-01, -1.63811371e-01],
      dtype=float32)

w2v模型的保存和复用:
w2vmodel.save(存盘路径及文件名称)

w2vmodel.load(存盘路径及文件名称)

w2vmodel.wv.most_similar(词条)

w2vmodel.wv.most_similar("郭靖",topn = 20)
[('&#x9EC4;&#x84C9;', 0.932035505771637),
 ('&#x6B27;&#x9633;&#x514B;', 0.8728183507919312),
 ('&#x7A46;&#x5FF5;&#x6148;', 0.7740538120269775),
 ('&#x6885;&#x8D85;&#x98CE;', 0.7715764045715332),
 ('&#x674E;&#x840D;', 0.7549037337303162),
 ('&#x5B8C;&#x989C;&#x5EB7;', 0.7546292543411255),
 ('&#x9EC4;&#x836F;&#x5E08;', 0.7424267530441284),
 ('&#x9646;&#x51A0;&#x82F1;', 0.7395749688148499),
 ('&#x6768;&#x5EB7;', 0.7385720014572144),
 ('&#x9C81;&#x6709;&#x811A;', 0.7347890138626099),
 ('&#x5468;&#x4F2F;&#x901A;', 0.7241591811180115),
 ('&#x88D8;&#x5343;&#x4EDE;', 0.722597599029541),
 ('&#x6B27;&#x9633;&#x950B;', 0.7211623787879944),
 ('&#x534E;&#x7B5D;', 0.7194427847862244),
 ('&#x6D2A;&#x4E03;&#x516C;', 0.7096649408340454),
 ('&#x7A0B;&#x7476;&#x8FE6;', 0.7055790424346924),
 ('&#x7A46;&#x6613;', 0.7018767595291138),
 ('&#x90A3;&#x516C;&#x5B50;', 0.6987137198448181),
 ('&#x90A3;&#x9053;&#x4EBA;', 0.6943396329879761),
 ('&#x4E00;&#x706F;', 0.6929605007171631)]

w2vmodel.wv.most_similar(positive=['郭靖', '小红马'], \
                         negative=['黄药师'], topn = 5)

[('&#x5954;', 0.8444687128067017),
 ('&#x8FC7;&#x53BB;', 0.7988641858100891),
 ('&#x6655;', 0.7967531681060791),
 ('&#x521A;', 0.7961419224739075),
 ('&#x51E0;&#x6B65;', 0.7911766767501831)]

print(w2vmodel.wv.similarity("郭靖", "黄蓉"))
print(w2vmodel.wv.similarity("郭靖", "杨康"))
print(w2vmodel.wv.similarity("郭靖", "杨铁心"))
0.93203545
0.73857194
0.6454911

w2vmodel.wv.doesnt_match("小红马 黄药师 鲁有脚".split())
'&#x5C0F;&#x7EA2;&#x9A6C;'

1.2文档相似度

sklearn.metrics.pairwise.pairwise_distances(

X : 用于计算距离的数组

[n_samples_a, n_samples_a] if metric == 'precomputed'

[n_samples_a, n_features] otherwise

Y = None : 用于计算距离的第二数组,当metric != ‘precomputed’时可用

metric = ‘euclidean’ : 空间距离计算方式

scikit-learn&#x539F;&#x751F;&#x652F;&#x6301; : ['cityblock', 'cosine', 'euclidean',
    'l1', 'l2', 'manhattan']&#xFF0C;&#x53EF;&#x76F4;&#x63A5;&#x4F7F;&#x7528;&#x7A00;&#x758F;&#x77E9;&#x9635;&#x683C;&#x5F0F;
&#x6765;&#x81EA;scipy.spatial.distance : ['braycurtis', 'canberra',
    'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard',
    'kulsinski', 'mahalanobis', 'matching', 'minkowski',
    'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener',
    'sokalsneath', 'sqeuclidean', 'yule'] &#x4E0D;&#x652F;&#x6301;&#x7A00;&#x758F;&#x77E9;&#x9635;&#x683C;&#x5F0F;

n_jobs = 1 : 用于计算的线程数,为-1时,所有CPU内核都用于计算


cleanchap = [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5]]

from sklearn.feature_extraction.text import CountVectorizer

countvec = CountVectorizer()

resmtx = countvec.fit_transform(cleanchap)
resmtx
<5x11623 sparse matrix of type '<class 'numpy.int64'>'
    with 16884 stored elements in Compressed Sparse Row format>
</5x11623>
from sklearn.metrics.pairwise import pairwise_distances

pairwise_distances(resmtx, metric = 'cosine')

array([[0.        , 0.63104216, 0.77665559, 0.78745025, 0.82985512],
       [0.63104216, 0.        , 0.62572437, 0.61666388, 0.73192845],
       [0.77665559, 0.62572437, 0.        , 0.51645443, 0.5299046 ],
       [0.78745025, 0.61666388, 0.51645443, 0.        , 0.42108002],
       [0.82985512, 0.73192845, 0.5299046 , 0.42108002, 0.        ]])
pairwise_distances(resmtx)
array([[  0.        , 290.85391522, 312.60678176, 315.54872841,
        311.46267834],
       [290.85391522,   0.        , 266.95130642, 265.77622166,
        277.24898557],
       [312.60678176, 266.95130642,   0.        , 233.9615353 ,
        226.09290126],
       [315.54872841, 265.77622166, 233.9615353 ,   0.        ,
        202.57344347],
       [311.46267834, 277.24898557, 226.09290126, 202.57344347,
          0.        ]])

from sklearn.feature_extraction.text import TfidfTransformer
txtlist = [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5]]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(txtlist)
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(X)
tfidf
pairwise_distances(tfidf[:5], metric = 'cosine')
array([[0.        , 0.69112864, 0.84741572, 0.85725434, 0.89178365],
       [0.69112864, 0.        , 0.74381846, 0.70586805, 0.81759092],
       [0.84741572, 0.74381846, 0.        , 0.60083695, 0.63539482],
       [0.85725434, 0.70586805, 0.60083695, 0.        , 0.54123168],
       [0.89178365, 0.81759092, 0.63539482, 0.54123168, 0.        ]])

主要是用于基于LDA计算余弦相似度
需要使用的信息:

&#x62DF;&#x5408;&#x5B8C;&#x6BD5;&#x7684;lda&#x6A21;&#x578B;
&#x6309;&#x7167;&#x62DF;&#x5408;&#x6A21;&#x578B;&#x65F6;&#x77E9;&#x9635;&#x79CD;&#x7C7B;&#x8F6C;&#x6362;&#x7684;&#x9700;&#x68C0;&#x7D22;&#x6587;&#x672C;
&#x9700;&#x68C0;&#x7D22;&#x7684;&#x6587;&#x672C;
&#x5EFA;&#x6A21;&#x65F6;&#x4F7F;&#x7528;&#x7684;&#x5B57;&#x5178;

from gensim import similarities
simmtx = similarities.MatrixSimilarity(corpus)
simmtx
<gensim.similarities.docsim.matrixsimilarity at 0x230bb8ec550>
</gensim.similarities.docsim.matrixsimilarity>

检索和第1章内容最相似(所属主题相同)的章节

simmtx = similarities.MatrixSimilarity(corpus)
simmtx
<gensim.similarities.docsim.matrixsimilarity at 0x230bb8ec438>
</gensim.similarities.docsim.matrixsimilarity>
simmtx.index[:2]
array([[0.00370857, 0.11125696, 0.00370857, ..., 0.        , 0.        ,
        0.        ],
       [0.        , 0.15784042, 0.        , ..., 0.        , 0.        ,
        0.        ]], dtype=float32)

query = chapter.txt[1]
query_bow = dictionary.doc2bow(m_cut(query))

lda_vec = ldamodel[query_bow]
sims = simmtx[lda_vec]
sims = sorted(enumerate(sims), key=lambda item: -item[1])
sims

[(38, 0.011200049),
 (1, 0.009635941),
 (26, 0.0077462136),
 ......

 (20, 0.0016734296),
 (19, 0.0012708347),
 (13, 0.0012344222)]

1.2.2 doc2vec(应多使用词袋模型的方式)

doc2vec建议多大量文本时候使用

word2vec用来计算词条相似度非常合适。

较短的文档如果希望计算文本相似度,可以将各自内部的word2vec向量分别进行平均,用平均后的向量作为文本向量,从而用于计算相似度。

但是对于长文档,这种平均的方式显然过于粗糙。

doc2vec是word2vec的拓展,它可以直接获得sentences/paragraphs/documents的向量表达,从而可以进一步通过计算距离来得到sentences/paragraphs/documents之间的相似性。

模型概况

&#x5206;&#x6790;&#x76EE;&#x7684;&#xFF1A;&#x83B7;&#x5F97;&#x6587;&#x6863;&#x7684;&#x4E00;&#x4E2A;&#x56FA;&#x5B9A;&#x957F;&#x5EA6;&#x7684;&#x5411;&#x91CF;&#x8868;&#x8FBE;&#x3002;
&#x6570;&#x636E;&#xFF1A;&#x591A;&#x4E2A;&#x6587;&#x6863;&#xFF0C;&#x4EE5;&#x53CA;&#x5B83;&#x4EEC;&#x7684;&#x6807;&#x7B7E;&#xFF0C;&#x4E00;&#x822C;&#x53EF;&#x4EE5;&#x7528;&#x6807;&#x9898;&#x4F5C;&#x4E3A;&#x6807;&#x7B7E;&#x3002;
&#x5F71;&#x54CD;&#x6A21;&#x578B;&#x51C6;&#x786E;&#x7387;&#x7684;&#x56E0;&#x7D20;&#xFF1A;&#x8BED;&#x6599;&#x7684;&#x5927;&#x5C0F;&#xFF0C;&#x6587;&#x6863;&#x7684;&#x6570;&#x91CF;&#xFF0C;&#x8D8A;&#x591A;&#x8D8A;&#x9AD8;&#xFF1B;&#x6587;&#x6863;&#x7684;&#x76F8;&#x4F3C;&#x6027;&#xFF0C;&#x8D8A;&#x76F8;&#x4F3C;&#x8D8A;&#x597D;&#x3002;
import jieba
import gensim
from gensim.models import doc2vec

def m_doc(doclist):
    reslist = []
    for i, doc in enumerate(doclist):
        reslist.append(doc2vec.TaggedDocument(jieba.lcut(doc), [i]))
    return reslist

corp = m_doc(chapter.txt)
corp[:2]
[TaggedDocument(words=['&#x7B2C;&#x4E00;&#x56DE;', ' ', '&#x98CE;&#x96EA;', '&#x60CA;&#x53D8;', '&#x90A3;', '&#x8BF4;&#x8BDD;', '&#x4EBA;', '&#x4E94;&#x5341;', '&#x6765;', '&#x5C81;', '&#x5E74;&#x7EAA;', '&#xFF0C;', '&#x4E00;&#x4EF6;', '&#x9752;&#x5E03;', '&#x957F;&#x888D;', '&#x65E9;&#x6D17;', '&#x5F97;', '&#x892A;&#x6210;', '&#x4E86;', '&#x84DD;&#x7070;', '&#x5E26;&#x767D;', '&#x3002;', '&#x53EA;',...... '&#x542C;', '&#x4ED6;', '&#x4E24;&#x7247;', '&#x68A8;&#x82B1;', '&#x6728;, '&#x8FC7;', '&#x4E86;', '&#x4E00;&#x4F1A;', '&#xFF0C;', '&#x989C;&#x70C8;', '&#x9053;', '&#xFF1A;', '&#x201C;', '&#x5A18;&#x5B50;', '&#x8BF7;', '&#x81EA;', '&#x5BBD;', '&#x4FBF;', '&#xFF0C;', '&#x5C0F;&#x4EBA;', '&#x51FA;&#x53BB;', '&#x4E70;', '&#x4E86;', '&#x7269;&#x54C1;', '&#x5C31;', '&#x56DE;', '&#x3002;', '&#x201D;', '&#x5305;&#x60DC;&#x5F31;', '&#x70B9;', '&#x4E86;', '&#x70B9;&#x5934;', '&#xFF0C;', '&#x9053;', '&#xFF1A;', '&#x201C;', '&#x76F8;&#x516C;', '&#x53EF;&#x522B;', '&#x592A;&#x591A;', '&#x82B1;&#x8D39;', '&#x4E86;', '&#x3002;', '&#x201D;', '&#x989C;&#x70C8;', '&#x5FAE;&#x7B11;', '&#x9053;', '&#xFF1A;', '&#x201C;', '&#x5C31;', '&#x53EF;&#x60DC;', '&#x5A18;&#x5B50;', '&#x5728;', '&#x670D;&#x4E27;', '&#xFF0C;', '&#x4E0D;&#x80FD;', '&#x6234;&#x7528;', '&#x73E0;&#x5B9D;', '&#xFF0C;', '&#x8981;', '&#x591A;&#x82B1;&#x94B1;', '&#x4E5F;', '&#x82B1;', '&#x4E0D;&#x4E86;', '&#x3002;', '&#x201D;', '], tags=[0]),
 TaggedDocument(words=['&#x7B2C;&#x4E8C;&#x56DE;', ' ', '&#x6C5F;&#x5357;&#x4E03;&#x602A;', '&#x989C;&#x70C8;', '&#x8DE8;&#x51FA;', '&#x623F;&#x95E8;', '&#xFF0C;', '&#x8FC7;&#x9053;', '&#x4E2D;', '&#x4E00;&#x4E2A;', ......'&#x4E2D;&#x5E74;', '&#x58EB;&#x4EBA;', '&#x62D6;', '&#x7740;', '&#x978B;', '&#x76AE;', '&#xFF0C;', '&#x8E22;', '&#x8DF6;', '&#x8E22;', '&#x8DF6;', '&#x7684;', '&#x76F4;&#x54CD;', '&#xFF0C;', '&#x4E00;&#x8DEF;&#x6253;', '&#x7740;', '&#x54C8;&#x6B20;', '&#x8FCE;&#x9762;', '&#x8FC7;&#x6765;', '&#xFF0C;', '&#x90A3;', '&#x58EB;&#x4EBA;', '&#x4F3C;&#x7B11;&#x975E;&#x7B11;', '&#xFF0C;', '&#x6324;&#x7709;&#x5F04;&#x773C;', '&#xFF0C;', ''&#x6CA1;', '&#x6D17;&#x8138;', '&#x4E86;', '&#xFF0C;', '&#x62FF;', '&#x7740;', '&#x4E00;', '&#x67C4;', '&#x7834;&#x70C2;', '&#x7684;', '&#x6CB9;&#x7EB8;', '&#x9ED1;&#x6247;', '&#xFF0C;', '&#x8FB9;&#x6447;&#x8FB9;&#x884C;', '&#x3002;&#x6BB5;&#x5929;&#x5FB7;', '&#x5413;', '&#x5F97;', '&#x9B42;&#x4E0D;&#x9644;&#x4F53;', '&#xFF0C;', '&#x54EA;&#x91CC;', '&#x8FD8;&#x6562;', '&#x505C;&#x7559;', '&#xFF0C;', '&#x62C9;', '&#x4E86;', '&#x674E;&#x840D;', '&#xFF0C;', '&#x6025;&#x5954;', '&#x800C;&#x51FA;', '&#x3002;', '&#x674E;&#x840D;&#x5927;', '&#x53EB;', '&#xFF1A;', '&#x201C;', '&#x6551;&#x547D;', '&#x554A;', '&#xFF0C;', '&#x6211;', '&#x4E0D;', '&#x53BB;', '&#xFF0C;', '&#x6551;&#x547D;', '&#x554A;', '&#xFF01;', '&#x201D;', '&#x7EC8;&#x4E8E;', '&#x58F0;&#x97F3;', '&#x8D8A;&#x6765;&#x8D8A;', '&#x8FDC;', '&#x3002;], tags=[1])]

d2vmodel = gensim.models.Doc2Vec(vector_size = 300,
                window = 20, min_count = 5)
%time d2vmodel.build_vocab(corp)
Wall time: 1.88 s
d2vmodel.wv.vocab
{' ': <gensim.models.keyedvectors.vocab at 0x230b88bef98>,
 '&#x98CE;&#x96EA;': <gensim.models.keyedvectors.vocab at 0x230b88bedd8>,
 '&#x90A3;': <gensim.models.keyedvectors.vocab at 0x230c4fea780>,
 '&#x8BF4;&#x8BDD;': <gensim.models.keyedvectors.vocab at 0x230c4fea828>,
  ......

 '&#x66F2;&#x4E09;': <gensim.models.keyedvectors.vocab at 0x230c7f9f470>,
 '&#x6398;': <gensim.models.keyedvectors.vocab at 0x230c7fb49e8>,
 '&#x6700;&#x540E;': <gensim.models.keyedvectors.vocab at 0x230c7fb4a20>,
 '&#x4E00;&#x5177;': <gensim.models.keyedvectors.vocab at 0x230c7fb4a58>,
 ...}
</gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab></gensim.models.keyedvectors.vocab>

newvec = d2vmodel.infer_vector(jieba.lcut(chapter.txt[1]))

d2vmodel.docvecs.most_similar([newvec], topn = 10)
[(33, 0.17437607049942017),
 (36, 0.10029015690088272),
 (22, 0.07945042848587036),
 (18, 0.04506886377930641),
 (14, 0.03940583020448685),
 (23, 0.035881608724594116),
 (6, 0.03350480645895004),
 (31, 0.03314536064863205),
 (16, 0.028236132115125656),
 (13, 0.022862425073981285)]

1.3文档聚类

在得到文档相似度的计算结果后,文档聚类问题在本质上已经和普通的聚类分析没有区别。

注意:最常用的Kmeans使用的是平方欧氏距离,这在文本聚类中很可能无法得到最佳结果。

算法的速度和效果同样重要。


chapter.index = [raw.txt[raw.chap == i].iloc[0] for i in chapter.index]
chapter.head()

txtcut第一回 风雪惊变第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…[第一回, , 风雪, 惊变, , , , , , , 那, 说话, 人, 五…第二回 江南七怪第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…[第二回, , 江南七怪, , , , , 颜烈, 跨出, 房门, ,, 过道, …第三回 黄沙莽莽第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…[第三回, , 黄沙, 莽莽, , , , , 寺里, 僧众, 见, 焦木, 圆寂…第四回 黑风双煞第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…[第四回, , 黑风双, 煞, , , , , 完颜洪熙, 笑, 道, :, “,…第五回 弯弓射雕第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…[第五回, , 弯弓, 射雕, , , , , 一行, 人下, 得, 山来, ,,…


import jieba
cuttxt = lambda x: " ".join(m_cut(x))
cleanchap = chapter.txt.apply(cuttxt)
cleanchap[:2]
&#x7B2C;&#x4E00;&#x56DE; &#x98CE;&#x96EA;&#x60CA;&#x53D8;    &#x7B2C;&#x4E00;&#x56DE; &#x98CE;&#x96EA; &#x60CA;&#x53D8; &#x8BF4;&#x8BDD; &#x4E94;&#x5341; &#x5E74;&#x7EAA; &#x4E00;&#x4EF6; &#x9752;&#x5E03; &#x957F;&#x888D; &#x65E9;&#x6D17; &#x892A;&#x6210; &#x84DD;&#x7070; &#x5E26;&#x767D; &#x4E24;&#x7247; &#x68A8;&#x82B1; ...

&#x7B2C;&#x4E8C;&#x56DE; &#x6C5F;&#x5357;&#x4E03;&#x602A;    &#x7B2C;&#x4E8C;&#x56DE; &#x6C5F;&#x5357;&#x4E03;&#x602A; &#x989C;&#x70C8; &#x8DE8;&#x51FA; &#x623F;&#x95E8; &#x8FC7;&#x9053; &#x4E00;&#x4E2A; &#x4E2D;&#x5E74; &#x58EB;&#x4EBA; &#x76F4;&#x54CD; &#x4E00;&#x8DEF;&#x6253; &#x54C8;&#x6B20; &#x8FCE;&#x9762; &#x58EB;&#x4EBA; ...

Name: txt, dtype: object

from sklearn.feature_extraction.text import TfidfTransformer

vectorizer = CountVectorizer()
wordmtx = vectorizer.fit_transform(cleanchap)

transformer = TfidfTransformer()
tfidf = transformer.fit_transform(wordmtx)
tfidf
<40x43931 sparse matrix of type '<class 'numpy.float64'>'
    with 129387 stored elements in Compressed Sparse Row format>
</40x43931>

from sklearn.cluster import KMeans

clf = KMeans(n_clusters = 5)
s = clf.fit(tfidf)
print(s)
clf.cluster_centers_
KMeans(n_clusters=5)

array([[0.        , 0.        , 0.        , ..., 0.        , 0.00281383,
        0.        ],
       [0.00048249, 0.        , 0.        , ..., 0.00098597, 0.        ,
        0.00048249],
       [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
        0.        ],
       [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
        0.        ],
       [0.        , 0.00080214, 0.00080214, ..., 0.        , 0.        ,
        0.        ]])
clf.cluster_centers_.shape
(5, 43931)
clf.labels_
array([2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 1, 4, 1, 1, 1, 1, 1, 1, 1,
       1, 4, 1, 1, 1, 1, 0, 0, 0, 1, 4, 1, 1, 1, 1, 1, 1, 1])

chapter['clsres'] = clf.labels_
chapter.head()

txtcutclsres第一回 风雪惊变第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…[第一回, , 风雪, 惊变, , , , , , , 那, 说话, 人, 五…2第二回 江南七怪第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…[第二回, , 江南七怪, , , , , 颜烈, 跨出, 房门, ,, 过道, …2第三回 黄沙莽莽第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…[第三回, , 黄沙, 莽莽, , , , , 寺里, 僧众, 见, 焦木, 圆寂…3第四回 黑风双煞第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…[第四回, , 黑风双, 煞, , , , , 完颜洪熙, 笑, 道, :, “,…3第五回 弯弓射雕第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…[第五回, , 弯弓, 射雕, , , , , 一行, 人下, 得, 山来, ,,…3

chapter.sort_values('clsres').clsres
&#x7B2C;&#x4E09;&#x5341;&#x56DE; &#x4E00;&#x706F;&#x5927;&#x5E08;      0
&#x7B2C;&#x4E09;&#x5341;&#x4E00;&#x56DE; &#x9E33;&#x9E2F;&#x9526;&#x5E15;     0
&#x7B2C;&#x4E8C;&#x5341;&#x4E5D;&#x56DE; &#x9ED1;&#x6CBC;&#x9690;&#x5973;     0
......

&#x7B2C;&#x5341;&#x4E09;&#x56DE; &#x4E94;&#x6E56;&#x5E9F;&#x4EBA;      4
&#x7B2C;&#x4E5D;&#x56DE; &#x94C1;&#x67AA;&#x7834;&#x7281;       4
Name: clsres, dtype: int32

chapgrp = chapter.groupby('clsres')
chapcls = chapgrp.agg(sum)

cuttxt = lambda x: " ".join(m_cut(x))
chapclsres = chapcls.txt.apply(cuttxt)
chapclsres
clsres
0    &#x7B2C;&#x4E8C;&#x5341;&#x4E5D;&#x56DE; &#x9ED1;&#x6CBC; &#x9690;&#x5973; &#x90ED;&#x9756; &#x547C;&#x53EB; &#x53EC;&#x5524; &#x5C0F;&#x7EA2;&#x9A6C; &#x5730;&#x4E0B; &#x8F6C;&#x773C;&#x4E4B;&#x95F4; &#x53CC;&#x96D5; &#x98DE;&#x51FA; &#x8001;&#x8FDC; &#x96CC;&#x96C4; &#x53CC;...

1    &#x7B2C;&#x5341;&#x56DB;&#x56DE; &#x6843;&#x82B1; &#x5C9B;&#x4E3B; &#x4E94;&#x7537;&#x4E00;&#x5973; &#x8D70;&#x8FDB; &#x5385;&#x6765; &#x5374;&#x662F; &#x6C5F;&#x5357; &#x516D;&#x602A; &#x5317;&#x5357; &#x6545;&#x4E61; &#x65E5;&#x8FD1; &#x8FD9;&#x5929; &#x592A;&#x6E56; ...

2    &#x7B2C;&#x4E00;&#x56DE; &#x98CE;&#x96EA; &#x60CA;&#x53D8; &#x8BF4;&#x8BDD; &#x4E94;&#x5341; &#x5E74;&#x7EAA; &#x4E00;&#x4EF6; &#x9752;&#x5E03; &#x957F;&#x888D; &#x65E9;&#x6D17; &#x892A;&#x6210; &#x84DD;&#x7070; &#x5E26;&#x767D; &#x4E24;&#x7247; &#x68A8;&#x82B1; ...

3    &#x7B2C;&#x4E09;&#x56DE; &#x9EC4;&#x6C99; &#x83BD;&#x83BD; &#x5BFA;&#x91CC; &#x50E7;&#x4F17; &#x7126;&#x6728; &#x5706;&#x5BC2; &#x60B2;&#x54ED; &#x4F24;&#x8005; &#x5305;&#x624E; &#x4F24;&#x5904; &#x62AC;&#x5165; &#x5BA2;&#x820D; &#x5DE8;&#x949F; &#x7F38;&#x5185; ...

4    &#x7B2C;&#x4E03;&#x56DE; &#x6BD4;&#x6B66;&#x62DB;&#x4EB2; &#x6C5F;&#x5357; &#x516D;&#x602A; &#x90ED;&#x9756; &#x6653;&#x884C;&#x591C;&#x5BBF; &#x4E1C;&#x5357; &#x8FDB;&#x53D1; &#x975E;&#x6B62; &#x4E00;&#x65E5; &#x5927;&#x6F20; &#x8349;&#x539F; &#x8FD9;&#x5929; &#x5F20;&#x5BB6;...

Name: txt, dtype: object

import jieba.analyse as ana

ana.set_stop_words('停用词.txt')

for item in chapclsres:
    print(ana.extract_tags(item, topK = 10))

['&#x9EC4;&#x84C9;', '&#x90ED;&#x9756;', '&#x4E00;&#x706F;', '&#x9EC4;&#x84C9;&#x9053;', '&#x90A3;&#x4E66;&#x751F;', '&#x6E14;&#x4EBA;', '&#x4E00;&#x706F;&#x5927;&#x5E08;', '&#x519C;&#x592B;', '&#x6BB5;&#x7687;&#x7237;', '&#x90ED;&#x9756;&#x9053;']
['&#x90ED;&#x9756;', '&#x9EC4;&#x84C9;', '&#x6B27;&#x9633;&#x950B;', '&#x9EC4;&#x836F;&#x5E08;', '&#x6D2A;&#x4E03;&#x516C;', '&#x5468;&#x4F2F;&#x901A;', '&#x88D8;&#x5343;&#x4EDE;', '&#x6B66;&#x529F;', '&#x90ED;&#x9756;&#x9053;', '&#x6B27;&#x9633;&#x514B;']
['&#x4E18;&#x5904;&#x673A;', '&#x6768;&#x94C1;&#x5FC3;', '&#x5305;&#x60DC;&#x5F31;', '&#x90ED;&#x5578;&#x5929;', '&#x6BB5;&#x5929;&#x5FB7;', '&#x989C;&#x70C8;', '&#x5B8C;&#x989C;&#x6D2A;&#x70C8;', '&#x67EF;&#x9547;&#x6076;', '&#x91D1;&#x5175;', '&#x9053;&#x957F;']
['&#x90ED;&#x9756;', '&#x94C1;&#x6728;&#x771F;', '&#x67EF;&#x9547;&#x6076;', '&#x97E9;&#x5C0F;&#x83B9;', '&#x6885;&#x8D85;&#x98CE;', '&#x54F2;&#x522B;', '&#x6731;&#x806A;', '&#x97E9;&#x5B9D;&#x9A79;', '&#x62D6;&#x96F7;', '&#x534E;&#x7B5D;']
['&#x90ED;&#x9756;', '&#x9EC4;&#x84C9;', '&#x6D2A;&#x4E03;&#x516C;', '&#x5B8C;&#x989C;&#x5EB7;', '&#x5E08;&#x7236;', '&#x5F6D;&#x8FDE;&#x864E;', '&#x9646;&#x51A0;&#x82F1;', '&#x6881;&#x5B50;&#x7FC1;', '&#x6B27;&#x9633;&#x514B;', '&#x7A46;&#x5FF5;&#x6148;']

在计算词条相似度时进行停用词清理,然后再进行拟合,思考为什么会有这样的结果出现。

在基于词袋模型,使用原始词频计算文本余弦相似度时,比较清理停用词前后的结果。


import jieba
import pandas as pd
tmpdf = pd.read_table('停用词.txt',names = ['w'], encoding = 'utf-8')
chapter['cut'] = chapter.txt.apply(jieba.lcut)
chapter['cut'] =[ w for w in chapter['cut']  if w not in list(tmpdf.w) ]
chapter.head()
Building prefix dict from the default dictionary ...

Loading model from cache C:\Users\A\AppData\Local\Temp\jieba.cache
Loading model cost 0.557 seconds.

Prefix dict has been built successfully.

txtcutchap1.0第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…[第一回, , 风雪, 惊变, , , , , , , 那, 说话, 人, 五…2.0第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…[第二回, , 江南七怪, , , , , 颜烈, 跨出, 房门, ,, 过道, …3.0第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…[第三回, , 黄沙, 莽莽, , , , , 寺里, 僧众, 见, 焦木, 圆寂…4.0第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…[第四回, , 黑风双, 煞, , , , , 完颜洪熙, 笑, 道, :, “,…5.0第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…[第五回, , 弯弓, 射雕, , , , , 一行, 人下, 得, 山来, ,,…


from gensim.models.word2vec import Word2Vec
n_dim = 300
w2vmodel = Word2Vec(size = n_dim, min_count = 10)
w2vmodel.build_vocab(chapter.cut)

w2vmodel.train(chapter.cut, \
               total_examples = w2vmodel.corpus_count, epochs = 10)

print(w2vmodel.wv["郭靖"].shape)
w2vmodel.wv["郭靖"]
(300,)

array([ 0.34924605,  0.21265522,  0.5632632 ,  0.3580753 ,  0.17115285,
        0.48449898,  0.11169011,  0.33496168,  0.8114401 ,  0.6040344 ,
        0.27803087, -0.56468904, -0.05460463, -0.11949269, -0.08181898,
        ......

        0.2831581 ,  0.60918677, -0.17329499,  0.11961225, -0.30500183,
       -0.31079102, -0.64060545,  0.03041249,  0.66229236,  0.88437986],
      dtype=float32)

import jieba
import pandas as pd
tmpdf = pd.read_table('停用词.txt',names = ['w'], encoding = 'utf-8')
chapter['cut'].iloc[:5]= [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5] ]
chapter['cut'].iloc[:5]=[ w for w in chapter['cut'].iloc[:5] if w not in list(tmpdf.w) ]

from sklearn.feature_extraction.text import CountVectorizer

countvec = CountVectorizer()

resmtx = countvec.fit_transform(chapter['cut'].iloc[:5])
resmtx
<5x11623 sparse matrix of type '<class 'numpy.int64'>'
    with 16884 stored elements in Compressed Sparse Row format>
</5x11623>
from sklearn.metrics.pairwise import pairwise_distances

pairwise_distances(resmtx, metric = 'cosine')

array([[0.        , 0.63104216, 0.77665559, 0.78745025, 0.82985512],
       [0.63104216, 0.        , 0.62572437, 0.61666388, 0.73192845],
       [0.77665559, 0.62572437, 0.        , 0.51645443, 0.5299046 ],
       [0.78745025, 0.61666388, 0.51645443, 0.        , 0.42108002],
       [0.82985512, 0.73192845, 0.5299046 , 0.42108002, 0.        ]])

朴素贝叶斯算法(优先考虑使用)

2.1 sklearn实现

sklearn是标准的数据挖掘建模工具包,在语料转换为d2m矩阵结构之后,就可以使用所有标准的DM建模手段在sklearn中进行分析。

在sklearn中也实现了朴素贝叶斯算法,使用方式上也和其他模型非常相似。


raw12 = raw[raw.chap.isin([1,2])]
raw12ana = raw12.iloc[list(raw12.txt.apply(len) > 50), :]
raw12ana.reset_index(drop = True, inplace = True)
print(len(raw12ana))
raw12ana.head()
376

txtchap0那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两片梨花木板碰了几下,左手…1.01那说话人将木板敲了几下,说道:”这首七言诗,说的是兵火过后,原来的家家户户,都变成了断墙…1.02″叶老汉和叶妈妈吓得呆了,扑将上去,搂住了儿子的尸体,放声大哭。那长官提起狼牙棒,一棒一…1.03″可是那金兵占了我大宋天下,杀人放火,奸淫掳掠,无恶不作,却又不见他遭到什么报应。只怪我…1.04村民中走出一个二十来岁的大汉,说道:”张先生,你可是从北方来吗?”说的是北方口音。张十五…1.0

import jieba
cuttxt = lambda x: " ".join(jieba.lcut(x))
raw12ana["cleantxt"] = raw12ana.txt.apply(cuttxt)
raw12ana.head()
<ipython-input-12-b42f436ca4f7>:3: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.

Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  raw12ana["cleantxt"] = raw12ana.txt.apply(cuttxt)
</ipython-input-12-b42f436ca4f7>

txtchapcleantxt0那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两片梨花木板碰了几下,左手…1.0那 说话 人 五十 来 岁 年纪 , 一件 青布 长袍 早洗 得 褪成 了 蓝灰 带…1那说话人将木板敲了几下,说道:”这首七言诗,说的是兵火过后,原来的家家户户,都变成了断墙…1.0那 说话 人 将 木板 敲 了 几下 , 说道 : ” 这首 七言诗 , 说 的 是…2″叶老汉和叶妈妈吓得呆了,扑将上去,搂住了儿子的尸体,放声大哭。那长官提起狼牙棒,一棒一…1.0″ 叶老汉 和 叶 妈妈 吓 得 呆 了 , 扑 将 上去 , 搂住 了 儿子 的 …3″可是那金兵占了我大宋天下,杀人放火,奸淫掳掠,无恶不作,却又不见他遭到什么报应。只怪我…1.0″ 可是 那金兵 占 了 我 大宋 天下 , 杀人放火 , 奸淫掳掠 , 无恶不作 …4村民中走出一个二十来岁的大汉,说道:”张先生,你可是从北方来吗?”说的是北方口音。张十五…1.0村民 中 走出 一个二十 来 岁 的 大汉 , 说道 : ” 张 先生 , 你 可是…


from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer()
wordmtx = countvec.fit_transform(raw12ana.cleantxt)
wordmtx
<376x6724 sparse matrix of type '<class 'numpy.int64'>'
    with 13642 stored elements in Compressed Sparse Row format>
</376x6724>

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(wordmtx, raw12ana.chap,
    test_size = 0.3, random_state = 111)


from sklearn import naive_bayes
NBmodel = naive_bayes.MultinomialNB()

NBmodel.fit(x_train, y_train)

x_test
<113x6724 sparse matrix of type '<class 'numpy.int64'>'
    with 4127 stored elements in Compressed Sparse Row format>
</113x6724>
NBmodel.predict(x_test)
array([2., 2., 1., 2., 1., 2., 1., 1., 2., 1., 2., 1., 2., 2., 1., 2., 1.,
       2., 1., 1., 2., 2., 1., 1., 2., 1., 2., 1., 1., 2., 1., 2., 1., 1.,
       2., 1., 1., 1., 1., 1., 1., 2., 1., 1., 2., 2., 2., 1., 1., 1., 2.,
       1., 1., 1., 2., 1., 2., 1., 2., 2., 2., 2., 1., 1., 1., 1., 1., 1.,
       1., 1., 2., 2., 1., 1., 1., 1., 1., 2., 1., 2., 1., 1., 2., 2., 2.,
       1., 1., 1., 2., 1., 1., 2., 1., 1., 1., 2., 2., 2., 2., 2., 1., 1.,
       2., 1., 1., 2., 2., 2., 2., 1., 1., 2., 2.])

print('训练集:', NBmodel.score(x_train, y_train),
      ',验证集:', NBmodel.score(x_test, y_test))
&#x8BAD;&#x7EC3;&#x96C6;&#xFF1A; 1.0 &#xFF0C;&#x9A8C;&#x8BC1;&#x96C6;&#xFF1A; 0.9557522123893806

from sklearn.metrics import classification_report

print(classification_report(y_test, NBmodel.predict(x_test)))
              precision    recall  f1-score   support

         1.0       0.94      0.98      0.96        61
         2.0       0.98      0.92      0.95        52

    accuracy                           0.96       113
   macro avg       0.96      0.95      0.96       113
weighted avg       0.96      0.96      0.96       113

将需要预测的文本转换为和建模时格式完全对应的d2m矩阵格式,随后即可进行预测。

countvec.vocabulary_
{'&#x8BF4;&#x8BDD;': 5703,
 '&#x4E94;&#x5341;': 765,
 '&#x5E74;&#x7EAA;': 2855,
 ......

 '&#x63E1;&#x4F4F;': 3565,
 '&#x730E;&#x53C9;': 4588,
 '&#x968F;&#x5373;': 6438,
 ...}
string = "杨铁心和包惜弱收养穆念慈"
words = " ".join(jieba.lcut(string))
words_vecs = countvec.transform([words])
words_vecs
<1x6724 sparse matrix of type '<class 'numpy.int64'>'
    with 2 stored elements in Compressed Sparse Row format>
</1x6724>
NBmodel.predict(words_vecs)
array([1.])
from sklearn.linear_model import LogisticRegression

logitmodel = LogisticRegression()

logitmodel.fit(x_train, y_train)
print(classification_report(y_test, logitmodel.predict(x_test)))
              precision    recall  f1-score   support

         1.0       0.94      0.98      0.96        61
         2.0       0.98      0.92      0.95        52

    accuracy                           0.96       113
   macro avg       0.96      0.95      0.96       113
weighted avg       0.96      0.96      0.96       113

2.2 NLTK实现

NLTK中内置了朴素贝叶斯算法,可直接实现文档分类。

数据集中语料的格式
用于训练的语料必须是分词完毕的字典形式,词条为键名,键值则可以是数值、字符、或者T/F

{‘张三’ : True, ‘李四’ : True, ‘王五’ : False}

{‘张三’ : 1, ‘李四’ : 1, ‘王五’ : 0}

{‘张三’ : ‘有’, ‘李四’ : ‘有’, ‘王五’ : ‘无’}

训练用数据集为list of list格式,每个成员为list[语料字典, 结果变量]

[{‘张三’ : 1, ‘李四’ : 1, ‘王五’ : 0}, ‘合格’],

[{‘张三’ : 0, ‘李四’ : 1, ‘王五’ : 0}, ‘不合格’]

考虑到过拟合问题,此处需要先拆分好训练集和测试集

model = NaiveBayesClassifier.train(training_data)


import nltk
from nltk import FreqDist

fdist1 = FreqDist(m_cut(chapter.txt[1]))
fdist2 = FreqDist(m_cut(chapter.txt[2]))
fdist3 = FreqDist(m_cut(chapter.txt[3]))
fdist1
FreqDist({'&#x7B2C;&#x4E00;&#x56DE;': 1,
          '&#x98CE;&#x96EA;': 3,
          '&#x60CA;&#x53D8;': 1,
          '&#x8BF4;&#x8BDD;': 9,
          ......

          '&#x4E00;&#x5177;': 1,
          '&#x9ED1;&#x8272;': 1,
          '&#x76D8;&#x5F62;': 1,
          ...})

from nltk.classify import NaiveBayesClassifier

training_data = [ [fdist1, 'chap1'], [fdist2, 'chap2'], [fdist3, 'chap3'] ]

NLTKmodel = NaiveBayesClassifier.train(training_data)

print(NLTKmodel.classify(FreqDist(m_cut("杨铁心收养穆念慈"))))
print(NLTKmodel.classify(FreqDist(m_cut("钱塘江 日日夜夜 包惜弱 颜烈 使出杨家枪"))))
chap3
chap2

nltk.classify.accuracy(NLTKmodel, training_data)

1.0

NLTKmodel.show_most_informative_features(5)
Most Informative Features
                      &#x98DE;&#x5C06; = None            chap3 : chap2  =      1.0 : 1.0
                      &#x51E0;&#x830E; = None            chap2 : chap1  =      1.0 : 1.0

对射雕的前两个章节提取关键字,然后使用关键字而不是原始文本进行文档分类,比较这样两种方式的分类效果有何变化。

减少用于训练的样本量,考察使用朴素贝叶斯算法或者其他标准分类算法时,模型效果的变化趋势。

自行实现基于NLTK的按段落为单位进行章节分类的程序。

import jieba
import jieba.analyse
import jieba
chapter['cut'] = chapter.txt.apply(jieba.lcut)
chapter.head()
Building prefix dict from the default dictionary ...

Loading model from cache C:\Users\A\AppData\Local\Temp\jieba.cache
Loading model cost 0.555 seconds.

Prefix dict has been built successfully.

txtcutchap1.0第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。只听他两…[第一回, , 风雪, 惊变, , , , , , , 那, 说话, 人, 五…2.0第二回 江南七怪 颜烈跨出房门,过道中一个中年士人拖着鞋皮,踢跶踢跶的直响,一路打着哈…[第二回, , 江南七怪, , , , , 颜烈, 跨出, 房门, ,, 过道, …3.0第三回 黄沙莽莽 寺里僧众见焦木圆寂,尽皆悲哭。有的便为伤者包扎伤处,抬入客舍。 忽…[第三回, , 黄沙, 莽莽, , , , , 寺里, 僧众, 见, 焦木, 圆寂…4.0第四回 黑风双煞 完颜洪熙笑道:”好,再打他个痛快。”蒙古兵前哨报来:”王罕亲自前来迎…[第四回, , 黑风双, 煞, , , , , 完颜洪熙, 笑, 道, :, “,…5.0第五回 弯弓射雕 一行人下得山来,走不多时,忽听前面猛兽大吼声一阵阵传来。韩宝驹一提缰…[第五回, , 弯弓, 射雕, , , , , 一行, 人下, 得, 山来, ,,…


key1=jieba.analyse.extract_tags(chapter.txt[1])
key2=jieba.analyse.extract_tags(chapter.txt[2])
key=key1+key2
key
['&#x6768;&#x94C1;&#x5FC3;',
 '&#x5305;&#x60DC;&#x5F31;',
 '&#x90ED;&#x5578;&#x5929;',
 '&#x989C;&#x70C8;',
 '&#x4E18;&#x5904;&#x673A;',
 '&#x6768;&#x4E8C;&#x4EBA;',
 ......

 '&#x91D1;&#x5175;',
 '&#x6B66;&#x529F;',
 '&#x9053;&#x957F;',
 '&#x5357;&#x5E0C;&#x4EC1;',
 '&#x5168;&#x91D1;&#x53D1;',
 '&#x674E;&#x840D;']

from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer()
wordmtx = countvec.fit_transform(key)
wordmtx
<40x34 sparse matrix of type '<class 'numpy.int64'>'
    with 40 stored elements in Compressed Sparse Row format>
</40x34>

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(wordmtx, raw12ana.chap[:40],
    test_size = 0.3, random_state = 1)

from sklearn import naive_bayes

NBmodel = naive_bayes.MultinomialNB()

NBmodel.fit(x_train, y_train)

x_test

print('训练集:', NBmodel.score(x_train, y_train),
      ',验证集:', NBmodel.score(x_test, y_test))
&#x8BAD;&#x7EC3;&#x96C6;&#xFF1A; 1.0 &#xFF0C;&#x9A8C;&#x8BC1;&#x96C6;&#xFF1A; 1.0

from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer()

wordmtx = countvec.fit_transform(raw12ana.cleantxt)
wordmtx
<376x6724 sparse matrix of type '<class 'numpy.int64'>'
    with 13642 stored elements in Compressed Sparse Row format>
</376x6724>

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(wordmtx, raw12ana.chap,
    test_size = 0.8, random_state = 111)
from sklearn import naive_bayes

NBmodel = naive_bayes.MultinomialNB()

NBmodel.fit(x_train, y_train)

from sklearn.metrics import classification_report

print(classification_report(y_test, NBmodel.predict(x_test)))
              precision    recall  f1-score   support

         1.0       0.84      0.96      0.89       156
         2.0       0.94      0.81      0.87       145

    accuracy                           0.88       301
   macro avg       0.89      0.88      0.88       301
weighted avg       0.89      0.88      0.88       301

可见训练集过少会直接影响训练的效果

情感分析概述

3.1 基于词袋模型的分析

数据概况:

取自购物网站的正向、负向评价各约1万条。


import pandas as pd

dfpos = pd.read_excel("购物评论.xlsx", sheet_name = "正向", header=None)
dfpos['y'] = 1
dfneg = pd.read_excel("购物评论.xlsx", sheet_name = "负向", header=None)
dfneg['y'] = 0
df0 = dfpos.append(dfneg, ignore_index = True)
df0.head()

0y0感觉用这个手机下载MP3听很方便,用T-Flash卡装上歌,就可以当一个小MP3用了,很不错…11外观美观,速度也不错。上面一排触摸键挺实用。应该对得起这个价格。当然再降点大家肯定也不反对。…12我刚拿到书,也没有仔细阅读,就是粗粗的翻了点,觉得还行。里面是蓝黑两种颜色的,有些单词的下面…13对于二胡曲的精选,应该出版一个系列套装,可分别出售,单独购买。精品二胡曲是有年代和阶段性的,…14用了一年半的 e680 终于送小偷了。刚刚买了一台e850,用了三天,说说我自己的感受。1:…1


import jieba

cuttxt = lambda x: " ".join(jieba.lcut(x))
df0["cleantxt"] = df0[0].apply(cuttxt)
df0.tail()

0ycleantxt20577服务员太差,房间很吵,隔音不好,房间味道很重,很一般,不如稀土国际大酒店好,收费有低,还是四…0服务员 太 差 , 房间 很 吵 , 隔音 不好 , 房间 味道 很 重 , 很 一般 , …20578很早我就说等”爱氏晨曦”吃进蒙牛股份后,对蒙牛的质疑就会停止,如今已应验。想知道单是这一笔…0很早 我 就 说 等 ” 爱氏 晨曦 ” 吃进 蒙牛 股份 后 , 对 蒙牛 的 质疑 …205792——10岁是什么意思?我不认为有一本书可以覆盖这样的范围,事实上,我两岁的女儿不能理解这套…02 — — 10 岁 是 什么 意思 ? 我 不 认为 有 一 本书 可以 覆盖 这样 的 …20580刚住过这里,感觉不好,房间味道太大,一股子霉味,房间各种设施都很陈旧,我所预订的大床间,却没…0刚住 过 这里 , 感觉不好 , 房间 味道 太 大 , 一股 子 霉味 , 房间 各种 设…20581一本书200页,综合一下只有3页可看 却要花200页的钱 唉 唉0一 本书 200 页 , 综合 一下 只有 3 页 可 看 却 要 花 200 页 …


from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer(min_df = 5)

wordmtx = countvec.fit_transform(df0.cleantxt)
wordmtx
<20582x12014 sparse matrix of type '<class 'numpy.int64'>'
    with 513919 stored elements in Compressed Sparse Row format>
</20582x12014>

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(
    wordmtx, df0.y, test_size=0.3)
x_train[0]
<1x12014 sparse matrix of type '<class 'numpy.int64'>'
    with 14 stored elements in Compressed Sparse Row format>
</1x12014>

from sklearn.svm import SVC

clf=SVC(kernel = 'rbf', verbose = True)
clf.fit(x_train, y_train)
clf.score(x_train, y_train)
[LibSVM]
0.9535642396057472

from sklearn.metrics import classification_report

print(classification_report(y_test, clf.predict(x_test)))
              precision    recall  f1-score   support

           0       0.87      0.90      0.88      3055
           1       0.89      0.86      0.88      3120

    accuracy                           0.88      6175
   macro avg       0.88      0.88      0.88      6175
weighted avg       0.88      0.88      0.88      6175

clf.predict(countvec.transform([df0.cleantxt[0]]))[0]

import jieba

def m_pred(string, countvec, model) :
    words = " ".join(jieba.lcut(string))
    words_vecs = countvec.transform([words])

    result = model.predict(words_vecs)

    if int(result[0]) == 1:
        print(string, ":正向")
    else:
        print(string, ":负向")

comment = "外观美观,速度也不错。上面一排触摸键挺实用。应该对得起这个价格。当然再降点大家肯定也不反对。风扇噪音也不大。"
m_pred(comment, countvec, clf)
&#x5916;&#x89C2;&#x7F8E;&#x89C2;&#xFF0C;&#x901F;&#x5EA6;&#x4E5F;&#x4E0D;&#x9519;&#x3002;&#x4E0A;&#x9762;&#x4E00;&#x6392;&#x89E6;&#x6478;&#x952E;&#x633A;&#x5B9E;&#x7528;&#x3002;&#x5E94;&#x8BE5;&#x5BF9;&#x5F97;&#x8D77;&#x8FD9;&#x4E2A;&#x4EF7;&#x683C;&#x3002;&#x5F53;&#x7136;&#x518D;&#x964D;&#x70B9;&#x5927;&#x5BB6;&#x80AF;&#x5B9A;&#x4E5F;&#x4E0D;&#x53CD;&#x5BF9;&#x3002;&#x98CE;&#x6247;&#x566A;&#x97F3;&#x4E5F;&#x4E0D;&#x5927;&#x3002; &#xFF1A;&#x6B63;&#x5411;
comment = "作为女儿6.1的礼物。虽然晚到了几天。等拿到的时候,女儿爱不释手,上洗手间也看,告知不好。上周末,告诉我她把火鞋和风鞋拿到学校,好多同学羡慕她。呵呵,我也看了其中的人鸦,只可惜没有看完就在老公的催促下睡了。说了这么多,归纳为一句:这套书买的值。"
m_pred(comment, countvec, clf)
&#x4F5C;&#x4E3A;&#x5973;&#x513F;6.1&#x7684;&#x793C;&#x7269;&#x3002;&#x867D;&#x7136;&#x665A;&#x5230;&#x4E86;&#x51E0;&#x5929;&#x3002;&#x7B49;&#x62FF;&#x5230;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5973;&#x513F;&#x7231;&#x4E0D;&#x91CA;&#x624B;&#xFF0C;&#x4E0A;&#x6D17;&#x624B;&#x95F4;&#x4E5F;&#x770B;&#xFF0C;&#x544A;&#x77E5;&#x4E0D;&#x597D;&#x3002;&#x4E0A;&#x5468;&#x672B;&#xFF0C;&#x544A;&#x8BC9;&#x6211;&#x5979;&#x628A;&#x706B;&#x978B;&#x548C;&#x98CE;&#x978B;&#x62FF;&#x5230;&#x5B66;&#x6821;&#xFF0C;&#x597D;&#x591A;&#x540C;&#x5B66;&#x7FA1;&#x6155;&#x5979;&#x3002;&#x5475;&#x5475;&#xFF0C;&#x6211;&#x4E5F;&#x770B;&#x4E86;&#x5176;&#x4E2D;&#x7684;&#x4EBA;&#x9E26;&#xFF0C;&#x53EA;&#x53EF;&#x60DC;&#x6CA1;&#x6709;&#x770B;&#x5B8C;&#x5C31;&#x5728;&#x8001;&#x516C;&#x7684;&#x50AC;&#x4FC3;&#x4E0B;&#x7761;&#x4E86;&#x3002;&#x8BF4;&#x4E86;&#x8FD9;&#x4E48;&#x591A;&#xFF0C;&#x5F52;&#x7EB3;&#x4E3A;&#x4E00;&#x53E5;&#xFF1A;&#x8FD9;&#x5957;&#x4E66;&#x4E70;&#x7684;&#x503C;&#x3002; &#xFF1A;&#x6B63;&#x5411;

3.2 基于分布式表达的分析(对于短文本来说,效果更优)

和词袋模型相比,分布式表达主要是改变了文本信息的提取方式。

目前主要使用gensim实现相应的算法。

注意:由于矩阵不再是频数值(正负想间),因此不能使用朴素贝叶斯算法来进行拟合。


import pandas as pd

dfpos = pd.read_excel("购物评论.xlsx", sheet_name = "正向", header=None)
dfpos['y'] = 1
dfneg = pd.read_excel("购物评论.xlsx", sheet_name = "负向", header=None)
dfneg['y'] = 0
df0 = dfpos.append(dfneg, ignore_index = True)
df0.head()

0y0感觉用这个手机下载MP3听很方便,用T-Flash卡装上歌,就可以当一个小MP3用了,很不错…11外观美观,速度也不错。上面一排触摸键挺实用。应该对得起这个价格。当然再降点大家肯定也不反对。…12我刚拿到书,也没有仔细阅读,就是粗粗的翻了点,觉得还行。里面是蓝黑两种颜色的,有些单词的下面…13对于二胡曲的精选,应该出版一个系列套装,可分别出售,单独购买。精品二胡曲是有年代和阶段性的,…14用了一年半的 e680 终于送小偷了。刚刚买了一台e850,用了三天,说说我自己的感受。1:…1


import jieba

df0['cut'] = df0[0].apply(jieba.lcut)
df0.head()

0ycut0感觉用这个手机下载MP3听很方便,用T-Flash卡装上歌,就可以当一个小MP3用了,很不错…1[感觉, 用, 这个, 手机, 下载, MP3, 听, 很, 方便, ,, 用, T, -,…1外观美观,速度也不错。上面一排触摸键挺实用。应该对得起这个价格。当然再降点大家肯定也不反对。…1[外观, 美观, ,, 速度, 也, 不错, 。, 上面, 一排, 触摸, 键, 挺, 实用…2我刚拿到书,也没有仔细阅读,就是粗粗的翻了点,觉得还行。里面是蓝黑两种颜色的,有些单词的下面…1[我刚, 拿到, 书, ,, 也, 没有, 仔细阅读, ,, 就是, 粗粗, 的, 翻, 了…3对于二胡曲的精选,应该出版一个系列套装,可分别出售,单独购买。精品二胡曲是有年代和阶段性的,…1[对于, 二胡曲, 的, 精选, ,, 应该, 出版, 一个系列, 套装, ,, 可, 分别…4用了一年半的 e680 终于送小偷了。刚刚买了一台e850,用了三天,说说我自己的感受。1:…1[用, 了, 一年, 半, 的, , e680, , 终于, 送, 小偷, 了, 。, …


from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(
    df0.cut, df0.y, test_size=0.3)
x_train[:2]
10965    [&#x513F;&#x5B50;, 1, &#x5E74;, 7, &#x4E2A;, &#x6708;, &#xFF0C;, &#x867D;&#x7136;, &#x597D;&#x591A;, &#x90FD;, &#x4E0D;&#x662F;, &#x5F88;, &#x61C2;, &#xFF0C;,...

17209    [&#x8FD9;&#x5BB6;, &#x9152;&#x5E97;, &#x7EDD;&#x5BF9;, &#x6CA1;&#x6709;, &#x56DB;&#x661F;&#x7EA7;, ,, &#x800C;&#x4E14;, &#x623F;&#x95F4;, &#x7684;, &#x9694;&#x97F3;, &#x592A;, &#x5DEE;, ...

Name: cut, dtype: object

from gensim.models.word2vec import Word2Vec
n_dim = 300
w2vmodel = Word2Vec(vector_size = n_dim, min_count = 10)
w2vmodel.build_vocab(x_train)

%time w2vmodel.train(x_train, \
               total_examples = w2vmodel.corpus_count, epochs = 10)
Wall time: 4.67 s

(6346434, 9495160)

w2vmodel.wv.most_similar("不错")
[('&#x597D;', 0.6664835810661316),
 ('&#x884C;', 0.6593450903892517),
 ('&#x633A;&#x4E0D;&#x9519;', 0.658033549785614),
 ('&#x6BD4;&#x8F83;&#x6EE1;&#x610F;', 0.6418668627738953),
 ('&#x5F88;&#x6F02;&#x4EAE;', 0.5996091365814209),
 ('&#x8FD8;&#x884C;', 0.5758983492851257),
 ('&#x5F88;&#x5FEB;', 0.5542255640029907),
 ('&#x6574;&#x4F53;', 0.5516241788864136),
 ('&#x6F02;&#x4EAE;', 0.5488754510879517),
 ('&#x8BF4;&#x5F97;&#x8FC7;&#x53BB;', 0.5477145910263062)]
w2vmodel.wv.most_similar("失望")
[('&#x9057;&#x61BE;', 0.7359662652015686),
 ('&#x4E0D;&#x723D;', 0.6579391360282898),
 ('&#x6C14;&#x6124;', 0.647860586643219),
 ('&#x540E;&#x6094;', 0.6342949271202087),
 ('&#x4E0D;&#x6EE1;', 0.6023170351982117),
 ('&#x751F;&#x6C14;', 0.5882432460784912),
 ('&#x4E0A;&#x5F53;', 0.5846213698387146),
 ('&#x96BE;&#x5F97;', 0.5696483254432678),
 ('&#x7CDF;&#x7CD5;', 0.5675068497657776),
 ('&#x6D45;', 0.5560178756713867)]

对购物评论、微博等短文本而言,一般是将所有词向量的平均值作为分类算法的输入值。


pd.DataFrame([w2vmodel.wv[w] for w in df0.cut[0] if w in w2vmodel.wv]).head()

0123456789…2902912922932942952962972982990-0.9113322.487151-0.4453550.959004-0.077999-0.157231-0.5110120.6362100.123468-1.307333…-0.798626-0.1331262.0846860.0841970.662218-0.7118581.7726251.529452-0.640140-0.09114710.046230-0.0357921.241354-1.3543660.447580-0.5604201.2724241.154507-0.583416-0.777637…-1.0289600.141009-0.139010-0.353615-0.9384530.0849640.510275-0.6980840.632293-0.49319720.5882860.292953-0.215384-0.420847-0.803060-0.0338200.0183330.384169-0.659887-0.146808…0.2775451.0462610.7420070.2306530.011729-1.6647410.1186310.510197-0.013587-0.13856630.191487-0.297445-0.385975-1.030350-1.255563-1.2140320.6162300.202745-1.1332470.830488…1.589015-0.153522-0.118775-1.095460-0.661065-1.3192110.029742-0.847319-0.7235971.2763794-0.704394-0.0719390.1009930.326895-0.685714-0.722568-0.2050360.934630-0.2402660.022127…0.595830-0.101508-0.191669-0.558731-0.897231-0.376409-0.159503-0.5682330.4716850.287301

5 rows × 300 columns


def m_avgvec(words, w2vmodel):
    return pd.DataFrame([w2vmodel.wv[w]
                  for w in words if w in w2vmodel.wv]).agg("mean")

%time train_vecs = pd.DataFrame([m_avgvec(s, w2vmodel) for s in x_train])
train_vecs.head()
Wall time: 2min 55s

0123456789…2902912922932942952962972982990-0.1388820.2439060.155527-0.057517-0.2433590.0385050.123457-0.029797-0.2510020.130891…-0.1585220.3286100.4685590.006440-0.0713260.0464290.262878-0.0735970.083408-0.13362710.1239530.2491460.031028-0.103072-0.012683-0.0612410.0167360.236264-0.238410-0.349272…-0.0948740.3161480.2838090.0352150.100668-0.1503880.1515850.2022990.011277-0.34662620.0096750.0635080.298085-0.113564-0.394117-0.197484-0.143916-0.264382-0.1652450.044551…-0.0770130.2193530.319499-0.054280-0.0175930.068092-0.043105-0.3146320.061136-0.3153243-0.2014780.054600-0.1972190.132496-0.1308590.0599310.0908970.447195-0.066142-0.080050…0.2165200.3367890.2427570.1923320.255502-0.0002680.000050-0.1004860.013850-0.16365240.0124180.231921-0.169535-0.0271080.1622330.0893450.1239970.351209-0.036844-0.328844…0.1541050.4719110.1310340.205431-0.0077950.1226200.1653090.214215-0.148311-0.170526

5 rows × 300 columns

%time train_vecs_t = pd.DataFrame([m_avgvec(x, w2vmodel) for x in x_test])
train_vecs_t
Wall time: 1min 14s

0123456789…2902912922932942952962972982990-0.360456-0.168126-0.1879020.139328-0.580499-0.4768090.3828650.8964780.0045190.060998…0.3379080.313125-0.248374-0.1927840.191395-0.0032970.034540-0.0243450.3077050.0600491-0.0065730.3641380.0565960.062842-0.164288-0.0388410.0045680.271801-0.263575-0.250043…-0.1078460.2474520.2252570.0200390.135461-0.1123770.059987-0.0933460.092084-0.3386792-0.342170-0.1075180.150887-0.012432-0.119799-0.1514690.0628100.317839-0.264509-0.119258…-0.1460240.2517000.4285590.1123630.0885080.0108390.1339460.158355-0.091741-0.3330993-0.0361960.3237470.038649-0.041602-0.247251-0.055818-0.0801030.083953-0.244907-0.281211…-0.2270690.3630510.4721900.0760830.067558-0.1418180.051008-0.112334-0.019767-0.42121040.0850830.1897930.0490330.066349-0.216909-0.0713970.0100420.301876-0.342764-0.218100…-0.1644970.4300710.1228700.0714520.140936-0.096815-0.089997-0.0065230.104536-0.479253…………………………………………………………6170-0.2649000.0843840.0112390.049277-0.1414440.1007490.3151260.146341-0.133169-0.027791…0.1471900.3365360.4516920.1759380.1920360.1654600.1625850.089151-0.057888-0.23366661710.026205-0.0718740.270545-0.027888-0.195445-0.299105-0.0790340.288189-0.127330-0.068026…-0.1921920.2486960.3235430.067723-0.0307340.0417970.171027-0.0150460.115095-0.2317216172-0.4050610.0058830.0379200.075662-0.0390780.1620150.0933000.189479-0.148963-0.012335…0.2102630.3911040.3873800.1361650.4232610.1318360.346203-0.0706950.047509-0.0274066173-0.062726-0.054550-0.029740-0.169576-0.004502-0.0923100.1810260.379483-0.213536-0.264653…0.0600710.4085610.3005020.1743860.0601650.0399830.1387490.1120430.105036-0.1806246174-0.1226030.1167250.177533-0.111880-0.154657-0.210409-0.0839180.100732-0.1648700.042741…-0.1960230.1223200.098776-0.089089-0.085931-0.149250-0.006701-0.1962860.078928-0.180735

6175 rows × 300 columns

train_vecs_t.isnull().any()
0      True
1      True
2      True
3      True
4      True
       ...

295    True
296    True
297    True
298    True
299    True
Length: 300, dtype: bool

from sklearn.svm import SVC

clf2 = SVC(kernel = 'rbf', verbose = True)
clf2.fit(train_vecs, y_train)
clf2.score(train_vecs, y_train)
[LibSVM]
0.8899146248351496
from sklearn.metrics import classification_report

print(classification_report(y_train, clf2.predict(train_vecs)))
              precision    recall  f1-score   support

           0       0.89      0.89      0.89      7129
           1       0.89      0.89      0.89      7278

    accuracy                           0.89     14407
   macro avg       0.89      0.89      0.89     14407
weighted avg       0.89      0.89      0.89     14407

import jieba

def m_pred(string, model):
    words = jieba.lcut(string)
    words_vecs = pd.DataFrame(m_avgvec(words, w2vmodel)).T

    result = model.predict(words_vecs)

    if int(result[0]) == 1:
        print(string, ":正向")
    else:
        print(string, ":负向")

comment = "作为女儿6.1的礼物。虽然晚到了几天。等拿到的时候,女儿爱不释手,上洗手间也看,告知不好。上周末,告诉我她把火鞋和风鞋拿到学校,好多同学羡慕她。呵呵,我也看了其中的人鸦,只可惜没有看完就在老公的催促下睡了。说了这么多,归纳为一句:这套书买的值。"
m_pred(comment, clf2)
&#x4F5C;&#x4E3A;&#x5973;&#x513F;6.1&#x7684;&#x793C;&#x7269;&#x3002;&#x867D;&#x7136;&#x665A;&#x5230;&#x4E86;&#x51E0;&#x5929;&#x3002;&#x7B49;&#x62FF;&#x5230;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5973;&#x513F;&#x7231;&#x4E0D;&#x91CA;&#x624B;&#xFF0C;&#x4E0A;&#x6D17;&#x624B;&#x95F4;&#x4E5F;&#x770B;&#xFF0C;&#x544A;&#x77E5;&#x4E0D;&#x597D;&#x3002;&#x4E0A;&#x5468;&#x672B;&#xFF0C;&#x544A;&#x8BC9;&#x6211;&#x5979;&#x628A;&#x706B;&#x978B;&#x548C;&#x98CE;&#x978B;&#x62FF;&#x5230;&#x5B66;&#x6821;&#xFF0C;&#x597D;&#x591A;&#x540C;&#x5B66;&#x7FA1;&#x6155;&#x5979;&#x3002;&#x5475;&#x5475;&#xFF0C;&#x6211;&#x4E5F;&#x770B;&#x4E86;&#x5176;&#x4E2D;&#x7684;&#x4EBA;&#x9E26;&#xFF0C;&#x53EA;&#x53EF;&#x60DC;&#x6CA1;&#x6709;&#x770B;&#x5B8C;&#x5C31;&#x5728;&#x8001;&#x516C;&#x7684;&#x50AC;&#x4FC3;&#x4E0B;&#x7761;&#x4E86;&#x3002;&#x8BF4;&#x4E86;&#x8FD9;&#x4E48;&#x591A;&#xFF0C;&#x5F52;&#x7EB3;&#x4E3A;&#x4E00;&#x53E5;&#xFF1A;&#x8FD9;&#x5957;&#x4E66;&#x4E70;&#x7684;&#x503C;&#x3002; &#xFF1A;&#x6B63;&#x5411;

在本章所用数据中,各抽取1千条正向、负向评论,重新拟合基于词袋的和基于分布式表达的模型,比较前两种模型效果的改变情况。


import pandas as pd

dfpos = pd.read_excel("购物评论.xlsx", sheet_name = "正向", header=None,nrows=1000)
dfpos['y'] = 1
dfneg = pd.read_excel("购物评论.xlsx", sheet_name = "负向", header=None,nrows=1000)
dfneg['y'] = 0
df0 = dfpos.append(dfneg, ignore_index = True)
df0.head()

0y0感觉用这个手机下载MP3听很方便,用T-Flash卡装上歌,就可以当一个小MP3用了,很不错…11外观美观,速度也不错。上面一排触摸键挺实用。应该对得起这个价格。当然再降点大家肯定也不反对。…12我刚拿到书,也没有仔细阅读,就是粗粗的翻了点,觉得还行。里面是蓝黑两种颜色的,有些单词的下面…13对于二胡曲的精选,应该出版一个系列套装,可分别出售,单独购买。精品二胡曲是有年代和阶段性的,…14用了一年半的 e680 终于送小偷了。刚刚买了一台e850,用了三天,说说我自己的感受。1:…1

4.1 自动摘要的python实现

chapter.txt[1]
'&#x7B2C;&#x4E00;&#x56DE; &#x98CE;&#x96EA;&#x60CA;&#x53D8;&#x90A3;&#x8BF4;&#x8BDD;&#x4EBA;&#x4E94;&#x5341;&#x6765;&#x5C81;&#x5E74;&#x7EAA;&#xFF0C;&#x4E00;&#x4EF6;&#x9752;&#x5E03;&#x957F;&#x888D;&#x65E9;&#x6D17;&#x5F97;&#x892A;&#x6210;&#x4E86;&#x84DD;&#x7070;&#x5E26;&#x767D;&#x3002;&#x53EA;&#x542C;&#x4ED6;&#x4E24;&#x7247;&#x68A8;&#x82B1;&#x6728;&#x677F;&#x78B0;&#x4E86;&#x51E0;&#x4E0B;&#xFF0C;&#x5DE6;&#x624B;&#x4E2D;&#x7AF9;&#x68D2;&#x5728;&#x4E00;&#x9762;&#x5C0F;&#x7FAF;&#x9F13;&#x4E0A;&#x6572;&#x8D77;&#x5F97;&#x5F97;&#x8FDE;&#x58F0;&#x3002;&#x5531;&#x9053;&#xFF1A;&#x201C;&#x5C0F;&#x6843;&#x65E0;&#x4E3B;&#x81EA;&#x5F00;&#x82B1;&#xFF0C;&#x70DF;&#x8349;&#x832B;&#x832B;&#x5E26;&#x665A;&#x9E26;&#x3002;&#x51E0;&#x5904;&#x8D25;&#x57A3;&#x56F4;&#x6545;&#x4E95;&#xFF0C;&#x5411;&#x6765;&#x4E00;&#x4E00;&#x662F;&#x4EBA;&#x5BB6;&#x3002;&#x201D;&#x90A3;&#x8BF4;&#x8BDD;&#x4EBA;&#x5C06;&#x6728;&#x677F;&#x6572;&#x4E86;&#x51E0;&#x4E0B;&#xFF0C;&#x8BF4;&#x9053;&#xFF1A;&#x201C;&#x8FD9;&#x9996;&#x4E03;&#x8A00;&#x8BD7;&#xFF0C;&#xFF0C;.......&#x201D;&#x989C;&#x70C8;&#x9053;&#xFF1A;&#x201C;&#x65E2;&#x6709;&#x59D3;&#x201D;&#x5305;&#x60DC;&#x5F31;&#x70B9;&#x4E86;&#x70B9;&#x5934;&#xFF0C;&#x9053;&#xFF1A;&#x201C;&#x76F8;&#x516C;&#x53EF;&#x522B;&#x592A;&#x591A;&#x82B1;&#x8D39;&#x4E86;&#x3002;&#x201D;&#x989C;&#x70C8;&#x5FAE;&#x7B11;&#x9053;&#xFF1A;&#x201C;&#x5C31;&#x53EF;&#x60DC;&#x5A18;&#x5B50;&#x5728;&#x670D;&#x4E27;&#xFF0C;&#x4E0D;&#x80FD;&#x6234;&#x7528;&#x73E0;&#x5B9D;&#xFF0C;&#x8981;&#x591A;&#x82B1;&#x94B1;&#x4E5F;&#x82B1;&#x4E0D;&#x4E86;&#x3002;&#x2019;

def cut_sentence(intxt):
    delimiters = frozenset('。!?')
    buf = []
    for ch in intxt:
        buf.append(ch)
        if delimiters.__contains__(ch):
            yield ''.join(buf)
            buf = []
    if buf:
        yield ''.join(buf)
sentdf = pd.DataFrame(cut_sentence(chapter.txt[1]))
sentdf

00第一回 风雪惊变 那说话人五十来岁年纪,一件青布长袍早洗得褪成了蓝灰带白。1只听他两片梨花木板碰了几下,左手中竹棒在一面小羯鼓上敲起得得连声。2唱道:”小桃无主自开花,烟草茫茫带晚鸦。3几处败垣围故井,向来一一是人家。4″ 那说话人将木板敲了几下,说道:”这首七言诗,说的是兵火过后,原来的家家户户,都变成了断………908包惜弱想要他另要一间客房,却又不知如何启齿才好,脸上一阵红一阵白,心事重重。909过了一会,颜烈道:”娘子请自宽便,小人出去买了物品就回。910″包惜弱点了点头,道:”相公可别太多花费了。911″颜烈微笑道:”就可惜娘子在服丧,不能戴用珠宝,要多花钱也花不了。912″

913 rows × 1 columns


sentdf['txtlen'] = sentdf[0].apply(len)
sentlist = sentdf[0][sentdf.txtlen > 20]
print(len(sentlist))
sentlist
583

0             &#x7B2C;&#x4E00;&#x56DE; &#x98CE;&#x96EA;&#x60CA;&#x53D8;&#x3000;&#x3000;&#x3000;&#x3000;&#x3000;&#x3000;&#x90A3;&#x8BF4;&#x8BDD;&#x4EBA;&#x4E94;&#x5341;&#x6765;&#x5C81;&#x5E74;&#x7EAA;&#xFF0C;&#x4E00;&#x4EF6;&#x9752;&#x5E03;&#x957F;&#x888D;&#x65E9;&#x6D17;&#x5F97;&#x892A;&#x6210;&#x4E86;&#x84DD;&#x7070;&#x5E26;&#x767D;&#x3002;
1                      &#x53EA;&#x542C;&#x4ED6;&#x4E24;&#x7247;&#x68A8;&#x82B1;&#x6728;&#x677F;&#x78B0;&#x4E86;&#x51E0;&#x4E0B;&#xFF0C;&#x5DE6;&#x624B;&#x4E2D;&#x7AF9;&#x68D2;&#x5728;&#x4E00;&#x9762;&#x5C0F;&#x7FAF;&#x9F13;&#x4E0A;&#x6572;&#x8D77;&#x5F97;&#x5F97;&#x8FDE;&#x58F0;&#x3002;
4      &#x201D;&#x3000;&#x3000;&#x90A3;&#x8BF4;&#x8BDD;&#x4EBA;&#x5C06;&#x6728;&#x677F;&#x6572;&#x4E86;&#x51E0;&#x4E0B;&#xFF0C;&#x8BF4;&#x9053;&#xFF1A;&#x201C;&#x8FD9;&#x9996;&#x4E03;&#x8A00;&#x8BD7;&#xFF0C;&#x8BF4;&#x7684;&#x662F;&#x5175;&#x706B;&#x8FC7;&#x540E;&#xFF0C;&#x539F;&#x6765;&#x7684;&#x5BB6;&#x5BB6;&#x6237;&#x6237;&#xFF0C;&#x90FD;&#x53D8;&#x6210;&#x4E86;&#x65AD;...

5                         &#x5C0F;&#x4EBA;&#x521A;&#x624D;&#x8BF4;&#x5230;&#x90A3;&#x53F6;&#x8001;&#x6C49;&#x4E00;&#x5BB6;&#x56DB;&#x53E3;&#xFF0C;&#x60B2;&#x6B22;&#x79BB;&#x5408;&#xFF0C;&#x805A;&#x4E86;&#x53C8;&#x6563;&#xFF0C;&#x6563;&#x4E86;&#x53C8;&#x805A;&#x3002;
6      &#x4ED6;&#x56DB;&#x4EBA;&#x7ED9;&#x91D1;&#x5175;&#x51B2;&#x6563;&#xFF0C;&#x597D;&#x5BB9;&#x6613;&#x53C8;&#x518D;&#x56E2;&#x805A;&#xFF0C;&#x6B22;&#x5929;&#x559C;&#x5730;&#x5730;&#x56DE;&#x5230;&#x6545;&#x4E61;&#x536B;&#x5DDE;&#xFF0C;&#x5374;&#x89C1;&#x623F;&#x5C4B;&#x5DF2;&#x7ED9;&#x91D1;&#x5175;&#x70E7;&#x5F97;&#x5E72;&#x5E72;&#x51C0;&#x51C0;&#xFF0C;&#x65E0;&#x53EF;...

                             ...

907                          &#x6F31;&#x6D17;&#x7F62;&#xFF0C;&#x989C;&#x70C8;&#x4E0E;&#x5305;&#x60DC;&#x5F31;&#x4E00;&#x8D77;&#x5403;&#x4E86;&#x4E9B;&#x70B9;&#x5FC3;&#xFF0C;&#x4E24;&#x4EBA;&#x76F8;&#x5BF9;&#x5750;&#x5728;&#x623F;&#x4E2D;&#x3002;
908               &#x5305;&#x60DC;&#x5F31;&#x60F3;&#x8981;&#x4ED6;&#x53E6;&#x8981;&#x4E00;&#x95F4;&#x5BA2;&#x623F;&#xFF0C;&#x5374;&#x53C8;&#x4E0D;&#x77E5;&#x5982;&#x4F55;&#x542F;&#x9F7F;&#x624D;&#x597D;&#xFF0C;&#x8138;&#x4E0A;&#x4E00;&#x9635;&#x7EA2;&#x4E00;&#x9635;&#x767D;&#xFF0C;&#x5FC3;&#x4E8B;&#x91CD;&#x91CD;&#x3002;
909                       &#x3000;&#x3000;&#x8FC7;&#x4E86;&#x4E00;&#x4F1A;&#xFF0C;&#x989C;&#x70C8;&#x9053;&#xFF1A;&#x201C;&#x5A18;&#x5B50;&#x8BF7;&#x81EA;&#x5BBD;&#x4FBF;&#xFF0C;&#x5C0F;&#x4EBA;&#x51FA;&#x53BB;&#x4E70;&#x4E86;&#x7269;&#x54C1;&#x5C31;&#x56DE;&#x3002;
910                               &#x201D;&#x5305;&#x60DC;&#x5F31;&#x70B9;&#x4E86;&#x70B9;&#x5934;&#xFF0C;&#x9053;&#xFF1A;&#x201C;&#x76F8;&#x516C;&#x53EF;&#x522B;&#x592A;&#x591A;&#x82B1;&#x8D39;&#x4E86;&#x3002;
911                    &#x201D;&#x989C;&#x70C8;&#x5FAE;&#x7B11;&#x9053;&#xFF1A;&#x201C;&#x5C31;&#x53EF;&#x60DC;&#x5A18;&#x5B50;&#x5728;&#x670D;&#x4E27;&#xFF0C;&#x4E0D;&#x80FD;&#x6234;&#x7528;&#x73E0;&#x5B9D;&#xFF0C;&#x8981;&#x591A;&#x82B1;&#x94B1;&#x4E5F;&#x82B1;&#x4E0D;&#x4E86;&#x3002;
Name: 0, Length: 583, dtype: object

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

txtlist = [ " ".join(jieba.lcut(w)) for w in sentlist]

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(txtlist)
Building prefix dict from the default dictionary ...

Dumping model to file cache C:\Users\lenovo\AppData\Local\Temp\jieba.cache
Loading model cost 0.758 seconds.

Prefix dict has been built successfully.


tfidf_matrix = TfidfTransformer().fit_transform(X)

import networkx as nx

similarity = nx.from_scipy_sparse_matrix(tfidf_matrix * tfidf_matrix.T)
scores = nx.pagerank(similarity)
scores
{0: 0.0010912193346531665,
 1: 0.0010514692451123236,
 2: 0.0016169794514779936,
 3: 0.0013256313810434896,
 ......

 579: 0.0022975642232750905,
 580: 0.0027147294491056168,
 581: 0.0016757809711476844,
 582: 0.0017710726716536757}

tops = sorted(scores.items(), key = lambda x: x[1], reverse = True)
tops[:3]
[(104, 0.003887937743683278),
 (440, 0.0035158614971836305),
 (442, 0.0034495123422054637)]

print(sentlist.iloc[tops[0][0]])
print(sentlist.iloc[tops[1][0]])
sentlist.iloc[tops[2][0]]
&#x201D;&#x90ED;&#x5578;&#x5929;&#x9053;&#xFF1A;&#x201C;&#x81EA;&#x5DF1;&#x5144;&#x5F1F;&#xFF0C;&#x8BF4;&#x4EC0;&#x4E48;&#x8FD8;&#x8BF7;&#x4E0D;&#x8FD8;&#x8BF7;&#xFF1F;
&#x6768;&#x94C1;&#x5FC3;&#x518D;&#x662F;&#x4E00;&#x7BAD;&#xFF0C;&#x5C04;&#x6B7B;&#x4E86;&#x6B66;&#x5B98;&#xFF0C;&#x62A2;&#x5C06;&#x8FC7;&#x53BB;&#xFF0C;&#x53EA;&#x89C1;&#x90A3;&#x5973;&#x5B50;&#x5728;&#x5730;&#x4E0B;&#x6323;&#x624E;&#x7740;&#x5750;&#x8D77;&#x8EAB;&#x6765;&#xFF0C;&#x6B63;&#x662F;&#x81EA;&#x5DF1;&#x59BB;&#x5B50;&#x3002;

'&#x201D;&#x5305;&#x60DC;&#x5F31;&#x9053;&#xFF1A;&#x201C;&#x5728;&#x524D;&#x9762;&#xFF0C;&#x7ED9;&#x2026;&#x2026;&#x7ED9;&#x5B98;&#x5175;&#x6349;&#x53BB;&#x5566;&#xFF01;'

topn = 5
topsent = sorted(tops[:topn])

abstract = ''
for item in topsent:
    abstract = abstract + sentlist.iloc[item[0]] + "......"
abstract[:-6]
'&#x201D;&#x90ED;&#x5578;&#x5929;&#x9053;&#xFF1A;&#x201C;&#x81EA;&#x5DF1;&#x5144;&#x5F1F;&#xFF0C;&#x8BF4;&#x4EC0;&#x4E48;&#x8FD8;&#x8BF7;&#x4E0D;&#x8FD8;&#x8BF7;&#xFF1F;......&#x6768;&#x94C1;&#x5FC3;&#x63D0;&#x8D77;&#x94C1;&#x67AA;&#x8981;&#x51FA;&#x5C4B;&#x52A9;&#x6218;&#xFF0C;&#x90ED;&#x5578;&#x5929;&#x4E00;&#x628A;&#x62C9;&#x4F4F;&#xFF0C;&#x4F4E;&#x58F0;&#x9053;&#xFF1A;&#x201C;&#x9053;&#x957F;&#x53EB;&#x54B1;&#x4EEC;&#x522B;&#x51FA;&#x53BB;&#x3002;......&#x6768;&#x94C1;&#x5FC3;&#x518D;&#x662F;&#x4E00;&#x7BAD;&#xFF0C;&#x5C04;&#x6B7B;&#x4E86;&#x6B66;&#x5B98;&#xFF0C;&#x62A2;&#x5C06;&#x8FC7;&#x53BB;&#xFF0C;&#x53EA;&#x89C1;&#x90A3;&#x5973;&#x5B50;&#x5728;&#x5730;&#x4E0B;&#x6323;&#x624E;&#x7740;&#x5750;&#x8D77;&#x8EAB;&#x6765;&#xFF0C;&#x6B63;&#x662F;&#x81EA;&#x5DF1;&#x59BB;&#x5B50;&#x3002;......&#x201D;&#x5305;&#x60DC;&#x5F31;&#x9053;&#xFF1A;&#x201C;&#x5728;&#x524D;&#x9762;&#xFF0C;&#x7ED9;&#x2026;&#x2026;&#x7ED9;&#x5B98;&#x5175;&#x6349;&#x53BB;&#x5566;&#xFF01;......&#x8FD9;&#x65F6;&#x989C;&#x70C8;&#x5DF2;&#x8D70;&#x51FA;&#x623F;&#x53BB;&#xFF0C;&#x5305;&#x60DC;&#x5F31;&#x95EE;&#x9053;&#xFF1A;&#x201C;&#x8FD9;&#x662F;&#x4EC0;&#x4E48;&#xFF1F;'

Original: https://blog.csdn.net/flytrybest/article/details/125481722
Author: ★追梦赤子心★
Title: 文本挖掘学习笔记(三):文档相似度、文档分类和情感分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/662986/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球