zoukankan      html  css  js  c++  java
  • 第一次个人编程作业

    第一次个人编程作业

    这是一个Github链接

    零、面向开源,春暖花开

    问:如何从零开始造火箭?
    答:去别的火箭厂搬个火箭。
    看看搬来的代码是什么…………py文件,哦是用python写的。打开vscode,ctrl c+v,运行代码……
    嗯?怎么报错了?没有python解释器?难道vscode不是自带python解释器的?原来vscode不是IDE,啊这……
    下载好python解释器,修改环境变量,下载python插件,用了一个世纪的时间终于ok,运行代码!
    嗯?第三方库不能用?
    又是一个世纪过去……哦,原来python要用pip指令来安装库。
    安装完第三方库,运行!
    踩了若干个坑之后……
    ohhhhhhhhhhhh!有结果了!
    分析性能,性能怎么分析?pycharm好像可以,下载!
    吃一堑长一智,选择好python解释器,下载好插件,ctrl c+v, 运行!
    又踩了若干个坑(包括但不局限于内存测试、单元测试、用git将代码上传到github仓库)之后,本菜鸡终于完成了第一次编程作业,泪目。

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

    1.总体流程

    实话实说,既然代码都是直接搬来的,在设计和实现方面我也没什么好说的(我爬_(°ω°」∠)_)。
    代码的核心是使用向量空间模型(Vector Space Model, VSM)计算相似度。先用jieba分词器对读入的文本进行分词处理,然后使用TF-IDF算法进行文本特征选择,计算出文本的特征向量,最后对得到的特征向量用余弦相似度算法计算出两个文本的相似度。

    VSM模型介绍
    TF-IDF算法介绍

    2.计算模块

    (1)分词处理

    #使用jieba分词器对文本text1、text2进行分词
    words1 = [word.word for word in pesg.cut(text1) if word.flag[0] not in ['u', 'x', 'w']]
    words2 = [word.word for word in pesg.cut(text2) if word.flag[0] not in ['u', 'x', 'w']]
    

    (2)使用TF-IDF算法计算文本的特征向量

    (俺很想注释,但俺没看懂……)

    def tfidf_rep(self, sents):
        sent_list = []
        df_dict = {}
        tfidf_list = []
        for sent in sents:
            tmp = {}
            for word in sent:
                if word not in tmp:
                    tmp[word] = 1
                else:
                    tmp[word] += 1
            tmp = {word:word_count/sum(tmp.values()) for word, word_count in tmp.items()}
            for word in set(sent):
                if word not in df_dict:
                    df_dict[word] = 1
                else:
                    df_dict[word] += 1
            sent_list.append(tmp)
        df_dict = {word :math.log(len(sents)/df+1) for word, df in df_dict.items()}
        words = list(df_dict.keys())
        for sent in sent_list:
            tmp = []
            for word in words:
                tmp.append(sent.get(word, 0))
            tfidf_list.append(tmp)
        return tfidf_list
    

    (3)使用余弦相似度算法计算相似度

    def cosine_sim(self, vector1, vector2):
        #向量1和向量2的内积
        cos1 = np.sum(vector1 * vector2)
        #求向量1与向量1的内积,再开方
        cos21 = np.sqrt(sum(vector1 ** 2))
        #求向量2与向量2的内积,再开方
        cos22 = np.sqrt(sum(vector2 ** 2))
        #计算相似度
        similarity = cos1 / float(cos21 * cos22)
        return similarity
    

    3.运行结果

    测试结果显示,文本相似度都非常高emmmmm
    因为在基于VSM的算法中,文本的内部结构对于相似度计算的影响不大,所以所有测试样例中的乱序现象不会降低相似度的计算结果。另外,算法较为简陋,没有对停用词和标点进行处理,或许也是文本相似度结果这么高的原因。(也是性能改进的方向)

    4.性能测试

    (1)时间

    使用pycharm自带的性能测试工具Profile对代码进行分析:

    统计图如下:

    函数调用图如下:

    最耗时的函数及其耗时:

    main函数耗时竟然高达6秒Orz,或许可以通过消去标点或者优化代码等方法来提速吧(瞎猜Orz)

    (2)空间

    大佬们用的内存分析工具我暂时用不来,使用如下两行代码来分析内存的使用以及消耗的CPU时间(虽说上面已经在时间方面进行了测试,但是既然知道了这个方法,不如也用一下)

    # 性能分析
    print(u'当前进程的内存使用:%.4f MB' % (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024) )
    print(u'当前进程的使用的CPU时间:%.4f s' % (psutil.Process(os.getpid()).cpu_times().user) )
    

    运行结果如下:

    很奇怪,为什么这里显示的时间就比profile分析出来的时间少多了,或许是因为这里计算的是进程占用的CPU时间而不是实际时间?

    (3)代码覆盖率

    使用coverage来进行测试:

    结果中列出了许多项,可以看到最后一行的main.py,其覆盖率为100% 。

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

    前面已经说过,可以从去除停用词和标点、优化代码等方面来改进性能。由于种种原因(人太菜Orz,花在写代码方面的时间太少,用在解决各种小问题的时间太多),暂未改进。

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

    单元测试中使用的测试数据全部是测试组提供的样例数据。先亮出单元测试的结果:

    先前曾进行过测试,耗时一栏在8s左右,人都傻了。之后又进行了一次单元测试,测试结果的耗时在2s左右,猜测是因为计算机上的进程太多,导致单元测试用的时间较长。

    单元测试代码

    这里使用的是python自带的标准单元测试库——unittest库。看了大佬的博客后,我选择使用BeautifulReport报告库,可以生成html格式的测试结果报告。
    Unittest-测试运行:查看测试结果

    单元测试内部函数test_inside:

    #!/usr/bin/env python3
    # coding: utf-8
    import jieba.posseg as pesg
    import math
    import numpy as np
    import sys
    import os
    
    class SimVsm:
        '''比较相似度'''
        def distance(self, text1, text2):
            words1 = [word.word for word in pesg.cut(text1) if word.flag[0] not in ['u', 'x', 'w']]
            words2 = [word.word for word in pesg.cut(text2) if word.flag[0] not in ['u', 'x', 'w']]
            tfidf_reps = self.tfidf_rep([words1, words2])
            return self.cosine_sim(np.array(tfidf_reps[0]), np.array(tfidf_reps[1]))
    
        '''对句子进行tfidf向量表示'''
        def tfidf_rep(self, sents):
            sent_list = []
            df_dict = {}
            tfidf_list = []
            for sent in sents:
                tmp = {}
                for word in sent:
                    if word not in tmp:
                        tmp[word] = 1
                    else:
                        tmp[word] += 1
                tmp = {word:word_count/sum(tmp.values()) for word, word_count in tmp.items()}
                for word in set(sent):
                    if word not in df_dict:
                        df_dict[word] = 1
                    else:
                        df_dict[word] += 1
                sent_list.append(tmp)
            df_dict = {word :math.log(len(sents)/df+1) for word, df in df_dict.items()}
            words = list(df_dict.keys())
            for sent in sent_list:
                tmp = []
                for word in words:
                    tmp.append(sent.get(word, 0))
                tfidf_list.append(tmp)
            return tfidf_list
    
        '''余弦相似度计算相似度'''
        def cosine_sim(self, vector1, vector2):
            cos1 = np.sum(vector1 * vector2)
            cos21 = np.sqrt(sum(vector1 ** 2))
            cos22 = np.sqrt(sum(vector2 ** 2))
            similarity = cos1 / float(cos21 * cos22)
            return similarity
    
    
    def test(s_position, d_position, ans_position):
    
        f1 = open(s_position, "rt", encoding = 'UTF-8')
        f2 = open(d_position, "rt", encoding = 'UTF-8')
        f3 = open(ans_position, "a+", encoding = 'UTF-8')
    
        txt1 = f1.read()
        txt2 = f2.read()
    
        simer = SimVsm()
        sim = simer.distance(txt1, txt2)
        print('查重结果为%.2f'%sim)
    
        f3.write("sim_0.8orig.txt, ")
        f3.write(d_position)
        f3.write(str("  Similarity: %.2f"%sim)+'
    ')
        f1.close()
        f2.close()
        f3.close()
    

    单元测试函数test_vsm:

    import unittest
    import test_inside
    from BeautifulReport import BeautifulReport
    
    class MyTestCase(unittest.TestCase):
        def setUp(self):
            print("开始单元测试:")
    
        def tearDown(self):
            print("测试结束")
    
        def test_self(self):
            print("正在载入orig.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig.txt', 'ans.txt')
    
        def test_add(self):
            print("正在载入orig_0.8_add.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_add.txt', 'ans.txt')
    
        def test_del(self):
            print("正在载入orig_0.8_del.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_del.txt', 'ans.txt')
    
        def test_dis_1(self):
            print("正在载入orig_0.8_dis_1.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_dis_1.txt', 'ans.txt')
    
        def test_dis_3(self):
            print("正在载入orig_0.8_dis_3.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_dis_3.txt', 'ans.txt')
    
        def test_dis_7(self):
            print("正在载入orig_0.8_dis_7.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_dis_7.txt', 'ans.txt')
    
        def test_dis_10(self):
            print("正在载入orig_0.8_dis_10.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_dis_10.txt', 'ans.txt')
    
        def test_dis_15(self):
            print("正在载入orig_0.8_dis_15.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_dis_15.txt', 'ans.txt')
    
        def test_mix(self):
            print("正在载入orig_0.8_mix.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_mix.txt', 'ans.txt')
    
        def test_rep(self):
            print("正在载入orig_0.8_rep.txt")
            test_inside.test('sim_0.8orig.txt', 'sim_0.8orig_0.8_rep.txt', 'ans.txt')
    
    if __name__ == '__main__':
        #unittest.main()
        suite = unittest.TestSuite()
        suite.addTest(MyTestCase('test_self'))
        suite.addTest(MyTestCase('test_add'))
        suite.addTest(MyTestCase('test_del'))
        suite.addTest(MyTestCase('test_dis_1'))
        suite.addTest(MyTestCase('test_dis_3'))
        suite.addTest(MyTestCase('test_dis_7'))
        suite.addTest(MyTestCase('test_dis_10'))
        suite.addTest(MyTestCase('test_dis_15'))
        suite.addTest(MyTestCase('test_mix'))
        suite.addTest(MyTestCase('test_rep'))
        runner = BeautifulReport(suite)
        runner.report
        (
            description = "论文查重单元测试报告", #报告描述
            filename = 'sim_vsm.html', #生成的报告文件名
            log_path = '.'  #报告路径
        )
    

    测试覆盖率

    使用coverage来进行测试:

    结果中列出了许多项,可以看到最后两行的test_inside.py和test_vsm.py,其覆盖率都为100% 。

    四、计算模块部分异常处理

    经过一番百度+看了大佬的博客后,发现计算模块可能出现异常的情况:空文本、无汉字文本等。
    具体异常处理模块暂无Orz

    五、PSP表格

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

    六、总结

    一次从零开始的编程体验,从一开始vscode都不会用,到最后勉强完成作业,尽管惨不忍睹,但还是学到了不少东西。程序成功运行时我天真地以为作业就快要完成了,但实事告诉我,这才完成了一小部分。性能测试、单元测试、异常处理、性能优化等等都是要面对的事。路漫漫其修远兮,吾将上下而求索。

  • 相关阅读:
    初识java反射机制
    基本数据类型-保装类型-string三种数据类型的转换
    java 正则表达式
    java 异常
    java 初识String
    java 接口
    java抽象
    初识多态 简单理解
    初来驾到学JAVA继承初识
    转载:Gearman php
  • 原文地址:https://www.cnblogs.com/Luotong-cnblogs/p/13686743.html
Copyright © 2011-2022 走看看