贫瘠之地

华北无浪漫,死海扬起帆
多少个夜晚,独自望着天

0%

简单了解相似性度量

相似性度量 Similarity Metric

了解不同相似性度量方式之间的区别和优缺点,为最佳相似性度量方式选择做出明智的决定

在下表中,您可以看到我们将在本文中讨论的相似性度量以及影响度量的向量的属性

Similarity Metric 使用向量特征
欧几里得距离 角度和方向
余弦相似度 方向
点基相似度 角度和方向

欧几里得距离 Euclidean distance

欧几里得距离是多维空间中两个向量之间的直线距离,计算向量相应分量之间的差的平方和的根

平面空间中的距离

向量之间的距离

Java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class EuclideanDistance {

public static double calculateDistance(double[] vector1, double[] vector2) {
if (vector1.length != vector2.length) {
throw new IllegalArgumentException("Vector dimensions must be the same");
}

double sum = 0.0;
for (int i = 0; i < vector1.length; i++) {
double difference = vector1[i] - vector2[i];
sum += difference * difference;
}

return Math.sqrt(sum);
}

public static void main(String[] args) {
double[] vector1 = {1.0, 2.0, 3.0};
double[] vector2 = {4.0, 5.0, 6.0};

double distance = calculateDistance(vector1, vector2);
System.out.println("Euclidean distance: " + distance);
}
}
  • 优势
    • 简单直观
    • 对角度和方向都敏感
  • 缺点
    • 高维诅咒(curse of dimensionality);在高维中表现不佳
  • 用例
    • 图像识别、语音识别、笔迹分析;人脸识别

点积 Dot Product

也可以称为内积 Inner Product

向量的点积给出了两个向量之间的夹角余弦值,从而反映了它们的方向相似程度;当两个向量的点积为正时,它们的方向大致相同;当点积为负时,它们的方向大致相反;当点积为零时,它们的方向大致垂直

点积还可以用于计算向量在另一个向量上的投影。通过计算一个向量在另一个向量上的投影长度,可以了解一个向量在另一个向量方向上的分量大小

点积计算

使用转置矩阵表示

Java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class DotProduct {
public static void main(String[] args) {
double[] vectorA = {1.0, 2.0, 3.0};
double[] vectorB = {4.0, 5.0, 6.0};

double dotProduct = calculateDotProduct(vectorA, vectorB);

System.out.println("Dot product: " + dotProduct);
}

public static double calculateDotProduct(double[] vectorA, double[] vectorB) {
if (vectorA.length != vectorB.length) {
throw new IllegalArgumentException("Vector dimensions do not match");
}

double dotProduct = 0.0;
for (int i = 0; i < vectorA.length; i++) {
dotProduct += vectorA[i] * vectorB[i];
}

return dotProduct;
}
}
  • 优势
    • 计算速度快
    • 同时反映向量的大小和方向
  • 缺点
    • 不止对方向敏感,同时对大小敏感
  • 用例
    • 推荐系统,协同过滤,矩阵分解

余弦相似度 Cosine Similarity

余弦相似性是两个向量之间角度的度量,它是通过取向量的点积并将其除以其大小的乘积来计算的,所以余弦相似度不受向量大小的影响,只受向量之间的角度的影响

即只要值大或值小的向量指向同一方向,它们就具有相同的余弦相似性

余弦相似度

范数

||A||表示向量 A 的模(范数),也称为向量的长度或大小,是通过对向量的元素进行某种数学运算后得到的一个标量值,用于衡量向量的大小

常见的向量范数有多种定义,其中最常用的是欧几里得范数(L2 范数)

Java 代码

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
public class CosineSimilarity {

public static double calculateCosineSimilarity(double[] vector1, double[] vector2) {
if (vector1.length != vector2.length) {
throw new IllegalArgumentException("Vector dimensions must be the same");
}

double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;

for (int i = 0; i < vector1.length; i++) {
dotProduct += vector1[i] * vector2[i];
normA += Math.pow(vector1[i], 2);
normB += Math.pow(vector2[i], 2);
}

double similarity;
if (normA == 0 || normB == 0) {
similarity = 0.0;
} else {
// 这里就是在计算两个向量 L2 范数的积
double denominator = Math.sqrt(normA) * Math.sqrt(normB);
similarity = dotProduct / denominator;
}

return similarity;
}

public static void main(String[] args) {
double[] vector1 = {1.0, 2.0, 3.0};
double[] vector2 = {4.0, 5.0, 6.0};

double similarity = calculateCosineSimilarity(vector1, vector2);
System.out.println("Cosine similarity: " + similarity);
}
}
  • 优势
    • 主要考虑向量的方向,使其非常适合高维空间,如文本比较,其中文档的长度可能并不重要
  • 缺点
    • 当向量的大小很重要时,例如当基于像素强度比较图像嵌入时并不适用;如果数据不形成凸集,则可能无法提供准确的相似性度量
  • 用例
    • 文档分类、语义搜索、推荐系统以及涉及高维和规范化数据的任何其他任务

