文本相似度计算基本方法小结

在计算文本相似项发现方面,有以下一些可参考的方法。这些概念和方法会帮助我们开拓思路。

相似度计算方面

Jaccard相似度:集合之间的Jaccard相似度等于交集大小与并集大小的比例。适合的应用包括文档文本相似度以及顾客购物习惯的相似度计算等。

Shingling:k-shingle是指文档中连续出现的任意k个字符。如果将文档表示成其k-shingle集合,那么就可以基于集合之间的 Jaccard相似度来计算文档之间的文本相似度。有时,将shingle哈希成更短的位串非常有用,可以基于这些哈希值的集合来表示文档。

最小哈希:集合上的最小哈希函数基于全集上的排序转换来定义。给定任意一个排列转换,集合的最小哈希值为在排列转换次序下出现的第一个集合元素。

最小哈希签名:可以选出多个排列转换,然后在每个排列转换下计算集合的最小哈希值,这些最小哈希值序列构成集合的最小哈希签名。给定两个集合,产生相同哈希值的排列转换所占的期望比率正好等于集合之间的Jaccard相似度。

高效最小哈希:由于实际不可能产生随机的排列转换,因此通常会通过下列方法模拟一个排列转换:选择一个随机哈希函数,利用该函数对集合中所有的元素进行哈希操作,其中得到的最小值看成是集合的最小哈希值。

签名的局部敏感哈希:该技术可以允许我们避免计算所有集合对或其最小哈希签名对之间的相似度。给定集合的签名,我们可以将它们划分成行条,然后仅仅计算至少有一个行条相等的集合对之间的相似度。通过合理选择行条大小,可以消除不满足相似度阈值的大部分集合对之间的比较。

向量空间距离方面

欧式距离:n维空间下的欧式距离,是两个点在各维上差值的平方和的算数平方根。适合欧式空间的另一个距离是曼哈顿距离,指两个点各维度的差的绝对值之和。

Jaccard距离:1减去Jaccard相似度也是一个距离测度。

余弦距离:向量空间下两个向量的夹角大小。

编辑距离:该距离测度应用于字符串,指的是通过需要的插入、删除操作将一个字符串处理成另一个字符串的操作次数。编辑距离还可以通过两个字符串长度之和减去两者最长公共子序列长度的两倍来计算。

海明距离:应用于向量空间。两个向量之间的海明距离计算的是它们之间不相同的位置个数。

索引辅助方面

字符索引:如果将集合表示成字符串,且需要达到的相似度阈值接近1。那么就可以将每个字符串按照其头部的一小部分字母建立索引。需要索引的前缀的长度大概等于整个字符串的长度乘以给定的最大的Jaccard距离。

位置索引:我们不仅可以给出索引字符串前缀中的字符,也可以索引其在前缀中的位置。如果两个字符串共有的一个字符并不出现在双方的第一个位置,那么我们就知道要么存在某些前面的字符出现在并集但不出现在交集中,那么在两个字符串中存在一个更前面的公共字符。这样的话,我们就可以减少需要比较的字符串对数目。

后缀索引:我们不仅可以索引字符串前缀中的字符及其位置,还可以索引当前字符后缀的长度,即字符串中该字符之后的位置数量。由于相同字符但是后缀长度不同意味着有额外的字符必须出现在并集但不出现在交集中,因此上述结构能够进一步减少需要比较的字符串数目。

总结

以上的一些概念和方法可以配合使用,可以基本满足许多场景下的相似度计算。相似度计算又可以为相关推荐做基础。怎么做好词的粒度切分,怎么划定阈值,选择何种距离测算,如何优化实现方法还是要下很多功夫的。

两个例子

Levenshtein其实是编辑距离,下面计算编辑距离的方法是把两个String串里的字/词当成一个矩阵来比较和计算。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

public class LevenshteinDis { 

  

