第一次个人编程作业----论文查重

作业 GitHub 仓库地址

1. PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 60
· Estimate · 估计这个任务需要多少时间 30 60
Development 开发 430 560
· Analysis · 需求分析 (包括学习新技术) 90 120
· Design Spec · 生成设计文档 20 20
· Design Review · 设计复审 10 10
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 30 30
· Coding · 具体编码 180 240
· Code Review · 代码复审 30 40
· Test · 测试(自我测试,修改代码,提交修改) 60 90
Reporting 报告 180 130
· Test Report · 测试报告 75 70
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 75 30
合计 640 750

2. 计算模块接口的设计与实现过程

本项目采用函数式 + 模块化组织,围绕“字符 n-gram 表示 → 集/袋建模 → 多相似度量 → 可配置回退”的主线展开。入口为 main.py

代码组织与关系

  • src/normalizer.py
    • def normalize(text: str) -> str
    • 作用:统一大小写、去除多余空白/标点(具体规则以实现为准),为 n-gram 稳定化输入。
  • src/tokenizer.py
    • def char_ngrams(text: str, n: int = 3) -> List[str]
    • 作用:基于字符的 n-gram(默认 3-gram)
  • src/shingler.py
    • def as_set(it: Iterable[str]) -> Set[str]
    • def as_bag(it: Iterable[str]) -> Counter
    • 作用:将 n-gram 序列转换为集合(用于 Jaccard)或词袋 Counter(用于余弦)。
  • src/similarity.py
    • def jaccard(a: set, b: set) -> float
    • def cosine_tf(a: Counter, b: Counter) -> float
    • def levenshtein_ratio(s1: str, s2: str) -> float
    • 作用:三种相似度量
  • src/pipeline.py

调用关系(自顶向下)
main.pycompute_similaritynormalizechar_ngramsas_setjaccard → 得出结果[0,1]

流程图

截屏2025-09-21 23.56

3. 计算模块接口的性能改进

  • 使用 cProfile 对 CLI 主流程进行剖析,并基于 cumtime 绘制前 20 个热点函数的耗时图

top20_big
瓶颈分析

  • 文本预处理开销较大,正则替换操作消耗明显,所以优先对normalize进行优化。
    原始:
t = unicodedata.normalize("NFKC", text).casefold()
t = t.translate(_PUNCT_TABLE)
t = re.sub(r"\s+", "", t) # 使用正则删除空白

改进:

t = unicodedata.normalize("NFKC", text).casefold()
t = t.translate(_PUNCT_TABLE)
# 使用纯内建方法删除空白,避免正则开销
t = t.replace(" ", "").replace("\t", "").replace("\n", "").replace("\r", "")
  • 改进后结果

top20_opt

4. 计算模块部分单元测试展示

为了保证核心计算模块的正确性,本项目在 tests/ 目录下编写了单元测试,确保文本归一化、分词、相似度计算以及完整流程的正确性。

测试思路

  • normalizer:测试大小写归一化、标点清理、同义词替换。
  • tokenizer:验证 n-gram 切分结果是否符合预期。
  • similarity:针对 Jaccard、Cosine、Levenshtein 相似度构造边界用例(完全相同/完全不同/部分重叠)。
  • pipeline:使用短文本模拟完整流程,检查最终重复率是否合理。

测试函数

def test_normalize_basic():
def test_char_ngrams_short_and_empty():
def test_similarity_jaccard_overlap():
def test_cosine_tf_bag():
def test_levenshtein_ratio_known_case():
def test_pipeline_chinese_example():
def test_pipeline_empty_files():
def test_pipeline_english_texts():
def test_synonyms_extension_improves_similarity():
def test_edit_fallback_extension():
def test_cli_behavior(tmp_path: Path):
def test_pipeline_cosine_branch():
def test_io_read_text_missing_file(tmp_path):
def test_io_read_text_gb18030_fallback(tmp_path):
  • 覆盖率

截屏2025-09-22 13.13

5. 计算模块部分异常处理说明

5.1 文件与输入异常

  • 文件不存在:在 io_utils.py 中对文件读写进行 try/except 封装,遇到路径错误时会抛出 FileNotFoundError,并给出提示信息。
  • 空文件输入:如果输入文本为空,归一化与分词阶段会返回空集合,最终相似度计算统一返回 0.0,避免出现除零错误。

5.2 文本归一化异常

  • 非字符串输入:在 normalizer.py 中增加类型检查,若传入 None 或非字符串对象,直接返回空字符串,保证后续流程不会中断。
  • 同义词替换缺失:若 synonyms.txt 中不存在对应词汇,则保持原词不变,不影响流程。

5.3 分词与 n-gram 生成异常

  • 文本过短:当文本长度小于设定的 n 值(如 n=3)时,tokenizer.py 会返回一个空列表,后续的相似度计算模块能够自然处理空输入。

5.4 相似度计算异常

  • 集合运算异常jaccardcosine_tf 在集合或字典为空时,均会返回 0.0,而不是报错。

5.5 流程级异常

  • pipeline.py 的主流程中使用 try/except 包装:
    • 遇到不可恢复的异常时,打印错误信息并安全退出;
    • 遇到可恢复的边界情况(如空输入文件),则返回重复率 0.0 并继续执行。

实际花费时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 60
· Estimate · 估计这个任务需要多少时间 30 60
Development 开发 430 560
· Analysis · 需求分析 (包括学习新技术) 90 120
· Design Spec · 生成设计文档 20 20
· Design Review · 设计复审 10 10
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 30 30
· Coding · 具体编码 180 240
· Code Review · 代码复审 30 40
· Test · 测试(自我测试,修改代码,提交修改) 60 90
Reporting 报告 180 130
· Test Report · 测试报告 75 70
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 75 30
合计 640 750
posted @ 2025-09-22 13:39  Z2X  阅读(24)  评论(0)    收藏  举报