自然语言处理:文本聚类

介绍

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

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

而实现文本聚类的方法有很多,其中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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老赵爱学习

您的鼓励是我创作的最大动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值