归一化 Normalization

归一化是将数据按比例缩放,使其落入特定的范围;在数据处理和分析中,归一化通常用于将不同范围和单位的数据转化为统一的标准,以消除由于数据尺度不同而引起的偏差

在向量的情况下,归一化也被称为向量的标准化,它是将向量按照其范数(模)进行缩放,使得向量的范数为一个固定值,通常为 1

通过归一化,可以消除向量长度的影响,只关注向量的方向,从而更好地比较和度量向量之间的相似性

举一个例子

假设我们有一个包含身高和体重的数据集,其中身高以厘米为单位,体重以千克为单位。数据集如下:

编号 身高(厘米) 体重(千克)
1 180 75
2 165 60
3 175 80
4 158 52

为了将身高和体重归一化,我们可以使用特定的归一化方法,例如将数据缩放到 0 到 1 之间的范围

身高的最大值 X_min_height = 158

身高的最小值 X_max_height = 180

编号 归一化身高
1 (180 - 158) / (180 - 158) = 1.0
2 (165 - 158) / (180 - 158) ≈ 0.4286
3 (175 - 158) / (180 - 158) ≈ 0.8571
4 (158 - 158) / (180 - 158) = 0.0

体重的最小值:X_min_weight = 52

体重的最大值:X_max_weight = 80

编号 归一化体重
1 (75 - 52) / (80 - 52) ≈ 0.6
2 (60 - 52) / (80 - 52) ≈ 0.2
3 (80 - 52) / (80 - 52) = 1.0
4 (52 - 52) / (80 - 52) = 0.0

在实际应用中,归一化可以帮助我们更好地比较和分析不同特征的数据,并减少由于数据尺度不同而引起的偏差

Java 代码

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
public class Normalization {
public static void main(String[] args) {
double[] array = {1.0, 2.0, 3.0, 4.0, 5.0};
double[] normalizedArray = normalize(array);

// 打印归一化后的数组
for (double element : normalizedArray) {
System.out.println(element);
}
}

public static double[] normalize(double[] array) {
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;

// 找到数组中的最小值和最大值
for (double element : array) {
if (element < min) {
min = element;
}
if (element > max) {
max = element;
}
}

// 归一化数组
double[] normalizedArray = new double[array.length];
for (int i = 0; i < array.length; i++) {
normalizedArray[i] = (array[i] - min) / (max - min);
}

return normalizedArray;
}
}

凸集 Convex Set

如果一组向量数据被认为是凸集,那么对于这组向量中的任意两个向量,它们之间的线段上的所有点也属于这组向量数据;换句话说,这组向量数据中的任意两个向量之间的连接线段上的所有向量也属于这组向量数据

在余弦相似度中提到,如果数据不形成凸集,则可能效果不好

原因是相似度假设了向量之间的角度可以反映它们的相似性

对于非凸集的数据,由于数据的分布形状不符合凸集的特性,向量之间的角度可能无法准确地反映它们的相似性,非凸集的数据分布可能会导致向量之间的夹角不准确,从而影响余弦相似性的计算结果

Java 代码判断凸集

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
import java.util.ArrayList;
import java.util.List;

public class ConvexSetChecker {
public static boolean isConvexSet(List<double[]> vectors) {
int n = vectors.size();
if (n < 3) {
return true; // 如果向量数量小于3,则认为是凸集
}

for (int i = 0; i < n; i++) {
double[] vector1 = vectors.get(i);
double[] vector2 = vectors.get((i + 1) % n);
double[] vector3 = vectors.get((i + 2) % n);

double crossProduct = calculateCrossProduct(vector1, vector2, vector3);

if (crossProduct < 0) {
return false; // 如果存在向量三元组的叉乘结果小于0,则不是凸集
}
}

return true;
}

private static double calculateCrossProduct(double[] vector1, double[] vector2, double[] vector3) {
double x1 = vector2[0] - vector1[0];
double y1 = vector2[1] - vector1[1];

double x2 = vector3[0] - vector2[0];
double y2 = vector3[1] - vector2[1];

return x1 * y2 - x2 * y1;
}

public static void main(String[] args) {
// 示例用例
List<double[]> vectors = new ArrayList<>();
vectors.add(new double[]{0, 0});
vectors.add(new double[]{1, 0});
vectors.add(new double[]{0, 1});

boolean isConvex = isConvexSet(vectors);
System.out.println("是否为凸集: " + isConvex);
}
}

参考

Which vector similarity metric should I use? (imaurer.com)

Vector Similarity Explained | Pinecone

推荐算法入门(1)相似度计算方法大全 - 知乎 (zhihu.com)

Hexo 数学公式支持: