自然语言处理中的文本聚类

       声明:代码的运行环境为Python3。Python3与Python2在一些细节上会有所不同,希望广大读者注意。本博客以代码为主,代码中会有详细的注释。相关文章将会发布在我的个人博客专栏《Python自然语言处理》,欢迎大家关注。


       聚类是典型的无监督学习方法,在自然语言处理中,聚类也是至关重要的。

【英文文档的聚类】

'''
英文文档的聚类
'''

import numpy as np
import pandas as pd
import nltk
from bs4 import BeautifulSoup
import re
import os
import codecs
from sklearn import feature_extraction
import mpld3

# 加载影片数据
titles = open('F:/title_list.txt').read().split('\n')
titles = titles[:100]  # 取前一百个影片名
print(titles[:10])  # 前 10 个片名

links = open('F:/link_list_imdb.txt').read().split('\n')
links = links[:100]  # 取前一百个影片介绍

synopses_wiki = open('F:/synopses_list_wiki.txt', encoding='utf-8').read().split('\n BREAKS HERE')
synopses_wiki = synopses_wiki[:100]  # 取前一百个来自wiki的简介

# 数据清洗,获取html代码中的文本内容
synopses_clean_wiki = []
for text in synopses_wiki:
    text = BeautifulSoup(text, 'lxml').getText()  # 获取html中的文本信息
    # strips html formatting and converts to unicode
    synopses_clean_wiki.append(text)

synopses_wiki = synopses_clean_wiki

genres = open('F:/genres_list.txt').read().split('\n')
genres = genres[:100]
# 打印出读进来的相关文本
print(str(len(titles)) + ' titles')
print(str(len(links)) + ' links')
print(str(len(synopses_wiki)) + ' synopses')
print(str(len(genres)) + ' genres')

synopses_imdb = open('F:/synopses_list_imdb.txt', encoding='utf-8').read().split('\n BREAKS HERE')
synopses_imdb = synopses_imdb[:100]

synopses_clean_imdb = []

for text in synopses_imdb:
    text = BeautifulSoup(text, 'html.parser').getText()
    # strips html formatting and converts to unicode
    synopses_clean_imdb.append(text)

synopses_imdb = synopses_clean_imdb

synopses = []

for i in range(len(synopses_wiki)):  # 合并wiki和imdb中的内容
    item = synopses_wiki[i] + synopses_imdb[i]
    synopses.append(item)

# 为每个项目生成索引的全集(在本例中它只是排名),以后我将使用这个得分
ranks = []

for i in range(0, len(titles)):
    ranks.append(i)

# 载入 nltk 的英文停用词作为“stopwords”变量
stopwords = nltk.corpus.stopwords.words('english')
print(stopwords[:10])

# 载入 nltk 的 SnowballStemmer 作为“stemmer”变量
from nltk.stem.snowball import SnowballStemmer  # 返回词语的原型,去掉ing等

stemmer = SnowballStemmer("english")

# 这里定义了一个分词器(tokenizer)和词干分析器(stemmer),它们会输出给定文本词干化后的词集合
def tokenize_and_stem(text):  # 词干分析器,返回词语的原型
    # 首先分句,接着分词,而标点也会作为词例存在
    tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    # 过滤所有不含字母的词例(例如:数字、纯标点)
    for token in tokens:
        if re.search('[a-zA-Z]', token):
            filtered_tokens.append(token)
    stems = [stemmer.stem(t) for t in filtered_tokens]
    return stems


