CosineSimilarity

余弦相似度

implementation 'org.apache.commons:commons-text:1.10.0'

    Measures the Cosine similarity of two vectors of an inner product space and compares the angle between them.
    For further explanation about the Cosine Similarity, refer to http://en.wikipedia.org/wiki/Cosine_similarity.
    Since:
    1.0

    百度百科:
    余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。
    其余弦相似性θ可以推倒得出:

    以程序为例:
    已知两个字符串
    a = "Hello World"

    b = "Hello Shaun Murphy Hello World"

    其内部有一个工具类CosineDistance.class

    1. 分别提取a和b中有多少单词,并对单词进行计数

    此时两个句子共有Hello,在a中出现频率为1,在b中出现频率为2,World在a中出现1次,在b中出现1次

    final CharSequence[] leftTokens = tokenizer.tokenize(left);
    final CharSequence[] rightTokens = tokenizer.tokenize(right);
    final Map<CharSequence, Integer> leftVector = Counter.of(leftTokens);
    final Map<CharSequence, Integer> rightVector = Counter.of(rightTokens);

      2. 求公式中的分子

      Hello单词在a中出现的次数在b中出现的次数 + World单词在a中出现的次数在b中出现的次数之和
      1 * 2 + 1 * 1 = 3

      private Set<CharSequence> getIntersection(final Map<CharSequence, Integer> leftVector,
      final Map<CharSequence, Integer> rightVector) {
      final Set<CharSequence> intersection = new HashSet<>(leftVector.keySet());
      intersection.retainAll(rightVector.keySet());
      return intersection;
      }

        先拿到两个句子公共单词,然后对两个句子的公共部分分别计数

        private double dot(final Map<CharSequence, Integer> leftVector, final Map<CharSequence, Integer> rightVector,
        final Set<CharSequence> intersection) {
        long dotProduct = 0;
        for (final CharSequence key : intersection) {
        dotProduct += leftVector.get(key) * (long) rightVector.get(key);
        }
        return dotProduct;
        }

          3. 求公式中的分母

          依旧是两个求和在相加,但这次是对各自的单词出现的次数的平方进行求和
          d1 = 1 * 1 + 1 * 1 = 2
          d2 = 2 * 2 + 1 * 1 + 1 * 1 + 1 * 1 = 7

          double d1 = 0.0d;
          for (final Integer value : leftVector.values()) {
          d1 += Math.pow(value, 2);
          }
          double d2 = 0.0d;
          for (final Integer value : rightVector.values()) {
          d2 += Math.pow(value, 2);
          }

            4. 求出相似度

            余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量的方向越接近;越趋近于-1,他们的方向越相反;接近于0,表示两个向量近乎于正交。

            两数相除得出相似度,这里对小于零的情况(这里不会发生)也做了判断
            cosineSimilarity ≈ 3 / 3.74166 ≈ 0.80

            final double cosineSimilarity;
            if (d1 <= 0.0 || d2 <= 0.0) {
            cosineSimilarity = 0.0;
            } else {
            cosineSimilarity = dotProduct / (Math.sqrt(d1) * Math.sqrt(d2));
            }

              5. 补充

              ①. 最后求CosineDistance的时候,用的是:

              1.0 - similarity

                这里表示两个字符序列的距离,距离越远,越不相似。
                ②. CosineDistance内部使用的正则表达式,不包含中文,因此测试用例用的英文

                final class RegexTokenizer implements Tokenizer<CharSequence> {
                /** The whitespace pattern. */
                private static final Pattern PATTERN = Pattern.compile("(\\w)+");
                /**
                * {@inheritDoc}
                *
                * @throws IllegalArgumentException if the input text is blank
                */
                @Override
                public CharSequence[] tokenize(final CharSequence text) {
                Validate.isTrue(StringUtils.isNotBlank(text), "Invalid text");
                final Matcher matcher = PATTERN.matcher(text);
                final List<String> tokens = new ArrayList<>();
                while (matcher.find()) {
                tokens.add(matcher.group(0));
                }
                return tokens.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
                }
                }
                  posted @   干翻苍穹  阅读(195)  评论(0编辑  收藏  举报
                  相关博文:
                  阅读排行:
                  · 全程不用写代码,我用AI程序员写了一个飞机大战
                  · DeepSeek 开源周回顾「GitHub 热点速览」
                  · 记一次.NET内存居高不下排查解决与启示
                  · MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
                  · .NET10 - 预览版1新功能体验(一)
                  点击右上角即可分享
                  微信分享提示