第一次个人编程作业

本次作业已上传至GitHub

PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

60

70

· Estimate

· 估计这个任务需要多少时间

60

70

Development

开发

830

1020

· Analysis

· 需求分析 (包括学习新技术)

300

350

· Design Spec

· 生成设计文档

200

210

· Design Review

· 设计复审

50

40

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

20

30

· Design

· 具体设计

30

30

· Coding

· 具体编码

100

120

· Code Review

· 代码复审

30

20

· Test

· 测试(自我测试,修改代码,提交修改)

100

120

Reporting

报告

370

410

· Test Repor

· 测试报告

300

320

· Size Measurement

· 计算工作量

20

20

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

50

70

· 合计

1260

1500

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

  • 算法流程图
  • 算法思想

整段代码用一个类(CosineSimilarity)封装,每个步骤分别用一个方法封装,最后用calculate方法串起来。
接下来简单介绍一下CosineSimilarity类中的各个方法:

  1. init 构造方法。 用于传入两个文本
  2. extract_keyword 提取关键词。 先通过jieba分词,再把分词结果提取出前K个权重最大的关键词
  3. one_hot 对关键词进行one_hot编码。 把文本转为向量
  4. calculate 计算余弦相似度。 把前面的所有方法串起来,并计算得到结果

  这段代码参考了网上大佬的GitHub。如果只对分词结果进行余弦相似度的计算,对于短文本的准确率还可以,而对于长文本就不太行。因此,提取了前topK个关键词,对关键词进行相似度的计算,准确率会提高一点点。
  关于topK的选取: 取群里的测试数据,一篇文章大概由10000个词组成,按照topK从1-2000,依次验证一下,发现topK在1600以后基本收敛,而在1100左右性能较好。因此,选取topK = int(len(seg)/9),其中seg为文章的分词结果。这个选取方法不是很严谨,缺少更多数据的验证。

验证的代码如下:

    import matplotlib.pyplot as plt
    y = []
    with open(r"test_dataorig.txt",encoding="utf-8") as fp:
        ori = fp.read()
    with open(r"test_dataorig_0.8_add.txt",encoding="utf-8") as fp:
        copy = fp.read()
    s = CosineSimilarity(ori,copy)
    x = [i for i in range(5,2000,5)]
    for i in range(5,2000,5):
        topK = i
        ans = s.calculate()
        y.append(ans)
        # print("topK = ",topK,"相似度:",ans)
    print(y)
    plt.plot(x,y)
    plt.xlabel("topK")
    plt.ylabel("rate")
    x2 = [5,2000]
    y2 = [0.8,0.8]
    plt.plot(x2,y2)
    plt.show()

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

  • 运行时间分析

   利用Python的profile工具进行分析,分析完两篇文章的总时间是3.53s,其中耗时最大的是在jieba分词这一块,因为是调用第三方库,所以但从算法的角度不太懂得怎么优化,可能开个多线程速度会快一点。哦,还有,我的余弦相似度算法为图省事直接调用的sklearn的cosine_similarity,这个函数会计算输入的n个向量两两之间的相似度,因为这道题是两篇文章对比,所以时间上的差距可能不是很大,但是自己写个函数可能会快一点。

  • 消耗内存分析

  这里用了Python的memory_profiler和psutil库进行内存分析,因为是对文章进行分词并转为向量所以内存的消耗还是比较大的。其中内存消耗最大的模块是:

    @staticmethod
    def extract_keyword(content):  # 提取关键词
        # 切割
        seg = [i for i in jieba.cut(content, cut_all=True) if i != '']
        # 提取关键词
        keywords = jieba.analyse.extract_tags("|".join(seg), topK=topK, withWeight=False)
        return keywords

