中文自然语言处理--基于KMeans++的中文短文本聚类

文本聚类是将一个个文档由原有的自然语言文字信息转化成数学信息,以高维空间点的形式展现出来,通过计算哪些点距离比较近,从而将那些点聚成一个簇,簇的中心叫做簇心。一个好的聚类要保证簇内点的距离尽量的近,但簇与簇之间的点要尽量的远。
而KMeans++:
KMeans++是KMeans的改进。K-means算法是很典型的基于距离的聚类算法,采用距离 作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。k-means算法特点在于:同一聚类的簇内的对象相似度较高;而不同聚类的簇内的对象相似度较小。k-means++算法对选择初始seeds进行了改进,基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。

代码:

import random
import jieba
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
import gensim
from gensim.models import Word2Vec
from sklearn.preprocessing import scale
import multiprocessing
from sklearn.manifold import TSNE

# 整个过程分为以下几个步骤:语料加载,分词,去停用词,抽取词向量特征,
# 实战 TF-IDF 的中文文本 K-means 聚类,实战 word2Vec 的中文文本 K-means 聚类

# 加载停用词
stopwords = pd.read_csv('./NB_SVM/stopwords.txt', index_col=False, quoting=3, sep="\t", names=['stopword'], encoding='utf-8')
stopwords = stopwords['stopword'].values
print("stopwords:\n", stopwords)

# 加载语料,语料是4个已经分好类的 csv 文件
laogong_df = pd.read_csv('./NB_SVM/beilaogongda.csv', encoding='utf-8', sep=',', index_col=[0])
laopo_df = pd.read_csv('./NB_SVM/beilaopoda.csv', encoding='utf-8', sep=',', index_col=[0])
erzi_df = pd.read_csv('./NB_SVM/beierzida.csv', encoding='utf-8', sep=',', index_col=[0])
nver_df = pd.read_csv('./NB_SVM/beinverda.csv', encoding='utf-8', sep=',', index_col=[0])
# 删除语料的nan行
laogong_df.dropna(inplace=True)
laopo_df.dropna(inplace=True)
erzi_df.dropna(inplace=True)
nver_df.dropna(inplace=True)
print("laogong_df:\n", laogong_df)
print("laopo_df:\n", laopo_df)
print("erzi_df:\n", erzi_df)
print("nver_df:\n", nver_df)
# 转换
laogong = laogong_df.segment.values.tolist()
laopo = laopo_df.segment.values.tolist()
erzi = erzi_df.segment.values.tolist()
nver = nver_df.segment.values.tolist()

# 分词和去停用词
# 定义分词函数preprocess_text
# 参数content_lines即为上面转换的list
# 参数sentences是定义的空list,用来储存分词后的数据
jieba.add_word("报警人")
jieba.add_word("防护装备")
jieba.add_word("防护设备")
jieba.suggest_freq(("人", "称"), tune=True)
def preprocess_text(content_lines, sentences):
    for line in content_lines:
        try:
            segs = jieba.lcut(line)
            segs = [v for v in segs if not str(v).isdigit()]  # 去数字
            segs = list(filter(lambda x: x.strip(), segs))  # 去左右空格
            segs = list(filter(lambda x: len(x) > 1, segs))  # 长度为1的字符
            segs = list(filter(lambda x: x not in stopwords, segs))  # 去掉停用词
            sentences.append(" ".join(segs))
        except Exception:
            print(line)
            continue

# 调用函数、生成训练数据
sentences = []
preprocess_text(laogong, sentences)
preprocess_text(laopo, sentences)
preprocess_text(erzi, sentences)
preprocess_text(nver, sentences)
# 将得到的数据集打散,生成更可靠的训练集分布,避免同类数据分布不均匀
random.shuffle(sentences)
# 控制台输出前10条数据
for sentence in sentences[:10]:
    print(sentence)

