第一次个人编程作业

论文查重项目报告

一、作业基本信息

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477
这个作业的目标 实现一个论文查重程序,并且熟悉项目开发的流程
github仓库链接 https://github.com/yeah-a/3123004804

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 70 75
· Estimate · 估计这个任务需要多少时间 70 75
Development 开发 500 540
· Analysis · 需求分析 (包括学习新技术) 55 60
· Design Spec · 生成设计文档 45 50
· Design Review · 设计复审 35 38
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 25 28
· Design · 具体设计 65 70
· Coding · 具体编码 140 150
· Code Review · 代码复审 45 48
· Test · 测试(自我测试,修改代码,提交修改) 90 96
Reporting 报告 130 145
· Test Report · 测试报告 50 55
· Size Measurement · 计算工作量 30 33
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 50 57
合计 - 700 760

三、需求分析

1 功能需求

核心功能:计算两篇论文(原文与抄袭版)的重复率,输出结果精确到小数点后两位。
输入输出:通过命令行参数接收3个文件路径(原文绝对路径、抄袭版绝对路径、结果输出绝对路径),程序读取输入文件内容,计算后将结果写入输出文件。
算法要求:需设计文本相似度计算算法,能处理中文文本的增删改(如示例中“星期天”改为“周天”、“天气晴”改为“天气晴朗”等场景)。

2 非功能需求

性能约束:单个测试点需在5秒内完成计算,内存占用≤2048MB,无严重内存泄漏。
安全性:禁止连接网络、读写非指定文件、执行系统命令(如shutdown)等危险操作。
鲁棒性:需处理文件不存在、空文件、格式错误等异常,避免程序崩溃或异常退出。
兼容性:Python程序入口为main.py,需通过命令行参数(python main.py [原文] [抄袭版] [结果])运行。

3 测试标准

至少10个测试点,覆盖不同文本修改场景(增、删、改、同义词替换等)。
程序需可正常编译/运行,逻辑与提交代码一致。

四、计算模块接口的设计与实现流程

1 需求细化

1.1 输入输出规范
命令行参数:3个绝对路径,格式为python main.py [原文路径] [抄袭版路径] [结果路径],例如:python src/main.py C:\test\orig.txt C:\test\orig_add.txt C:\test\ans.txt。
文件读写:支持.txt格式,编码为utf-8,处理空文件、超大文件(≤1GB)。
结果精度:输出浮点型,保留两位小数(如0.85而非0.850)。

1.2 核心算法设计
流程:文本预处理→分词→TF-IDF向量化→余弦相似度计算。
预处理步骤:

  • 去除标点符号(正则表达式:re.sub(r'[^\w\s\u4e00-\u9fa5]', '', text));
  • 分词(使用jieba.lcut,精确模式);
  • 去停用词(自定义停用词表,包含“的”“是”等通用词及领域无关词)。

2 模块设计与关键函数流程图

2.1 模块划分

模块文件 核心函数/类 功能描述
file_utils.py read_file(path) 读取文件内容,处理编码错误
write_result(path, score) 写入结果到文件,确保两位小数
preprocess.py clean_text(text) 去除标点、特殊字符
segment_text(text) 分词并过滤停用词
preprocess(text) 整合清洗+分词,返回处理后文本
similarity.py tfidf_vectorize(texts) 生成TF-IDF向量
cosine_similarity(vec1, vec2) 计算两向量余弦相似度
main.py main() 解析命令行参数,串联模块执行流程

3 模块接口实现步骤

步骤1:文件处理模块(file_utils.py)
核心需求:安全读取文本、精确写入结果,处理文件异常(不存在、权限不足、编码错误)。

  • read_file(path)实现:
    检查文件是否存在(os.path.exists),不存在则抛出FileNotFoundError;
    检查是否为文件(非目录),否则抛出IsADirectoryError;
    尝试以utf-8编码读取,失败则捕获UnicodeDecodeError(提示“文件编码需为utf-8”);
    返回读取的文本内容(空文件返回空字符串)。
  • write_result(path, score)实现:
    格式化得分(f"{score:.2f}"),确保两位小数(如0.8→0.80);
    尝试写入文件,捕获PermissionError(提示“无写入权限”);
    无返回值,直接写入结果到指定路径。

