首页 > 其他分享 >《线性代数》2. 向量的高级话题

《线性代数》2. 向量的高级话题

时间:2023-08-26 18:57:03浏览次数:41  
标签:__ Vector self 话题 线性代数 vec 单位向量 向量

规范化和单位向量

在了解完向量的基础知识后,我们来探讨更多和向量有关的高级话题。首先向量是一个有向线段,由原点指向空间中的某一个点,所以向量除了具有方向之外,还应该具有大小。比如有两个向量 \(\vec{u}\)、\(\vec{w}\),分别是 \((3, 4)^{T}\)、\((4, 3)^{T}\),那么它们的长度是多少呢?

很明显,它们的长度都是 \(5\)。当然在数学领域,向量的长度还有一个更专业的名称,叫向量的,符号是在向量的周围加上两条双竖线,比如向量 \(\vec{u}\) 的模就是 \(||\vec{u}||\)。

以上是二维向量,如果让你求三维向量的模,肯定也不在话下。比如 \(\vec{u} = (2, 3, 5)^{T}\),那么它的模 \(||\vec{u}||\) 就等于 \(\sqrt{2^{2} + {3^{2}} + {5^{2}}}\)。如果将向量扩展到 \(n\) 维:

\(\vec{u} = (u_{1}, u_{2}, u_{3}, ..., u_{n})^{T}\),那么它的模就是 \(\sqrt{u_{1}^{2} + {u_{2}^{2}} + {u_{3}^{2}} + ... + {u_{n}^{2}}}\),说白了向量的模就是坐标点和原点之间的欧拉距离。

介绍完什么是向量的模,以及如何求之后,我们就可以引入一个新的概念:单位向量(unit vector)。单位向量指的是模为 \(1\) 的向量,所以对于单位向量来说,它的长度已经不重要了,它只表示方向。

对于 \(n\) 维向量 \(\vec{u} = (u_{1}, u_{2}, u_{3}, ..., u_{n})^{T}\),那么对应的单位向量就是 \(\hat{{u}} = (\frac{u_{1}}{||\vec{u}||}, \frac{u_{2}}{||\vec{u}||}, \frac{u_{3}}{||\vec{u}||}, ..., \frac{u_{n}}{||\vec{u}||})^{T}\),也就是让向量的在每个维度的分量都除以模。然后单位向量也有专门的表示方式,在符号的上方加上一个类似于 ^ 的符号,就表示该向量是单位向量,并且满足 \(||\hat{u}|| == 1\)。而单位向量 \(\hat{u}\) 和原向量 \(\vec{u}\) 之间的差别仅仅在于它们的长度不同,而方向是一致的,比如向量 \(\vec{u} = (3, 4)^{T}\),它的单位向量就是 \(\hat{u} = (\frac{3}{5}, \frac{4}{5})^{T}\)。

引入单位向量的意义就在于它将向量的模和方向给拆开了,大小可以由向量的模表示,方向可以由向量对应的单位向量表示。如果只关心向量的大小,那么求向量的模即可,如果只关心方向,那么求单位向量即可。而根据向量(\(\vec{u}\))求出单位向量 \(\hat{u}\) 的过程,就叫做归一化、或者规范化(normalize)。

而且从单位向量的概念上来理解,我们不难发现单位向量有无数个,以二维空间为例,以原点为圆心,半径为 \(1\) 画圆,那么原点到圆上的每一条有向线段都是一个单位向量。如果以三维空间为例,那么以原点为球心,半径为 \(1\) 做一个球,那么球心到球上的每一条有向线段也都是一个单位向量。至于更高维度的向量也是如此。

另外对于二维空间,有两个特殊的单位向量:\(\vec{e_{1}} = (1, 0)\) 和 \(\vec{e_{2}} = (0, 1)\),因为它们和坐标轴是重合的。同理三维空间有三个特殊的单位向量:\(\vec{e_{1}} = (1, 0, 0)\)、 \(\vec{e_{2}} = (0, 1, 0)\)、\(\vec{e_{3}} = (0, 0, 1)\)。当然这些特殊的单位向量,也叫做标准单位向量,默认使用字母 \(\vec{e}\) 表示。如果拓展到 \(n\) 维的话,那么就有 \(n\) 个标准单位向量。

注意:标准单位向量是很重要的一组向量,由于标准单位向量指向坐标轴的正方向,所以可以说整个坐标系就是由标准单位向量所构建的。

从原理上我们已经介绍了单位向量是什么,那么接下来就用代码来实现它,由于在上一篇文章中我们实现了 Vector 类,那么下面就继续在 Vector 之上添砖加瓦。