'''
    CountVectorizer 的作用是将文本文档转换为计数的稀疏矩阵;
    TfidfTransformer 使用计算 tf-idf;
    TfidfVectorizer 相当于 CountVectorizer + TfidfTransformer,如:
    vectorizer=CountVectorizer()
    transformer=TfidfTransformer()
    tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
    等价于:
    transformer=TfidfVectorizer()
    tfidf=transformer.fit_transform(corpus)
'''
# 抽取特征,将文本中的词语转换为词频矩阵,统计每个词语的 tf-idf 权值,获得词在对应文本中的 tf-idf 权重
# 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
# CountVectorizer是属于常见的特征数值计算类,是一个文本特征提取方法。对于每一个训练文本,它只考虑每种词汇在该训练文本中出现的频率。
# CountVectorizer会将文本中的词语转换为词频矩阵,它通过fit_transform函数计算各个词语出现的次数。
# analyzer    一般使用默认,可设置为string类型,如’word’, ‘char’, ‘char_wb’,还可设置为callable类型,比如函数是一个callable类型
# max_features    默认为None,可设为int,对所有关键词的term frequency进行降序排序,只取前max_features个作为关键词集
vectorizer = CountVectorizer(analyzer='word',  # tokenise by character ngrams
                             ngram_range=(1, 2),  # use ngrams of size 1 and 2
                             max_features=20000  # keep the most common 1000 ngrams
                             )
#统计每个词语的tf-idf权值
transformer = TfidfTransformer()
# 第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(sentences))
# 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
print(word)
# 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
weight = tfidf.toarray()
#查看特征大小
print('Features length: ' + str(len(word)))

# TF-IDF 的中文文本 K-means 聚类
numClass = 4  # 聚类分几簇
clf = KMeans(n_clusters=numClass, max_iter=10000, init="k-means++", tol=1e-6)  # 这里也可以选择随机初始化init="random"
pca = PCA(n_components=10)  # 降维
TnewData = pca.fit_transform(weight)  # 载入N维
s = clf.fit(TnewData)

# 聚类结果可视化函数 plot_cluster(result,newData,numClass),该函数包含3个参数,
# 其中 result 表示聚类拟合的结果集;
# newData 表示权重 weight 降维的结果,这里需要降维到2维,即平面可视化;
# numClass 表示聚类分为几簇
def plot_cluster(result, newData, numClass):
    # num: 图像编号或名称,字符串为名称
    plt.figure(2)
    Lab = [[] for i in range(numClass)]
    index = 0
    for labi in result:
        Lab[labi].append(index)
        index += 1
    color = ['oy', 'ob', 'og', 'cs', 'ms', 'bs', 'ks', 'ys', 'yv', 'mv', 'bv', 'kv', 'gv', 'y^', 'm^', 'b^', 'k^',
             'g^'] * 3
    for i in range(numClass):
        x1 = []
        y1 = []
        for ind1 in newData[Lab[i]]:
            # print ind1
            try:
                y1.append(ind1[1])
                x1.append(ind1[0])
            except:
                pass
        plt.plot(x1, y1, color[i])

    # 绘制初始中心点
    x1 = []
    y1 = []
    for ind1 in np.array(clf.cluster_centers_):
        try:
            y1.append(ind1[1])
            x1.append(ind1[0])
        except:
            pass
    plt.plot(x1, y1, "rv")  # 绘制中心
    plt.show()

pca = PCA(n_components=2)  # 输出两维
newData = pca.fit_transform(weight)  # 载入N维
result = list(clf.predict(TnewData))
print(clf.cluster_centers_)
print(result)
plot_cluster(result,newData,numClass)

# TSNE 保留下的属性信息,更具代表性,也即最能体现样本间的差异,但是 TSNE 运行极慢,PCA 则相对较快
ts = TSNE(2)
newData = ts.fit_transform(weight)
result = list(clf.predict(TnewData))
# 结果并不是很好
plot_cluster(result, newData, numClass)

# 更为一般的处理,常常先用 PCA 进行降维,再使用 TSNE
newData = PCA(n_components=4).fit_transform(weight)  # 载入N维
newData = TSNE(2).fit_transform(newData)
result = list(clf.predict(TnewData))
plot_cluster(result, newData, numClass)

# 从优化和提高模型准确率来说,主要有两方面可以尝试:
# 特征向量的构建,除了词袋模型、tf-idf模型,可以考虑使用 word2vec 和 doc2vec 等;
# 模型上可以采用基于密度的 DBSCAN、层次聚类等算法。

原文:
https://soyoger.blog.csdn.net/article/details/108729407

猜你喜欢

转载自blog.csdn.net/fgg1234567890/article/details/114861529
今日推荐