步骤2:文本预处理模块(preprocess.py)
核心需求:将原始文本转换为可用于特征提取的“干净”token序列,适配中文场景。

  • clean_text(text)实现:
    若输入文本为空,直接返回空字符串;
    用正则表达式r'[^\u4e00-\u9fa5a-zA-Z0-9\s]'去除所有非中文字符、字母、数字及空格;
    用re.sub(r'\s+', ' ', text)合并连续空格为单个空格,返回清洗后文本。
  • segment_text(text)实现:
    加载自定义停用词表(从样例数据统计论文领域高频无意义词,如“摘要”“关键词”“引言”);
    调用jieba.lcut(text, cut_all=False)(精确模式)分词,返回词语列表;
    过滤列表中的停用词和空字符串(如“的”“是”),返回核心token列表。
  • preprocess(text)实现:
    串联调用clean_text(text)→segment_text(text);
    将token列表用空格连接为字符串(如["今天", "天气"]→"今天 天气"),适配TF-IDF向量器输入格式。

步骤3:相似度计算模块(similarity.py)
核心需求:将预处理后的文本转换为数学向量,计算相似度得分。

  • tfidf_vectorize(texts)实现:
    输入为列表[orig_token_str, copy_token_str](原文与抄袭版的token字符串);
    初始化TfidfVectorizer(max_features=10000, min_df=1):限制最大特征数为10000(保留高频词),过滤出现次数<1的低频词;
    调用fit_transform(texts)生成TF-IDF向量矩阵(2行10000列,每行对应一篇文本的向量)。
  • calculate_similarity(orig_token_str, copy_token_str)实现:
    若两篇文本预处理后均为空(如均为标点符号),返回1.0(完全重复);
    若一篇为空,返回0.0(无重复);
    调用tfidf_vectorize([orig_token_str, copy_token_str])生成向量矩阵;
    计算向量矩阵的余弦相似度(调用sklearn.metrics.pairwise.cosine_similarity),返回0~1之间的得分。

步骤4:主流程模块(main.py)
核心需求:解析命令行参数,串联各模块执行流程,处理异常并输出结果。

  • main()实现:
    解析命令行参数(sys.argv),检查参数数量是否为4(程序名+3个文件路径),否则抛出参数错误异常;
    调用file_utils.read_file(orig_path)和read_file(copy_path)读取原文与抄袭版文本;
    调用preprocess.preprocess()分别预处理两篇文本,得到token字符串;
    调用similarity.calculate_similarity()计算得分;
    调用file_utils.write_result(result_path, score)写入结果;
    捕获所有异常(如文件不存在、编码错误),输出友好提示并退出(sys.exit(1))。

五、函数关系与调用流程

1 各模块通过函数输入输出实现接口交互,核心调用流程如下:

模块内函数协作:

  • preprocess.py:preprocess(text)依赖clean_text(text)和segment_text(text),形成“清洗→分词→过滤”的流水线;
  • similarity.py:calculate_similarity()依赖tfidf_vectorize(texts),先向量化再计算相似度;
  • main.py:作为入口,仅依赖其他模块的顶层函数(如read_file而非clean_text),降低耦合。

2 关键函数流程图

2.1 preprocess(text)函数流程
image

2.2 main()函数流程
image

3 算法关键与独到之处

3.1 TF-IDF+余弦相似度原理
TF-IDF:词频(TF)× 逆文档频率(IDF),降低通用词权重(如“今天”),提升稀有词权重(如“电影”)。
余弦相似度:通过向量夹角余弦值衡量相似度,公式:

3.2 独到优化
使用哈工大停用词表:结合论文领域特点,使用最常用的停用词表;
分块处理超大文件:对>100MB文件,按段落分块读取预处理,避免一次性加载占用内存>2GB;
分词缓存:对测试用例中重复的原文,缓存分词结果(使用functools.lru_cache),减少重复计算。

