社交媒体话题文本分词后用sklearn的kmeans算法做聚类分析

2023-10-27 11:06| 发布者: Fuller| 查看: 2710| 评论: 0

摘要: 使用GooSeeker文本分词和情感分析软件将文本进行分词处理,导出分词效果数据表,文本已经被切分成一个个词,作为被分析对象,利用sklearn的特征工程函数对分词结果进行特征选择,得到数量较少的特征词,基于特征词进 ...

1  背景介绍

1.1  实验目的

在《Jupyter Notebook使用Python做K-Means聚类分析》和《机器学习库sklearn的K-Means聚类算法的使用方法》这两篇notebook中,我们对kmeans算法做了如下的介绍和实验:

1. 什么是K-means聚类算法?

2. K-means聚类算法应用场景

3. K-means聚类算法步骤

4. 在python下使用随机生成的测试数据进行kmeans算法实验

5. 调用机器学习库sklearn里现成的函数进行kmeans算法实验

有同学留言指出上面的实验都是使用随机生成的测试数据进行的,希望我们基于一些有实际意义的社交媒体数据进行类似的算法实验,这样他们在论文写作时可以更好的参考借鉴。

我们认为这个建议很好,这段时间将使用集搜客GooSeeker网络爬虫工具收集微博或知乎等社交媒体上的原始实验数据,经GooSeeker文本分词和情感分析软件处理后导出各种格式的excel结果表,在sklearn下做各种算法的实验,实验的过程会以JupyterNotebook 形式记录下来与大家分享。

1.2 本notebook实验内容

  • 使用GooSeeker文本分词和情感分析软件将文本进行分词处理,导出分词效果数据表,文本已经被切分成一个个词,作为被分析对象
  • 利用sklearn的特征工程函数对分词结果进行特征选择,得到数量较少的特征词
  • 基于特征词进行聚类分析
  • 利用sklearn的降维算法,将被分析数据的维度降到2维,在二维空间展示聚类结果

1.3  实验数据来源

使用知乎_独立话题动态内容采集这个快捷采集,爬取知乎上关于二舅话题的讨论,导出excel格式的采集结果数据。

1.4  实验数据预处理:中文文本分词和选词

将采集得到的知乎二舅话题的excel,导入到Gooseeker文本分词和情感分析软件,经自动分词后,导出“分词效果表”excel。

图1. 分词结果导出

1.5  sklearn库简介

转载知乎文章《sklearn库主要模块功能简介》的介绍如下:

sklearn,全称scikit-learn,是python中的机器学习库,建立在numpy、scipy、matplotlib等数据科学包的基础之上,涵盖了机器学习中的样例数据、数据预处理、模型验证、特征选择、分类、回归、聚类、降维等几乎所有环节,功能十分强大,目前sklearn版本是0.23。与深度学习库存在pytorch、TensorFlow等多种框架可选不同,sklearn是python中传统机器学习的首选库,不存在其他竞争者。

图2. sklearn-思维导图

1.6  本notebook使用方法

基本操作顺序是:

1. 在GooSeeker分词和文本分析软件上进行任务创建并导入包含原始内容的excel

2. 导出分词效果表

3. 将导出的分词效果表放在本notebook的data/raw文件夹中

4. 从头到尾执行本notebook的单元

注意:每个notebook项目目录都预先规划好了,具体参看《Jupyter Notebook项目目录规划参考》。如果要做多个分析项目,把整个模板目录专门拷贝一份给每个分析项目。

2  第三方库

本notebook使用了sklearn库做k-means算法实验。

如果未安装,请先使用下面的命令安装sklearnm库,再运行实验本notebook:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple sklearn #国内安装使用清华的源,速度快

图3. pip安装

3  准备程序环境

导入必要的Python程序包,设定要分析的文件名变量。在这一系列notebook中,我们都使用以下变量对应GooSeeker分词结果表:

  • file_word_freq:词频表
  • file_seg_effect: 分词效果表
  • file_word_choice_matrix: 选词矩阵表
  • file_word_choice_match: 选词匹配表
  • file_word_choice_result: 选词结果表
  • file_co_word_matrix: 共词矩阵表

3.1  导入程序包并声明变量

import pandas as pd

import os

import numpy as np

from sklearn.cluster import KMeans, MiniBatchKMeans

from sklearn.decomposition import PCA

from sklearn.decomposition import TruncatedSVD

from sklearn.feature_extraction.text import TfidfVectorizer

from sklearn.feature_extraction.text import HashingVectorizer

from sklearn.feature_extraction.text import TfidfTransformer

from sklearn.pipeline import make_pipeline

from sklearn.preprocessing import Normalizer

from sklearn import metrics

from time import time

%xmode Verbose

import warnings

warnings.filterwarnings("ignore", category=DeprecationWarning)

