文本相似度计算之余弦定理

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

前言

余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中。用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,反之越接近0就表示两个向量相似度越低,这就叫"余弦相似性"。

正文

重温余弦定理

先简单的重温一下高中数学知识,余弦定理

这个公式大家不知道还有没有印象呢?没有的话我们看下下面的图

此时a=(xa,ya),b=(xb,0),那么怎么计算各边长的长度呢?

此时将各边长代入上图的公式当中,最后可以得出最终的计算公式

文本相似度计算步骤

那么在我们的文本相似度计算中,都有哪些步骤呢?

maven 引入ikanalyzer依赖

这里使用ikanalyzer来实现一个简单的分词功能

 <dependency>
    <groupId>com.janeluo</groupId>
    <artifactId>ikanalyzer</artifactId>
    <version>2012_u6</version>
</dependency>

IKUtils分词工具类,代码比简单,唯一一个方法返回的是语句分词的List对象

/**
 * 分词相关工具类
 * @author wangzh
 */
public class IKUtils {

    /**
     * 以List的格式返回文本分词的结果
     * @param text
     * @return
     */
    public static List<String> divideText(String text){
        if(null == text || "".equals(text.trim())){
            return null;
        }
        List<String> resultList = new ArrayList<>();
        StringReader re = new StringReader(text);
        IKSegmenter ik = new IKSegmenter(re, true);
        Lexeme lex = null;
        try {
            while ((lex = ik.next()) != null) {
                resultList.add(lex.getLexemeText());
            }
        } catch (Exception e) {
            //TODO
        }
        return resultList;
    }

}

下面是主要的代码逻辑,相关步骤已注释在代码里面

public class Analysis {
    public static void main(String[] args) {
        Map<String,int[]> resultMap = new HashMap<>();
        //测试文本
        String text1 = "你好,我是小王,我是个程序员";
        String text2 = "你好,我是设计师";
        //统计
        statistics(resultMap, IKUtils.divideText(text1),1);
        statistics(resultMap, IKUtils.divideText(text2),0);
        //计算类
        final Calculation calculation = new Calculation();
        resultMap.forEach((k,v)->{
            int[] arr = resultMap.get(k);
            calculation.setNumerator(calculation.getNumerator() + arr[0] * arr[1]);
            calculation.setElementA(calculation.getElementA() + arr[0] * arr[0]);
            calculation.setElementB(calculation.getElementB() + arr[1] * arr[1]);
        });

       System.out.println("文本相似度:" + calculation.result());
    }

    /**
     * 组合词频向量
     
     * @param words
     * @param direction
     * @return
     */
    private static void statistics(Map<String,int[]> map,List<String> words ,int direction){
        if(null == words || words.size() == 0){
            return ;
        }
        int[] in = null;
        boolean flag = direction(direction);
        for (String word : words){
            int[] wordD = map.get(word);
            if(null == wordD){
                if(flag){
                    in = new int[]{1,0};
                }else {
                    in = new int[]{0,1};
                }
                map.put(word,in);
            }else{
                if(flag){
                    wordD[0]++;
                }else{
                    wordD[1]++;
                }
            }
        }
    }
    
    //判断不同句子
    private static boolean direction(int direction){
        return direction == 1?true:false;
    }

}

用于计算余弦相似度的类

public class Calculation{

    private  double elementA;
    private  double elementB;
    private  double numerator;

    public double result(){
        return numerator / Math.sqrt(elementA * elementB);
    }
    //省略get/set
}

输出结果:

文本相似度:0.7216878364870323

从结果可以看出这两句话大致上还是比较相似的。用通俗一点的话来说就是有72%的相似度。

参考图例:

https://www.jianshu.com/p/f4606ae118b3


公众号博文同步Github仓库,有兴趣的朋友可以帮忙给个Star哦,码字不易,感谢支持。

https://github.com/PeppaLittlePig/blog-wechat

推荐阅读

如何提高使用Java反射的效率?
Java日志正确使用姿势
论JVM爆炸的几种姿势及自救方法

有收获的话,就点个赞吧

关注「深夜里的程序猿」,分享最干的干货

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看