文章目录
向量和向量空间
-
标量(Scalar只是一个单独的数字,而且不能表示方向。从计算机数据结构的角度来看,标量就是编程中最基本的变量。
-
和标量对应的概念,就是线性代数中最常用、也最重要的概念,向量(Vector),也可以叫做矢量。它代表一组数字,并且这些数字是有序排列的。我们用数据结构的视角来看,向量可以用数组或者链表来表达。
这里面的 n 就是向量的维
向量和标量最大的区别在于,向量除了拥有数值的大小,还拥有方向。向量或者矢量中的“向”和“矢”这两个字,都表明它们是有方向的。
为什么这一串数字能表示方向呢?
这是因为,如果我们把某个向量中的元素看作坐标轴上的坐标,那么这个向量就可以看作空间中的一个点。以原点为起点,以向量代表的点为终点,就能形成一条有向直线。而这样的处理其实已经给向量赋予了代数的含义,使得计算的过程中更加直观。
由于一个向量包含了很多个元素,因此我们自然地就可以把它运用在机器学习的领域。
由于特征有很多维,因此我们可以使用向量来表示某个物体的特征。其中,向量的每个元素就代表一维特征,而元素的值代表了相应特征的值,我们称这类向量为特征向量(Feature Vector)
向量的运算
标量和向量之间可以进行运算,比如标量和向量相加或者相乘时,我们直接把标量和向量中的每个元素相加或者相乘就行了.
可是,向量和向量之间的加法或乘法应该如何进行呢?
我们需要先定义向量空间。向量空间理论上的定义比较繁琐,不过二维或者三维的坐标空间可以很好地帮助你来理解。这些空间主要有几个特性:
- 空间由无穷多个的位置点组成;
- 这些点之间存在相对的关系;
- 可以在空间中定义任意两点之间的长度,以及任意两个向量之间的角度;
- 这个空间的点可以进行移动。
有了这些特点,我们就可以定义向量之间的加法、乘法(或点乘)、距离和夹角等等。
两个向量之间的加法,首先它们需要维度相同,然后是对应的元素相加。
所以说,向量的加法实际上就是把几何问题转化成了代数问题,然后用代数的方法实现了几何的运算。下面画了一张图,来解释二维空间里,两个向量的相加,看完就能理解了。
在这张图中,有两个向量 x 和 y,它们的长度分别是 x’和 y’,它们的相加结果是 x+y,这个结果所对应的点相当于 x 向量沿着 y 向量的方向移动 y’,或者是 y 向量沿着 x 向量的方向移动 x’
向量之间的乘法默认是点乘,向量 x 和 y 的点乘是这么定义的:
点乘的作用是把相乘的两个向量转换成了标量,它有具体的几何含义。我们会用点乘来计算向量的长度以及两个向量间的夹角,所以一般情况下我们会默认向量间的乘法是点乘。
至于向量之间的夹角和距离,它们在向量空间模型(Vector Space Model)中发挥了重要的作用。信息检索和机器学习等领域充分利用了向量空间模型,计算不同对象之间的相似程度
什么是向量空间?
首先假设有一个数的集合 F,它满足“F 中任意两个数的加减乘除法(除数不为零)的结果仍然在这个 F 中”,我们就可以称 F 为一个“域”。我们处理的数据通常都是实数,所以这里我只考虑实数域。而如果域 F 里的元素都为实数,那么 F 就是实数域。
或者写成转置的形式:
向量空间的几个重要概念
有了刚才的铺垫,接下来我们来看几个重要的概念:向量的长度、向量之间的距离和夹角。
向量之间的距离
有了向量空间,我们就可以定义向量之间的各种距离。我们之前说过,可以把一个向量想象为 n 维空间中的一个点。而向量空间中两个向量的距离,就是这两个向量所对应的点之间的距离。距离通常都是大于 0 的,这里介绍几种常用的距离,包括曼哈顿距离、欧氏距离、切比雪夫距离和闵可夫斯基距离。
曼哈顿距离(Manhattan Distance)
这个距离度量的名字由来非常有趣。你可以想象一下,在美国人口稠密的曼哈顿地区,从一个十字路口开车到另外一个十字路口,驾驶距离是多少呢?当然不是两点之间的直线距离,因为你无法穿越挡在其中的高楼大厦。你只能驾车绕过这些建筑物,实际的驾驶距离就叫作曼哈顿距离。由于这些建筑物的排列都是规整划一的,形成了一个个的街区,所以我们也可以形象地称它为“城市街区”距离。
这里画了张图方便理解这种距离。
从图中可以看出,从 A 点到 B 点有多条路径,但是无论哪条,曼哈顿距离都是一样的。
欧氏距离(Euclidean Distance)
切比雪夫距离(Chebyshev Distance)
向量的长度
有了向量距离的定义,向量的长度就很容易理解了。向量的长度,也叫向量的模,是向量所对应的点到空间原点的距离。通常我们使用欧氏距离来表示向量的长度。
向量之间的夹角
余弦相似度
余弦相似度是一种用于衡量两个向量之间相似性的度量方法,通常用于文本挖掘、信息检索和自然语言处理等领域。它通过计算两个向量之间的夹角余弦值来衡量它们在多维空间中的方向相似性。余弦相似度通常用于比较两个文本文档之间的相似性,或者用于向量空间模型中的相关性分析。
余弦相似度的计算公式如下:
余弦相似度的取值范围在 -1 到 1 之间。
- 如果余弦相似度接近 1,表示两个向量非常相似,它们的方向几乎一致;
- 如果余弦相似度接近 -1,表示两个向量方向完全相反;
- 如果余弦相似度接近 0,表示两个向量之间几乎没有方向相似性。
在自然语言处理中,可以使用余弦相似度来衡量文档之间的相似性,或者在信息检索中用于排序搜索结果,以便找到与查询最相关的文档。
应用场景
-
文本相似度:
假设有两个文本文档,分别是 “机器学习是人工智能的一部分” 和 “深度学习是AI领域的一个分支”。我们可以将这两个文档表示为词频向量,其中每个维度代表一个词汇,值表示该词汇在文档中的频率。然后,可以使用余弦相似度来比较这两个文档的相似性。如果它们在使用的词汇上有很多重叠,余弦相似度将接近1,表示它们非常相似。
-
用户兴趣:
想象一个在线商店,有两个用户,分别在网站上浏览过的商品类别如下:用户A看了电子产品和家居用品,而用户B看了电子产品和厨房用具。可以将每个用户的兴趣表示为一个向量,其中每个维度代表一个商品类别,值表示用户对该类别的兴趣程度。然后,通过余弦相似度来比较两个用户的兴趣向量。如果它们在感兴趣的商品类别上有很多重叠,余弦相似度将接近1,表示这两个用户的兴趣相似。
-
图像相似度:
在计算机视觉中,余弦相似度也可以用于比较图像。如果两张图像表示为像素值的向量,余弦相似度可以用来衡量它们的结构和颜色的相似性。两张相似的图像的余弦相似度将接近1,而完全不同的图像余弦相似度将接近0。
这些例子说明了余弦相似度的概念,即在多维空间中,两个向量的方向越接近,余弦相似度越高,表示它们越相似。这种相似性度量在各种应用中都非常有用,从文本和用户兴趣分析到图像和推荐系统。
Code
import java.util.Arrays;
public class CosineSimilarity {
public static void main(String[] args) {
// 两个文本文档 0.44474958999666075
// String text1 = "机器学习是人工智能的一部分";
// String text2 = "深度学习是AI领域的一个分支";
// 两个文本文档 0.9999999999999998
// String text1 = "我是小工匠";
// String text2 = "小工匠是我";
// 两个文本文档 0.687922956942992
String text1 = "RAG 一直在自然语言处理(NLP) 领域掀起波澜。RAG 的核心是一个混合框架,它集成了检索模型和生成模型,生成的文本不仅上下文准确,而且信息丰富。";
String text2 = "RAG(Retrieval-Augmented Generation)是一项引人注目的技术,已经引发了自然语言处理(NLP)领域的广泛关注。它的核心思想是将检索模型和生成模型相融合,使得生成的文本不仅具有准确的上下文信息,而且内容丰富多彩";
// 将文本分割成单词
String[] words1 = text1.split("");
String[] words2 = text2.split("");
// 创建词汇表
String[] vocabulary = getVocabulary(words1, words2);
// 将文本转换为词频向量
int[] vector1 = getVector(words1, vocabulary);
int[] vector2 = getVector(words2, vocabulary);
// 计算余弦相似度
double cosineSimilarity = calculateCosineSimilarity(vector1, vector2);
System.out.println("余弦相似度: " + cosineSimilarity);
}
// 创建词汇表
public static String[] getVocabulary(String[] words1, String[] words2) {
String[] vocabulary = Arrays.copyOf(words1, words1.length);
for (String word : words2) {
if (!Arrays.asList(vocabulary).contains(word)) {
vocabulary = Arrays.copyOf(vocabulary, vocabulary.length + 1);
vocabulary[vocabulary.length - 1] = word;
}
}
return vocabulary;
}
// 将文本转换为词频向量
public static int[] getVector(String[] words, String[] vocabulary) {
int[] vector = new int[vocabulary.length];
Arrays.fill(vector, 0);
for (String word : words) {
for (int i = 0; i < vocabulary.length; i++) {
if (word.equals(vocabulary[i])) {
vector[i]++;
}
}
}
return vector;
}
// 计算余弦相似度
public static double calculateCosineSimilarity(int[] vector1, int[] vector2) {
double dotProduct = 0;
double magnitude1 = 0;
double magnitude2 = 0;
for (int i = 0; i < vector1.length; i++) {
dotProduct += vector1[i] * vector2[i];
magnitude1 += Math.pow(vector1[i], 2);
magnitude2 += Math.pow(vector2[i], 2);
}
magnitude1 = Math.sqrt(magnitude1);
magnitude2 = Math.sqrt(magnitude2);
return dotProduct / (magnitude1 * magnitude2);
}
}
向量空间模型
理解了向量间距离和夹角余弦这两个概念,再来看向量空间模型(Vector Space Model)就不难了。
向量空间模型假设所有的对象都可以转化为向量,然后使用向量间的距离(通常是欧氏距离)或者是向量间的夹角余弦来表示两个对象之间的相似程度。
使用下图来展示空间中向量之间的距离和夹角。
由于夹角余弦的取值范围已经在 -1 到 1 之间,而且越大表示越相似,所以可以直接作为相似度的取值。相对于夹角余弦,欧氏距离 ED 的取值范围可能很大,而且和相似度呈现反比关系,所以通常要进行 1/(ED+1) 这种归一化。
当 ED 为 0 的时候,变化后的值就是 1,表示相似度为 1,完全相同。当 ED 趋向于无穷大的时候,变化后的值就是 0,表示相似度为 0,完全不同。所以,这个变化后的值,取值范围是 0 到 1 之间,而且和相似度呈现正比关系。
早在上世纪的 70 年代,人们把向量空间模型运用于信息检索领域。由于向量空间可以很形象地表示数据点之间的相似程度,因此现在我们也常常把这个模型运用在基于相似度的一些机器学习算法中,例如 K 近邻(KNN)分类、K 均值(K-Means)聚类等等。
总结
为了让计算机理解现实世界中的事物,我们会把事物的特点转换成为数据,并使用多维度的特征来表示某个具体的对象。多个维度的特征很容易构成向量,因此我们就可以充分利用向量和向量空间,来刻画事物以及它们之间的关系。
我们可以在向量空间中定义多种类型的向量长度和向量间距离,用于衡量向量之间的差异或者说相似程度。此外,夹角余弦也是常用的相似度衡量指标。和距离相比,夹角余弦的取值已经控制在[-1, 1]的范围内,不会因为异常点所产生的过大距离而受到干扰。
向量空间模型充分利用了空间中向量的距离和夹角特性,来描述文档和查询之间的相似程度,或者说相关性。虽然向量空间模型来自信息检索领域,但是也被广泛运用在机器学习领域中。