第一次个人编程作业
作业要求
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229 |
GitHub链接 | https://github.com/OathKeepsir/3122004953 |
这个作业的目标 | 实现论文查重算法 |
需求分析
题目:论文查重
描述如下:
设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。
- 原文示例:今天是星期天,天气晴,今天晚上我要去看电影。
- 抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。
要求输入输出采用文件输入输出,规范如下:> >
- 从命令行参数给出:论文原文的文件的绝对路径。
- 从命令行参数给出:抄袭版论文的文件的绝对路径。
- 从命令行参数给出:输出的答案文件的绝对路径。
我们提供一份样例,课堂上下发,上传到班级群,使用方法是:orig.txt是原文,其他orig_add.txt等均为抄袭版论文。
注意:答案文件中输出的答案为浮点型,精确到小数点后两位
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 20 |
Development | 开发 | 200 | 235 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
· Design Spec | · 生成设计文档 | 10 | 10 |
· Design Review | · 设计复审 | 5 | 5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 15 |
· Design | · 具体设计 | 20 | 20 |
· Coding | · 具体编码 | 60 | 70 |
· Code Review | · 代码复审 | 10 | 10 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 35 |
Reporting | 报告 | 100 | 105 |
· Test Repor | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 20 |
· 合计 | 315 | 360**************** |
接口设计与实现过程
设计思路
流程图:
- 读取文档内容并文本预处理:从两个不同的文件中读取文本内容,并对文本进行预处理。文本可能包含各种标点符号、换行符等不必要的字符,需要去除它们以便后续处理。
- 支持从不同格式(如 .txt)的文件中读取内容。
- 能够正确处理文件路径错误或文件缺失的情况,并给出适当的错误提示。
- 读取内容时,需要确保编码格式一致(如 UTF-8 编码),避免由于编码问题导致的错误。
- 去除所有不必要的标点符号和换行符,以确保文本内容可以进一步处理。
- 对文档内容分词:对两个文件的文本内容进行分词。分词是自然语言处理中的基础步骤,用于将一段文本切分为单个词语(或词组)。分词后的结果通常通过空格分隔开,便于后续向量化处理。
- 使用一种高效的分词算法(如 jieba 分词库)来进行中文文本的分词。
- 分词的结果需要是以空格分隔的词语序列,以便后续的向量化处理。
- 分词算法应当能够处理包含特殊字符的文本,并能够正确分割出意义明确的词语。
- 能够处理文本为空的情况,如果输入为空字符串,返回空字符串。
- 分词后的文本转换为向量:使用 TF-IDF 算法将分词后的文本转换为向量表示。向量化是将文本转换为数值形式的关键步骤,以便后续的相似度计算。TF-IDF 是一种广泛使用的向量化方法,它通过衡量词语的重要性(基于词频和逆文档频率)来表示文本。
- 使用 TF-IDF 算法对两个文本进行向量化,生成数值向量。
- 向量化的结果应能代表每个词语在文本中的重要性。
- 向量化结果应该是两个数值向量,维度相同,且维度大小由文本的词语数量决定。
- 能够处理文本内容为空的情况,若输入为空,则返回空向量。
- 计算两个向量之间的余弦相似度:计算两个文本的相似度。相似度计算是查重算法的核心功能。本文使用余弦相似度来衡量两个文本的相似程度。余弦相似度通过计算两个向量的夹角来表示它们的相似性,值在 -1 到 1 之间,其中 1 表示完全相同,0 表示完全不同。
- 计算两个向量的余弦相似度,并返回相似度值。
- 能够处理向量的长度为 0 的情况(如空文本转换的向量),此时应返回相似度为 0。
- 计算过程应确保高效,特别是对于长文本,性能应能接受。
- 相似度结果应保留适当的小数位(如保留两位小数)。
- 输出相似度结果到指定文件:将相似度结果保存到指定的文件中。论文查重的结果需要持久化保存,通常以文本文件的形式保存。保存时应确保结果的精度,并且要处理文件路径错误的情况。
- 将计算得到的相似度结果保存到指定的文件中,并保留两位小数。
- 若指定的文件路径不存在或无法访问,应提示用户错误,并确保程序不崩溃。
- 能够处理文件名冲突等情况,必要时覆盖已有文件。
接口设计及代码实现
1.文件处理接口
# 读取并处理文件内容
def load_and_process_files(filepath1, filepath2):
try:
# 读取文件内容
with open(filepath1, 'r', encoding='utf-8') as file1, open(filepath2, 'r', encoding='utf-8') as file2:
content1 = file1.read()
content2 = file2.read()
except FileNotFoundError:
print("文件路径错误,请检查输入的文件路径!")
return None, None
# 去除文件中的标点符号
punctuation = "\n\r 、,。;:?‘’“”''""!《》,.;:?!<>"
for p in punctuation:
content1 = content1.replace(p, '')
content2 = content2.replace(p, '')
return content1, content2
-
参数:两个文件的路径 filepath1 和 filepath2。
-
返回:处理过的两个文件内容,去除了标点符号。
2.分词接口
# 分词函数
def tokenize_content(content1, content2):
# 使用 jieba 分词,将内容进行分词处理
tokens1 = ' '.join(tokenizer.lcut(content1))
tokens2 = ' '.join(tokenizer.lcut(content2))
return tokens1, tokens2
-
参数:文件内容 content1 和 content2。
-
返回:分词后的文本字符串,空格分隔。
3.向量化接口
# 文本向量化
def convert_to_vector(text1, text2):
# 利用 TF-IDF 向量化分词后的文本内容
vectorizer = TfidfVectorizer()
# 输入两段文本进行向量化
transformed_vectors = vectorizer.fit_transform([text1, text2]).toarray()
return transformed_vectors[0], transformed_vectors[1]
-
参数:分词后的文本 text1 和 text2。
-
返回:文本的向量表示。
4.余弦相似度计算接口
# 计算向量的余弦相似度
def compute_cosine_similarity(vec1, vec2):
# 计算向量的点积和模的乘积
dot_product = sum(a * b for a, b in zip(vec1, vec2))
magnitude1 = math.sqrt(sum(a ** 2 for a in vec1))
magnitude2 = math.sqrt(sum(b ** 2 for b in vec2))
# 计算并返回余弦相似度
if magnitude1 == 0 or magnitude2 == 0:
return 0.0
return dot_product / (magnitude1 * magnitude2)
-
参数:文本向量 vec1 和 vec2。
-
返回:两个向量的余弦相似度。
5.结果保存接口
# 将结果保存到文件中
def save_similarity_result(output_path, similarity_score):
try:
if isinstance(similarity_score, float):
with open(output_path, 'w') as file:
# 保留两位小数写入文件
file.write(f'{similarity_score:.2f}')
return round(similarity_score, 2)
else:
raise ValueError("结果应为浮点类型!")
except FileNotFoundError:
print("结果保存路径不存在,请检查路径!")
return None
- 参数:文件路径 output_path 和相似度结果 similarity_score。
- 返回:保存的相似度结果,保留两位小数。
接口部分的性能改进
采用pycharm中自带的profile分析性能插件进行分析
图中展示函数之间的调用关系、调用次数和执行时间等信息。
用于余弦相似度计算的两个函数和用于分词处理的函数在查重过程中占据了程序大部分的执行时间,这部分是整个程序的核心。由于分词过程的性能受文章长度和内容结构的影响,无法进一步优化。
模块部分单元测试展示
测试结果
图中展示10个测试通过的总耗时802ms,文章查重测试为698ms,分词消耗101ms。大部分时间用在文章查重和分词消耗,对不正确的用例能够检测得出。
模块部分异常值处理
文件处理模块中的异常处理
- FileNotFoundError:
处理:当文件路径不正确或文件不存在时,open()
函数会抛出FileNotFoundError
。在捕获到这个错误后,程序不会崩溃,而是返回None
,None
,并提示用户检查文件路径。
作用:确保程序能够优雅地处理文件不存在的情况,并且不继续传递空文件内容到后续步骤,避免其他模块因无效输入导致更多错误。 -
- Exception:
处理:除了FileNotFoundError
,还有可能发生其他异常(如编码错误、权限问题等)。捕获所有的异常并输出具体错误信息,可以帮助调试和问题排查。
作用:提高程序的健壮性,避免程序在遇到意外问题时崩溃。
- Exception:
分词模块中的异常处理
1.None 值检查:
处理:在 tokenize_content 函数中,首先检查传入的 content1 和 content2 是否为 None。如果为空,说明文件读取出现了问题或文本内容为空,此时直接返回空字符串,不进行分词操作。
作用:防止传递无效数据给 jieba 分词库。如果继续使用 None 值进行分词,会导致库内部报错,因此这里通过早期返回避免了问题扩散。
向量化模块中的异常处理
1.ValueError:
处理:在 TfidfVectorizer
进行向量化时,如果输入数据不符合预期(如文本太短或为空),可能会抛出 ValueError
。这里通过捕获异常,避免程序崩溃,并返回 None
, None
。
作用:向量化是关键步骤,捕获此类错误可以确保向量化失败时,不会进一步导致相似度计算错误。同时打印错误信息有助于开发者理解问题原因。
相似度计算模块中的异常处理
1.ZeroDivisionError:
处理:当两个向量中有一个是零向量时(向量长度为零),计算余弦相似度会遇到除以零的问题。为了避免这个错误,代码检查向量的模是否为零。如果是零向量,直接返回相似度为 0,避免出现 ZeroDivisionError
。
作用:防止由于向量不合法而导致程序崩溃。即使其中一个或两个文本没有有效内容,程序依旧能够返回合理的结果(相似度为 0),保证稳定性。
结果保存模块中的异常处理
1.FileNotFoundError:
处理:如果保存路径不存在或不正确,open()
函数会抛出 FileNotFoundError
。捕获这个异常,并输出错误提示,确保程序不崩溃。
作用:当文件路径无效时,防止程序意外中止,并引导用户检查路径问题。
2.IOError:
处理:IOError
捕获了所有文件写入相关的 I/O 错误(如磁盘满、权限不足等)。程序通过捕获这个异常,避免由于 I/O 问题导致的崩溃。
作用:确保即使出现 I/O 错误,程序仍能提供适当的提示信息,帮助用户排查问题。