六、计算模块接口部分的性能改进

1 工具准备与环境配置

工具用途 工具名称 安装命令 特点
函数耗时分析 cProfile Python自带 精确统计函数调用耗时、调用次数
内存占用分析 memory-profiler pip install memory-profiler 逐行分析内存使用,定位内存泄漏
可视化耗时火焰图 py-spy pip install py-spy 生成SVG火焰图,直观展示函数调用链耗时

2 第一步:用 cProfile 定位耗时瓶颈(函数级分析)

核心目标:找出程序中 耗时最长的函数,替代手动计时的粗糙分析。
2.1 基础使用:生成性能报告
操作步骤:

  • 在命令行中运行程序,并使用 cProfile 分析:
    image
  • 打开 profile_report.txt,重点关注 前5行函数(累计耗时最长):
    image
    累计耗时最高的用户自定义函数是preprocess.py:34(segment_text)(0.547秒),其核心耗时来自调用jieba.lcut进行分词。因此,当前程序的性能瓶颈是文本预处理阶段的分词步骤,具体表现为用户自定义的segment_text函数耗时过高。

3 第二步:segment_text函数拆解与计时逻辑添加

目标:通过手动添加时间戳,分离“分词(jieba.lcut)”和“停用词过滤”的耗时占比。

  • 步骤1:修改preprocess.py,添加内部计时
    在segment_text函数中,对“分词”和“过滤停用词”两个核心步骤分别计时,代码如下:
    image
  • 步骤2:运行程序,收集耗时数据
    在命令行中运行程序,得到结果:
    image
  • 第一次调用(含jieba初始化开销)
    总耗时:0.4696s
    分词耗时:0.4696s(占比100%)
    过滤耗时:0.0000s(占比0%)
    关键说明:首次调用时,jieba需加载词典和模型(控制台输出Building prefix dict... Loading model cost 0.440 seconds),初始化耗时(0.440s)被计入分词耗时,导致分词占比100%,属于一次性启动开销,非常态性能瓶颈。
  • 第二次调用(常态情况,无初始化开销)
    总耗时:0.0493s
    分词耗时:0.0483s(占比97.92%)
    过滤耗时:0.0010s(占比2.08%)
    关键说明:模型已加载到内存,分词耗时为纯算法耗时(无初始化),分词占比近98%,过滤占比极低(2.08%),是常态下的真实耗时分布。

4 第三步:基于分词步骤的优化方案

目标:通过加载论文领域自定义词典,减少jieba.lcut对专业术语的拆分歧义(如避免“余弦相似度”拆分为“余弦/相似度”),降低分词算法(DAG图构建、动态规划)的计算量,从而优化segment_text函数的核心瓶颈——分词步骤耗时,同时提升论文场景分词精度。

  • 步骤1:创建论文领域自定义词典文件
    在程序根目录下新建文本文件,命名为paper_dict.txt,按“词语 词频 词性”格式(词频和词性可选,核心为“词语”)写入论文高频术语,覆盖查重场景常见词汇。词典内容示例:
  • 步骤2:修改preprocess.py,加载自定义词典
    在preprocess.py中,于jieba.lcut调用前(模块加载阶段)添加加载自定义词典的代码,确保分词时优先使用论文术语。
    在命令行中运行程序,得到结果:
    image
  • 分词耗时与总耗时优化效果
    分词耗时下降8.28%:
    优化前常态分词耗时0.0483s(第二次调用,无初始化),优化后降至0.0443s,减少0.004s。核心原因是加载自定义词典后,论文术语(如“论文查重”“余弦相似度”)被正确分词,减少了jieba.lcut底层DAG图构建和动态规划的计算量(避免术语拆分歧义导致的重复计算)。
    总耗时下降8.11%:
    优化前总耗时0.0493s,优化后降至0.0453s,减少0.004s,与分词耗时下降幅度基本一致,说明总耗时优化完全来自分词步骤的提升,验证了“分词是核心瓶颈”的历史判断。
  • 分词耗时占比稳定,过滤耗时可忽略
    分词占比维持高位(97.78%):
    优化后分词耗时占比仅比优化前(97.92%)下降0.14%,说明分词仍是segment_text函数的绝对核心耗时步骤,过滤耗时占比(2.22%)极低且稳定,进一步验证了“停用词过滤不是性能瓶颈”的结论。
    过滤耗时稳定(0.0010s):
    两次调用过滤耗时均为0.0010s,说明STOPWORDS集合查找效率(O(1))未受优化影响,保持高效。
  • 首次调用耗时异常降低的原因
    现象:优化后首次调用总耗时0.0291s(分词耗时0.0281s),显著低于第二次调用(0.0453s)。
    解释:结合优化目标(“预加载jieba模型,分离初始化开销”),首次调用前的“Loading model cost 0.471 seconds”为模型初始化耗时(一次性),已被剥离到程序启动阶段,因此首次segment_text耗时为纯分词耗时,且可能因orig.txt文本长度短于orig_0.8_add.txt(第二次调用文件),导致分词耗时更低,符合“初始化开销不影响业务逻辑”的优化目标。

5 综合以上改进,我们得到最终的性能分析图如下:

image
image

七、计算模块部分单元测试展示

1 测试目标

基于测试方法,覆盖核心模块(preprocess.py、similarity.py)的所有逻辑分支与边界条件,验证文本清洗、分词、相似度计算的正确性,确保程序在各种输入下的健壮性。

2 测试用例设计

image
image
image
image

3 最终覆盖率如下

image

八、计算模块异常处理说明

1 preprocess.py 中自定义词典加载异常

当论文领域自定义词典 paper_dict.txt 不存在时,程序不崩溃,自动降级使用jieba默认词典,并通过警告提示用户,确保分词功能基础可用。
image

2 preprocess.py 中停用词表加载异常

当停用词表 stopwords.txt 不存在时,使用内置默认停用词集合,避免因文件缺失导致分词时无法过滤通用无意义词汇。
image

3 similarity.py 中空文本输入的逻辑处理

通过逻辑判断处理空文本边界情况,避免因输入文本为空导致TF-IDF向量化失败。
image

九、最终查重结果及部分说明

image

存放地点:
image

项目目录说明:
3123004804/ # 项目根目录
├─ docs/ # 文档目录
├─ result/ # 结果输出目录(存放各类生成结果文件)
│ ├─ ans_add.txt # 查重结果文件
│ ├─ ans_del.txt # 查重结果文件
│ ├─ ans_dis_1.txt # 查重结果文件
│ ├─ ans_dis_10.txt # 查重结果文件
│ ├─ ans_dis_15.txt # 查重结果文件
│ └─ profile_report.txt # 性能概要报告文件
├─ src/ # 源代码目录
│ ├─ file_utils.py # 文件工具模块(处理文件读写、路径等操作)
│ ├─ preprocess.py # 数据预处理模块(对数据进行清洗、转换等预处理操作)
│ └─ similarity.py # 相似度计算模块(用于计算数据间的相似度)
├─ tests/ # 测试目录
│ ├─ orig.txt # 原始测试数据文件
│ ├─ orig_0.8_add.txt #
│ ├─ orig_0.8_del.txt #
│ ├─ orig_0.8_dis_1.txt #
│ ├─ orig_0.8_dis_10.txt #
│ ├─ orig_0.8_dis_15.txt #
│ ├─ test_calculate.py # 计算相关测试
│ └─ .coverage # 测试覆盖率文件(记录代码测试覆盖情况)
├─ main.py # 项目主程序入口文件
├─ paper_dict.txt # 论文相关字典/术语文件
└─ requirements.txt # 项目依赖包列表文件(记录所需Python库及版本)

posted @ 2025-09-23 00:20  再也不熬夜通肖  阅读(50)  评论(0)    收藏  举报