基于N-gram模型的中文文本分析系统设计与实现

news2025/3/12 19:14:26

前言

          在数字化人文研究快速发展的背景下,中文古典文本的量化分析面临着独特的挑战。古典文献中繁简异体字共存、语义单元边界模糊、意象隐喻密集等特征,使得传统的词频统计方法难以准确捕捉其深层语言规律。现有文本分析工具多面向现代汉语设计,在古汉语处理中存在分词粒度失准停用词表适配性差未登录词频发等问题。针对这一研究缺口,本文提出了一种融合N-gram模型与动态可视化技术的中文文本分析系统。

        本系统创新性地构建了三级处理架构:通过正则表达式组合拳实现多级文本清洗,采用滑动窗口算法动态生成n-gram序列,并基于马尔可夫假设构建概率模型。特别设计了可扩展的平滑算法接口,为后续集成Kneser-Ney等高级平滑技术预留空间。在宋词语料上的实验表明,系统不仅能够有效识别"东风"(157次)、"何处"(89次)等关键双字意象,还可通过三字词频分布揭示婉约派"无人会"(24次)、豪放派"千古事"(18次)等流派的用词特征。相较于传统方法,本方案在保持O(L)线性时间复杂度同时,通过内存映射技术实现了对GB级语料的高效处理。

本项目源代码以及训练预料均已上传,有需要的朋友可以点击

基于N-gram模型的中文文本分析系统设计与实现
 

一、模型方法

        本工程主要的用到了N-gram模型。这是一种基于概率统计的模型,它用于自然语言处理(NLP)中的语言模型。

        N-gram模型通过考虑一个固定长度的上下文(即前n-1个词)来预测下一个词。

2.1 N-gram模型基本概念

        •(Token):语言的最小单元,可以是单词、字符或者子词。

        •N-gram:一个由N个连续词组成的序列。例如下面的例子:

        当N=1时,称为unigram(一元组),如“我”。

        当N=2时,称为bigram(二元组),如“我爱”。

        当N=3时,称为trigram(三元组),如“我爱你”。

2.2 工作原理

        N-gram模型基于一个简单的假设:一个词出现的概率只与它前面的n-1个词相关,这个假设称为马尔可夫假设。具体来说:

        对于unigram模型(一元),假设每个词的出现概率是独立的。

        对于bigram模型(二元),假设当前词的出现概率只依赖于它前面的一个词。

        对于trigram模型(三元),假设当前词的出现概率只依赖于它前面的两个词。

2.3 计算概率

        设文本序列为W=w_1+w_2+...w_n,则N-gram概率可表示为:

                                        P(w_n|w_{1}^{n-1})\approx P(w_n|w_{n-N+1}^{n-1})

        采用最大似然估计(MLE)进行参数学习:

        Unigram(1-gram)

                        P(w_i)=\frac{C(w_i)}{T}   (T为总次数,C(•)是计数函数,表示某个n-gram在语料库中出现的次数,下同)

        Bigram(2-gram)

                        P(w_i|w_{i-1})=\frac{C(w_{i-1}w_i)}{C(w_{i-1})}

        Trigram(3-gram)

                        P(w_i|w_{i-2}w_{i-1})=\frac{C(w_{i-2}w_{i-1}w_{i})}{C(w_{i-2}w_{i-1})}

示例:在语料库"东风夜放花千树"中:

  • P(东) = 1/7

  • P(风|东) = 1/1

  • P(夜|风) = 1/1

2.4 处理未知词汇

在实际的训练中,可能会遇到在训练数据中没有出现过的n-gram。为了解决这个问题,通常会采用以下技术:

拉普拉斯平滑(Laplace Smoothing):也称为加一平滑,通过给每个n-gram增加一个计数来避免概率为零的问题。

        平滑的基本思想是给语料库中未出现的词组合赋予一个小的概率值,而不是零概率,这样可以避免计算联合概率时出现零概率的情况。

