介绍
大家好,博主又来和大家分享自然语言处理领域的知识了。今天给大家分享的内容是自然语言处理中的文本聚类。
文本聚类在自然语言处理领域占据着重要地位,它能将大量无序的文本按照内容的相似性自动划分成不同的类别,极大地提高了文本处理和信息提取的效率。就好比在一个大型图书馆中,文本聚类能够像智能管理员一样,把各种书籍按照主题分类摆放,方便读者快速找到所需资料。
而实现文本聚类的方法有很多,其中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-8
with 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_list
def 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:
# 生成一个新的ID
index = len(self.label_to_id)
# 将类别标签和对应的ID添加到标签到ID的映射中
self.label_to_id[category] = index
# 将ID和对应的类别标签添加到ID到标签的映射中
self.id_to_label[index] = category
# 获取类别标签对应的ID
label_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 = {}
# 初始化总词频为0
total_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的映射中,并分配一个新的ID
self.token_to_id[token] = len(self.token_to_id)
# 将ID添加到ID到词元的映射中,并关联对应的词元
self.id_to_token[len(self.id_to_token)] = token
# 累加词元的频率到总词频中
total_frequency += freq
else:
# 跳出循环
break
# 打印最小词频、最小词长、最大词汇表大小、剩余词元数量和词表内词元占比
print(f'最小词频 = {min_frequency}, 最小词长 = {min_length}, 最大词表大小 = {max_vocab_size}, '
f'剩余词元数量 = {len(self.token_to_id)}, '
f'词表内词元占比 = {total_frequency / sum(word_frequency.values())}')
# 定义一个方法,用于将词元转换为对应的ID
def convert_tokens_to_indices(self):
# 遍历训练集和测试集数据
for data_group in [self.training_data, self.testing_data]:
# 遍历每个数据项
for data_item in data_group:
# 初始化一个空列表,用于存储词元对应的ID
data_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):
pass
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()
# 程序入口,当作为脚本直接运行时执行以下代码
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:
# 遍历数据项中唯一的词元ID
for 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):
# 遍历数据项中的词元ID
for 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):
pass
def text_clustering(self):
# 创建书籍数据集对象
book_dataset = BookDataCollection()
# 打印标签到ID的映射
print(book_dataset.id_to_label)
# 对文本进行分词处理
book_d
自然语言处理之文本聚类方法介绍

最低0.47元/天 解锁文章
1129

被折叠的 条评论
为什么被折叠?



