自然语言处理:文本聚类

介绍

大家好,博主又来和大家分享自然语言处理领域的知识了。今天给大家分享的内容是自然语言处理中文本聚类

文本聚类在自然语言处理领域占据着重要地位,它能将大量无序的文本按照内容的相似性自动划分成不同的类别,极大地提高了文本处理和信息提取的效率。就好比在一个大型图书馆中,文本聚类能够像智能管理员一样,把各种书籍按照主题分类摆放,方便读者快速找到所需资料。

而实现文本聚类的方法有很多,其中k均值聚类算法基于高斯混合模型的最大期望值算法,以及无监督朴素贝叶斯模型是比较常用的。好了,话不多说,我们直接进入正题。

文本聚类

k均值聚类算法

k均值聚类算法是一种简单且经典的聚类算法。它的核心思想就像是在一群人中寻找几个代表,让其他人都围绕着这些代表“站队”

具体来说,我们首先要确定聚类的数量k,这个k就像是我们设定的几个“代表小组”的数量。然后随机选择k个文本作为初始的聚类中心,就好像是先选了几个有代表性的人。接下来,计算每个文本与这k个聚类中心的距离,把文本分配到距离最近的聚类中心所代表的类别中。之后,不断地重新计算每个类别中所有文本的中心位置,也就是更新聚类中心,就像代表们根据自己所在小组的人员情况调整位置一样。重复这个过程,直到聚类中心不再变化或者变化很小,此时就完成了文本聚类。

这种算法的优点是简单易懂、计算效率高,对于大规模文本数据的处理有较好的可扩展性。但它也有一些缺点,比如需要预先指定聚类的数量k,这个k值如果选择不当,可能会导致聚类结果不理想;而且它对初始聚类中心的选择比较敏感,不同的初始中心可能会得到不同的聚类结果。

接下来,我们将着手实现k均值算法,以此来开展文本聚类的工作。此次所运用的数据集涵盖了大约1万本图书的相关信息,并且这些图书被划分为3种不同的主题类别。在这个数据集中,我们所关注的文本内容并非图书的标题,而是图书的摘要部分。我们用代码来演示:

预处理完整代码

nlp_book_data_collection.py

# 导入用于处理JSON数据的库
import json
# 导入defaultdict类,用于创建具有默认值的字典
from collections import defaultdict
# 导入spaCy库,用于自然语言处理
import spacy
# 从spaCy的中文停用词模块中导入停用词集合
from spacy.lang.zh.stop_words import STOP_WORDS
# 导入tqdm库,用于显示进度条
from tqdm import tqdm# 加载中文语言模型
nlp_model = spacy.load('zh_core_web_sm')# 定义一个类,用于收集和处理书籍数据
class BookDataCollection:# 类的初始化方法def __init__(self):# 训练集和测试集的文件名self.training_file, self.testing_file = 'train_original.jsonl', 'test_original.jsonl'self.init_data()# 定义一个内部函数,用于读取JSONL文件def read_json_file(self, file_path):# 以只读模式打开文件,并指定编码为UTF-8with open(file_path, 'r', encoding='utf-8') as file:# 将文件的每一行读取为一个列表json_lines = list(file)# 初始化一个空列表,用于存储解析后的JSON数据data_list = []# 遍历每一行JSON数据for json_str in json_lines:# 将JSON字符串解析为Python对象,并添加到数据列表中data_list.append(json.loads(json_str))# 返回解析后的数据列表return data_listdef init_data(self):# 调用read_json_file函数读取训练集和测试集数据self.training_data, self.testing_data = self.read_json_file(self.training_file), self.read_json_file(self.testing_file)# 打印训练集和测试集的大小print('训练集大小 =', len(self.training_data), ', 测试集大小 =', len(self.testing_data))# 初始化两个空字典,用于存储标签到ID和ID到标签的映射self.label_to_id, self.id_to_label = {}, {}# 遍历训练集和测试集数据for data_group in [self.training_data, self.testing_data]:# 遍历每个数据项for data_item in data_group:# 获取数据项的类别标签category = data_item['class']# 如果类别标签不在标签到ID的映射中if category not in self.label_to_id:# 生成一个新的IDindex = len(self.label_to_id)# 将类别标签和对应的ID添加到标签到ID的映射中self.label_to_id[category] = index# 将ID和对应的类别标签添加到ID到标签的映射中self.id_to_label[index] = category# 获取类别标签对应的IDlabel_index = self.label_to_id[category]# 在数据项中添加标签ID字段data_item['label'] = label_index# 定义一个方法,用于对文本进行分词处理def tokenize_text(self, attribute='book'):# 遍历训练集和测试集数据for data_group in [self.training_data, self.testing_data]:# 使用tqdm显示进度条,遍历每个数据项for data_item in tqdm(data_group):# 获取数据项指定属性的文本内容,并转换为小写text_content = data_item[attribute].lower()# 使用spaCy模型对文本进行处理,过滤掉停用词,并提取词元tokens = [token.text for token in nlp_model(text_content) if token.text not in STOP_WORDS]# 在数据项中添加分词结果字段data_item['tokens'] = tokens# 定义一个方法,用于创建词汇表def create_vocabulary(self, min_frequency=3, min_length=2, max_vocab_size=None):# 初始化一个defaultdict,用于统计词元的频率word_frequency = defaultdict(int)# 遍历训练集数据for data_item in self.training_data:# 获取数据项的分词结果tokens = data_item['tokens']# 遍历每个词元for token in tokens:# 统计词元的频率word_frequency[token] += 1# 打印唯一词元数量、总词元数量、最大词频和最小词频print(f'唯一词元数量 = {len(word_frequency)}, 总词元数量 = {sum(word_frequency.values())}, 'f'最大词频 = {max(word_frequency.values())}, 最小词频 = {min(word_frequency.values())}')# 初始化两个空字典,用于存储词元到ID和ID到词元的映射self.token_to_id = {}self.id_to_token = {}# 初始化总词频为0total_frequency = 0# 按词频从高到低对词元进行排序,并遍历for token, freq in sorted(word_frequency.items(), key=lambda x: -x[1]):# 如果指定了最大词汇表大小,且当前词汇表大小已达到最大值if max_vocab_size and len(self.token_to_id) >= max_vocab_size:# 跳出循环break# 如果词元的频率大于最小频率if freq > min_frequency:# 如果未指定最小词长,或者词元长度大于等于最小词长if (min_length is None) or (min_length and len(token) >= min_length):# 将词元添加到词元到ID的映射中,并分配一个新的IDself.token_to_id[token] = len(self.token_to_id)# 将ID添加到ID到词元的映射中,并关联对应的词元self.id_to_token[len(self.id_to_token)] = token# 累加词元的频率到总词频中total_frequency += freqelse:# 跳出循环break# 打印最小词频、最小词长、最大词汇表大小、剩余词元数量和词表内词元占比print(f'最小词频 = {min_frequency}, 最小词长 = {min_length}, 最大词表大小 = {max_vocab_size}, 'f'剩余词元数量 = {len(self.token_to_id)}, 'f'词表内词元占比 = {total_frequency / sum(word_frequency.values())}')# 定义一个方法,用于将词元转换为对应的IDdef convert_tokens_to_indices(self):# 遍历训练集和测试集数据for data_group in [self.training_data, self.testing_data]:# 遍历每个数据项for data_item in data_group:# 初始化一个空列表,用于存储词元对应的IDdata_item['token_indices'] = []# 遍历数据项的分词结果for token in data_item['tokens']:# 如果词元在词元到ID的映射中if token in self.token_to_id:# 将词元对应的ID添加到列表中data_item['token_indices'].append(self.token_to_id[token])

nlp_text_clustering.py

# 从book_data_container模块导入BookDataContainer类
from nlp_book_data_collection import BookDataCollection# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()

预处理运行结果

训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [02:29<00:00, 57.74it/s]
100%|██████████| 2156/2156 [00:37<00:00, 56.76it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699进程已结束,退出代码为 0

然后,我们引入实现TF-IDF算法的相关函数,并把经过处理的数据集输入至该函数内,从而获取文档的特征信息。我们用代码来演示:

特征信息完整代码

nlp_tfidf_transformer.py

# 导入numpy库,用于进行数值计算
import numpy as np# 定义一个类,用于进行TF-IDF转换
class TFIDFTransformer:# 类的初始化方法def __init__(self, vocabulary_size, normalization='l2', smooth_idf_flag=True, sublinear_tf_flag=True):# 存储词汇表大小self.vocabulary_size = vocabulary_size# 存储归一化方法self.normalization = normalization# 存储是否进行平滑IDF计算的标志self.smooth_idf_flag = smooth_idf_flag# 存储是否进行子线性TF计算的标志self.sublinear_tf_flag = sublinear_tf_flag# 定义一个方法,用于拟合数据,计算IDF值def fit_data(self, input_data):# 初始化一个全零数组,用于存储每个词元的文档频率document_frequency = np.zeros(self.vocabulary_size, dtype=np.float64)# 遍历输入数据for data_item in input_data:# 遍历数据项中唯一的词元IDfor token_index in set(data_item):# 统计词元的文档频率document_frequency[token_index] += 1# 根据平滑IDF标志,对文档频率进行平滑处理document_frequency += int(self.smooth_idf_flag)# 计算样本数量,并根据平滑IDF标志进行调整num_samples = len(input_data) + int(self.smooth_idf_flag)# 计算每个词元的IDF值self.idf = np.log(num_samples / document_frequency) + 1# 定义一个方法,用于对输入数据进行转换def transform_data(self, input_data):# 确保已经计算了IDF值assert hasattr(self, 'idf')# 初始化一个全零矩阵,用于存储词频矩阵term_frequency = np.zeros((len(input_data), self.vocabulary_size), dtype=np.float64)# 遍历输入数据for i, data_item in enumerate(input_data):# 遍历数据项中的词元IDfor token in data_item:# 统计词元的词频term_frequency[i, token] += 1# 如果启用了子线性TF计算if self.sublinear_tf_flag:# 对词频矩阵进行子线性变换term_frequency = np.log(term_frequency + 1)# 计算TF-IDF矩阵transformed_data = term_frequency * self.idf# 如果启用了归一化if self.normalization:# 计算每行的L2范数row_norm = (transformed_data ** 2).sum(axis=1)# 将范数为0的行的范数设为1,避免除零错误row_norm[row_norm == 0] = 1# 对TF-IDF矩阵进行归一化处理transformed_data /= np.sqrt(row_norm)[:, None]# 返回转换后的矩阵return transformed_data# 定义一个方法,用于拟合数据并进行转换def fit_and_transform(self, input_data):# 调用fit_data方法拟合数据self.fit_data(input_data)# 调用transform_data方法进行转换return self.transform_data(input_data)

nlp_text_clustering.py

# 从nlp_book_data_collection模块导入BookDataCollection类
from nlp_book_data_collection import BookDataCollection
# 从nlp_tfidf_transformer模块导入TFIDFTransformer类
from nlp_tfidf_transformer import TFIDFTransformer# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 获取训练集和测试集数据training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 获取词汇表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化训练输入列表training_input = []# 遍历训练数据for data_item in training_data:# 将词元索引添加到训练输入列表training_input.append(data_item['token_indices'])# 创建TF-IDF转换器对象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 拟合训练数据tfidf_transformer.fit_data(training_input)# 转换训练数据training_features = tfidf_transformer.transform_data(training_input)# 打印训练特征的形状print(training_features.shape)# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()

特征信息运行结果

训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [02:29<00:00, 57.74it/s]
100%|██████████| 2156/2156 [00:37<00:00, 56.76it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699
(8626, 9504)进程已结束,退出代码为 0

当获取到数据后,便可运用k均值聚类算法对文本展开聚类操作。在此过程中,一个关键的前提是要预先设定簇数K。为了便于与实际的标签数据进行对照分析,此次设定K值为 3。下面,我们用代码来演示:

聚类操作完整代码

nlp_kmeans_clusterer.py

# 导入NumPy库,用于数值计算
import numpy as np# 定义一个类,用于实现K-Means聚类算法
class KMeansClusterer:# 类的初始化方法def __init__(self, num_clusters, data_dimension, stop_threshold=1e-4, max_steps=100):# 存储聚类的数量self.num_clusters = num_clusters# 存储数据的维度self.data_dimension = data_dimension# 存储停止迭代的阈值self.stop_threshold = stop_threshold# 存储最大迭代步数self.max_steps = max_steps# 定义一个方法,用于更新聚类中心def update_cluster_centers(self, input_data):# 初始化一个全零矩阵,用于存储聚类中心cluster_centers = np.zeros([self.num_clusters, self.data_dimension])# 遍历每个聚类for k in range(self.num_clusters):# 获取属于当前聚类的数据点cluster_data = input_data[self.cluster_assignments == k]# 如果当前聚类中有数据点if len(cluster_data) > 0:# 计算当前聚类的中心cluster_centers[k] = cluster_data.mean(axis=0)# 返回更新后的聚类中心return cluster_centers# 定义一个方法,用于拟合模型def fit_model(self, input_data):# 打印初始化信息print('-----------初始化-----------')# 获取输入数据的样本数量num_samples = len(input_data)# 获取输入数据的维度data_dim = len(input_data[0])# 随机初始化每个数据点的聚类分配self.cluster_assignments = np.random.randint(0, self.num_clusters, num_samples)# 调用update_cluster_centers方法更新聚类中心self.cluster_centers = self.update_cluster_centers(input_data)# 打印初始化完成信息print('-----------初始化完成-----------')# 初始化当前迭代步数为0current_step = 0# 当当前迭代步数小于最大迭代步数时,继续迭代while current_step < self.max_steps:# 迭代步数加1current_step += 1# 初始化每个数据点的聚类分配为0self.cluster_assignments = np.zeros(num_samples, int)# 遍历每个数据点for i, data_point in enumerate(input_data):# 计算每个数据点和每个簇中心的L2距离distances = np.linalg.norm(data_point[None, :] - self.cluster_centers, ord=2, axis=-1)# 找到每个数据点所属新的聚类self.cluster_assignments[i] = distances.argmin(-1)# 调用update_cluster_centers方法更新聚类中心new_cluster_centers = self.update_cluster_centers(input_data)# 计算聚类中心的平均移动距离center_movement = np.linalg.norm(new_cluster_centers - self.cluster_centers, ord=2, axis=-1).mean()# 打印当前迭代步数和聚类中心的平均移动距离print(f"第{current_step}步,中心点平均移动距离:{center_movement}")# 如果聚类中心的平均移动距离小于停止迭代的阈值if center_movement < self.stop_threshold:# 打印停止迭代信息print("中心点不再移动,退出程序")# 跳出循环break# 更新聚类中心self.cluster_centers = new_cluster_centers

nlp_text_clustering.py

# 从nlp_book_data_collection模块导入BookDataCollection类
from nlp_book_data_collection import BookDataCollection
# 从nlp_tfidf_transformer模块导入TFIDFTransformer类
from nlp_tfidf_transformer import TFIDFTransformer
# 从nlp_kmeans_clusterer模块导入KMeansClusterer类
from nlp_kmeans_clusterer import KMeansClusterer# 聚类的数量
num_clusters = 3# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 获取训练集和测试集数据training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 获取词汇表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化训练输入列表training_input = []# 遍历训练数据for data_item in training_data:# 将词元索引添加到训练输入列表training_input.append(data_item['token_indices'])# 创建TF-IDF转换器对象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 拟合训练数据tfidf_transformer.fit_data(training_input)# 转换训练数据training_features = tfidf_transformer.transform_data(training_input)# 打印训练特征的形状print(training_features.shape)# 创建K-Means聚类器对象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 拟合训练特征kmeans_clusterer.fit_model(training_features)# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()
训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [03:05<00:00, 46.54it/s]
100%|██████████| 2156/2156 [00:36<00:00, 58.80it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心点平均移动距离:0.06890890571733081
第2步,中心点平均移动距离:0.05478241286369078
第3步,中心点平均移动距离:0.018892396863314006
第4步,中心点平均移动距离:0.013429342825043227
第5步,中心点平均移动距离:0.013566480916429499
第6步,中心点平均移动距离:0.013989222795632706
第7步,中心点平均移动距离:0.011440477943083664
第8步,中心点平均移动距离:0.007103963059848575
第9步,中心点平均移动距离:0.006074946365616077
第10步,中心点平均移动距离:0.00477369638045405
第11步,中心点平均移动距离:0.0037873833801874888
第12步,中心点平均移动距离:0.003169778238863545
第13步,中心点平均移动距离:0.0026544650799152428
第14步,中心点平均移动距离:0.0029003953549483793
第15步,中心点平均移动距离:0.002845907460494008
第16步,中心点平均移动距离:0.0029565530700650586
第17步,中心点平均移动距离:0.0046409302413431995
第18步,中心点平均移动距离:0.007601779316323283
第19步,中心点平均移动距离:0.008556943854755123
第20步,中心点平均移动距离:0.007326270837156583
第21步,中心点平均移动距离:0.004774654456463306
第22步,中心点平均移动距离:0.004131223162570739
第23步,中心点平均移动距离:0.0034665383237183632
第24步,中心点平均移动距离:0.0028326750163131242
第25步,中心点平均移动距离:0.0024085910806691804
第26步,中心点平均移动距离:0.0025467882048793647
第27步,中心点平均移动距离:0.0024867293424594233
第28步,中心点平均移动距离:0.00232851482144344
第29步,中心点平均移动距离:0.002858996918517763
第30步,中心点平均移动距离:0.0027176858630063097
第31步,中心点平均移动距离:0.0024908107307621374
第32步,中心点平均移动距离:0.002725824149863784
第33步,中心点平均移动距离:0.0029918663142641765
第34步,中心点平均移动距离:0.0021179114918197877
第35步,中心点平均移动距离:0.0019590759238497786
第36步,中心点平均移动距离:0.0021350571707413643
第37步,中心点平均移动距离:0.0029617190015900938
第38步,中心点平均移动距离:0.0033936270370122233
第39步,中心点平均移动距离:0.0029231439612436316
第40步,中心点平均移动距离:0.0032854686271343914
第41步,中心点平均移动距离:0.0031848632066834794
第42步,中心点平均移动距离:0.003748152169792271
第43步,中心点平均移动距离:0.0034990023467288468
第44步,中心点平均移动距离:0.003588666039850141
第45步,中心点平均移动距离:0.003444720739168902
第46步,中心点平均移动距离:0.004132389860665119
第47步,中心点平均移动距离:0.0042080010326193
第48步,中心点平均移动距离:0.004440204058509182
第49步,中心点平均移动距离:0.002555459672126323
第50步,中心点平均移动距离:0.0025740606612964256
第51步,中心点平均移动距离:0.0017377711466915063
第52步,中心点平均移动距离:0.001173105458515561
第53步,中心点平均移动距离:0.0014038477302960907
第54步,中心点平均移动距离:0.002010183332161079
第55步,中心点平均移动距离:0.0025465099651279664
第56步,中心点平均移动距离:0.003412787991913555
第57步,中心点平均移动距离:0.00491581405490893
第58步,中心点平均移动距离:0.006009725805675484
第59步,中心点平均移动距离:0.0030275349972018945
第60步,中心点平均移动距离:0.0023261247949924505
第61步,中心点平均移动距离:0.0019728143627690867
第62步,中心点平均移动距离:0.0021625513846364737
第63步,中心点平均移动距离:0.001665616845718412
第64步,中心点平均移动距离:0.000931590997003939
第65步,中心点平均移动距离:0.001013041855168734
第66步,中心点平均移动距离:0.0008405371362220621
第67步,中心点平均移动距离:0.001367543177768866
第68步,中心点平均移动距离:0.0014425643303775304
第69步,中心点平均移动距离:0.001116963591121085
第70步,中心点平均移动距离:0.001491048450742236
第71步,中心点平均移动距离:0.002040608920857972
第72步,中心点平均移动距离:0.0036833183433489704
第73步,中心点平均移动距离:0.010667221023078093
第74步,中心点平均移动距离:0.022518116498817724
第75步,中心点平均移动距离:0.019497945687248934
第76步,中心点平均移动距离:0.012781228163674337
第77步,中心点平均移动距离:0.006360174137399897
第78步,中心点平均移动距离:0.004201484107008095
第79步,中心点平均移动距离:0.0035826113950724998
第80步,中心点平均移动距离:0.0037105073591507513
第81步,中心点平均移动距离:0.00219649791629415
第82步,中心点平均移动距离:0.0015802391154533663
第83步,中心点平均移动距离:0.001539134331937778
第84步,中心点平均移动距离:0.0
中心点不再移动,退出程序进程已结束,退出代码为 0

为了能更直观地呈现聚类效果,我们定义了display_clusters()函数,它的作用是展示每个真实分类下各个簇的占比情况。接下来,我们会利用这个函数展示k均值算法的聚类结果,借此观察3个标签中不同簇的占比情况。 我们用代码来演示:

聚类结果完整代码

nlp_text_clustering.py

# 从nlp_book_data_collection模块导入BookDataCollection类
from nlp_book_data_collection import BookDataCollection
# 从nlp_tfidf_transformer模块导入TFIDFTransformer类
from nlp_tfidf_transformer import TFIDFTransformer
# 从nlp_kmeans_clusterer模块导入KMeansClusterer类
from nlp_kmeans_clusterer import KMeansClusterer# 聚类的数量
num_clusters = 3# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化标签聚类字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每个标签下每个聚类的计数为0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍历标签和聚类分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加对应标签和聚类的计数label_clusters[label_id][cluster_id] += 1# 按标签ID排序遍历for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚类信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍历每个聚类for cluster_id in range(num_clusters):# 获取当前聚类的计数count = label_clusters[label_id][cluster_id]# 计算总数total = sum(label_clusters[label_id].values())# 拼接聚类信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 结束聚类信息字符串cluster_str += '}'# 打印聚类信息print(cluster_str)def text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 获取训练集和测试集数据training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 获取词汇表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化训练输入列表training_input = []# 遍历训练数据for data_item in training_data:# 将词元索引添加到训练输入列表training_input.append(data_item['token_indices'])# 创建TF-IDF转换器对象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 拟合训练数据tfidf_transformer.fit_data(training_input)# 转换训练数据training_features = tfidf_transformer.transform_data(training_input)# 打印训练特征的形状print(training_features.shape)# 创建K-Means聚类器对象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 拟合训练特征kmeans_clusterer.fit_model(training_features)# 初始化标签列表labels_list = []# 遍历训练数据for data_item in training_data:# 将标签添加到标签列表labels_list.append(data_item['label'])# 打印标签列表的长度print(len(labels_list))# 获取K-Means聚类结果cluster_assignments = kmeans_clusterer.cluster_assignments# 显示K-Means聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()

聚类结果运行结果

训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [03:00<00:00, 47.89it/s]
100%|██████████| 2156/2156 [00:36<00:00, 58.84it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心点平均移动距离:0.07187923654672244
第2步,中心点平均移动距离:0.044157507688436216
第3步,中心点平均移动距离:0.022896531888343605
第4步,中心点平均移动距离:0.0159488039593934
第5步,中心点平均移动距离:0.012182250613021412
第6步,中心点平均移动距离:0.012091131336056611
第7步,中心点平均移动距离:0.011953069179042916
第8步,中心点平均移动距离:0.007551366337940332
第9步,中心点平均移动距离:0.00607274740895354
第10步,中心点平均移动距离:0.0051005041228621905
第11步,中心点平均移动距离:0.0037475278494419353
第12步,中心点平均移动距离:0.002873947676155657
第13步,中心点平均移动距离:0.0032856197227294393
第14步,中心点平均移动距离:0.0037597515304094073
第15步,中心点平均移动距离:0.005154214384698833
第16步,中心点平均移动距离:0.01429702264635499
第17步,中心点平均移动距离:0.011662801294254196
第18步,中心点平均移动距离:0.01061592695683227
第19步,中心点平均移动距离:0.01964312337838108
第20步,中心点平均移动距离:0.012665767903761787
第21步,中心点平均移动距离:0.005392861470868378
第22步,中心点平均移动距离:0.002772730322582759
第23步,中心点平均移动距离:0.0018288923370850228
第24步,中心点平均移动距离:0.0014922387193858154
第25步,中心点平均移动距离:0.0010964808376660166
第26步,中心点平均移动距离:0.0011809417903345324
第27步,中心点平均移动距离:0.0008189027391701968
第28步,中心点平均移动距离:0.0008794506802535549
第29步,中心点平均移动距离:0.0003873751480754425
第30步,中心点平均移动距离:0.00023008235702405927
第31步,中心点平均移动距离:0.0
中心点不再移动,退出程序
8626
计算机类:	{ 0: 397(0.10), 1: 845(0.22), 2: 2600(0.68), }
艺术传媒类:	{ 0: 177(0.08), 1: 2115(0.92), 2: 7(0.00), }
经管类:	{ 0: 2408(0.97), 1: 36(0.01), 2: 41(0.02), }进程已结束,退出代码为 0

基于高斯混合模型的最大期望值算法

基于高斯混合模型的最大期望值算法则是另一种强大的文本聚类方法。高斯混合模型认为文本数据是由多个高斯分布混合而成的,就好像是把不同颜色的颜料混合在一起。

最大期望值算法通过不断地迭代,分为期望步骤和最大化步骤。在期望步骤中,根据当前的模型参数,计算每个文本属于不同高斯分布的概率,就像是猜测每个文本更可能属于哪一种“颜料”;在最大化步骤中,利用期望步骤得到的概率,重新计算模型的参数,比如高斯分布的均值、方差和权重,就像调整颜料的混合比例。通过这样反复迭代,让模型更好地拟合文本数据,从而实现文本聚类。

这种算法的优势在于能够灵活地拟合复杂的数据分布,对于多模态的数据聚类效果很好,而且它有完善的理论基础。不过,它也存在一些问题,比如计算复杂度比较高,对超参数的选择很敏感,像高斯分布的数量、协方差类型等,而且容易陷入局部最优解,就像在一个复杂的迷宫中,容易被困在某个局部区域,找不到全局最优的聚类结果。

下面,我们来演示怎样运用高斯混合模型进行聚类操作。需要注意的是,高斯混合模型会计算每个数据点属于各个簇的概率分布,在实际应用中,我们通常把概率最高的簇作为最终的聚类输出结果。通过这种方式,我们就能依据数据点的概率分布情况,将它们划分到最合适的簇中,实现文本数据的有效聚类。我们用代码来演示:

高斯混合聚类完整代码

nlp_gaussian_mixture_model.py

# 导入NumPy库,用于数值计算
import numpy as np
# 从scipy库中导入多元正态分布函数
from scipy.stats import multivariate_normal as gaussian# 定义高斯混合模型类
class GaussianMixtureModel:# 初始化方法,设置聚类数量、数据维度和最大迭代次数def __init__(self, num_clusters, data_dimension, max_iterations=100):# 存储聚类数量self.num_clusters = num_clusters# 存储数据维度self.data_dimension = data_dimension# 存储最大迭代次数self.max_iterations = max_iterations# 初始化混合系数,每个聚类初始权重相同self.mixing_coefficients = np.ones(num_clusters) / num_clusters# 随机初始化每个聚类的均值self.means = np.random.rand(num_clusters, data_dimension) * 2 - 1# 初始化协方差矩阵为全零矩阵self.covariances = np.zeros((num_clusters, data_dimension, data_dimension))# 遍历每个聚类for i in range(num_clusters):# 将每个聚类的协方差矩阵设为单位矩阵self.covariances[i] = np.eye(data_dimension)# 期望步骤,计算每个数据点属于每个聚类的概率def expectation_step(self, input_data):# 遍历每个聚类for i in range(self.num_clusters):# 计算每个数据点属于当前聚类的概率self.responsibilities[:, i] = self.mixing_coefficients[i] * gaussian.pdf(input_data, mean=self.means[i],cov=self.covariances[i])# 对概率进行归一化处理self.responsibilities /= self.responsibilities.sum(axis=1, keepdims=True)# 最大化步骤,更新模型参数def maximization_step(self, input_data):# 计算每个聚类的责任权重总和responsibility_sum = self.responsibilities.sum(axis=0)# 更新混合系数self.mixing_coefficients = responsibility_sum / self.num_samples# 更新每个聚类的均值self.means = np.matmul(self.responsibilities.T, input_data) / responsibility_sum[:, None]# 遍历每个聚类for i in range(self.num_clusters):# 计算数据点与当前聚类均值的差值delta = np.expand_dims(input_data, axis=1) - self.means[i]# 计算协方差矩阵covariance = np.matmul(delta.transpose(0, 2, 1), delta)# 更新当前聚类的协方差矩阵self.covariances[i] = np.matmul(covariance.transpose(1, 2, 0), self.responsibilities[:, i]) / \responsibility_sum[i]# 计算对数似然值def log_likelihood_value(self, input_data):# 初始化对数似然值为0log_likelihood = 0# 遍历每个数据点for data_point in input_data:# 初始化概率为0probability = 0# 遍历每个聚类for i in range(self.num_clusters):# 累加数据点属于当前聚类的概率probability += self.mixing_coefficients[i] * gaussian.pdf(data_point, mean=self.means[i],cov=self.covariances[i])# 累加对数概率log_likelihood += np.log(probability)# 返回平均对数似然值return log_likelihood / self.num_samples# 拟合模型def fit_model(self, input_data):# 存储输入数据的样本数量self.num_samples = len(input_data)# 初始化责任矩阵为全零矩阵self.responsibilities = np.zeros((self.num_samples, self.num_clusters))# 计算初始对数似然值log_likelihood = self.log_likelihood_value(input_data)# 打印开始迭代信息print('开始迭代')# 开始迭代for i in range(self.max_iterations):# 执行期望步骤self.expectation_step(input_data)# 执行最大化步骤self.maximization_step(input_data)# 计算新的对数似然值new_log_likelihood = self.log_likelihood_value(input_data)# 打印当前迭代步数和对数似然值print(f'第{i}步, log-likelihood = {new_log_likelihood:.4f}')# 如果对数似然值变化小于阈值,停止迭代if new_log_likelihood - log_likelihood < 1e-4:print('log-likelihood不再变化,退出程序')breakelse:# 更新对数似然值log_likelihood = new_log_likelihood# 转换数据,返回每个数据点所属的聚类def transform_data(self, input_data):# 确保责任矩阵已计算且长度与输入数据一致assert hasattr(self, 'responsibilities') and len(self.responsibilities) == len(input_data)# 返回每个数据点所属的聚类索引return np.argmax(self.responsibilities, axis=1)# 拟合并转换数据def fit_and_transform(self, input_data):# 拟合模型self.fit_model(input_data)# 转换数据return self.transform_data(input_data)

和k均值聚类方法相似,在运用基于最大期望值法的高斯混合模型时,我们来观察一下在Books数据集中,3个真实类别里不同簇各自所占的比例情况:

nlp_text_clustering.py

# 从nlp_book_data_collection模块导入BookDataCollection类
from nlp_book_data_collection import BookDataCollection
from nlp_gaussian_mixture_model import GaussianMixtureModel
# 从nlp_tfidf_transformer模块导入TFIDFTransformer类
from nlp_tfidf_transformer import TFIDFTransformer
# 从nlp_kmeans_clusterer模块导入KMeansClusterer类
from nlp_kmeans_clusterer import KMeansClusterer
# 从sklearn库中导入主成分分析(PCA)类
from sklearn.decomposition import PCA# 聚类的数量
num_clusters = 3# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化标签聚类字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每个标签下每个聚类的计数为0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍历标签和聚类分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加对应标签和聚类的计数label_clusters[label_id][cluster_id] += 1# 按标签ID排序遍历for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚类信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍历每个聚类for cluster_id in range(num_clusters):# 获取当前聚类的计数count = label_clusters[label_id][cluster_id]# 计算总数total = sum(label_clusters[label_id].values())# 拼接聚类信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 结束聚类信息字符串cluster_str += '}'# 打印聚类信息print(cluster_str)def text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 获取训练集和测试集数据training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 获取词汇表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化训练输入列表training_input = []# 遍历训练数据for data_item in training_data:# 将词元索引添加到训练输入列表training_input.append(data_item['token_indices'])# 创建TF-IDF转换器对象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 拟合训练数据tfidf_transformer.fit_data(training_input)# 转换训练数据training_features = tfidf_transformer.transform_data(training_input)# 打印训练特征的形状print(training_features.shape)# 创建K-Means聚类器对象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 拟合训练特征kmeans_clusterer.fit_model(training_features)# 初始化标签列表labels_list = []# 遍历训练数据for data_item in training_data:# 将标签添加到标签列表labels_list.append(data_item['label'])# 打印标签列表的长度print(len(labels_list))# 获取K-Means聚类结果cluster_assignments = kmeans_clusterer.cluster_assignments# 显示K-Means聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 创建PCA降维器对象pca_reducer = PCA(n_components=50)# 对训练特征进行降维training_pca = pca_reducer.fit_transform(training_features)# 创建高斯混合模型对象gmm_model = GaussianMixtureModel(num_clusters, data_dimension=training_pca.shape[1])# 拟合并转换训练数据cluster_assignments = gmm_model.fit_and_transform(training_pca)# 打印高斯混合模型聚类结果print(cluster_assignments)# 显示高斯混合模型聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()

高斯混合聚类运行结果

训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [02:36<00:00, 54.95it/s]
100%|██████████| 2156/2156 [00:36<00:00, 59.32it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心点平均移动距离:0.07821463876668384
第2步,中心点平均移动距离:0.05703827383757104
第3步,中心点平均移动距离:0.0227878128221168
第4步,中心点平均移动距离:0.012985270767233279
第5步,中心点平均移动距离:0.010560323354748833
第6步,中心点平均移动距离:0.011526155533891311
第7步,中心点平均移动距离:0.014447526451567915
第8步,中心点平均移动距离:0.01480361614434264
第9步,中心点平均移动距离:0.007805741764265261
第10步,中心点平均移动距离:0.004696899248898042
第11步,中心点平均移动距离:0.003858954772666219
第12步,中心点平均移动距离:0.0037634051652670757
第13步,中心点平均移动距离:0.0036342433215190346
第14步,中心点平均移动距离:0.004037746948698562
第15步,中心点平均移动距离:0.0037074902463420808
第16步,中心点平均移动距离:0.0025126860295755764
第17步,中心点平均移动距离:0.0017742952721109479
第18步,中心点平均移动距离:0.001311558516669006
第19步,中心点平均移动距离:0.0008183675564787064
第20步,中心点平均移动距离:0.0011545852276947674
第21步,中心点平均移动距离:0.0006864544106957334
第22步,中心点平均移动距离:0.0009260959472041178
第23步,中心点平均移动距离:0.0004974572613901904
第24步,中心点平均移动距离:0.00047763554726450624
第25步,中心点平均移动距离:0.000561811847711657
第26步,中心点平均移动距离:0.00030713124482862106
第27步,中心点平均移动距离:0.0
中心点不再移动,退出程序
8626
计算机类:	{ 0: 1233(0.32), 1: 2572(0.67), 2: 37(0.01), }
艺术传媒类:	{ 0: 74(0.03), 1: 282(0.12), 2: 1943(0.85), }
经管类:	{ 0: 26(0.01), 1: 2452(0.99), 2: 7(0.00), }
开始迭代
第0步, log-likelihood = 77.2117
第1步, log-likelihood = 87.3795
第2步, log-likelihood = 90.4587
第3步, log-likelihood = 91.7301
第4步, log-likelihood = 92.7042
第5步, log-likelihood = 93.5868
第6步, log-likelihood = 94.6852
第7步, log-likelihood = 95.8890
第8步, log-likelihood = 96.4726
第9步, log-likelihood = 96.8001
第10步, log-likelihood = 96.9560
第11步, log-likelihood = 97.0857
第12步, log-likelihood = 97.5848
第13步, log-likelihood = 97.5959
第14步, log-likelihood = 97.6033
第15步, log-likelihood = 97.6140
第16步, log-likelihood = 97.6341
第17步, log-likelihood = 97.6695
第18步, log-likelihood = 97.6801
第19步, log-likelihood = 97.6888
第20步, log-likelihood = 97.7062
第21步, log-likelihood = 97.7420
第22步, log-likelihood = 97.7711
第23步, log-likelihood = 97.8122
第24步, log-likelihood = 97.8207
第25步, log-likelihood = 97.8232
第26步, log-likelihood = 97.8244
第27步, log-likelihood = 97.8255
第28步, log-likelihood = 97.8284
第29步, log-likelihood = 97.8330
第30步, log-likelihood = 97.8486
第31步, log-likelihood = 97.9543
第32步, log-likelihood = 98.0578
第33步, log-likelihood = 98.0620
第34步, log-likelihood = 98.0625
第35步, log-likelihood = 98.0626
第36步, log-likelihood = 98.0628
第37步, log-likelihood = 98.0628
log-likelihood不再变化,退出程序
[0 2 0 ... 2 0 2]
计算机类:	{ 0: 2742(0.71), 1: 299(0.08), 2: 801(0.21), }
艺术传媒类:	{ 0: 9(0.00), 1: 421(0.18), 2: 1869(0.81), }
经管类:	{ 0: 829(0.33), 1: 1625(0.65), 2: 31(0.01), }进程已结束,退出代码为 0

无监督朴素贝叶斯模型

无监督朴素贝叶斯模型也是文本聚类的有力工具。它基于贝叶斯定理和特征条件独立假设,认为文本中的各个词之间是相互独立的,就好像每个人都是独立行动的个体。在文本聚类时,它通过统计文本中词的出现频率,结合贝叶斯定理,计算文本属于不同主题(类别)的概率,然后把概率相近的文本归为一类。

这种算法的优点是简单高效,对数据量的要求相对较低,可扩展性也不错。然而,它的特征条件独立假设在实际情况中往往不太符合,因为文本中的词之间其实是有语义关联的;而且它对多义词的处理能力有限,可能会把不同含义的同一个词都当作相同的来处理,影响聚类的准确性;此外,模型的性能很大程度上依赖于文本的表示方式,如果文本表示不准确,聚类效果也会受到影响。

接下来,为大家展示基于朴素贝叶斯模型的聚类算法具体是如何实现的:

朴素贝叶斯模型聚类完整代码

nlp_unsupervised_naive_bayes_classifier.py

# 导入NumPy库,用于数值计算
import numpy as np
# 从scipy库中导入对数求和指数函数
from scipy.special import logsumexp# 定义无监督朴素贝叶斯分类器类
class UnsupervisedNaiveBayesClassifier:# 初始化方法,设置聚类数量、数据维度和最大迭代次数def __init__(self, num_clusters, data_dimension, max_iterations=100):# 存储聚类数量self.num_clusters = num_clusters# 存储数据维度self.data_dimension = data_dimension# 存储最大迭代次数self.max_iterations = max_iterations# 初始化先验概率,每个聚类初始概率相同self.prior_probabilities = np.ones(num_clusters) / num_clusters# 随机初始化条件概率self.conditional_probabilities = np.random.random((num_clusters, data_dimension))# 对条件概率进行归一化处理self.conditional_probabilities /= self.conditional_probabilities.sum(axis=1, keepdims=True)# 期望步骤,计算每个数据点属于每个聚类的概率def expectation_step(self, input_data):# 遍历每个数据点for i, data_point in enumerate(input_data):# 计算对数概率self.responsibilities[i, :] = np.log(self.prior_probabilities) + (np.log(self.conditional_probabilities) * data_point).sum(axis=1)# 减去对数求和指数,避免数值溢出self.responsibilities[i, :] -= logsumexp(self.responsibilities[i, :])# 计算概率self.responsibilities[i, :] = np.exp(self.responsibilities[i, :])# 最大化步骤,更新模型参数def maximization_step(self, input_data):# 计算每个聚类的责任权重总和self.prior_probabilities = self.responsibilities.sum(axis=0) / self.num_samples# 对先验概率进行归一化处理self.prior_probabilities /= self.prior_probabilities.sum()# 遍历每个聚类for i in range(self.num_clusters):# 更新条件概率self.conditional_probabilities[i] = (self.responsibilities[:, i:i + 1] * input_data).sum(axis=0) / \(self.responsibilities[:, i] * input_data.sum(axis=1)).sum()# 避免条件概率为0self.conditional_probabilities += 1e-10# 对条件概率进行归一化处理self.conditional_probabilities /= self.conditional_probabilities.sum(axis=1, keepdims=True)# 计算对数似然值def log_likelihood_value(self, input_data):# 初始化对数似然值为0log_likelihood = 0# 遍历每个数据点for data_point in input_data:# 存储每个聚类的对数概率log_probabilities = []# 遍历每个聚类for i in range(self.num_clusters):# 计算对数概率log_probabilities.append(np.log(self.prior_probabilities[i]) + (np.log(self.conditional_probabilities[i]) * data_point).sum())# 累加对数求和指数log_likelihood += logsumexp(log_probabilities)# 返回平均对数似然值return log_likelihood / len(input_data)# 拟合模型def fit_model(self, input_data):# 存储输入数据的样本数量self.num_samples = len(input_data)# 初始化责任矩阵为全零矩阵self.responsibilities = np.zeros((self.num_samples, self.num_clusters))# 计算初始对数似然值log_likelihood = self.log_likelihood_value(input_data)# 打印初始对数似然值print(f'初始化log-likelihood = {log_likelihood:.4f}')# 打印开始迭代信息print('开始迭代')# 开始迭代for i in range(self.max_iterations):# 执行期望步骤self.expectation_step(input_data)# 执行最大化步骤self.maximization_step(input_data)# 计算新的对数似然值new_log_likelihood = self.log_likelihood_value(input_data)# 打印当前迭代步数和对数似然值print(f'第{i}步, log-likelihood = {new_log_likelihood:.4f}')# 如果对数似然值变化小于阈值,停止迭代if new_log_likelihood - log_likelihood < 1e-4:print('log-likelihood不再变化,退出程序')breakelse:# 更新对数似然值log_likelihood = new_log_likelihood# 转换数据,返回每个数据点所属的聚类def transform_data(self, input_data):# 确保责任矩阵已计算且长度与输入数据一致assert hasattr(self, 'responsibilities') and len(self.responsibilities) == len(input_data)# 返回每个数据点所属的聚类索引return np.argmax(self.responsibilities, axis=1)# 拟合并转换数据def fit_and_transform(self, input_data):# 拟合模型self.fit_model(input_data)# 转换数据return self.transform_data(input_data)

nlp_text_clustering.py

# 从nlp_book_data_collection模块导入BookDataCollection类
from nlp_book_data_collection import BookDataCollection
from nlp_gaussian_mixture_model import GaussianMixtureModel
# 从nlp_tfidf_transformer模块导入TFIDFTransformer类
from nlp_tfidf_transformer import TFIDFTransformer
# 从nlp_kmeans_clusterer模块导入KMeansClusterer类
from nlp_kmeans_clusterer import KMeansClusterer
# 从sklearn库中导入主成分分析(PCA)类
from sklearn.decomposition import PCA
# 从nlp_unsupervised_naive_bayes_classifier模块导入UnsupervisedNaiveBayesClassifier类
from nlp_unsupervised_naive_bayes_classifier import UnsupervisedNaiveBayesClassifier
# 导入NumPy库,用于数值计算
import numpy as np# 聚类的数量
num_clusters = 3# 定义一个用于自然语言处理文本聚类的类
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化标签聚类字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每个标签下每个聚类的计数为0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍历标签和聚类分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加对应标签和聚类的计数label_clusters[label_id][cluster_id] += 1# 按标签ID排序遍历for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚类信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍历每个聚类for cluster_id in range(num_clusters):# 获取当前聚类的计数count = label_clusters[label_id][cluster_id]# 计算总数total = sum(label_clusters[label_id].values())# 拼接聚类信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 结束聚类信息字符串cluster_str += '}'# 打印聚类信息print(cluster_str)def text_clustering(self):# 创建书籍数据集对象book_dataset = BookDataCollection()# 打印标签到ID的映射print(book_dataset.id_to_label)# 对文本进行分词处理book_dataset.tokenize_text(attribute='abstract')# 创建词汇表book_dataset.create_vocabulary(min_frequency=3)# 将词元转换为索引book_dataset.convert_tokens_to_indices()# 获取训练集和测试集数据training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 获取词汇表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化训练输入列表training_input = []# 遍历训练数据for data_item in training_data:# 将词元索引添加到训练输入列表training_input.append(data_item['token_indices'])# 创建TF-IDF转换器对象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 拟合训练数据tfidf_transformer.fit_data(training_input)# 转换训练数据training_features = tfidf_transformer.transform_data(training_input)# 打印训练特征的形状print(training_features.shape)# 创建K-Means聚类器对象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 拟合训练特征kmeans_clusterer.fit_model(training_features)# 初始化标签列表labels_list = []# 遍历训练数据for data_item in training_data:# 将标签添加到标签列表labels_list.append(data_item['label'])# 打印标签列表的长度print(len(labels_list))# 获取K-Means聚类结果cluster_assignments = kmeans_clusterer.cluster_assignments# 显示K-Means聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 创建PCA降维器对象pca_reducer = PCA(n_components=50)# 对训练特征进行降维training_pca = pca_reducer.fit_transform(training_features)# 创建高斯混合模型对象gmm_model = GaussianMixtureModel(num_clusters, data_dimension=training_pca.shape[1])# 拟合并转换训练数据cluster_assignments = gmm_model.fit_and_transform(training_pca)# 打印高斯混合模型聚类结果print(cluster_assignments)# 显示高斯混合模型聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 初始化训练计数矩阵training_count_matrix = np.zeros((len(training_input), vocabulary_size))# 遍历训练输入for i, data_item in enumerate(training_input):# 遍历词元索引for token_index in data_item:# 增加对应位置的计数training_count_matrix[i, token_index] += 1# 创建无监督朴素贝叶斯分类器对象naive_bayes_classifier = UnsupervisedNaiveBayesClassifier(num_clusters, data_dimension=vocabulary_size,max_iterations=100)# 拟合并转换训练数据cluster_assignments = naive_bayes_classifier.fit_and_transform(training_count_matrix)# 打印无监督朴素贝叶斯分类器聚类结果print(cluster_assignments)# 显示无监督朴素贝叶斯分类器聚类结果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,当作为脚本直接运行时执行以下代码
if __name__ == "__main__":# 创建NLPTextClustering类的一个实例nlp_text_clustering = NLPTextClustering()# 调用实例的text_clustering方法进行文本聚类操作nlp_text_clustering.text_clustering()

朴素贝叶斯模型聚类运行结果

训练集大小 = 8626 , 测试集大小 = 2156
{0: '计算机类', 1: '艺术传媒类', 2: '经管类'}
100%|██████████| 8626/8626 [02:36<00:00, 54.95it/s]
100%|██████████| 2156/2156 [00:36<00:00, 59.32it/s]
唯一词元数量 = 34251, 总词元数量 = 806791, 最大词频 = 19194, 最小词频 = 1
最小词频 = 3, 最小词长 = 2, 最大词表大小 = None, 剩余词元数量 = 9504, 词表内词元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心点平均移动距离:0.07821463876668384
第2步,中心点平均移动距离:0.05703827383757104
第3步,中心点平均移动距离:0.0227878128221168
第4步,中心点平均移动距离:0.012985270767233279
第5步,中心点平均移动距离:0.010560323354748833
第6步,中心点平均移动距离:0.011526155533891311
第7步,中心点平均移动距离:0.014447526451567915
第8步,中心点平均移动距离:0.01480361614434264
第9步,中心点平均移动距离:0.007805741764265261
第10步,中心点平均移动距离:0.004696899248898042
第11步,中心点平均移动距离:0.003858954772666219
第12步,中心点平均移动距离:0.0037634051652670757
第13步,中心点平均移动距离:0.0036342433215190346
第14步,中心点平均移动距离:0.004037746948698562
第15步,中心点平均移动距离:0.0037074902463420808
第16步,中心点平均移动距离:0.0025126860295755764
第17步,中心点平均移动距离:0.0017742952721109479
第18步,中心点平均移动距离:0.001311558516669006
第19步,中心点平均移动距离:0.0008183675564787064
第20步,中心点平均移动距离:0.0011545852276947674
第21步,中心点平均移动距离:0.0006864544106957334
第22步,中心点平均移动距离:0.0009260959472041178
第23步,中心点平均移动距离:0.0004974572613901904
第24步,中心点平均移动距离:0.00047763554726450624
第25步,中心点平均移动距离:0.000561811847711657
第26步,中心点平均移动距离:0.00030713124482862106
第27步,中心点平均移动距离:0.0
中心点不再移动,退出程序
8626
计算机类:	{ 0: 1233(0.32), 1: 2572(0.67), 2: 37(0.01), }
艺术传媒类:	{ 0: 74(0.03), 1: 282(0.12), 2: 1943(0.85), }
经管类:	{ 0: 26(0.01), 1: 2452(0.99), 2: 7(0.00), }
开始迭代
第0步, log-likelihood = 77.2117
第1步, log-likelihood = 87.3795
第2步, log-likelihood = 90.4587
第3步, log-likelihood = 91.7301
第4步, log-likelihood = 92.7042
第5步, log-likelihood = 93.5868
第6步, log-likelihood = 94.6852
第7步, log-likelihood = 95.8890
第8步, log-likelihood = 96.4726
第9步, log-likelihood = 96.8001
第10步, log-likelihood = 96.9560
第11步, log-likelihood = 97.0857
第12步, log-likelihood = 97.5848
第13步, log-likelihood = 97.5959
第14步, log-likelihood = 97.6033
第15步, log-likelihood = 97.6140
第16步, log-likelihood = 97.6341
第17步, log-likelihood = 97.6695
第18步, log-likelihood = 97.6801
第19步, log-likelihood = 97.6888
第20步, log-likelihood = 97.7062
第21步, log-likelihood = 97.7420
第22步, log-likelihood = 97.7711
第23步, log-likelihood = 97.8122
第24步, log-likelihood = 97.8207
第25步, log-likelihood = 97.8232
第26步, log-likelihood = 97.8244
第27步, log-likelihood = 97.8255
第28步, log-likelihood = 97.8284
第29步, log-likelihood = 97.8330
第30步, log-likelihood = 97.8486
第31步, log-likelihood = 97.9543
第32步, log-likelihood = 98.0578
第33步, log-likelihood = 98.0620
第34步, log-likelihood = 98.0625
第35步, log-likelihood = 98.0626
第36步, log-likelihood = 98.0628
第37步, log-likelihood = 98.0628
log-likelihood不再变化,退出程序
[0 2 0 ... 2 0 2]
计算机类:	{ 0: 2742(0.71), 1: 299(0.08), 2: 801(0.21), }
艺术传媒类:	{ 0: 9(0.00), 1: 421(0.18), 2: 1869(0.81), }
经管类:	{ 0: 829(0.33), 1: 1625(0.65), 2: 31(0.01), }
初始化log-likelihood = -779.6002
开始迭代
第0步, log-likelihood = -589.5137
第1步, log-likelihood = -583.1897
第2步, log-likelihood = -579.2382
第3步, log-likelihood = -577.0352
第4步, log-likelihood = -575.8822
第5步, log-likelihood = -575.3356
第6步, log-likelihood = -574.8611
第7步, log-likelihood = -574.5028
第8步, log-likelihood = -574.2625
第9步, log-likelihood = -574.1030
第10步, log-likelihood = -573.9690
第11步, log-likelihood = -573.7704
第12步, log-likelihood = -573.6187
第13步, log-likelihood = -573.4686
第14步, log-likelihood = -573.3280
第15步, log-likelihood = -573.2186
第16步, log-likelihood = -573.1160
第17步, log-likelihood = -572.9857
第18步, log-likelihood = -572.8356
第19步, log-likelihood = -572.7076
第20步, log-likelihood = -572.4683
第21步, log-likelihood = -572.3301
第22步, log-likelihood = -572.2375
第23步, log-likelihood = -572.0611
第24步, log-likelihood = -571.8587
第25步, log-likelihood = -571.7117
第26步, log-likelihood = -571.6412
第27步, log-likelihood = -571.6158
第28步, log-likelihood = -571.5863
第29步, log-likelihood = -571.5397
第30步, log-likelihood = -571.5281
第31步, log-likelihood = -571.5163
第32步, log-likelihood = -571.4902
第33步, log-likelihood = -571.4693
第34步, log-likelihood = -571.4604
第35步, log-likelihood = -571.4457
第36步, log-likelihood = -571.4339
第37步, log-likelihood = -571.4301
第38步, log-likelihood = -571.4288
第39步, log-likelihood = -571.4272
第40步, log-likelihood = -571.4257
第41步, log-likelihood = -571.4254
第42步, log-likelihood = -571.4176
第43步, log-likelihood = -571.4060
第44步, log-likelihood = -571.4028
第45步, log-likelihood = -571.4027
log-likelihood不再变化,退出程序
[0 1 0 ... 1 0 1]
计算机类:	{ 0: 2266(0.59), 1: 1479(0.38), 2: 97(0.03), }
艺术传媒类:	{ 0: 64(0.03), 1: 703(0.31), 2: 1532(0.67), }
经管类:	{ 0: 2237(0.90), 1: 117(0.05), 2: 131(0.05), }进程已结束,退出代码为 0

结束

好了,以上就是本次分享的全部内容了。不知道大家是否对自然语言处理中的文本聚类有了更深入的了解呢?

本次分享主要围绕文本聚类展开,介绍了几种常见的文本聚类方法,包括 k 均值聚类算法、基于高斯混合模型的最大期望值算法和无监督朴素贝叶斯模型。

  • k均值聚类算法:简单且经典,计算效率高,但需要预先指定聚类数量 k,对初始聚类中心的选择比较敏感。
  • 基于高斯混合模型的最大期望值算法:能够灵活地拟合复杂的数据分布,对于多模态的数据聚类效果较好,但计算复杂度高,对超参数的选择敏感,容易陷入局部最优解。
  • 无监督朴素贝叶斯模型:简单高效,对数据量的要求相对较低,可扩展性不错,但特征条件独立假设在实际情况中往往不太符合,对多义词的处理能力有限,模型性能依赖于文本的表示方式。

文本聚类在自然语言处理领域有着广泛的应用前景,如信息检索、文本分类、情感分析等。随着技术的不断发展,未来可能会出现更高效、更准确的文本聚类算法。同时,结合深度学习等技术,也有望进一步提升文本聚类的效果。

那么本次分享就到这里了。最后,博主还是那句话:请大家多去大胆的尝试和使用,成功总是在不断的失败中试验出来的,敢于尝试就已经成功了一半。如果大家对博主分享的内容感兴趣或有帮助,请点赞和关注。大家的点赞和关注是博主持续分享的动力🤭,博主也希望让更多的人学习到新的知识。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/33129.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python----计算机视觉处理(opencv:像素,RGB颜色,图像的存储,opencv安装,代码展示)

一、计算机眼中的图像 像素 像素是图像的基本单元&#xff0c;每个像素存储着图像的颜色、亮度和其他特征。一系列像素组合到一起就形成 了完整的图像&#xff0c;在计算机中&#xff0c;图像以像素的形式存在并采用二进制格式进行存储。根据图像的颜色不 同&#xff0c;每个像…

Spring Boot(十六):使用 Jenkins 部署 Spring Boot

Jenkins 是 Devops 神器&#xff0c;本篇文章介绍如何安装和使用 Jenkins 部署 Spring Boot 项目 Jenkins 搭建、部署分为四个步骤&#xff1b; 第一步&#xff0c;Jenkins 安装 第二步&#xff0c;插件安装和配置 第三步&#xff0c;Push SSH 第四步&#xff0c;部署项目 第…

【Linux 指北】常用 Linux 指令汇总

第一章、常用基本指令 # 注意&#xff1a; # #表示管理员 # $表示普通用户 [rootlocalhost Practice]# 说明此处表示管理员01. ls 指令 语法&#xff1a; ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xf…

解决Windows版Redis无法远程连接的问题

&#x1f31f; 解决Windows版Redis无法远程连接的问题 在Windows系统下使用Redis时&#xff0c;很多用户会遇到无法远程连接的问题。尤其是在配置了Redis并尝试通过工具如RedisDesktopManager连接时&#xff0c;可能会报错“Cannot connect to ‘redisconnection’”。今天&am…

大语言模型学习及复习笔记(1)语言模型的发展历程

1.大模型进入人们视野 ChatGPT 于2022年11月底上线 模型名称 发布时间 核心突破 GPT-3 2020年6月 首款千亿参数模型&#xff0c;少样本学习 GPT-3.5-Turbo 2022年11月 对话能力优化&#xff0c;用户级应用落地 GPT-4 2023年3月 多模态、强逻辑推理 GPT-4o / GPT-4…

MySQL中count(*)与count(字段区别)

核心规则 表达式 统计规则 COUNT(*) 统计所有行数&#xff08;包括所有字段为NULL的行&#xff09;。 COUNT(字段) 仅统计该字段不为NULL的行数&#xff08;若字段为NULL则自动忽略该行&#xff09;。 误区 A表连接B表&#xff0c;若A和B是一对多的关系时&#xff0c;会出现…

【Unity】在项目中使用VisualScripting

1. 在packagemanager添加插件 2. 在设置中进行初始化。 Edit > Project Settings > Visual Scripting Initialize Visual Scripting You must select Initialize Visual Scripting the first time you use Visual Scripting in a project. Initialize Visual Scripting …

vue 仿deepseek前端开发一个对话界面

后端&#xff1a;调用deepseek的api&#xff0c;所以返回数据格式和deepseek相同 {"model": "DeepSeek-R1-Distill-Qwen-1.5B", "choices": [{"index": 0, "delta": {"role": "assistant", "cont…

基于Spring Boot的小区疫情购物系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【openwebui 搭建本地知识库(RAG搭建本地知识库)】

安装准备 openwebui 这个本地安装之前写过使用python安装。也可以直接用docker 命令 docker run --rm -d \-p 3080:8080 \-p 3081:8081 \-e WEBUI_AUTHtrue \-e DEFAULT_LOCALEcn \-e GLOBAL_LOG_LEVEL"INFO" \-e AIOHTTP_CLIENT_TIMEOUT100 \--privilegedtrue \-…

Linux 提权

文章目录 前言1. 内核漏洞提权脏牛&#xff08;CVE-2016-5195&#xff09; 2. 不安全的系统配置项2.1 SUID/SGID提权2.2 sudo提权2.3 定时任务提权2.4 capabilities提权 3. 第三方软件提权Tomcat managerNginx本地提权&#xff08;CVE-2016-1247&#xff09;Redis未授权 4. 参考…

谷粒商城:性能压测JVM堆区

目录 Kit Apache JMeter VisualVM 堆内存 jvm内存模型 垃圾回收&#xff08;Garbage Collection, GC&#xff09; 新对象分配内存 GC步骤 MinorGC 性能优化 影响因素 优化 nginx动静分离 优化三级分类获取 Jvm参数配置堆区 测试 Kit Apache JMeter 压力测试&…

TCP协议支持全双工原因TCP发送接收数据是生产者消费者模型

一、TCP支持全双工的原因 TCP协议支持全双工&#xff0c;即使用TCP协议进行通信时&#xff0c;服务端和客户端可以同时进行数据的发送和接收&#xff0c;互不干扰&#xff0c;实现同时双向传输数据。 这是因为使用TCP协议通信时&#xff0c;读写套接字的文件描述符既用来发送…

观成科技:​加密C2框架Platypus流量分析

一、工具介绍 Platypus 是一款支持多会话的交互式反向 Shell 管理器。在实际的渗透测试中&#xff0c;为了解决 Netcat/Socat 等工具在文件传输、多会话管理方面的不足,该工具在多会话管理的基础上增加了在渗透测试中能更好发挥作用的功能&#xff08;如&#xff1a;交互式 Sh…

在 C# 中,is null 和 == null ‌不完全等价‌

最近遇到了一个看似奇怪的问题&#xff0c;判断一个对象是否为null&#xff0c;我使用了null来判断&#xff0c;结果他是null但是仍然进入了判断。 经过讨论和验证&#xff0c;发现使用is null 可以解决问题&#xff0c;于是查阅了资料。 在 C# 中&#xff0c;is null 和 nul…

go语言zero框架拉取内部平台开发的sdk报错的修复与实践

在开发过程中&#xff0c;我们可能会遇到由于认证问题无法拉取私有 SDK 的情况。这种情况常发生在使用 Go 语言以及 Zero 框架时&#xff0c;尤其是在连接到私有平台&#xff0c;如阿里云 Codeup 上托管的 Go SDK。如果你遇到这种错误&#xff0c;通常是因为 Go 没有适当的认证…

VBA+FreePic2Pdf 找出没有放入PDF组合的单个PDF工艺文件

设计部门针对某个项目做了一个工艺汇总报告&#xff0c;原先只要几十个工艺文件&#xff0c;组合成一个PDF&#xff0c;但后来要求要多放点PDF进去&#xff0c;但工艺文件都混在一起又不知道哪些是重复的&#xff0c;找上我让我帮忙处理一下&#xff0c;我开始建议让她重新再组…

Webservice如何调用

webservice调用方式&#xff1a; &#xff08;1&#xff09;http方式调用 请求头增加Content-type:text/xml 或application/soapxml SOAPAction:方法名 请求body以xml字符串传递&#xff0c;xml格式定义 返回以xml字符串返回&#xff0c;xml某个字段是一个json字符串。 入…

2025-03-10 吴恩达机器学习1——机器学习概述

文章目录 1 监督学习1.1 回归1.2 分类 2 无监督学习2.1 聚类2.2 异常检测2.3 降维 3 使用 Jupyter Notebook ​ 1959 年&#xff0c;Arthur Samuel 将机器学习定义如下&#xff1a; ​ Field of study that gives computers the ability to learn without being explicitly pro…

Python 进程与线程-分布式进程

目录 分布式进程 小结 分布式进程 在Thread和Process中&#xff0c;应当优选Process&#xff0c;因为Process更稳定&#xff0c;而且&#xff0c;Process可以分布到多台机器上&#xff0c;而Thread最多只能分布到同一台机器的多个CPU上。 Python的multiprocessing模块不但支…