import math


class Vector:
    """
    向量
    """

    def __init__(self, values):
        self._values = values

    def __len__(self):
        """返回向量的维度(有多少个元素)"""
        return len(self._values)

    def __repr__(self):
        return f"Vector({self._values})"

    def __str__(self):
        return f"{tuple(self._values)}"

    def __getitem__(self, item):
        """基于索引获取向量的元素"""
        return self._values[item]

    def __add__(self, other):
        if type(other) is not Vector:
            raise TypeError("Vector 只能和 Vector 相加")
        if len(self) != len(other):
            raise TypeError("相加的两个 Vector 的维度必须相同")
        return Vector(
            [self[i] + other[i] for i in range(len(self))]
        )

    def __mul__(self, other):
        if type(other) is not int:
            raise TypeError("Vector 只能和一个整数相乘")
        return Vector(
            [self[i] * other for i in range(len(self))]
        )

    __rmul__ = __mul__

    @classmethod
    def zero(cls, dim: int):
        """构建一个零向量"""
        return cls([0] * dim)

    def __neg__(self):
        """构建一个负向量"""
        return Vector(
            [-self[i] for i in range(len(self))]
        )

    def __eq__(self, other):
        if type(other) is not Vector:
            raise TypeError("Vector 只能和 Vector 进行比较")
        if len(self) != len(other):
            return False
        return all([self[i] == other[i] for i in range(len(self))])

    def norm(self):
        """返回向量的模"""
        return math.sqrt(sum(self[i] ** 2 for i in range(len(self))))

    def normalize(self):
        """返回对应的单位向量"""
        norm = self.norm()  # 如果是 0 向量,那么返回它的单位向量是没有意义的,而且会报错
        if norm < 1e-8:  # 浮点数有误差,不能直接判断 == 0
            raise ValueError("不能获取零向量对应的单位向量")
        return Vector(
            [self[i] / norm for i in range(len(self))]
        )

此时向量的模和单位向量就已经实现了,来测试一下。

from vector import Vector

vec = Vector([3, 4])
print(vec.norm())  # 5.0
print(vec.normalize())  # (0.6, 0.8)
# 单位向量的模是 1
print(vec.normalize().norm())  # 1.0

结果没有问题。

向量的点乘与几何意义

我们之前介绍了两个向量相加,以及一个向量和一个整数相乘,都比较简单,那么两个向量相乘会有什么结果呢?有小伙伴觉得这个还不简单,不就是像下面这样吗?

\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ u_{3} \\ ... \\ u_{n} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ w_{3} \\ ... \\ w_{n} \\ \end{pmatrix} = \begin{pmatrix} u_{1} · w_{1} \\ u_{2} · w_{2} \\ u_{3} · w_{3} \\ ... \\ u_{n} · w_{n} \\ \end{pmatrix}\)

不好意思,这是错误的,向量的乘法不是这么定义的,至于为什么,后续揭晓。而向量乘法的真正定义如下:

\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ u_{3} \\ ... \\ u_{n} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ w_{3} \\ ... \\ w_{n} \\ \end{pmatrix} = sum(\begin{pmatrix} u_{1} · w_{1} \\ u_{2} · w_{2} \\ u_{3} · w_{3} \\ ... \\ u_{n} · w_{n} \\ \end{pmatrix}) = u_{1} · w_{1} + u_{2} · w_{2} + u_{3} · w_{3} + ... + u_{n} · w_{n}\)

所以真正的定义相比错误的定义只是多了一个求和操作,因此一个结论就产生了:两个向量相乘的结果不再是向量,而是一个标量,也就是一个数。当然啦,为了方便描述,我们说两个向量相乘,但更严格的说法应该叫两个向量的点乘,或者两个向量的内积

那么两个向量的点乘为什么要这么定义?背后有什么数学意义呢?其实很简单:

\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ u_{3} \\ ... \\ u_{n} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ w_{3} \\ ... \\ w_{n} \\ \end{pmatrix} = u_{1} · w_{1} + u_{2} · w_{2} + u_{3} · w_{3} + ... + u_{n} · w_{n} = ||\vec{u}|| · ||\vec{w}|| · cosθ\)

两个向量(\(\vec{u}\)、\(\vec{w}\))点乘的结果等于:\(\vec{u}\)、\(\vec{w}\) 的每个维度上的分量分别相乘,然后再相加,同时它也等于:\(\vec{u}\)、\(\vec{w}\) 的模相乘之后再乘上两者夹角的余弦值。下面我们就以二维平面为例,来证明这个结论。不过证明之前,需要补充一个知识点:向量的减法。