def tokenize_only(text):  # 分词器,仅分词
    # 首先分句,接着分词,而标点也会作为词例存在
    tokens = [word.lower() for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    # 过滤所有不含字母的词例(例如:数字、纯标点)
    for token in tokens:
        if re.search('[a-zA-Z]', token):
            filtered_tokens.append(token)
    return filtered_tokens


# 扩充列表后变成了非常庞大的二维(flat)词汇表
totalvocab_stemmed = []
totalvocab_tokenized = []
for i in synopses:
    allwords_stemmed = tokenize_and_stem(i)  # 对每个电影的剧情简介进行分词和词干化
    totalvocab_stemmed.extend(allwords_stemmed)  # 扩充“totalvocab_stemmed”列表

    allwords_tokenized = tokenize_only(i)
    totalvocab_tokenized.extend(allwords_tokenized)

vocab_frame = pd.DataFrame({'words': totalvocab_tokenized}, index=totalvocab_stemmed)  # 转换为数据框
print('there are ' + str(vocab_frame.shape[0]) + ' items in vocab_frame')

print(vocab_frame.head())

# 定义向量化参数
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vectorizer = TfidfVectorizer(max_df=0.8, max_features=200000,
                                   min_df=0.2, stop_words='english',
                                   use_idf=True, tokenizer=tokenize_and_stem, ngram_range=(1,
                                                                                           3))  # max_df:用于描述一个词在文档中出现的频率,此处为80%,频率大于这个数,说明该词意义不大;min_df:此处和max_df相对,当一个词出现的频率在二者之间的时候才会被计算tfidf;ngram_range:设置一元模型、二元模型等等
# 在语句前面加:%time 可以在控制台输出运行时间
tfidf_matrix = tfidf_vectorizer.fit_transform(synopses)  # 向量化剧情简介文本
print(tfidf_matrix.shape)
terms = tfidf_vectorizer.get_feature_names()  # 获取特征

from sklearn.metrics.pairwise import cosine_similarity  # 计算余弦相似度
dist = 1 - cosine_similarity(tfidf_matrix)  # 计算余弦相似度

# k-means聚类
from sklearn.cluster import KMeans

num_clusters = 5
km = KMeans(n_clusters=num_clusters)
km.fit(tfidf_matrix)
clusters = km.labels_.tolist()

from sklearn.externals import joblib

# 注释语句用来存储你的模型
# 因为我已经从 pickle 载入过模型了
# joblib.dump(km,  'doc_cluster.pkl')
km = joblib.load('doc_cluster.pkl')
clusters = km.labels_.tolist()

films = {'title': titles, 'rank': ranks, 'synopsis': synopses, 'cluster': clusters, 'genre': genres}
frame = pd.DataFrame(films, index=[clusters], columns=['rank', 'title', 'cluster', 'genre'])
frame['cluster'].value_counts()

grouped = frame['rank'].groupby(frame['cluster'])  # 为了凝聚(aggregation),由聚类分类。
grouped.mean()  # 每个聚类的平均排名(1 到 100)

from __future__ import print_function

# 按离质心的距离排列聚类中心,由近到远
order_centroids = km.cluster_centers_.argsort()[:, ::-1]

for i in range(num_clusters):
    print("Cluster %d words:" % i, end='')

    for ind in order_centroids[i, :6]:  # 每个聚类选 6 个词
        print(' %s' % vocab_frame.ix[terms[ind].split(' ')].values.tolist()[0][0].encode('utf-8', 'ignore'), end=',')
    print()  # 空行
    print()  # 空行

    print("Cluster %d titles:" % i, end='')
    for title in frame.ix[i]['title'].values.tolist():
        print(' %s,' % title, end='')
    print()  # 空行
    print()  # 空行

# 多维尺度分析MDS
import os  # 为了使用 os.path.basename 函数
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.manifold import MDS

MDS()
# 将二维度平面中绘制的点转化成两个元素(components)
# 设置为“precomputed”是因为我们提供的是距离矩阵
# 我们可以将“random_state”具体化来达到重复绘图的目的
mds = MDS(n_components=2, dissimilarity="precomputed", random_state=1)
pos = mds.fit_transform(dist)  # 形如 (n_components, n_samples)
xs, ys = pos[:, 0], pos[:, 1]

##可视化聚类
# 用字典设置每个聚类的颜色
cluster_colors = {0: '#1b9e77', 1: '#d95f02', 2: '#7570b3', 3: '#e7298a', 4: '#66a61e'}
# 用字典设置每个聚类名称
cluster_names = {0: 'Family, home, war',
                 1: 'Police, killed, murders',
                 2: 'Father, New York, brothers',
                 3: 'Dance, singing, love',
                 4: 'Killed, soldiers, captain'}

# 用 MDS 后的结果加上聚类编号和绘色创建 DataFrame
df = pd.DataFrame(dict(x=xs, y=ys, label=clusters, title=titles))

# 聚类归类
groups = df.groupby('label')

# 设置绘图
fig, ax = plt.subplots(figsize=(17, 9))  # 设置大小
ax.margins(0.05)  # 可选项,只添加 5% 的填充(padding)来自动缩放(auto scaling)。

# 对聚类进行迭代并分布在绘图上
# 我用到了 cluster_name 和 cluster_color 字典的“name”项,这样会返回相应的 color 和 label
for name, group in groups:
    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12,
            label=cluster_names[name], color=cluster_colors[name],
            mec='none')
    ax.set_aspect('auto')
    ax.tick_params(
        axis='x',  # 使用 x 坐标轴
        which='both',  # 同时使用主刻度标签(major ticks)和次刻度标签(minor ticks)
        bottom='off',  # 取消底部边缘(bottom edge)标签
        top='off',  # 取消顶部边缘(top edge)标签
        labelbottom='off')
    ax.tick_params(
        axis='y',  # 使用 y 坐标轴
        which='both',  # 同时使用主刻度标签(major ticks)和次刻度标签(minor ticks)
        left='off',  # 取消底部边缘(bottom edge)标签
        top='off',  # 取消顶部边缘(top edge)标签
        labelleft='off')