拉普拉斯平滑(也称作Laplace平滑)是最简单的平滑方法,它通过在所有可能的词组合计数上加一来实现,对于bigram模型来说,加一平滑的条件概率计算公式为:

                        P_{laplace}(w_i|w_{i-1})=\frac{C(w_{i-1,w_i})+1}{C(w_{i-1})+V}

其中:

        C(w_{i-1,w_i}) 表示在语料库中词 w_{i-1} 和词 w_i 连续出现的次数。

        C(w_{i-1}) 表示词w_{i-1}在语料库中出现的次数。

        V 是语料库中不同词的综素,即词汇表的大小。


线性插值Linear Interpolation):结合不同阶数的n-gram模型,通过线性组合来估计概率。

假设我们有一元语法(unigram)、二元语法(bigram)和三元语法(trigram)模型,线性插值通过线性组合这三种模型来估计概率。设\lambda _{1}\lambda _{2}\lambda _{3}为权重,且\lambda _{1}+\lambda _{2}+\lambda _{3}=1

二、系统设计

        本系统的整体设计流程图如下:

系统设计流程

具体流程如下:

        1.下载语料库(ci.txt和新闻语料库)到特定目录下

        2.根据文本编码,加载语料库文本

        3.处理语料:

        (1)读取输入文件:使用Python的open函数以读模式打开原始宋词文本文件。

        (2)移除空白行:逐行读取文本,使用strip()方法检查并移除空白行,将非空白行写入新文件。

        (3)移除标点符号:定义正则表达式匹配标点符号,使用re.sub()方法移除文本中的标点符号。

    def __init__(self):
        # 定义标点符号和停用词
        self.PUNCTUATION_PATTERN = re.compile(
            r'[\s,。!?、::""\'()□《》\n|()<>,.,⒄{}?\\/$$$$【】;~`:]'
        )
        self._load_stopwords()

    def _load_stopwords(self, stopwords_file: str = 'stopwords.txt') -> None:
        """
        加载停用词表

        Args:
            stopwords_file: 停用词文件路径
        """
        try:
            with open(stopwords_file, 'r', encoding='utf-8') as f:
                self.stopwords = set([line.strip() for line in f])
        except FileNotFoundError:
            logger.warning("Stopwords file not found. Using empty stopwords list.")
            self.stopwords = set()

    def preprocess_text(self, text: str) -> str:
        """
        文本预处理

        Args:
            text: 输入文本

        Returns:
            处理后的文本
        """
        # 转换为小写
        text = text.lower()
        # 移除标点符号
        text = re.sub(self.PUNCTUATION_PATTERN, '', text)
        # 移除停用词
        words = [word for word in jieba.cut(text) if word not in self.stopwords]
        return ''.join(words)

    def remove_blank_lines(self, input_file: str, output_file: str) -> None:
        """
        移除文件中的空白行

        Args:
            input_file: 输入文件路径
            output_file: 输出文件路径
        """
        try:
            input_path = Path(input_file)
            output_path = Path(output_file)

            with input_path.open('r', encoding='utf-8') as infile, \
                    output_path.open('w', encoding='utf-8') as outfile:
                for line in infile:
                    if line.strip():
                        outfile.write(line)
            logger.info(f"Successfully removed blank lines from {input_file}")
        except Exception as e:
            logger.error(f"Error processing file: {str(e)}")
            raise

   4.分别统计n-gram(n=1,2,3)的词频,存储到相应的数据结构,该数据结构包括词(词本身)和词的频度(出现次数)

 def count_ngram_frequency(self, text: str, n: int) -> Counter:
        """
        统计n-gram频率

        Args:
            text: 输入文本
            n: n-gram的n值

        Returns:
            Counter对象,包含n-gram频率
        """
        if n < 1:
            raise ValueError("n must be greater than 0")

        ngrams = [text[i:i + n] for i in range(len(text) - n + 1)]
        return Counter(ngrams)

    def analyze_text(self, text: str, n_range: List[int]) -> Dict[int, Counter]:
        """
        分析文本,计算多个n值的n-gram频率

        Args:
            text: 输入文本
            n_range: 需要分析的n值列表

        Returns:
            包含各n值对应频率的字典
        """
        results = {}
        for n in n_range:
            results[n] = self.count_ngram_frequency(text, n)
        return results

