在利用sql实现余弦相似度匹配之前,先讲一讲实现余弦相似度的原理,相信搞清楚原理之后,你可以用多种方法计算出两个向量之间的余弦相似度。
余弦相似度是通过计算两个向量的夹角余弦值来评估它们的相似度,也可以说是根据两个空间向量的夹角来评估两个个体的差异度。
由下图可以看出,夹角越接近0°,余弦值越接近于1,这时它们之间的相似性越高,反之,夹角越接近180°,余弦值越接近于-1,这时它们之间的余弦相似度越低,当然等于-1不完全等同于他们之间没有相似度,这个得视情况而定。
余弦相似度也可以用余弦距离表示,余弦距离通常定义为
,也就是用 1 减去它们的余弦相似度来得到一个表示距离的数值,该数值范围在[0,2]之间,值越小表示两个向量越 “接近”,相似度越高。
这里假设有两个向量
和
,
,向量
,则
、
两向量的余弦相似度为:
从上述公式可以看出,要计算两个向量的余弦相似度,只需要计算出两个向量的点积与模即可,接下来我们就分别计算两个向量的点积与模。
两个向量的点积可以解释为,一个向量的模长与另一个向量在此向量方向上投影的长度的乘积,假设有两个向量
,向量
,向量的点积也就是
,其计算公式为:
上述公式中
,
为空间向量的坐标。
从上例可以看出,模其实就是向量的长度(也称为范数),向量的长度是用欧几里得距离(Euclidean distance)算出,假设有个向量
,则
模的计算方法为:
这和欧氏距离的计算方法比较相似,所以上手很简单。
通过上面的学习你应该已经搞清楚了余弦相似度的基本原理,接下来我们就开始利用sql来进行余弦相似度的计算。
上述可知,我们通体进行的都是向量的计算,所以在进行相似度计算之前,要先将数据转换成向量的形式,这里以 My sql
为例(以下简称为sql),而sql并不会直接将数据转换为向量形式,所以我们也不能真正的进行向量之间的运算,只能将数据转换成类似于向量的形式(如int类型),所以在进行计算之前,应先将数据转换为 int
或float类型。
以下做一个简单的示例,在这里我们创建一个简单的数据表table_a
,在向其中添加一些数据:
-- 创建table_a表
create table table_a(
field1 int,
field2 int,
field3 char
);
-- 向其中添加多条数据
insert into table_a (field1, field2, field3)
values
(3, 4, '否'),
(6, 8, '是'),
(2, 1, '是');
根据建表语句,可以看出其中有三列数据,两列int类型数据,一列char
类型,接下来我们要做的就是将第三列 field3
转换为 int
类型,并进行相似度计算。
在这里我们可以重新创建一个中间表,来将 field3
列转换为数据类型,并保存到新数据表中:
create table_b as
select field1,
field2,
case when field3='是' then 1
when field3='否' then 0 end as field3
from table_a;
这条 SQL 语句的主要作用是基于已有的 table_a
表创建一个新的表 table_b
,并且在创建新表的同时对从 table_a
中选取的数据进行了一定的转换操作。具体来说,它从 table_a
表中选取了 field1
和 field2
字段的原始数据,然后对 field3
字段进行了条件判断转换,将字符型的 '是'
转换为数值 1
,'否'
转换为数值 0
,并将转换后的数据填充到新表 table_b
对应的字段中。这样就将第三列 field3
的文本分类数据转化为数据1
和0
,接下来就可以计算相似度了。
想要计算余弦相似度,先要计算两个向量的点积与模,表 table_b
中的 field1
、field2
和 field3
可以分别看做是三个向量,则由点积计算的公式可以知道他们的点积为:
select sum(field1 * field2 * field3) as dot_product
from table_b;
这里先将每行记录的 field1
、field2
和 field3
三个字段的值相乘,对于每一行记录都会进行这样的操作,得到该行三个字段的乘积结果。
SUM
是聚合函数,用于对前面乘法运算得到的每一行的乘积结果进行求和操作,使用 SUM
函数对所有行的乘积结果进行求和,将最终的点积值以 dot_product
作为列名返回。
点积计算完之后,接下来我们通过SQRT
函数计算向量的模,SQRT
函数是求平方根函数。
select sqrt(sum(field1 * field1)) as field1_norm
from table_b;
这里先使用乘法运算符 *
将 field1
的值与其自身相乘(即求平方),然后通过聚合函数 SUM
对所有行的平方值进行求和,最后使用 SQRT
函数(求平方根函数)计算出总和的平方根,也就是 field1
这个 “向量” 的模,通过 AS field1_norm
给结果列命名为 field1_norm
。
同理可以得出 field2
与
field3
的模,这里不再赘述,接下来只需要将上述结果合起来相除便可。
select dot_product/field1_norm as similar
from (
select sum(field1 * field2 * field3) as dot_product
, sqrt(sum(field1 * field1))*sqrt(sum(field2 * field2))*sqrt(sum(field3 * field3)) as field1_norm
from table_b) as t;
到这里相似度的计算就算是结束了,不过需要注意的是,这里的相似度只是用余弦相似度公式计算出来的数值而已,而使用的也并不是真正的向量,只是将sql中的字段转换为类似向量的一种形式而已,所以仍要注意的是,这种方法只针对数值型数据可行,使用之前尤其要注意这一点,而且因为数据量级过大会导致计算量较大,所以并不适用于大批量的数据。