前言
| 软件工程 | 软件工程 |
| :-: | :-: |:-: |
| 作业要求| 要求 |
|作业目标 |代码实现,功能优化,单元测试PSP表格实现|
代码在github
具体要求如下:
功能:论文查重
需求描述如下:
设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。
原文示例:今天是星期天,天气晴,今天晚上我要去看电影。
抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。
要求输入输出采用文件输入输出,规范如下:
从命令行参数给出:论文原文的文件的绝对路径。
从命令行参数给出:抄袭版论文的文件的绝对路径。
从命令行参数给出:输出的答案文件的绝对路径。
我们提供一份样例,课堂上下发,上传到班级群,使用方法是:orig.txt是原文,其他orig_add.txt等均为抄袭版论文。
注意:答案文件中输出的答案为浮点型,精确到小数点后两位
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 35 | 45 |
Development | 开发 | 200 | 300 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 150 |
· Design Spec | · 生成设计文档 | 30 | 50 |
· Design Review | · 设计复审 | 30 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 50 |
· Design | · 具体设计 | 50 | 60 |
· Coding | · 具体编码 | 200 | 300 |
· Code Review | · 代码复审 | 30 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 30 | 50 |
· Test Repor | · 测试报告 | 30 | 50 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 400 | 500 |
实现思路
思考了很久,最后发现该程序主要是需要实现比较两个文本的文本相似度问题
于是搜索引擎查找应该如何实现,发现有如下方法:
查看了各种方法之后,比较觉得利用余弦距离实现比较实际。
余弦相似度
余弦相似度量:计算个体间的相似度。
相似度越小,距离越大。相似度越大,距离越小。
假设有3个物品,item1,item2和item3,用向量表示分别为:
item1[1,1,0,0,1],
item2[0,0,1,2,1],
item3[0,0,1,2,0],
即五维空间中的3个点。用欧式距离公式计算item1、itme2之间的距离,以及item2和item3之间的距离,分别是:
item1-item2=
item2-item3=
用余弦函数计算item1和item2夹角间的余弦值为:
用余弦函数计算item2和item3夹角间的余弦值为:
由此可得出item1和item2相似度小,两个之间的距离大(距离为7),item2和itme3相似度大,两者之间的距离小(距离为1)。
余弦相似度算法: 一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似,余弦值接近于0,夹角趋于90度,表明两个向量越不相似。
余弦相似度量: 计算个体间的相似度。
相似度越小,距离越大。相似度越大,距离越小。
余弦相似度算法:一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似,余弦值接近于0,夹角趋于90度,表明两个向量越不相似。
下面我们介绍使用余弦相似度计算两段文本的相似度。思路:1、分词;2、列出所有词;3、分词编码;4、词频向量化;5、套用余弦函数计量两个句子的相似度。
句子A:这只皮靴号码大了。那只号码合适。
句子B:这只皮靴号码不小,那只更合适。
1、分词:
使用结巴分词对上面两个句子分词后,分别得到两个列表:
listA=[‘这‘, ‘只‘, ‘皮靴‘, ‘号码‘, ‘大‘, ‘了‘, ‘那‘, ‘只‘, ‘号码‘, ‘合适‘]
listB=[‘这‘, ‘只‘, ‘皮靴‘, ‘号码‘, ‘不小‘, ‘那‘, ‘只‘, ‘更合‘, ‘合适‘]
2、列出所有词,将listA和listB放在一个set中,得到:
set={‘不小’, ‘了’, ‘合适’, ‘那’, ‘只’, ‘皮靴’, ‘更合’, ‘号码’, ‘这’, ‘大’}
将上述set转换为dict,key为set中的词,value为set中词出现的位置,即‘这’:1这样的形式。
dict1={‘不小’: 0, ‘了’: 1, ‘合适’: 2, ‘那’: 3, ‘只’: 4, ‘皮靴’: 5, ‘更合’: 6, ‘号码’: 7, ‘这’: 8, ‘大’: 9},可以看出“不小”这个词在set中排第1,下标为0。
3、将listA和listB进行编码,将每个字转换为出现在set中的位置,转换后为:
listAcode=[8, 4, 5, 7, 9, 1, 3, 4, 7, 2]
listBcode=[8, 4, 5, 7, 0, 3, 4, 6, 2]
我们来分析listAcode,结合dict1,可以看到8对应的字是“这”,4对应的字是“只”,9对应的字是“大”,就是句子A和句子B转换为用数字来表示。
4、对listAcode和listBcode进行oneHot编码,就是计算每个分词出现的次数。oneHot编号后得到的结果如下:
listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]
步骤一
将我们的文本处理,因为我们要比对两个文本的话,一定需要将文本的一切符号,包括换行符以及所有的标点符号,去掉。这里我主要利用了python的正则表达式实现
def Symbol_filter(str):
# 使用正则表达式过滤标点符号
result = re.sub('W+', '', str).replace("_", '')
return result
def get_content (path):
# 文本处理,将我们的文本处理为字符串,并且过滤掉标点符号
string = ''
file = open(path, 'r', encoding='UTF-8')
one_line = file.readline()
while one_line:
string += one_line
one_line = file.readline()
# 调用标点符号过滤函数
string = Symbol_filter(string)
file.close()
return string
实现效果:
步骤二
接下来需要思考我们怎样去实现文本相似度的计算,这里我查找了很多资料找到了jieba包和gensim包的利用
大致思路如上图(但是有几步步骤可以省略)
通过jieba分词进行处理
代码实现:
def Turn_vector(str):
# 将我们的字符串切片
string = jieba.lcut(str)
return string
求文本相似度
def similarity_vul(str_x,str_y):
texts = [str_x, str_y]
# 语料库
dictionary = gensim.corpora.Dictionary(texts)
print(dictionary)
num_features = len(dictionary.token2id)
# 利用doc2bow作为词袋模型
corpus = [dictionary.doc2bow(text) for text in texts]
print(corpus)
similarity = gensim.similarities.Similarity('-Similarity-index', corpus, num_features)
# 获取文章相似度
test_corpus_1 = dictionary.doc2bow(str_x)
cosine_sim = similarity[test_corpus_1][1]
return cosine_sim
函数主要流程图
性能分析
时间分析:
代码覆盖率:
函数时间占用
单元测试
测试代码
import unittest
from main import *
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertEqual(main_test(), 0.98)
if __name__ == '__main__':
unittest.main()
测试效果如下图
异常处理
考虑输入的路径可能出错,不存在文件的情况,则可以用以下的代码处理这个异常
if not os.path.exists(path_1):
print("文件不存在")
exit()