个人项目——论文查重

news/2024/11/17 14:43:12/文章来源:https://www.cnblogs.com/KLkeLin/p/18414776
这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229
这个作业的目标 个人项目——论文查重

一:我的github仓库地址

https://github.com/kelin-KL/kelin-KL

二:PSP表格

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

三:模块设计及其接口文档

基本功能及其实现方法

该代码实现了一个基于文本相似度的查重功能,专门用于比较两篇中文文本的相似度,并将相似度结果输出到指定文件中。核心的实现思路是利用文本分词和余弦相似度算法来评估文本间的相似性。

代码组成

  • init:类的构造函数。用于初始化类变量,包括保存原始文本和待查重文本的内容及其分词结果。初始化后,类的实例会拥有两个主要属性:original_text 和 compare_text,以及它们的关键词列表 original_list 和 compare_list。

  • read_file:该函数负责读取文本文件的内容。具体而言,它从指定的文件路径读取原始文本和待查重文本,并将其分别存储在 original_text 和 compare_text 中。读取完成后,文本内容会保存在类的实例变量中,供后续处理使用。

  • long_text_preprocess:此函数负责对文本进行预处理。预处理包括去除文本中的标点符号,以保证分词的准确性。随后,使用 jieba.analyse.extract_tags 方法提取每篇文本中的前20个关键词,这些关键词将存储在 original_list 和 compare_list 中,作为后续相似度计算的基础。

  • text_checking:核心的查重函数。该函数首先调用 read_file 方法读取文本文件,然后调用 long_text_preprocess 方法对文本进行预处理。接着,构建两个文本的词频向量,并计算它们之间的余弦相似度。最终,将计算得到的相似度结果输出到指定的文件中,以便于进一步分析和记录。

核心算法

  1. 文本预处理:去除文本中的标点符号,并使用 jieba.analyse.extract_tags 提取关键词。关键词的提取数量设置为20个,这些关键词代表了文本的主要内容。

  2. 构建词频向量:将提取的关键词转换为词频向量,以便于进行数学计算。词频向量是对文本中关键词出现频率的数值化表示。

  3. 计算余弦相似度:使用余弦相似度算法来计算两个词频向量的相似度。余弦相似度度量的是两个向量在多维空间中夹角的余弦值,从而衡量它们的相似性。值越接近1,表示文本越相似。

流程图如下:

四:性能分析

使用内置profile来进行性能分析

性能分析图

性能消耗较大部分

  1. 重复计算词频向量
    # 对每个词汇,计算它在 original_listcompare_list中出现的频次 for word in self.word_store: original_vector.append(self.original_list.count(word)) compare_vector.append(self.compare_list.count(word))
  2. 频繁的 I/O 操作
    with open(duplicate_data_address, "a", encoding="utf-8") as file: file.write(f"待查文本与原文本的相似度为:{round(cos_sim, 2)}\n")

性能优化思路

  1. 优化词频向量计算
  2. 优化文件操作

优化后