    public static void main(String[] args) { 

        // 要比较的两个字符串 

        String str1 = "相似度计算方法"

        String str2 = "文本相似项发现"

        levenshtein(str1, str2); 

    

  

    public static void levenshtein(String str1, String str2) { 

  

        int len1 = str1.length(); 

        int len2 = str2.length(); 

  

        int[][] dif = new int[len1 + 1][len2 + 1]; 

  

        for (int a = 0; a <= len1; a++) { 

            dif[a][0] = a; 

        

        for (int a = 0; a <= len2; a++) { 

            dif[0][a] = a; 

        

          

        int temp; 

        for (int i = 1; i <= len1; i++) { 

            for (int j = 1; j <= len2; j++) { 

                if (str1.charAt(i - 1) == str2.charAt(j - 1)) { 

                    temp = 0

                else

                    temp = 1

                

                // 取三个值中最小的 

                dif[i][j] = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1

                        dif[i - 1][j] + 1); 

            

        

        System.out.println("字符串\"" + str1 + "\"与\"" + str2 + "\"的比较"); 

        System.out.println("差异步骤:" + dif[len1][len2]); 

        // 计算相似度 

        float similarity = 1 - (float) dif[len1][len2] 

                / Math.max(str1.length(), str2.length()); 

        System.out.println("相似度:" + similarity); 

    

  

    private static int min(int... is) { 

        int min = Integer.MAX_VALUE; 

        for (int i : is) { 

            if (min > i) { 

                min = i; 

            

        

        return min; 

    

  

}

下面是余弦距离计算的例子:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

public class CosineSimilarAlgorithm { 

    public static double getSimilarity(String doc1, String doc2) { 

        if (doc1 != null && doc1.trim().length() > 0 && doc2 != null

                && doc2.trim().length() > 0) { 

              

            Map<Integer, int[]> AlgorithmMap = new HashMap<Integer, int[]>(); 

              

            //将两个字符串中的中文字符以及出现的总数封装到,AlgorithmMap中 

            for (int i = 0; i < doc1.length(); i++) { 

                char d1 = doc1.charAt(i); 

                if(isHanZi(d1)){ 

                    int charIndex = getGB2312Id(d1); 

                    if(charIndex != -1){ 

                        int[] fq = AlgorithmMap.get(charIndex); 

                        if(fq != null && fq.length == 2){ 

                            fq[0]++; 

                        }else

                            fq = new int[2]; 

                            fq[0] = 1

                            fq[1] = 0

                            AlgorithmMap.put(charIndex, fq); 

                        

                    

                

            

  

            for (int i = 0; i < doc2.length(); i++) { 

                char d2 = doc2.charAt(i); 

                if(isHanZi(d2)){ 

                    int charIndex = getGB2312Id(d2); 

                    if(charIndex != -1){ 

                        int[] fq = AlgorithmMap.get(charIndex); 

                        if(fq != null && fq.length == 2){ 

                            fq[1]++; 

                        }else

                            fq = new int[2]; 

                            fq[0] = 0

                            fq[1] = 1

                            AlgorithmMap.put(charIndex, fq); 

                        

                    

                

            

              

            Iterator<Integer> iterator = AlgorithmMap.keySet().iterator(); 

            double sqdoc1 = 0

            double sqdoc2 = 0

            double denominator = 0;  

            while(iterator.hasNext()){ 

                int[] c = AlgorithmMap.get(iterator.next()); 

                denominator += c[0]*c[1]; 

                sqdoc1 += c[0]*c[0]; 

                sqdoc2 += c[1]*c[1]; 

            

              

            return denominator / Math.sqrt(sqdoc1*sqdoc2); 

        else

            throw new NullPointerException( 

                    " the Document is null or have not cahrs!!"); 

        

    

  

    public static boolean isHanZi(char ch) { 

        // 判断是否汉字 

        return (ch >= 0x4E00 && ch <= 0x9FA5); 

  

    

  

    /**

     * 根据输入的Unicode字符,获取它的GB2312编码或者ascii编码,

     

     * @param ch

     *            输入的GB2312中文字符或者ASCII字符(128个)

     * @return ch在GB2312中的位置,-1表示该字符不认识

     */

    public static short getGB2312Id(char ch) { 

        try

            byte[] buffer = Character.toString(ch).getBytes("GB2312"); 

            if (buffer.length != 2) { 

                // 正常情况下buffer应该是两个字节,否则说明ch不属于GB2312编码,故返回‘?‘,此时说明不认识该字符 

                return -1

            

            int b0 = (int) (buffer[0] & 0x0FF) - 161// 编码从A1开始,因此减去0xA1=161 

            int b1 = (int) (buffer[1] & 0x0FF) - 161// 第一个字符和最后一个字符没有汉字,因此每个区只收16*6-2=94个汉字 

            return (short) (b0 * 94 + b1); 

        catch (UnsupportedEncodingException e) { 

            e.printStackTrace(); 

        

        return -1

    

}

来自:http://blog.csdn.net/pelick/article/details/8741346

时间: 2024-10-14 00:19:58

文本相似度计算基本方法小结的相关文章

java文本相似度计算(Levenshtein Distance算法(中文翻译:编辑距离算法))----代码和详解

算法代码实现: package com.util; public class SimFeatureUtil { private static int min(int one, int two, int three) { int min = one; if (two < min) { min = two; } if (three < min) { min = three; } return min; } public static int ld(String str1, String str2)

字符串相似度计算的方法,使用SQL以及C#实现,本文非原创摘自网络(.NET SQL技术交流群入群206656202需注明博客园)

1 CREATE function get_semblance_By_2words 2 ( 3 @word1 varchar(50), 4 @word2 varchar(50) 5 ) 6 returns nvarchar(4000) 7 as 8 begin 9 declare @re int 10 declare @maxLenth int 11 declare @i int,@l int 12 declare @tb1 table(child varchar(50)) 13 declare

word2vec词向量训练及中文文本相似度计算

本文是讲述如何使用word2vec的基础教程,文章比较基础,希望对你有所帮助! 官网C语言下载地址:http://word2vec.googlecode.com/svn/trunk/ 官网Python下载地址:http://radimrehurek.com/gensim/models/word2vec.html 1.简单介绍 参考:<Word2vec的核心架构及其应用 · 熊富林,邓怡豪,唐晓晟 · 北邮2015年> <Word2vec的工作原理及应用探究 · 周练 · 西安电子科技大学

利用word分词来计算文本相似度

word分词提供了两种文本相似度计算方式: 方式一:余弦相似度,通过计算两个向量的夹角余弦值来评估他们的相似度 实现类:org.apdplat.word.analysis.CosineTextSimilarity 用法如下: String text1 = "我爱学习"; String text2 = "我爱读书"; String text3 = "他是黑客"; TextSimilarity textSimilarity = new CosineT

【NLP】Python实例:基于文本相似度对申报项目进行查重设计

Python实例:申报项目查重系统设计与实现 作者:白宁超 2017年5月18日17:51:37 摘要:关于查重系统很多人并不陌生,无论本科还是硕博毕业都不可避免涉及论文查重问题,这也对学术不正之风起到一定纠正作用.单位主要针对科技项目申报审核,传统的方式人力物力比较大,且伴随季度性的繁重工作,效率不高.基于此,单位觉得开发一款可以达到实用的智能查重系统.遍及网络文献,终未得到有价值的参考资料,这个也是自然.首先类似知网,paperpass这样的商业公司其毕业申报专利并进行保密,其他科研单位因发

【java算法】---余弦相似度计算字符串相似率

余弦相似度计算字符串相似率 功能需求:最近在做通过爬虫技术去爬取各大相关网站的新闻,储存到公司数据中.这里面就有一个技术点,就是如何保证你已爬取的新闻,再有相似的新闻 或者一样的新闻,那就不存储到数据库中.(因为有网站会去引用其它网站新闻,或者把其它网站新闻拿过来稍微改下内容就发布到自己网站中). 解析方案:最终就是采用余弦相似度算法,来计算两个新闻正文的相似度.现在自己写一篇博客总结下. 一.理论知识 先推荐一篇博客,对于余弦相似度算法的理论讲的比较清晰,我们也是按照这个方式来计算相似度的.网

海量数据相似度计算之simhash和海明距离

通过 采集系统 我们采集了大量文本数据,但是文本中有很多重复数据影响我们对于结果的分析.分析前我们需要对这些数据去除重复,如何选择和设计文本的去重算法?常见的有余弦夹角算法.欧式距离.Jaccard相似度.最长公共子串.编辑距离等.这些算法对于待比较的文本数据不多时还比较好用,如果我们的爬虫每天采集的数据以千万计算,我们如何对于这些海量千万级的数据进行高效的合并去重.最简单的做法是拿着待比较的文本和数据库中所有的文本比较一遍如果是重复的数据就标示为重复.看起来很简单,我们来做个测试,就拿最简单的

python 用gensim进行文本相似度分析

http://blog.csdn.net/chencheng126/article/details/50070021 参考于这个博主的博文. 原理 1.文本相似度计算的需求始于搜索引擎. 搜索引擎需要计算"用户查询"和爬下来的众多"网页"之间的相似度,从而把最相似的排在最前返回给用户. 2.主要使用的算法是tf-idf tf:term frequency 词频 idf:inverse document frequency 倒文档频率 主要思想是:如果某个词或短语在一

【转】海量数据相似度计算之simhash和海明距离

通过 采集系统 我们采集了大量文本数据,但是文本中有很多重复数据影响我们对于结果的分析.分析前我们需要对这些数据去除重复,如何选择和设计文本的去重算法?常见的有余弦夹角算法.欧式距离.Jaccard相似度.最长公共子串.编辑距离等.这些算法对于待比较的文本数据不多时还比较好用,如果我们的爬虫每天采集的数据以千万计算,我们如何对于这些海量千万级的数据进行高效的合并去重.最简单的做法是拿着待比较的文本和数据库中所有的文本比较一遍如果是重复的数据就标示为重复.看起来很简单,我们来做个测试,就拿最简单的