好久没有写文章了,抱歉了,以后每天都会更新一篇的....
向量的点乘,也就是两个向量相乘:
我们是不这么定义的,不是两个向量对应的坐标元素相乘:
两个向量“相乘”,结果是⼀个数!,两个向量"相乘",更严格的说法:两个向量的点乘,两个向量的内积。
两个向量“相乘”:等于两个向量的模(长度)乘于夹角的余弦
在二维空间中,向量的点乘:
使用余弦定理证明:
向量点乘的直观理解:
向量的点乘,两个向量必须是同方向的,所以做投影以后的长度再相乘
同样,可以用坐标来理解:
v向量分解为x轴的x2向量,y轴的y2向量,u向量分解为x轴的x1向量,和y轴的y1向量,然后分别相乘,有4种情况,垂直的向量相乘为0,所以是x1.x2+y1.y2
使用Python实现向量的点乘:
具体代码:
定义一个内部使用的文件_globals,用来存储全局使用的变量 EPSILON,用来判断精度用的
EPSILON = 1e-8
Vector的代码:
import math
from ._globals import EPSILON
class Vector:
def __init__(self, lst):
self._values = list(lst)
@classmethod
def zero(cls, dim):
"""返回一个dim维的零向量"""
return cls([0] * dim)
def __add__(self, another):
"""向量加法,返回结果向量"""
assert len(self) == len(another), \
"Error in adding. Length of vectors must be same."
return Vector([a + b for a, b in zip(self, another)])
def __sub__(self, another):
"""向量减法,返回结果向量"""
assert len(self) == len(another), \
"Error in subtracting. Length of vectors must be same."
return Vector([a - b for a, b in zip(self, another)])
def norm(self):
"""返回向量的模"""
return math.sqrt(sum(e**2 for e in self))
def normalize(self):
"""返回向量的单位向量"""
if self.norm() < EPSILON:
raise ZeroDivisionError("Normalize error! norm is zero.")
return Vector(self._values) / self.norm()
def dot(self, another):
"""向量点乘,返回结果标量"""
assert len(self) == len(another), \
"Error in dot product. Length of vectors must be same."
return sum(a * b for a, b in zip(self, another))
def __mul__(self, k):
"""返回数量乘法的结果向量:self * k"""
return Vector([k * e for e in self])
def __rmul__(self, k):
"""返回数量乘法的结果向量:k * self"""
return self * k
def __truediv__(self, k):
"""返回数量除法的结果向量:self / k"""
return (1 / k) * self
def __pos__(self):
"""返回向量取正的结果向量"""
return 1 * self
def __neg__(self):
"""返回向量取负的结果向量"""
return -1 * self
def __iter__(self):
"""返回向量的迭代器"""
return self._values.__iter__()
def __getitem__(self, index):
"""取向量的第index个元素"""
return self._values[index]
def __len__(self):
"""返回向量长度(有多少个元素)"""
return len(self._values)
def __repr__(self):
return "Vector({})".format(self._values)
def __str__(self):
return "({})".format(", ".join(str(e) for e in self._values))
测试代码:
from playLA.Vector import Vector
if __name__ == "__main__":
vec = Vector([5, 2])
print(vec)
print("len(vec) = {}".format(len(vec)))
print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1]))
vec2 = Vector([3, 1])
print("{} + {} = {}".format(vec, vec2, vec + vec2))
print("{} - {} = {}".format(vec, vec2, vec - vec2))
print("{} * {} = {}".format(vec, 3, vec * 3))
print("{} * {} = {}".format(3, vec, 3 * vec))
print("+{} = {}".format(vec, +vec))
print("-{} = {}".format(vec, -vec))
zero2 = Vector.zero(2)
print(zero2)
print("{} + {} = {}".format(vec, zero2, vec + zero2))
print("norm({}) = {}".format(vec, vec.norm()))
print("norm({}) = {}".format(vec2, vec2.norm()))
print("norm({}) = {}".format(zero2, zero2.norm()))
print("normalize {} is {}".format(vec, vec.normalize()))
print(vec.normalize().norm())
print("normalize {} is {}".format(vec2, vec2.normalize()))
print(vec2.normalize().norm())
try:
zero2.normalize()
except ZeroDivisionError:
print("Cannot normalize zero vector {}.".format(zero2))
print(vec.dot(vec2))
注意:
向量的点乘的应⽤
1、求出两个向量之间的夹角范围,或者具体值
2、判断两个向量的相似程度(推荐系统)
一组物品进行推荐,可能是电影,音乐....在推荐的时候,最典型的推荐策略就是推荐和你的喜好最相似的内容,比如说,你已经在系统上留下足迹,喜欢电影A,此时电影B和A相似,推荐系统就会倾向把电影B推荐给你,电影C和电影B极其不相似,推荐系统就不会推荐。
在这个背后如何判断两个电影是否相似?
很简单的判断就是使用向量的点乘,在这种情况下,可以想象一下,每一个电影都可以理解为一个高维空间中的点,把向量另外一种视角看待(高维空间中的一个点),对电影来说,它可能有(电影的时间,演员的列表,电影的导演,电影的类型,电影的色调,台词量....)的一个高维度信息,就是在一个高维空间中的点,在这种情况下,如果我们转换我们的视角,把每个电影又转换成一个高维空间中的一个向量,每两个电影之间就会存在一个夹角,这时候,我们可以看这两个电影之间的夹角是直角,钝角,还是锐角,,如果是锐角,那么这两个电影之间有一部分是重合的,我就说这两个电影是相似的,如果是垂直的,那么这两部电影就是无关的,如果是顿角那么这两部电影是背离的。
在这里我们不仅可以看两个向量之间的夹角,还可以看点乘的结果(值),如果点乘的值为正值,并且越大,说明这两个电影越相似,在最极端的情况下,这两个向量完全重合的时候,那么这时候这两个向量的点乘将达到最大值,也就是两个电影的高维空间中的点(电影的时间,演员的列表,电影的导演,电影的类型,电影的色调,台词量....)已经重合了。
换句话说,我们可以使用向量点乘的方式,来看两个向量(电影)的相似,值越大,越相似,值越小,越不一样,这就是向量的点乘在推荐系统中的一个典型应用,当然了,现在的推荐
系统都比较复杂,在判断两个物品是否相似的时候,也有其他更加准确的方法,不仅如此,在具体判断两个物品是否相似之前,对两个物品所对应的数据也也需要进行很多的处理。
3、⼏何计算
v向量投影到u向量上
投影点的方向,也就是u向量除以u向量的模,也就是u向量方向的单位向量,举例子,u向量为(3,4),模长为5,u向量方向的单位向量为(3/5,4/5)