# 存原始数据的目录

raw_data_dir = os.path.join(os.getcwd(), '..\\..\\data\\raw')

# 存处理后的数据的目录

processed_data_dir = os.path.join(os.getcwd(), '..\\..\\data\\processed')

filename_temp = pd.Series(['分词效果'])

file_seg_effect = ''

输出结果如下:

Exception reporting mode: Verbose

3.2  检测data\raw目录下是否有分词效果表

以下的演示以GooSeeker分词和文本分析软件生成的分词效果excel表为例,需要把分词效果表放到本notebook的data/raw文件夹下,如果没有数据表,下面代码执行后将提示“不存在”

# 0:'词频', 1:'分词效果', 2:'选词矩阵', 3:'选词匹配', 4:'选词结果', 5:'共词矩阵'

print(raw_data_dir + '\r\n')

for item_filename in os.listdir(raw_data_dir):

    if filename_temp[0] in item_filename:

        file_seg_effect = item_filename

        continue

if file_seg_effect:

    print("分词效果excel表:", "data\\raw\\", file_seg_effect)

else:

    print("分词效果excel表:不存在")

输出结果如下:

C:\Users\work\workspace_219\notebook\知乎话题文本分词后用sklearn做kmeans算法实验\notebook\eda\..\..\data\raw

分词效果excel表: data\raw\ 分词效果-知乎-二舅.xlsx

4  读入分词效果表并观察

4.1  读取分词效果表

df = pd.read_excel(os.path.join(raw_data_dir, file_seg_effect))

输出结果如下:

C:\ProgramData\Anaconda3\lib\site-packages\openpyxl\styles\stylesheet.py:214: UserWarning: Workbook contains no default style, apply openpyxl's default

  warn("Workbook contains no default style, apply openpyxl's default")

4.2  查看前10行数据

使用df.head()函数查看dataframe的前n行数据

df.head(10)

输出结果如下:

4.3  使用“分词数据”这一列作为实验数据

“分词数据”和“关键词”这2列都是从文本中提取的词汇,通过观察可以看到,“分词数据”就是在词之间插入了空格,显示了分词后的效果;而“关键词”含有的词比较少,是GooSeeker文本分析软件利用自然语言处理算法抽取出来的关键词。

【注意】:本Notebook我们使用“分词数据”这一列作为实验数据,利用sklearn抽取特征词。

df['已分词内容'] =  df['分词数据']

df['已分词内容']

输出结果如下:

Name: 已分词内容, Length: 829, dtype: object

4.4  去掉值为NULL的行

df = df.dropna(axis = 0, how ='any')

4.5  查看有多少篇文档

print("%d 篇文档" % len(df['已分词内容']))

输出结果:

819 篇文档

5  特征选择

原始数据经过分词切分以后,得到的词的数量很大,很多词没有分析价值,除了增加处理的难度,而且会干扰分析结果的合理性,所以,要进行特征选择,可以手工选择,也可以自动选择。本notebook使用sklearn的特征工程函数进行自动特征选择,后面还会发布另一个notebook展示利用GooSeeker分词软件的选词功能进行手工选择,以提高分析结果的合理程度。

本方法使用稀疏向量(Sparse Vectorizer)从训练集中抽取特征,即,从df['已分词内容']字段抽取特征。结束时显示耗时多少及样本数量和特征数量。这个算法使用了一个十分简单的策略:将分词数据转换成一个矩阵,并且统计词频和文档频率,并计算tf-idf,然后由用户决定使用哪些限定条件选择词语。下面我们只限定文档频率:一方面不要文档频率过高的词,那样会引入很多没有意义的噪音词;另一方面不要文档频率过低的词,会造成过拟合而产生分析偏差。下面代码并没有使用tf-idf值。

可以自己实验其他参数,达到更好效果。具体参看TfidfVectorizer的API

t0 = time()

vectorizer = TfidfVectorizer(max_df=0.7, min_df=0.005, stop_words=None,ngram_range=(1, 2))

X = vectorizer.fit_transform(df['已分词内容'].values.astype('U'))

print("完成所耗费时间: %fs" % (time() - t0))

print("样本数量: %d, 特征数量: %d" % X.shape)

print('特征抽取完成!')

输出结果如下:

完成所耗费时间: 0.287845s

样本数量: 819, 特征数量: 2271

特征抽取完成!

6  实验一:人工设置K值为3

也就是限定为只分成三类

true_k = 3 #聚类数量

6.1  对文本进行kmeans聚类

labels = df['已分词内容']

km = KMeans(n_clusters=true_k, init='k-means++', max_iter=300, n_init=5)

print("对稀疏数据(Sparse Data) 采用 %s" % km)

t0 = time()

km.fit(X)

print("完成所耗费时间:%0.3fs" % (time() - t0))

print()

