美文网首页Python
Python实现文本过滤去重

Python实现文本过滤去重

作者: Donald_32e5 | 来源:发表于2019-02-19 18:36 被阅读0次

    背景

    • 爬虫会获取大量的数据,为不浪费资源,过滤重复数据就很有必要了

    算法简介

    • 1、余弦相似性
      · 通过两个向量的夹角余弦值来评估相似度,应用较广泛

    • 2、欧几里得距离
      · 用使用较为广泛的公式来评估相似度

    • 3、简单共有词
      · 比较基础的文本过滤算法。使用两篇文档共有的字符,除以较长的那一篇文章来评估相似度

    • 4、编辑距离
      · 通过计算两字符串之间,由一个转成另外一个所需编辑的次数来评估它们的相似度,又称Levenshtein距离

    • 5、Simhash+汉明距离
      · 通过对比两篇文章hash串的汉明距离来评估相似度,多用于文档层面去重

    算法详情

    I、余弦相似性

    具体的公式就不贴出来了,外面一大堆,主要记录一下Python的实现方式

    • 1、首先进行分词,并列出两个句子的并集
    • 2、计算词频的向量
    • 4、计算余弦值,余弦值越大,证明夹角越小,两个向量越相似
    • 5、代码实现,也就是TF-IDF算法
      def words2vec(words1=None, words2=None):
            """
            通过分词、计算并集、词频,得出词频向量
            :param words1:
            :param words2:
            :return: 词频的向量
            """
            v1 = []
            v2 = []
            tag1 = jieba.analyse.extract_tags(words1, withWeight=True)
            tag2 = jieba.analyse.extract_tags(words2, withWeight=True)
            tag_dict1 = {i[0]: i[1] for i in tag1}
            tag_dict2 = {i[0]: i[1] for i in tag2}
            merged_tag = set(tag_dict1.keys()) | set(tag_dict2.keys())
            for i in merged_tag:
                if i in tag_dict1:
                    v1.append(tag_dict1[i])
                else:
                    v1.append(0)
                if i in tag_dict2:
                    v2.append(tag_dict2[i])
                else:
                    v2.append(0)
            return v1, v2
    
    • 6、使用jieba分词计算出语句的向量

    • 7、 根据向量计算余弦值

        def cosine_similarity(vector1, vector2):
            """
            余弦值相似度计算
            :param vector1:
            :param vector2:
            :return:
            """
    
            dot_product = 0.0
            norm1 = 0.0
            norm2 = 0.0
            for a, b in zip(vector1, vector2):
                dot_product += a * b
                norm1 += a ** 2
                norm2 += b ** 2
            if norm1 == 0.0 or norm2 == 0.0:
                return 0
            else:
                return round(dot_product / ((norm1 ** 0.5) * (norm2 ** 0.5)) * 100, 2)
    

    II、欧几里得距离

    • 1、首先也需要获得比较对象的向量,代码可以和上面的通用
    • 2、根据向量,和欧几里得公式,计算出距离,使用1/(1+距离)算得相似度
    • 3、代码实现
        def euclid_similarity(self, vector1, vector2):
            """
            欧几里得相似度算法
            :return:
            """
            v1, v2 = self.words2vec(vector1, vector2)
            similarity = 0.0
            for a, b in zip(v1, v2):
                d1 = np.sqrt(np.sum(np.square(a - b)))
                similarity = d1 if d1 > similarity else similarity
            print(1/(1+similarity))
    

    III、编辑距离

    • 1、Levenshtein有第三方包,可以通过pip install python-Levenshtein
    • 2、计算相似度,使用1-(距离/较长的语句)计算得出相似度
    • 3、代码实现
    import Levenshtein
    aa = Levenshtein(a, b)
    max_len = len(a) if len(a) > len(b) else len(b)
    print(1- aa/max_len)
    

    IV、Simhash+汉明距离

    • 1、谷歌的引擎爬虫每天获取的海量数据,就是用的Simhash来进行过滤的
    • 2、他的基本原理是把两篇文档转换成字节,不过不是用普通的hash来进行转换
    • 3、Simhash是一种局部敏感hash,所谓的局部敏感,就是在两个对比对象在hash后,任能保持之前的相似性
    • 4、被hash之后的hash串的长度是相同的,通过汉明距的比较,就可以判断相似度了
    • 5、代码实现
    class Simhash:
        """经过测试和查找资料发现,Simhash更加适合文档去重,文本越短,精度缺失越厉害"""
    
        def __init__(self, content):
            self.simhash = self.simhash(content)
    
        def __str__(self):
            return str(self.simhash)
    
        def simhash(self, content):
            jieba.load_userdict('userdict.text')
            seg = jieba.cut(content)
            jieba.analyse.set_stop_words('testtimi.text')
            key_word = jieba.analyse.extract_tags(
                '|'.join(seg), topK=20, withWeight=True, allowPOS=())  # 在这里对jieba的tfidf.py进行了修改
            # 将tags = sorted(freq.items(), key=itemgetter(1), reverse=True)修改成tags = sorted(freq.items(), key=itemgetter(
            # 1,0), reverse=True) 即先按照权重排序,再按照词排序
            key_list = []
            # print(key_word)
            for feature, weight in key_word:
                weight = int(weight * 20)
                feature = self.string_hash(feature)
                temp = []
                for i in feature:
                    if i == '1':
                        temp.append(weight)
                    else:
                        temp.append(-weight)
                # print(temp)
                key_list.append(temp)
            list1 = np.sum(np.array(key_list), axis=0)
            # print(list1)
            if not key_list:  # 编码读不出来
                return '00'
            simhash = ''
            for i in list1:
                if i > 0:
                    simhash = simhash + '1'
                else:
                    simhash = simhash + '0'
            return simhash
    
        @staticmethod
        def string_hash(source):
            if source == "":
                return 0
            else:
                x = ord(source[0]) << 7
                m = 1000003
                mask = 2 ** 128 - 1
                for c in source:
                    x = ((x * m) ^ ord(c)) & mask
                x ^= len(source)
                if x == -1:
                    x = -2
                x = bin(x).replace('0b', '').zfill(64)[-64:]
                # print(source, x)
    
                return str(x)
    
            # 以下是使用系统自带hash生成,虽然每次相同的会生成的一样,
            # 不过,对于不同的汉子产生的二进制,在计算海明码的距离会不一样,
            # 即每次产生的海明距离不一致
            # 所以不建议使用。
    
            # x=str(bin(hash(source)).replace('0b','').replace('-','').zfill(64)[-64:])
            # print(source,x,len(x))
            # return x
    
        def hammingDis(self, com):
            t1 = '0b' + self.simhash
            t2 = '0b' + com.simhash
            n = int(t1, 2) ^ int(t2, 2)
            i = 0
            while n:
                n &= (n - 1)
                i += 1
            return i
    
    • 6、最后的计算汉明距,也可以使用Levenshtein的hamming(str1,str2)方法
    • 7、 如果汉明距小于3,基本就说明文本的相似度很高

    总结

    • 1、如果需要比较的是短文本,余弦相似性是最好的选择,可以根据具体的业务,来判断使用哪种算法
    • 2、Simhash在文档比较中表现强劲,但是随着比较字符的越来越短,它的精度就流失的越厉害

    相关文章

      网友评论

        本文标题:Python实现文本过滤去重

        本文链接:https://www.haomeiwen.com/subject/hudfyqtx.html