5.将内存中的数据结构存储到文本中(.CSV形式)并且可视化top前20的词频图,方便后面随时加载。

    def save_results(self, results: Dict[int, Counter], output_dir: str = 'results') -> None:
        """
        保存分析结果

        Args:
            results: 分析结果字典
            output_dir: 输出目录
        """
        # 确保中文显示正确
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

        output_path = Path(output_dir)
        output_path.mkdir(exist_ok=True)

        for n, frequency in results.items():
            # 保存为CSV
            df = pd.DataFrame.from_dict(frequency, orient='index',
                                        columns=['frequency'])
            df.index.name = f'{n}-gram'
            df.sort_values('frequency', ascending=False, inplace=True)

            csv_path = output_path / f'ngram_{n}.csv'
            df.to_csv(csv_path, encoding='utf-8')

            # 创建图形
            plt.figure(figsize=(15, 8))

            # 获取前20个项目
            top_20 = df.head(20)

            # 准备数据
            x = np.arange(len(top_20))
            values = top_20['frequency'].values
            labels = top_20.index.tolist()

            # 创建条形图
            bars = plt.bar(x, values, color='skyblue', alpha=0.8)

            # 设置x轴标签
            plt.xticks(x, labels, rotation=45, ha='right')

            # 在柱子上添加数值标签
            for i, v in enumerate(values):
                plt.text(i, v, f'{int(v):,}',
                         ha='center', va='bottom')

            # 设置标题和标签
            plt.title(f'Top 20 {n}-gram 频率分布', fontsize=14, pad=20)
            plt.xlabel(f'{n}-gram', fontsize=12, labelpad=10)
            plt.ylabel('频率', fontsize=12, labelpad=10)

            # 设置网格
            plt.grid(axis='y', linestyle='--', alpha=0.3)

            # 调整布局
            plt.tight_layout()

            # 保存图片
            plot_path = output_path / f'ngram_{n}_plot.png'
            plt.savefig(plot_path, dpi=300, bbox_inches='tight')
            plt.close()

        logger.info(f"Results saved to {output_dir}")

三、系统演示与分析

3.1 ngram_1
ngram_1CSV文件top前20词汇
3.2 ngram_2
ngram_2CSV文件top前20词汇
3.3 ngram_3
ngram_3CSV文件top前20词汇

3.4 语言学发现

        高频单字"风"、"春"反映宋词的自然意象偏好

        Bigram"东风"的高频出现印证了宋代诗词的季节隐喻传统

        Trigram"无人会"的分布体现婉约派的抒情特征

四、性能优化与拓展

4.1 内存管理策略

采用分块处理机制应对大文本:

def chunked_processing(file_path, chunk_size=1024*1024):
    with open(file_path) as f:
        while chunk := f.read(chunk_size):
            yield process(chunk)
4.2 分布式计算扩展

基于Ray框架实现并行统计:

@ray.remote
def distributed_count(chunk, n):
    return Counter([chunk[i:i+n] for i in range(len(chunk)-n+1)]

results = ray.get([distributed_count.remote(chunk, 2) for chunk in chunks])
final_count = sum(results, Counter())

五、不足与展望

5.1 现存挑战

        未登录词处理:需集成Kneser-Ney等先进平滑算法

        分词粒度:古典诗词中的专名识别准确率待提升

        语义关联:当前模型未捕捉跨n-gram的语义关系

5.2 应用前景

        结合LSTM构建混合语言模型

        扩展诗歌风格分类功能

        开发基于n-gram的自动对联生成系统

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础购买阿里云服务器,XShell连接云服务器

目录 1.环境搭建方式 2. 使用云服务器 3.使用终端软件登录到Linux 4.使用XShell登录主机 5.连接失败的原因&#xff1a; 下一篇更新&#xff1a;Linux的基础指令以及如何Linux的环境搭建 1.环境搭建方式 主要有四种: 1.直接安装在物理机上&#xff0c;虽然Linux有图形化…

CNN手写数字识别1——模型搭建与数据准备

模型搭建 我们这次使用LeNet模型&#xff0c;LeNet是一个经典的卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;架构&#xff0c;最初由Yann LeCun等人在1998年提出&#xff0c;用于手写数字识别任务 创建一个文件model.py。实现以下代码。 源码 #…

深度学习04 数据增强、调整学习率

目录 数据增强 常用的数据增强方法 调整学习率 学习率 调整学习率 ​调整学习率的方法 有序调整 等间隔调整 多间隔调整 指数衰减 余弦退火 ​自适应调整 自定义调整 数据增强 数据增强是通过对训练数据进行各种变换&#xff08;如旋转、翻转、裁剪等&#xff09;&am…

PH热榜 | 2025-02-16

1. Cal.com Routing 标语&#xff1a;根据客户线索&#xff0c;系统会智能地自动安排约会。 介绍&#xff1a;告别繁琐的排期&#xff01;Cal.com 推出了新的路由功能&#xff0c;能更智能地分配预约&#xff0c;让你的日程安排更顺畅。这项功能运用智能逻辑和深入的数据分析…

数据库基本概念及基本使用

数据库基本概念 什么是数据库&#xff1a; 数据库特点&#xff1a; 常见的数据库软件&#xff1a; 不同的公司进行不同的实践&#xff0c;生成了不同的产品。 比如买汽车&#xff0c;汽车只是一个概念&#xff0c;你要买哪个牌子哪个型号的汽车&#xff0c;才是真正的汽车的一…

gozero实现数据库MySQL单例模式连接

在 GoZero 框架中实现数据库的单例连接可以通过以下步骤来完成。GoZero 使用 gorm 作为默认的数据库操作框架&#xff0c;接下来我会展示一个简单的单例模式实现。 ### 1. 定义数据库连接的单例结构 首先&#xff0c;你需要定义一个数据库连接的结构体&#xff0c;并在初始化…

CSS flex布局 列表单个元素点击 本行下插入详情独占一行

技术栈&#xff1a;Vue2 javaScript 简介 在实际开发过程中有遇到一个场景&#xff1a;一个list&#xff0c;每行个数固定&#xff0c;点击单个元素后&#xff0c;在当前行与下一行之间插入一行元素详情&#xff0c;便于更直观的查看到对应的数据详情。 这种情形&#xff0c…

无人机航迹规划: 梦境优化算法(Dream Optimization Algorithm,DOA)求解无人机路径规划MATLAB

一、梦境优化算法 梦境优化算法&#xff08;Dream Optimization Algorithm&#xff0c;DOA&#xff09;是一种新型的元启发式算法&#xff0c;其灵感来源于人类的梦境行为。该算法结合了基础记忆策略、遗忘和补充策略以及梦境共享策略&#xff0c;通过模拟人类梦境中的部分记忆…

权限五张表

重点&#xff1a;权限五张表的设计 核心概念&#xff1a; 在权限管理系统中&#xff0c;经典的设计通常涉及五张表&#xff0c;分别是用户表、角色表、权限表、用户角色表和角色权限表。这五张表的设计可以有效地管理用户的权限&#xff0c;确保系统的安全性和灵活性。 用户&…

Docker-数据卷

1.数据卷 容器是隔离环境&#xff0c;容器内程序的文件、配置、运行时产生的容器都在容器内部&#xff0c;我们要读写容器内的文件非常不方便。大家思考几个问题&#xff1a; 如果要升级MySQL版本&#xff0c;需要销毁旧容器&#xff0c;那么数据岂不是跟着被销毁了&#xff1…

IT : 是工作還是嗜好? Delphi 30周年快乐!

又到2月14日了, 自从30多年前收到台湾宝蓝(Borland)公司一大包的3.5 磁盘片, 上面用黑色油性笔写着Delphi Beta开始, Delphi便和我的工作生涯有了密不可分的关系. 一年后Delphi大获成功, 自此对于使用Delphi的使用者来说2月14日也成了一个特殊的日子! 我清楚记得Delphi Beta使用…

DeepPose

目录 摘要 Abstract DeepPose 算法框架 损失函数 创新点 局限性 训练过程 代码 总结 摘要 DeepPose是首个将CNN应用于姿态估计任务的模型。该模型在传统姿态估计方法的基础上&#xff0c;通过端到端的方式直接从图像中回归出人体关键点的二维坐标&#xff0c;避免了…

[HarmonyOS]鸿蒙(添加服务卡片)推荐商品 修改卡片UI(内容)

什么是服务卡片 &#xff1f; 鸿蒙系统中的服务卡片&#xff08;Service Card&#xff09;就是一种轻量级的应用展示形式&#xff0c;它可以让用户在不打开完整应用的情况下&#xff0c;快速访问应用内的特定功能或信息。以下是服务卡片的几个关键点&#xff1a; 轻量级&#…

DeepSeek R1 本地部署和知识库搭建

一、本地部署 DeepSeek-R1&#xff0c;是幻方量化旗下AI公司深度求索&#xff08;DeepSeek&#xff09;研发的推理模型 。DeepSeek-R1采用强化学习进行后训练&#xff0c;旨在提升推理能力&#xff0c;尤其擅长数学、代码和自然语言推理等复杂任务 。 使用DeepSeek R1, 可以大大…

领域驱动设计叕创新,平安保险申请DDD专利

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 见下图&#xff1a; 这个名字拼得妙&#xff1a;领域驱动设计模式。 是领域驱动设计&#xff1f;还是设计模式&#xff1f;还是领域驱动设计设计模式&#xff1f;和下面这个知乎文章的…

团体程序设计天梯赛-练习集——L1-041 寻找250

前言 10分的题&#xff0c;主要的想法就一个&#xff0c;按这个想法可以出几个写法 L1-041 寻找250 对方不想和你说话&#xff0c;并向你扔了一串数…… 而你必须从这一串数字中找到“250”这个高大上的感人数字。 输入格式&#xff1a; 输入在一行中给出不知道多少个绝对值…

C#控制台大小Console.SetWindowSize函数失效解决

在使用C#修改控制台大小相关API会失效. 由于VS将控制台由命令提示符变成了终端&#xff0c;因此在设置大小时会出现问题 测试代码&#xff1a; Console.SetWindowSize(100, 50);

spring boot 对接aws 的S3 服务,实现上传和查询

1.aws S3介绍 AWS S3&#xff08;Amazon Simple Storage Service&#xff09;是亚马逊提供的一种对象存储服务&#xff0c;旨在提供可扩展、高可用性和安全的数据存储解决方案。以下是AWS S3的一些主要特点和功能&#xff1a; 1.1. 对象存储 对象存储模型&#xff1a;S3使用…

25/2/16 <算法笔记> DirectPose

DirectPose 是一种直接从图像中预测物体的 6DoF&#xff08;位姿&#xff1a;6 Degrees of Freedom&#xff09;姿态 的方法&#xff0c;包括平移和平面旋转。它在目标检测、机器人视觉、增强现实&#xff08;AR&#xff09;和自动驾驶等领域中具有广泛应用。相比于传统的位姿估…

数据结构-8.Java. 七大排序算法(下篇)

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 下篇主要实现最后一种排序算法: 归并排序。同时把中篇剩下的快排非递归实现补上. 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是…