这个作业属于哪个课程 计科国际班
这个作业要求在哪里 个人项目
这个作业的目标 实现简易的论文查重

Github链接

Github地址(存放在以学号命名的文件夹内)
由于前期没有注意看评分细则和不知道如何整理github内已上传的文件,故在交作业时另开了以学号为名的文件夹单独存放
前期的增添和修改记录在同一仓库下main为名的branch中

psp表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 15
Estimate 估计这个任务需要多少时间 25 15
Development 开发 1080 670
Analysis 需求分析 (包括学习新技术) 240 200
Design Spec 生成设计文档 20 30
Design Review 设计复审 30 30
Coding Standard 代码规范 (为目前的开发制定合适的规范) 10 0
Design 具体设计 60 100
Coding 具体编码 450 360
Code Review 代码复审 20 100
Test 测试(自我测试,修改代码,提交修改) 15 50
Reporting 报告 50 190
Test Repor 测试报告 20 100
Size Measurement 计算工作量 15 30
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 15 60
Sum up 合计 1430 875

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

没有包,只有main.py

功能函数列表

没有使用类封装对象

刚开始拿到这个题目的时候我没有一点头绪,于是我在网络的海洋中搜寻着如何实现简易的查重算法
于是我看到了师兄的作业,我了解到简易的查重算法可以通过:结巴分词将文章按语义分割为若干个词——使用余弦相似度得出两篇文章的相似度
于是我按照百度百科上的公式:余弦相似度
在计算的时候我发现我并不知道每个关键词的权重,进而无法筛选出有用的关键词,于是我又了解到:TF-IDF值
想到要用IF-IDF值代表关键词的权重才会更显合理。
于是便将公式翻译了一遍,就写出了“查重算法”,略显赶鸭子上架......

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

性能测试耗时截图
流程和耗时
可以看到,最主要的耗时为:结巴分词、读取文本、统计每个词在文章中出现的次数
其中结巴分词和读取文本,由于这两种功能已经封装好并且用户接触不到核心算法,无法进行更好的改进
而统计每个词在文章中出现的次数这个功能,由于我在获得用于计算余弦相似度的向量前,将关键词数量进行了限制(在代码中,并没有要求用户输入),我认为,关键词列表后段的词语对相似度的计算贡献并不大,适当限制检测词的数量将有利于内存管理,也可以减少计算的次数。此处没有要求用户输入,一是考虑到此值需要通过反复的测试才能不影响到查重的精度,二是如果面对篇幅巨大的文章,关键词的数量多可能会导致在某些低内存设备上运行崩溃,所以此值最好由开发人员决定。

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


测试的思路就是检查每个函数的功能是否能正常实现,所以测试数也和定义的函数数量一样定格在了5个。
此处有特殊情况为:在测试计算权重函数的时候,只需要验证sorted函数是否能够正常排序第一篇文章的权重字典即可,因为无论是计算每个词的词频或者是计算tf-idf值,都属于“开门见山”类型,即结巴分词给出什么样的结果,就会通过循环计数得出什么样的结论,花费的只是时空间复杂度,逻辑并不难(相当于翻译数学表达式),并且由于前期设计的时候没有将统计词频和计算权重分离开来,单独测试tf-idf权重计算的式子既无法实现,互联网上也没有正确的结果可供参考。此处容易出错的点反而在sorted函数的参数上,按降序排序的参数是reverse=False,sorted函数能对字典排序的参数是x: x[1]
附上测试代码:(test_unit.py文件也已放入以学号为名的github仓库文件夹中)

    def test_get_encoding(self):
        with self.assertRaises(UnicodeDecodeError):
            main.read_papers(self.error_file_type1, self.paper2_path)

    def test_read(self):  # 测试读取文本和分词是否成功
        with self.assertRaises(FileNotFoundError):
            main.read_papers(self.error_path, self.paper2_path)
        paper1, paper2 = main.read_papers(self.paper1_path, self.paper2_path)
        self.assertNotEqual(paper1, [])
        self.assertNotEqual(paper2, [])

    def test_weight(self):  # 测试sorted函数是否正常运转
        test_result1, test_result2 = main.weight_calculation(self.__test_paper1, self.__test_paper2)
        for i in test_result1:
            self.assertEqual(str(type(i)), "<class 'tuple'>")

    def test_vector(self):  # 测试向量的运算
        vector1, vector2 = main.vector_calculation(self.__test_weight1, self.__test_weight2, len(self.__test_weight1))
        self.assertEqual(vector1, [1, 2, 3, 4])
        self.assertEqual(vector2, [7, 8, 5, 6])

    def test_cos(self):  # 测试余弦相似度的计算
        self.assertEqual(main.cosine_calculation(self.__test_vector1, self.__test_vector1), '1.000')

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

只有一种情况会引起程序异常:无法找到需要查重的文章路径

在cmd中会直接报错并直白的讲述:No such File or directory:"输入错误的路径"
注意:如果在输出的txt文档中原本有内容,输出结果后会进行覆盖,只留下相似度,会因此导致数据不慎丢失
请注意cmd引用的方式:python [main.py的路径] [需要查重的原文章路径] [疑似抄袭的文章路径] [输出文档的路径]

写程序过程中遇到的问题

1、首先是读取txt文档过程中,由于不知道文件的编码格式,便需要加入一个简单的检测函数,通过检测函数返回的类型才能写入encoding的参数,不然无论是写utf-8格式读取或者gbk读取都会报错,并且不支持使用read函数
2、其次是在形成关键词字典后,通过value,也就是词频对字典进行排序也是无法直接实现的,幸好字典可以使用sorted函数,但是返回的是一个元组列表,[('key:value')]形式,我想到的解决办法是可以将元组格式化为str类型,再通过:将其分割,再将'()'这样的符号去除,就可以得到分开的key和value值。
3、最后是在计算向量时,必须保证向量所属的词语列表是一样的,也就是向量与word_list是一一对应的,但是向量个数并不能太多,我想到的解决办法是使用对照文章的前60个高频词作为word_list,权重作为vector1,然后再将抄袭文章这60个词的权重导入成为vector2,目前没有想到更客观的解决办法。

posted on 2021-09-20 18:39  抱着键盘睡觉的Sebs  阅读(57)  评论(0编辑  收藏  举报