ax.legend(numpoints=1)  # 图例(legend)中每项只显示一个点

# 在坐标点为 x,y 处添加影片名作为标签(label)
for i in range(len(df)):
    ax.text(df.ix[i]['x'], df.ix[i]['y'], df.ix[i]['title'], size=8)

plt.show()  # 展示绘图

# 以下注释语句可以保存需要的绘图
# plt.savefig('clusters_small_noaxes.png', dpi=200)
plt.close()

# 层次聚类
from scipy.cluster.hierarchy import ward, dendrogram

linkage_matrix = ward(dist)  # 聚类算法处理之前计算得到的距离,用 linkage_matrix 表示
fig, ax = plt.subplots(figsize=(15, 20))  # 设置大小
ax = dendrogram(linkage_matrix, orientation="right", labels=titles)
plt.tick_params(
    axis='x',  # 使用 x 坐标轴
    which='both',  # 同时使用主刻度标签(major ticks)和次刻度标签(minor ticks)
    bottom='off',  # 取消底部边缘(bottom edge)标签
    top='off',  # 取消顶部边缘(top edge)标签
    labelbottom='off')

plt.tight_layout()  # 展示紧凑的绘图布局
# 注释语句用来保存图片
plt.savefig('ward_clusters.png', dpi=200)  # 保存图片为 ward_clusters

plt.close()

【英文文档聚类结果】

【中文文档聚类】

'''
中文聚类
'''

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import codecs
from scipy import ndimage
from sklearn import manifold, datasets
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import HashingVectorizer

####第一步 计算TFIDF####

# 文档预料 空格连接
corpus = []

# 读取预料 一行预料为一个文档
for line in open('F:/01_All_BHSpider_Content_Result.txt', 'r', encoding='utf8').readlines():
    # print line
    corpus.append(line.strip())
# print corpus
# 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer()

# 该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer()

# 第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))

# 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()

# 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
weight = tfidf.toarray()

# 打印特征向量文本内容
print('Features length:' + str(len(word)))
resName = "BHTfidf_Result.txt"
result = codecs.open(resName, 'w', 'utf-8')
for j in range(len(word)):
    result.write(word[j] + ' ')
result.write('\r\n\r\n')

# 打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
for i in range(len(weight)):
    # print u"-------这里输出第", i, u"类文本的词语tf-idf权重------"
    for j in range(len(word)):
        # print weight[i][j],
        result.write(str(weight[i][j]) + ' ')
    result.write('\r\n\r\n')
result.close()

####第二步 聚类Kmeans####
print('Start Kmeans:')
from sklearn.cluster import KMeans

clf = KMeans(n_clusters=4)  # 景区 动物 人物 国家
s = clf.fit(weight)
print(s)

# 中心点
print(clf.cluster_centers_)

# 每个样本所属的簇
label = []  # 存储1000个类标 4个类
print(clf.labels_)
i = 1
while i <= len(clf.labels_):
    print(i, clf.labels_[i - 1])
    label.append(clf.labels_[i - 1])
    i = i + 1

# 用来评估簇的个数是否合适,距离越小说明簇分的越好,选取临界点的簇个数  958.137281791
print(clf.inertia_)

####第三步 图形输出 降维####
from sklearn.decomposition import PCA