print("Homogeneity值: %0.3f" % metrics.homogeneity_score(labels, km.labels_))

print("Completeness值: %0.3f" % metrics.completeness_score(labels, km.labels_))

print("V-measure值: %0.3f" % metrics.v_measure_score(labels, km.labels_))

print("Adjusted Rand-Index值: %.3f"

      % metrics.adjusted_rand_score(labels, km.labels_))

print("Silhouette Coefficient值: %0.3f"

      % metrics.silhouette_score(X, km.labels_, sample_size=1000))

print()

#用训练好的聚类模型反推文档的所属的主题类别

label_prediction = km.predict(X)   

label_prediction = list(label_prediction)

输出结果如下:

对稀疏数据(Sparse Data) 采用 KMeans(n_clusters=3, n_init=5)

完成所耗费时间:0.210s


Homogeneity值: 0.138

Completeness值: 1.000

V-measure值: 0.242

Adjusted Rand-Index值: 0.000

Silhouette Coefficient值: 0.009

6.2  输出每个簇群去重后的关键词

目的是为了观察每一类中哪些词对聚类打分有贡献,也可以根据词义了解类别代表的意义。

print("每个聚类的TOP关键词:")

order_centroids = km.cluster_centers_.argsort()[:, ::-1]

# 由于sklearn存在不同版本,这里检测后再调用get_feature_names_out或者get_feature_names

if hasattr(vectorizer,'get_feature_names_out'):

    terms = vectorizer.get_feature_names_out()

elif hasattr(vectorizer,'get_feature_names'):

    terms = vectorizer.get_feature_names()

for i in range(true_k):

    print("簇群 %d   " % (i+1), end='')

    print("该簇群所含文档占比为",'%.4f%%' % (int(label_prediction.count(i))/int(len(df['已分词内容']))))

    print("簇群关键词:")

    wordset = []

    for ind in order_centroids[i, :20]:

        wordset.append(terms[ind].replace(' ',''))

    for word in wordset:

        print(' %s ' % word, end='')

    print('\n------------------------------------------------------------------------------------------------')

输出结果如下:

6.3  可视化

6.3.1  降维

被分析的数据是高维数据,无法可视化展示出来聚类结果,那么,首先要将高维数据降为成2维或者3维数据。降维必然造成信息损失,到底选择哪个降为算法好,可以多尝试几个,进行对比。

我们使用TruncatedSVD将文档向量从2000维空间降维到2维空间,因为TruncatedSVD恰好适合处理我们当前使用的数据结构。TruncateSVD介绍参看sklearn官网

labels=km.labels_.tolist()

#l =  km.fit_predict(X)  

svd = TruncatedSVD(n_components=2).fit(X)

datapoint = svd.transform(X)

6.3.2  使用matplotlib进行可视化画图

import matplotlib.pyplot as plt

%matplotlib inline

plt.figure(figsize=(12, 10))

label1 = [

'#FFFF00', '#008008', '#0000FF','#800080','#FFF5EE','#98FB98','#A0522D',

'#FF7F00','#FFC125','#FFFFFF','#FFFAFA','#FFF68F','#FFEFD5','#FFE4E1',

'#FFDEAD','#FFC1C1','#FFB90F','#FFA54F','#FF8C00','#C0FF3E','#FF6EB4',

'#FF4500','#FF3030','#8A2BE2','#87CEEB','#8470FF','#828282','#7EC0EE',

'#7CFC00','#7A8B8B','#79CDCD','#76EE00']

color = [label1[i] for i in labels]

plt.scatter(datapoint[:, 0], datapoint[:, 1], c=color)

centroids = km.cluster_centers_

centroidpoint = svd.transform(centroids)

plt.scatter(centroidpoint[:, 0], centroidpoint[:, 1], marker='^', s=150, c='#000000')

plt.show()

图中的黑色三角是每个簇群的中心,不同的颜色代表不同的簇群。分的界限还算清晰。

7  实验二:使用“手肘法”确定最佳的K值

7.1  执行“手肘法”

在《Jupyter Notebook使用Python做K-Means聚类分析》中,我们介绍过“手肘法”确定最佳的K的大小:通过观察明显的拐点来确定最佳的K值。 我们在此也实验下:

import matplotlib.pyplot as plt

%matplotlib inline

plt.rcParams['font.sans-serif'] = ['SimHei']

plt.rcParams['axes.unicode_minus'] = False

n_clusters= 10

wcss = []

for i in range(1,n_clusters):

    km = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=2)

    km.fit(X)

    wcss.append(km.inertia_)

plt.plot(range(1,n_clusters),wcss)

plt.title('肘 部 方 法')

plt.xlabel('聚类的数量')

plt.ylabel('wcss')

plt.show()