注:向量的尾巴(没有箭头)是向量的起始点,向量的箭头是向量的结束点。

如果将绿色的线段记作 \(\vec{u}\),蓝色的线段记作 \(\vec{w}\),那么红色的线段等于多少呢?毫无疑问,等于 \(\vec{u} + \vec{w}\)。这个我们在高中的时候学过,如果多个向量像上图一样满足首尾相连,那么第一个向量的起始点和最后一个向量的结束点之间的连线,就等于所有向量之和。然后我们还将绿色的线段记作 \(\vec{u}\),但是将红色的线段记作 \(\vec{w}\),那么此时蓝色的线段等于多少呢?很明显,它等于红色线段对应的向量,减去绿色线段对应的向量,也就是 \(\vec{w} - \vec{u}\)。

和加法一样,两个向量相减等于每一个维度上的分量进行相减,并且返回的也是向量。

然后还要补充一个余弦定理,假设有一个三角形,三条边记作:a、b、c,并且 a 的对角为 \(θ_{a}\),b 的对角为 \(θ_{b}\),c 的对角为 \(θ_{c}\)。

那么结论如下:

  • \(a^{2} = b^{2} + c ^ {2} - 2·b·c·cosθ_{a}\)
  • \(b^{2} = a^{2} + c ^ {2} - 2·a·c·cosθ_{b}\)
  • \(c^{2} = a^{2} + b^{2} - 2·a·b·cosθ_{c}\)

好了,补充完向量的减法和余弦定理之后,我们在二维空间中证明上面那个结论:

\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ \end{pmatrix} = u_{1} · w_{1} + u_{2} · w_{2} = ||\vec{u}|| · ||\vec{w}|| · cosθ\)

基于已有的余弦定理,我们就证明了这个结论:\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ \end{pmatrix} = u_{1} · w_{1} + u_{2} · w_{2} = ||\vec{u}|| · ||\vec{w}|| · cosθ\),这便是向量的点乘(内积)。

当然扩展到 \(n\) 维也是成立的:\(\vec{u} · \vec{w} = \begin{pmatrix} u_{1} \\ u_{2} \\ u_{3} \\ ... \\ u_{n} \\ \end{pmatrix} · \begin{pmatrix} w_{1} \\ w_{2} \\ w_{3} \\ ... \\ w_{n} \\ \end{pmatrix} = u_{1} · w_{1} + u_{2} · w_{2} + u_{3} · w_{3} + ... + u_{n} · w_{n} = ||\vec{u}|| · ||\vec{w}|| · cosθ\),有兴趣可以自己验证一下。

编程实现向量点乘

这里我需要事先说明一下,乘法在计算机中就表示单纯的乘法,即使在 Numpy 中也是如此。

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([11, 22, 33])
# 让每个元素分别进行相乘
print(arr1 * arr2)  # [11 44 99]

如果你想实现向量的点乘,在 Python 里面你应该使用 @ 符号。也就是说:@ 才是向量的点乘,而 * 始终表示普通的乘法。

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([11, 22, 33])
print(arr1 * arr2)  # [11 44 99]

# 1 * 11 + 2 * 22 + 3 * 33
print(arr1 @ arr2)  # 154

而 @ 操作符对应的魔法方法是 __matmul__,所以我们需要重载 Vector 的 __matmul__ 方法。这里的 mat 表示矩阵的意思,也就是矩阵乘法,而向量可以看作是一维矩阵。

import math