pca = PCA(n_components=2)  # 输出两维
newData = pca.fit_transform(weight)  # 载入N维
print(newData)

# 5A景区
x1 = []
y1 = []
i = 0
while i < 400:
    x1.append(newData[i][0])
    y1.append(newData[i][1])
    i += 1

# 动物
x2 = []
y2 = []
i = 400
while i < 600:
    x2.append(newData[i][0])
    y2.append(newData[i][1])
    i += 1

# 人物
x3 = []
y3 = []
i = 600
while i < 800:
    x3.append(newData[i][0])
    y3.append(newData[i][1])
    i += 1

# 国家
x4 = []
y4 = []
i = 800
while i < 1000:
    x4.append(newData[i][0])
    y4.append(newData[i][1])
    i += 1

# 四种颜色 红 绿 蓝 黑
plt.plot(x1, y1, 'or')
plt.plot(x2, y2, 'og')
plt.plot(x3, y3, 'ob')
plt.plot(x4, y4, 'ok')
plt.show()

【中文文档聚类结果】

 

### 自然语言处理中的文本聚类实验 #### 方法概述 文本聚类是一种无监督学习方法,旨在将具有相似特征的文档分组在一起。在自然语言处理领域,常用的文本表示方式有词袋模型(Bag of Words, BoW)、TF-IDF 和词嵌入(Word Embedding)。基于这些表示方法,可以采用多种聚类算法完成任务,例如 K-Means、层次聚类(Hierarchical Clustering)以及 DBSCAN。 以下是具体的方法描述: 1. **数据预处理** 数据预处理阶段通常包括去除停用词、标点符号和数字等内容,并对文本进行分词操作[^4]。对于中文文本,还需要考虑编码格式的一致性问题[^3]。 2. **向量化表示** 使用 TF-IDF 或 Word2Vec 将文本转化为数值型向量。其中,TF-IDF 是一种统计方法,能够反映词语的重要性;而 Word2Vec 则能捕捉词语之间的语义关系[^2]。 3. **选择合适的聚类算法** 常见的选择包括但不限于 K-Means 聚类法,它简单高效且适用于大规模数据集。如果希望探索更复杂的结构,则可以选择谱聚类或者密度基聚类如 DBSCAN[^1]。 4. **评估聚类效果** 可以利用轮廓系数(Silhouette Coefficient)或其他内部指标来衡量不同参数设置下所得结果的质量。 #### 示例代码 下面提供了一个完整的 Python 实现例子,展示如何运用 scikit-learn 库来进行简单的文本聚类分析: ```python from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans import numpy as np # 定义样本数据 documents = [ "Natural language processing is a field within artificial intelligence.", "Text clustering involves grouping similar documents together based on their content.", "Machine learning algorithms are widely used in natural language applications." ] # 初始化并拟合Tf-idf矢量化器 vectorizer = TfidfVectorizer(stop_words='english') X = vectorizer.fit_transform(documents) # 设置K-means模型参数 num_clusters = 2 model = KMeans(n_clusters=num_clusters, init='k-means++', max_iter=100, n_init=1) model.fit(X) # 输出每个文档所属类别标签及其距离中心远近程度得分 labels = model.labels_ centroids = model.cluster_centers_ print("Cluster Labels:", labels) for i, doc in enumerate(documents): print(f"Document {i}: Cluster {labels[i]}") def get_top_keywords(data, vectors, labels, n_terms): df = pd.DataFrame(vectors.todense()).groupby(labels).mean() terms = vectorizer.get_feature_names_out() for i,r in df.iterrows(): print('\nCluster %d:' % i, end='') print(' '.join([terms[t] for t in np.argsort(r)[-n_terms:]])) get_top_keywords(documents, X, labels, 5) ``` 此脚本首先定义了一些示例句子作为输入资料源,接着创建了 Tf-idf 向量矩阵用于后续计算。之后实例化了一个两分类别的 K-Means 对象执行训练过程最后打印出了每篇文章分配到哪个簇里去了同时还给出了各群集中最具代表性的关键词项列表. #### 结果解释 上述程序会返回各个文档对应的集群编号以及该集合内的主要术语。这有助于理解哪些类型的材料被归为一类从而为进一步研究提供了基础。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

象在舞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值