# 计算文本相似度 def text_checking(self): """ 主功能方法,负责根据文本长度选择适合的预处理方法。 长文本(超过1000字符)会调用long_text_preprocess方法,短文本会调用short_text_preprocess` 方法。
最终使用余弦相似度计算两个文本之间的相似度。
"""
original_vector = []
compare_vector = []

    # 读取文件,检查读取是否成功# 调用 `read_file` 方法读取原始文本和比较文本if not self.read_file():return False# 根据文本长度判断选择预处理方法# 如果原始文本或比较文本的长度超过1000字符,则视为长文本if len(self.original_text) > 1000 or len(self.compare_text) > 1000:self.long_text_preprocess()else:self.short_text_preprocess()# 合并分词列表并去重,创建词汇表# 使用 `set` 去除重复的词汇,`self.word_store` 存储所有不重复的词汇self.word_store = list(set(self.original_list + self.compare_list))# 构建词频向量# 对每个词汇,计算它在 `original_list` 和 `compare_list` 中出现的频次for word in self.word_store:original_vector.append(self.original_list.count(word))compare_vector.append(self.compare_list.count(word))# 将词频向量转换为 numpy 数组original_vector = numpy.array(original_vector)compare_vector = numpy.array(compare_vector)# 使用 scipy 库的余弦相似度函数计算相似度# `spatial.distance.cosine` 计算两个向量之间的余弦距离,余弦相似度为 1 减去余弦距离cos_sim = 1 - spatial.distance.cosine(original_vector, compare_vector)# 将相似度写入文件并提示用户# 获取用户输入的文件路径并尝试打开文件duplicate_data_address = input("查重结果文件输出的地址:")try:# 使用追加模式打开文件,若文件不存在则创建with open(duplicate_data_address, "a", encoding="utf-8") as file:# 将相似度写入文件file.write(f"待查文本与原文本的相似度为:{round(cos_sim, 2)}\n")print("查重结果已输出到文件!")except IOError:# 捕捉文件打开或写入失败的异常print("查重结果文件创建失败,请检查路径并重试。")return True`

五:单元测试模块

单元测试将利用 PyCharm 提供的 unittest 框架进行。测试通过继承 unittest.TestCase 类来创建测试类,并在其中定义测试函数。运行测试时,将执行测试 Python 文件,从而对主程序进行测试。

在测试项目的输入模块时,将使用 unittest 框架中的 patch 类来模拟用户输入,以此实现对输入模块的测试。测试过程包括模拟用户操作以及对 main.py 中 DuplicateChecking 类的每个方法进行单独测试。通过使用框架中的 assertEqual() 函数,验证类及其方法的行为是否符合预期
`import unittest
import random
from main import DuplicateChecking
from unittest.mock import patch # 用于模拟输入

记录测试文本地址

original_text = [r'C:\Users\86150\Desktop\查重测试样例\orig.txt',
r'C:\Users\86150\Desktop\查重测试样例\原文1.txt',
r'C:\Users\86150\Desktop\查重测试样例\原文2.txt',
r'C:\Users\86150\Desktop\查重测试样例\原文3.txt',
r'C:\Users\86150\Desktop\查重测试样例\原文4.txt']
test_text = [r'C:\Users\86150\Desktop\查重测试样例\orig_0.8_add.txt',
r'C:\Users\86150\Desktop\查重测试样例\orig_0.8_del.txt',
r'C:\Users\86150\Desktop\查重测试样例\orig_0.8_dis_1.txt',
r'C:\Users\86150\Desktop\查重测试样例\orig_0.8_dis_10.txt',
r'C:\Users\86150\Desktop\查重测试样例\orig_0.8_dis_15.txt',
r'C:\Users\86150\Desktop\查重测试样例\抄袭1.txt',
r'C:\Users\86150\Desktop\查重测试样例\抄袭2.txt',
r'C:\Users\86150\Desktop\查重测试样例\抄袭3.txt',
r'C:\Users\86150\Desktop\查重测试样例\抄袭4.txt']

class MyTestCase(unittest.TestCase):
@patch('builtins.input')
def test_IO(self, mock_input):
"""
测试输入和文件读取功能的正确性。

    使用 mock_input 来模拟用户输入。- 第一个测试用例模拟正确的输入,检查 `read_file` 方法是否返回 True。- 第二个测试用例模拟错误的输入,检查 `read_file` 方法是否返回 False。"""result = DuplicateChecking()  # 实例化 DuplicateChecking 类对象mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)]]  # 模拟正确的用户输入self.assertEqual(result.read_file(), True)  # 断言 read_file 方法返回 True 表示文件读取成功# 模拟错误的用户输入mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)]]self.assertEqual(result.read_file(), False)  # 断言 read_file 方法返回 False 表示文件读取失败@patch('builtins.input')
def test_long_text_preprocess(self, mock_input):"""测试长文本预处理功能。使用 mock_input 来模拟用户输入。- 模拟正确的输入,检查 `long_text_preprocess` 方法是否返回 True。"""result = DuplicateChecking()  # 实例化 DuplicateChecking 类对象mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)]]  # 模拟正确的用户输入result.read_file()  # 先读取文件self.assertEqual(result.long_text_preprocess(), True)  # 断言 long_text_preprocess 方法返回 True 表示预处理成功def test_short_text_preprocess(self):"""测试短文本预处理功能。- 设置短文本数据,检查 `short_text_preprocess` 方法是否返回 True。"""result = DuplicateChecking()  # 实例化 DuplicateChecking 类对象result.original_text = "original"  # 设置原始文本result.compare_text = "compare"  # 设置比较文本self.assertEqual(result.short_text_preprocess(), True)  # 断言 short_text_preprocess 方法返回 True 表示预处理成功@patch('builtins.input')
def test_text_checking(self, mock_input):"""测试文本相似度检查功能。使用 mock_input 来模拟用户输入。- 第一个测试用例模拟正确的输入,检查 `text_checking` 方法是否返回 True。- 第二个测试用例模拟错误的输入,检查 `text_checking` 方法是否返回 False。"""result = DuplicateChecking()  # 实例化 DuplicateChecking 类对象mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)],r'C:\Users\86150\Desktop\查重测试样例\结果记录.txt']  # 模拟正确的用户输入,包括文件路径self.assertEqual(result.text_checking(), True)  # 断言 text_checking 方法返回 True 表示检查成功# 模拟错误的用户输入mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)],r'C:\Users\86150\Desktop\查重测试样例\结果记录.txt']self.assertEqual(result.text_checking(), False)  # 断言 text_checking 方法返回 False 表示检查失败

if name == 'main':
unittest.main() # 运行所有测试用例`

六:异常处理

  • 读取文件异常
    ` # 读取原始文本文件
    try:
    with open(original_text_address, "r", encoding="utf-8") as file1:
    self.original_list = file1.readlines()
    # 将读取的行拼接成单个字符串存储
    self.original_text = self.original_text.join(self.original_list)
    except FileNotFoundError:
    # 如果文件未找到,提示用户并重置文件路径
    print("未找到原始文本文件 " + original_text_address + " 请重试")
    original_text_address = ""

      # 读取抄袭文本文件try:with open(copy_text_address, "r", encoding="utf-8") as file2:self.compare_list = file2.readlines()self.compare_text = self.compare_text.join(self.compare_list)except FileNotFoundError:# 如果文件未找到,提示用户并重置文件路径print("未找到抄袭文件 " + copy_text_address + " 请重试")copy_text_address = ""# 如果文件不存在,重置文本内容并返回 Falseif original_text_address == "" or copy_text_address == "":self.original_text = ""self.compare_text = ""self.compare_list = []self.original_list = []return Falsereturn True`
    
  • 输出文件异常
    try: # 使用追加模式打开文件,若文件不存在则创建 with open(duplicate_data_address, "a", encoding="utf-8") as file: # 将相似度写入文件 file.write(f"待查文本与原文本的相似度为:{round(cos_sim, 2)}\n") print("查重结果已输出到文件!") except IOError: # 捕捉文件打开或写入失败的异常 print("查重结果文件创建失败,请检查路径并重试。") return True

七:心得体会

通过本项目的实践,我不仅加深了对文本处理和相似度计算等基础技术的理解,还在单元测试和性能优化方面积累了宝贵的经验。在文本处理方面,我学会了如何高效地进行数据预处理和分析,掌握了各种相似度计算方法的实际应用。在单元测试方面,通过使用 unittest 框架及其相关工具,我提高了对代码质量的把控能力,确保了程序的稳定性和可靠性。在性能优化方面,我探索了提升代码执行效率的方法,如改进算法和减少不必要的计算。这些经验不仅增强了我解决实际问题的能力,还为未来参与更复杂的项目打下了坚实的基础,使我能够更自信地应对各种技术挑战。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OI海海

在pyq里看到了殿禾Wrose的“X的随波逐流”,附文是“我与OI的365天”,我才想到,我打OI也整整一年了 有人或许不理解我这样的一个蒟蒻为什么这么喜欢喜欢写怀念类的东西,即使到现在我还没有取得任何的奖项以及未来也并没有概率拿牌 因为我只觉得一步步走来,不记得的话,是很…

GIS可视化软件:地理信息与遥感领域中的洞察之眼

在地理信息与遥感技术的广阔天地中,可视化软件如同一双洞察世界的明眸,将复杂的数据编织成生动、直观的画卷,为我们揭示地球的奥秘与城市的律动。本文将深入挖掘其技术核心、应用实例、未来趋势,探讨可视化软件如何为地理信息与遥感技术带来深刻洞见。 可视化软件的核心与技…

USB分析仪USB3.2日志分析

1.简介 USB2.0总线采用轮询模式,即总线事务开始时,都要先发送IN或者OUT令牌包,以通知端点或者查询端点是否准备好。而USB3.2采用了异步通知模式,若端点没有准备好,则主机无需轮询,端点准备好后会通知主机,而对于OUT端点,主机会直接发送DP数据包,相当于将USB2.0中的OUT…

[第一章 web入门]SQL注入-1

启动靶机很明显注入点为id值,单引号闭合影响语句,说明为单引号闭合构造注入语句 ?id=1 and 1 =1 --+ 发现没报错,说明没有其他过滤 ,开始sql注入 ?id=1 order by 4 --+ 直到=4报错说明有3个字节段测试回显位2,3 ?id=1 and 1 = 2 union select 1,2,3 --+ 测试当…

数据结构 —堆

今天学习算法了没,你小子瞅啥呢!一:堆 1、一种二叉树的结构(完全二叉树) 2、完全二叉树:从上到下;从左到右;填满 3、最大堆:根节点的权值大于孩子节点 4、最小堆:根节点的权值依次小于孩子节点 5、常用操作 import heapq# 创建最小堆和最大堆 min_heap = [] max_heap…

软工第一次作业-论文查重

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229这个作业的目标 通过Java开发个人项目,实现项目单元测试作业github地址(含jar包) github:https://github.com/…

第二十讲:为什么我只改一行的语句,锁这么多?

该文章深刻揭示了一点:加索引=行锁+间隙锁=(next-key lock),分析了加锁的规则:对主键(唯一索引),普通非唯一索引进行等值与范围查询的加锁。这篇文章我认为收获最大的是让我们知道“明明对一行加了锁,为什么在他相邻部分,或是相相邻部分无法插入数据(这根主键类型,…

【视频讲解】线性时间序列原理及混合ARIMA-LSTM神经网络模型预测股票收盘价研究实例

原文链接:https://tecdat.cn/?p=37702 原文出处:拓端数据部落公众号 分析师:Dongzhi Zhang 近年来人工神经网络被学者们应用十分广泛,预测领域随着神经网络的引入得到了很大的发展。本文认为单一神经网络模型对序列所包含的线性信息和非线性信息的挖掘是有限的,因此本…

Docker-Compose搭建RustDesk服务器

前置条件:电脑安装RustDesk客户端,服务器安装Docker及docker-compose官方文档:安装 :: RustDesk文档操作流程:使用Vim编写docker-compose.yml文件,修改需要的端口,最好按照官方对应的端口来操作,< >内替换成服务器对外的端口。记住挂载文件路径,容器运行后会生成密…

CMake构建学习笔记16-使用VS进行CMake项目的开发

详细介绍了通过Visual Studio 2019 这款IDE进行CMake项目开发过程,能够极大增加C/C++程序的开发效率。目录1. 概论2. 详论2.1 创建工程2.2 加载工程2.3 配置文件2.4 工程配置2.5 调试执行3. 项目案例4. 总结 1. 概论 在之前的系列博文中,我们学习了如何构建第三方的依赖库,也…

USB总线-Linux内核USB3.0主机控制器驱动初始化流程分析(十三)

1.概述 RK3588有2个USB3.0 DRD控制器,2个USB2.0 Host控制器。USB3.0 DRD控制器既可以做Host,也可以做Device,向下兼容USB2.0和USB1.0。USB3.0 DRD控制器的内部结构如下图所示;总线接口为AXI或AHB;USB3.0和USB2.0及USB1.0硬件上独立;USB3.0控制器数字逻辑部分对应SS MAC,…