class Vector:
    """
    向量
    """

    def __init__(self, values):
        self._values = values

    def __len__(self):
        """返回向量的维度(有多少个元素)"""
        return len(self._values)

    def __repr__(self):
        return f"Vector({self._values})"

    def __str__(self):
        return f"{tuple(self._values)}"

    def __getitem__(self, item):
        """基于索引获取向量的元素"""
        return self._values[item]

    def __add__(self, other):
        if type(other) is not Vector:
            raise TypeError("Vector 只能和 Vector 相加")
        if len(self) != len(other):
            raise TypeError("相加的两个 Vector 的维度必须相同")
        return Vector(
            [self[i] + other[i] for i in range(len(self))]
        )

    def __mul__(self, other):
        if type(other) is not int:
            raise TypeError("Vector 只能和一个整数相乘")
        return Vector(
            [self[i] * other for i in range(len(self))]
        )

    __rmul__ = __mul__

    @classmethod
    def zero(cls, dim: int):
        """构建一个零向量"""
        return cls([0] * dim)

    def __neg__(self):
        """构建一个负向量"""
        return Vector(
            [-self[i] for i in range(len(self))]
        )

    def __eq__(self, other):
        if type(other) is not Vector:
            raise TypeError("Vector 只能和 Vector 进行比较")
        if len(self) != len(other):
            return False
        return all([self[i] == other[i] for i in range(len(self))])

    def norm(self):
        """返回向量的模"""
        return math.sqrt(sum(self[i] ** 2 for i in range(len(self))))

    def normalize(self):
        """返回对应的单位向量"""
        norm = self.norm()  # 如果是 0 向量,那么返回它的单位向量是没有意义的,而且会报错
        if norm < 1e-8:  # 浮点数有误差,不能直接判断 == 0
            raise ValueError("不能获取零向量对应的单位向量")
        return Vector(
            [self[i] / norm for i in range(len(self))]
        )
    
    def __matmul__(self, other):
        if type(other) is not Vector:
            raise TypeError("Vector 只能和 Vector 进行比较")
        if len(self) != len(other):
            raise TypeError("点乘的两个 Vector 的维度必须相同")
        return sum(self[i] * other[i] for i in range(len(self)))

实现完毕,我们来测试一下:

from vector import Vector

vec1 = Vector([1, 2, 3])
vec2 = Vector([11, 22, 33])
print(vec1 @ vec2)  # 154

结果没有问题,非常简单。

向量点乘的应用

基于向量的点乘公式 \(\vec{u} · \vec{w} = u_{1} · w_{1} + u_{2} · w_{2} + u_{3} · w_{3} + ... + u_{n} · w_{n} = ||\vec{u}|| · ||\vec{w}|| · cosθ\),我们可以很容易求出两个向量的夹角:\(cosθ = \frac{\vec{u} · \vec{w}}{||\vec{u}|| · ||\vec{w}||}\)。

  • 如果 \(θ\) 等于 \(90°\),那么这两个向量点乘的结果就是 \(0\);
  • 反之,如果两个向量点乘的结果是 \(0\),那么这两个向量互相垂直(夹角为 \(90°\));

而且我们也能够推出:

  • 如果 \(\vec{u} · \vec{w} > 0\),那么两个向量之间夹角为锐角;
  • 如果 \(\vec{u} · \vec{w} < 0\),那么两个向量之间夹角为钝角;
  • 如果 \(\vec{u} · \vec{w} = 0\),那么两个向量之间夹角为直角,比如标准单位向量点乘的结果就是 0。并且无论维度是多少,标准单位向量永远是两两垂直的;

然后向量点乘还可以用在推荐系统当中,比如你看了某部电影之后觉得非常喜欢,那么如果再有与之相似的电影,系统就会推荐给你。当然电商系统里的商品也是如此,而在这个策略背后,就是如何判断两个商品的相似度呢?答案就是向量的点乘。首先基于商品的特征,可以抽象成高维空间的一个向量,那么我们只需要看两个向量的夹角即可。

  • 如果为锐角,那么两个商品是具有一定相似度的,并且角度越小,相似度越高
  • 如果为直角,那么两个商品是不具有相似度,彼此无关
  • 如果为钝角,那么两个商品的特征是相反的

在 Numpy 中如何使用向量

说到科学计算,就不得不说 Numpy,它是奠定 Python 数据科学的最大功臣。那么在 Numpy 中,我们如何使用向量呢?

import numpy as np

# 在 numpy 中我们一般会创建一维数组来表示向量
# 如果是矩阵,那么就用多维数组来表示
vec1 = np.array([1, 2, 3])
vec2 = np.array([2, 3, 4])

# 向量的加法
print(vec1 + vec2)  # [3 5 7]
# 向量的减法
print(vec1 - vec2)  # [-1 -1 -1]
# 向量和一个标量相乘
print(vec1 * 3)  # [3 6 9]
# 向量的乘法(每个元素相乘,得到的还是向量)
print(vec1 * vec2)  # [ 2  6 12]
# 向量的点乘
print(vec1 @ vec2)  # 20

然后 np 下面有一个子模块 linalg,里面包含了大量和线性代数相关的方法,因为 linalg 就是线性代数的缩写。

import numpy as np

