这个作业属于哪个课程 | 计科国际班 |
---|---|
这个作业要求在哪里 | 个人项目 |
这个作业的目标 | 实现简易的论文查重 |
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,目前没有想到更客观的解决办法。