计算模块单元测试展示

  • 测试代码

    root = "sim_0.8"
    fileName = os.listdir(root)  # 得到当前目录下所有的文件名
    with open(root+'\'+fileName[0],encoding='UTF-8') as fp:
        ori = fp.read()
        # print("len article = ",len(ori))
        seg = [i for i in jieba.cut(ori, cut_all=True) if i != '']
        topK = int(len(seg)/9)
    processText(ori)
    for i in range(1,10):
        with open(root+'\'+fileName[i],encoding='UTF-8') as fp:
            copy = fp.read()
            processText(copy)
            similarity = CosineSimilarity(ori,copy)
            similarity = similarity.calculate()
            print(fileName[i] + ' 相似度: %.2f%%' % (similarity*100)) 
  • 测试代码构建思路

  测试代码在本地完成,读取文件夹下的所有文件名,接着读入原文和修改过的文章,创建一个CosineSimilarity对象,调用对象的calculate方法,最后对结果保留两位小数并写入输出文件。

  测试数据采用群里提供的9个文件,这9个文件包括了大部分查重情况。

  • 测试结果

orig_0.8_add.txt 相似度: 81.00%
orig_0.8_del.txt 相似度: 70.50%
orig_0.8_dis_1.txt 相似度: 89.00%
orig_0.8_dis_10.txt 相似度: 75.00%
orig_0.8_dis_15.txt 相似度: 58.50%
orig_0.8_dis_3.txt 相似度: 86.50%
orig_0.8_dis_7.txt 相似度: 82.50%
orig_0.8_mix.txt 相似度: 80.50%
orig_0.8_rep.txt 相似度: 70.50%

  从测试结果可以看出这个算法的准确度还算可以,但是感觉会偏低一点

  • 测试二

  利用两段相同的文本进行分析

原文
  矛盾分析法是唯物辩证法的核心,唯物辩证法包括很多规律和范畴,其中,对立统一规律是其核心,在《矛盾论》的开头,事物的矛盾法则,即对立统一的法则,是唯物辩证法的最根本的法则。因而,掌握了矛盾分析法,我们就能更好地理解唯物辩证法的其他内容。
修改后
  矛盾分析法是唯物辩证法的核心,唯物辩证法包括很多规律和范畴,其中,对立统一规律是其核心,在《矛盾论》的开头,事物的矛盾法则,即对立统一的法则,是唯物辩证法的最根本的法则。因而,掌握了矛盾分析法,我们就能更好地理解唯物辩证法的其他内容。

测试结果

相似度 100.00%

  • 单元测试代码

    
class Test(unittest.TestCase):
    def test1(self):
        "改动0.8的文本"
        with open("sim_0.8orig.txt",encoding="utf-8") as fp:
            ori = fp.read()
        with open("sim_0.8orig_0.8_add.txt",encoding="utf-8") as fp:
            copy = fp.read()
        model = CosineSimilarity(ori,copy)
        ans = model.calculate()
        self.assertEqual(round(ans,2), 0.8)
    def test2(self):
        "相同文本"
        with open("sim_0.8orig.txt",encoding="utf-8") as fp:
            ori = fp.read()
        with open("sim_0.8orig.txt",encoding="utf-8") as fp:
            copy = fp.read()
        model = CosineSimilarity(ori,copy)
        ans = model.calculate()
        self.assertEqual(round(ans,2), 1.0)
    def test3(self):
        "空文本"
        with open("sim_0.8orig.txt",encoding="utf-8") as fp:
            ori = fp.read()
        with open("sim_0.8empty.txt",encoding="utf-8") as fp:
            copy = fp.read()
        model = CosineSimilarity(ori,copy)
        ans = model.calculate()
        self.assertEqual(ans, 0.0)

if __name__ == '__main__':
    unittest.main()
  • 单元测试覆盖率

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

  • IO流异常
    try:
        with open(path,encoding='UTF-8') as fp:
            ori = fp.read()
    except:
        print("路径错误")

  当路径名有错时,可能无法打开文件,产生异常

  • 除0异常
    try:
        sim = cosine_similarity(sample)
        return sim[1][0]
    except Exception as e:
        print(e)
        return 0.0

在计算两个向量的余弦值时,当输入的文本为空时,分母有可能为0,会产生异常

小结

  这次作业最主要的收获是学会了用Git进行上传和下载项目和如何搭建一个项目的完整性,比如前期工作,性能分析,测试数据等,特别是单元测试,这在以前是根本不会去做的。在这之中体会到了代码不是最主要的,大部分的时间都在处理其他的事,比如写博客,学习新技术等,真正编码的时间只占了一小部分。同时,这次作业还提高了查找和收集资料的能力。

原文地址:https://www.cnblogs.com/koun/p/13656341.html