手肘法是一个经验方法,而且肉眼观察也因人而异,特别是遇到模棱两可的时候。从上面的图来看,最佳的K值应该是8

# 把人工观察手肘法图的拐点作为K值

true_k = 8 #聚类数量

7.2  对文本进行kmeans聚类

labels = df['已分词内容']

km = KMeans(n_clusters=true_k, init='k-means++', max_iter=300, n_init=5)

print("对稀疏数据(Sparse Data) 采用 %s" % km)

t0 = time()

km.fit(X)

print("完成所耗费时间:%0.3fs" % (time() - t0))

print()

print("Homogeneity值: %0.3f" % metrics.homogeneity_score(labels, km.labels_))

print("Completeness值: %0.3f" % metrics.completeness_score(labels, km.labels_))

print("V-measure值: %0.3f" % metrics.v_measure_score(labels, km.labels_))

print("Adjusted Rand-Index值: %.3f"

      % metrics.adjusted_rand_score(labels, km.labels_))

print("Silhouette Coefficient值: %0.3f"

      % metrics.silhouette_score(X, km.labels_, sample_size=1000))

print()

#用训练好的聚类模型反推文档的所属的主题类别

label_prediction = km.predict(X)   

label_prediction = list(label_prediction)

输出结果如下:

对稀疏数据(Sparse Data) 采用 KMeans(n_init=5)

完成所耗费时间:0.284s


Homogeneity值: 0.288

Completeness值: 1.000

V-measure值: 0.447

Adjusted Rand-Index值: 0.000

Silhouette Coefficient值: 0.013

7.3  输出每个簇群去重后的关键词

print("每个聚类的TOP关键词:")

order_centroids = km.cluster_centers_.argsort()[:, ::-1]

# 由于sklearn存在不同版本,这里检测后再调用get_feature_names_out或者get_feature_names

if hasattr(vectorizer,'get_feature_names_out'):

    terms = vectorizer.get_feature_names_out()

elif hasattr(vectorizer,'get_feature_names'):

    terms = vectorizer.get_feature_names()

for i in range(true_k):

    print("簇群 %d   " % (i+1), end='')

    print("该簇群所含文档占比为",'%.4f%%' % (int(label_prediction.count(i))/int(len(df['已分词内容']))))

    print("簇群关键词:")

    wordset = []

    for ind in order_centroids[i, :20]:

        wordset.append(terms[ind].replace(' ',''))

    for word in wordset:

        print(' %s ' % word, end='')

    print('\n------------------------------------------------------------------------------------------------')

输出结果如下:

7.4  可视化

7.4.1  降维

labels=km.labels_.tolist()

#l =  km.fit_predict(X)  

svd = TruncatedSVD(n_components=2).fit(X)

datapoint = svd.transform(X)

7.4.2  使用matplotlib进行可视化画图

import matplotlib.pyplot as plt

%matplotlib inline

plt.figure(figsize=(12, 10))

label1 = [

'#FFFF00', '#008008', '#0000FF','#800080','#FFF5EE','#98FB98','#A0522D',

'#FF7F00','#FFC125','#FFFFFF','#FFFAFA','#FFF68F','#FFEFD5','#FFE4E1',

'#FFDEAD','#FFC1C1','#FFB90F','#FFA54F','#FF8C00','#C0FF3E','#FF6EB4',

'#FF4500','#FF3030','#8A2BE2','#87CEEB','#8470FF','#828282','#7EC0EE',

'#7CFC00','#7A8B8B','#79CDCD','#76EE00']

color = [label1[i] for i in labels]

plt.scatter(datapoint[:, 0], datapoint[:, 1], c=color)

centroids = km.cluster_centers_

centroidpoint = svd.transform(centroids)

plt.scatter(centroidpoint[:, 0], centroidpoint[:, 1], marker='^', s=150, c='#000000')

plt.show()

图中的黑色三角是每个簇群的中心,不同的颜色代表不同的簇群。

8  总结

本Notebook使用Gooseeker文本分词和情感分析软件导出的分词效果excel表格,在python下使用sklearn库进行k-means聚类,实验有2个:

实验1. 人工直接设置K值为3进行实验

实验2. 使用“手肘法”人工观察拐点,取K值为7

可视化输出的图看起来可能稍显杂乱,可能是数据样本本身不适合聚类,也可能是聚类算法选择不合适或者参数设置不合适,或者文档向量降维算法选择不合适等等,需要多次实验,改变设置,找到比较好的处理效果。

后续我们会使用来自不同数据源的数据(比如:微博,知乎,新闻等),使用sklearn进行多种算法的实验,在实验中总结和改进。

9  下载源代码

下载notebook源代码请进入:用sklearn的kmeans算法对分词后的社交媒体话题文本做聚类分析


鲜花

握手

雷人

路过

鸡蛋

最新评论

GMT+8, 2024-10-4 21:57