vec = np.array([1, 2, 3])
# 计算向量的模
print(np.linalg.norm(vec))
"""
3.7416573867739413
"""
# 计算单位向量,numpy 并没有直接提供单位向量,需要我们手动做一下除法
print(vec / np.linalg.norm(vec))
"""
[0.26726124 0.53452248 0.80178373]
"""
# 单位向量的模是 1
print(np.linalg.norm(vec / np.linalg.norm(vec)))  # 1.0

另外计算单位向量的时候,要确保原向量不是零向量,这一点需要由使用者保证。

标签:__,Vector,self,话题,线性代数,vec,单位向量,向量
From: https://www.cnblogs.com/traditional/p/17659278.html

相关文章

  • 《线性代数》1. 一切从向量开始
    什么是向量我们在初等数学的时候,研究的都是一个数,而到线性代数,我们将从研究一个数拓展到研究一组数,而一组数的基本表示方法就是向量(Vector)。向量是线性代数研究的基本元素,它就是一组数,比如\((1,2,3)\)就是一个向量。那么问题来了,向量究竟有什么作用呢?或者说我们研究一组数有......
  • 向量数据库(第 2 部分):了解其内部结构
    这是关于向量数据库的系列文章中的第二篇。正如本系列的第一篇所提到的,2023年上半年关于向量数据库的营销(不幸的是,有些是炒作)非常多,如果你正在阅读这篇文章,你可能对向量数据库在底层是如何工作的,以及如何在高效的向量存储之上构建搜索功能感兴趣。为什么现在大家都在谈论向量数据库......
  • Hello Cuda(二)——向量加法
    #include<stdio.h>#include<stdlib.h>#include<cuda.h>#include<math.h>#include<cuda_runtime.h>#include<device_launch_parameters.h>typedeffloatFLOAT;doubleget_time();voidwarm_up();voidvec_add_host(FLOA......
  • 【线性代数】第五章 特征值和特征向量
    1.特征值和特征向量特征值和特征向量的定义:对于n阶矩阵A,如果存在一个数λ以及非零n维列向量α,使得Aα=λα成立则称λ是矩阵A的一个特征值。非零向量α是矩阵A属于特征值的一个特征向量。这个式子可以写成(λE-A)α=0,α≠0,所以特征向量α可以说成这个齐次方程的非零......
  • 线性代数为什么是计算机专业的基础课程
    线性代数在机器学习中比较低阶的应用是矩阵运算,比如softmax分类器y^=σ(WTx+b)\hat{\mathbf{y}}=\sigma(W^T\mathbf{x}+\mathbf{b}),在这里矩阵形式使得书写、计算更方便,也能帮助理解模型(将矩阵看作是一种变换);高阶一点的应用在无监督学习中,可以参考奇异值分解(SVD)等矩阵分解方......
  • 使用docker-compose安装Milvus向量数据库及Attu可视化连接工具
    服务器需要安装docker-compose如何没有安装可以参考这篇博客https://www.cnblogs.com/likecoke/p/17596918.html1、创建宿主机上存储的目录mkdir-p/home/milvus/dbmkdir-p/home/milvus/confmkdir-p/home/milvus/etcd2、下载milvus官网的docker-compose.yml文件wgethtt......
  • 学习笔记411—【词向量基础】:one-hot
    【词向量基础】:one-hot词向量(wordvector),也叫词嵌入(wordembedding),是一种词表征形式,将词从符号形式映射为向量形式,渐渐演变成了一种知识表示的方法。将词语从符号表示形式转换为了向量表示形式,方便了机器对自然语言的计算,因此,词向量几乎成为了所有自然语言处理和理解的下游任务的......
  • Unity3D 向量大小比较
    Vector3.sqrMagnitude是指长度的平方,也就是Vector3.magnitude的平方计算向量大小的平方会比计算向量的大小要快很多,因为向量的大小由勾股定理得出,所以有开方操作,如果只是单纯的比较两个向量的大小,可以使用sqrMagnitude会快很多。(获取开始和结束时间经过对比发现没有差别,可能这一......
  • ITK 实例12 置信连接对PNG向量图像进行二维分割
    1#include"itkVectorConfidenceConnectedImageFilter.h"2#include"itkImage.h"3#include"itkImageFileReader.h"4#include"itkImageFileWriter.h"5#include"itkRGBPixel.h"67intmain(i......
  • ITK 实例7 向量图像(将一个向量存储到一个图像像素中)
    1#include"itkVector.h"//向量类的头文件2#include"itkImage.h"34intmain(int,char*[])5{6/*向量类的使用是在基于空间中代表坐标和维数的类型之上进行模板化的。在此例中,向7量的长度和图像长度相匹配,但是并不是完全相同。我们可以用一个三维的向......