版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1433124
代码语言:txt复制 MADlib的线性代数模块(linalg module)包括基本线性代数操作的实用函数,其中包括多种范式、距离、相似度、向量均值、矩阵聚合等函数。本篇先从讨论相似性和相异性的基本概念,然后对照概念说明MADlib的线性代数函数,并用简单示例描述这些函数的用法。
一、邻近度的度量
代码语言:txt复制 相似性要和相异性是重要的概念,因为它们被许多数据挖掘技术所使用,如聚类、最邻近分类和异常检测等。在许多情况下,一旦计算出相似性或相异性,就不再需要原始数据了。这种方法可以看作将数据变换到相似性(相异性)空间,然后进行分析。为方便起见,我们使用术语邻近度(proximity)表示相似性或相异性。
代码语言:txt复制 两个对象之间的相似度(similarity)是指这两个对象相似程度的数值度量。两个对象越相似,它们的相似度就越高。通常,相似度是非负的,并常常在0(不相似)和1(完全相似)之间取值。两个对象之间的相异度(dissimilarity)是这两个对象差异程度的数值度量。对象越类似,它们的相异度就越低。术语距离(distance)经常用作相异度的同义词,用来表示特定类型的相异度。有时,相异度在区间[0,1]中取值,但相异度在0和∞之间取值也很常见。
代码语言:txt复制 通常使用变换把相似度转换成相异度或相反,或者把邻近度变换到一个特定区间,如[0,1]。例如,我们可能有相似度,其值域从1到10,但是我们打算使用的算法或软件只能处理相异度,或只能处理[0,1]区间的相似度。这种变换相对独立于特定的邻近度度量方法。
代码语言:txt复制 邻近度度量(特别是相似度)常被定义为或变换到区间[0,1]中的值。这样做的动机是使用一种适当的尺度,由邻近度的值表明两个对象之间的相似(或相异)程度。这种变换通常是比较直接的。例如,如果对象之间的相似度在1和10之间变化,则我们可以使用如下变换将它变换到[0,1]区间:s'=(s-1)/9,其中s和s'分别是相似度的原值和新值。一般来说,相似度到[0,1]区间的变换由如下表达式给出:s'=(s-min_s)/(max_s-min_s),其中max_s和min_s分别是相似度的最大值和最小值。类似地,有限值域的相异度也能用d'=(d-min_d)/(max_d-min_d)映射到[0,1]区间。这种方法有时也称为Min-Max标准化法。当使用诸如神经网络、最近邻分类或聚类这种基于距离的挖掘算法进行建模或挖掘时,如果待分析的数据已经标准化,即按比例映射到一个较小的区间(如[0,1]),则这些方法将得到更好的结果。
代码语言:txt复制 然而,将邻近度映射到[0,1]区间也可能非常复杂。例如,如果邻近度度量原来在区间[0,∞]上取值,则需要使用非线性变换,并且在新的尺度上,值之间不再具有与原来相同的联系。对于从0变化到∞的相异度度量,考虑变换d'=d/(1 d),相异度0、0.5、2、10、100和1000分别被变换到0、0.33、0.67、0.90、0.99和0.999。在原来相异性尺度上较大的值被压缩到1附近,但是否希望如此取决于应用。另一个问题是邻近度度量的含义可能会被改变。例如,相关性是一种相似性度量,在区间[-1,1]上取值,通过取绝对值将这些值映射到[0,1]区间丢失了符号信息,而对于某些应用,符号信息可能是重要的。
代码语言:txt复制 将相似度变换成相异度或相反也是比较直接的,尽管我们可能再次面临保持度量的含义问题和将线性尺度改变成非线性尺度的问题。如果相似度(相异度)落在[0,1]区间,则相异度(相似度)可以定义为d=1-s(或s=1-d)。另一种简单的方法是定义相似度为负的相异度(或相反)。例如,相异度0、1、10和100可以分别换成相似度0、-1、-10和-100。
代码语言:txt复制 负变换产生的相似度不必局限于[0,1]区间,但如果希望的话,则可以使用变换s=1/(d 1)、
或
。
代码语言:txt复制 一般来说,任何单调减函数都可以用来将相异度转换到相似度(或相反)。当然,在将相似度变换到相异度(或相反),或者在将邻近度的值变换到新的尺度时,也必须考虑一些其它因素,如前面提到过的涉及保持意义、扰乱标度和数据分析工具的需要等等问题。
二、MADlib的邻近度相关函数
1. 函数概览
代码语言:txt复制 利用MADlib提供的邻近度相关函数,可以很方便地实现新算法。这些函数操作的对象是向量(1维FLOAT8数组)和矩阵(2维FLOAT8数组)。注意,这类函数只接受FLOAT8数组参数,因此在调用函数时,需要将其它类型的数组转换为FLOAT8[]。表1列出了相关函数的简要说明。
函数名称 | 描述 | 参数 | 返回值 |
---|---|---|---|
norm1() | 向量的1范数 | 向量 | |
norm2() | 向量的2范数 | 向量 | |
dist_norm1() | 两个向量之差的1范数 | 向量 向量 | |
dist_norm2() | 两个向量之差的2范数 | 向量 向量 | |
dist_pnorm() | 两个向量之差的p范数 | 向量 向量 标量p,p>0 | |
dist_inf_norm() | 两个向量之差的无穷范数 | 向量 向量 | |
squared_dist_norm2() | 两个向量之差的2范数平方 | 向量 向量 | |
cosine_similarity() | 两个向量的余弦相似度 | 向量 向量 | |
dist_angle() | 欧氏空间中两个向量之间的角距离 | 向量 向量 | |
dist_tanimoto() | 两个向量间的谷本距离 | 向量 向量 | |
dist_jaccard() | 两个字符向量集之间的杰卡德距离 | 向量 向量 | |
get_row() | 返回矩阵的行 | 二维数组行下标 | 二维数组的一行 |
get_col() | 返回矩阵的列 | 二维数组列下标 | 二维数组的一列 |
avg() | 计算向量的平均值 | m个n维向量 | |
normalized_avg() | 计算向量的归一化平均值(欧氏空间中的单位向量) | m个n维向量 | |
matrix_agg() | 将向量合并进一个矩阵 | 向量 | 包含列的矩阵 |
表1 MADlib邻近度相关函数
2. 函数示例
(1)范数
代码语言:javascript复制select madlib.norm1('{1,-2,3}'),madlib.norm2('{1,-2,3}');
代码语言:txt复制 结果:
代码语言:javascript复制 norm1 | norm2
------- ------------------
6 | 3.74165738677394
(1 row)
代码语言:txt复制 1范数的定义为向量各元素绝对值的和,2范数的定义是向量各元素平方和的平方根。根据定义下面的查询与范数函数的结果相同。
代码语言:javascript复制select sum(norm1) norm1, sqrt(sum(norm2))norm2
from (select abs(unnest('{1,-2,3}'::float[])) norm1 ,
power(unnest('{1,-2,3}'::float[]),2) norm2) t;
(2)欧几里得距离(L2范数)
代码语言:javascript复制select madlib.dist_norm2('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_norm2
------------------
5.19615242270663
代码语言:javascript复制select madlib.dist_pnorm('{1,-2,3}','{4,-5,6}', 5);
(1 row)
代码语言:txt复制 2范数与欧几里得距离相对应。一维、二维、三维或高维空间中两个点x和y之间的欧几里得距离(Euclideandistance)d由如下公式定义:
其中,n是维数,而
和
分别是x和y的第k个属性值(分量)。
(3)闵可夫斯基距离(Lp范数)
代码语言:javascript复制select madlib.dist_pnorm('{1,-2,3}','{4,-5,6}', 5);
代码语言:txt复制 结果:
代码语言:javascript复制 dist_pnorm
------------------
3.73719281884655
(1 row)
代码语言:txt复制 欧几里得距离可以用闵可夫斯基距离(Minkowski distance)来推广:
其中r是标量参数。注意不要将参数r与维数(属性数)n混淆。欧几里得距离、曼哈顿距离和上确界距离是对n的所有值(1,2,3…)定义的,并且指定了将每个维(属性)上的差的组合成总距离的不同方法。
(4)曼哈顿距离(L1范数)
代码语言:javascript复制select madlib.dist_norm1('{1,-2,3}', '{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_norm1
------------
9
(1 row)
代码语言:txt复制 当闵可夫斯基距离的r = 1时称为曼哈顿距离。r = 2,就是欧几里得距离。
(5)上确界距离(Lmax或L∞范数)。
代码语言:javascript复制select madlib.dist_inf_norm('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_inf_norm
---------------
3
(1 row)
代码语言:txt复制 当闵可夫斯基距离的r = ∞,称为上确界距离。这是对象属性之间的最大距离。更正式地,
距离由下面的公式定义:
(6)平方欧几里得距离
代码语言:javascript复制select madlib.squared_dist_norm2('{1,-2,3}', '{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 squared_dist_norm2
--------------------
27
(1 row)
代码语言:txt复制 平方欧几里得距离即:
代码语言:txt复制 距具有一些基本性质。如果d(x,y)是两个点x和y之间的距离,则如下性质成立:
- 非负性。(a)对于所有x和y,d(x,y)≥0,(b)仅当x=y时d(x,y)=0。
- 对称性。对于所有x和y,d(x,y)=d(y,x)。
- 三角不等式。对于所有x、y和z,d(x,z) ≤ d(x,y) d(y,z)。
对于相似度,三角不等式(或类似的性质)通常不成立,但是对称性和非负性通常成立。更明确地说,如果s(x,y)是数据点x和y之间的相似度,则相似度具有如下典型性质。
- 仅当x=y时s(x,y)=1。(0≤s≤1)
- 对于所有x和y,s(x,y)=s(y,x)。(对称性)
对于相似度,没有与三角不等式对应的一般性质。然而,有时可以将相似度简单地变换成一种度量距离。
(7)Jaccard距离
代码语言:javascript复制select madlib.dist_jaccard('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_jaccard
--------------
1
(1 row)
代码语言:txt复制 Jaccard距离的定义是1- Jaccard系数(Jaccard Coefficient)。Jaccard 系数定义为A与B交集的大小与A与B并集的大小的比值。假定x和y是两个数据对象,代表两个事务。如果每个二元属性对应于商店的一种商品,1表示该商品被购买,而0表示该商品未被购买。由于未被顾客购买的商品数远远大于被其购买的商品数,常常使用Jaccard系数来处理这种仅包含非对称二元属性的对象。Jaccard系数通常用符号J表示,由如下等式定义:
其中:
=x取0并且y取0的属性个数
=x取0并且y取1的属性个数
=x取1并且y取0的属性个数
=x取1并且y取1的属性个数
(8)Tanimoto距离
代码语言:javascript复制select madlib.dist_tanimoto('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_tanimoto
-------------------
0.457627118644068
(1 row)
代码语言:txt复制 Tanimoto距离(谷本距离)定义为1-Tanimoto系数。Tanimoto系数又称广义Jaccard系数,可以用于文档数据,并在二元属性情况下归约为Jaccard系数。该系数用EJ表示,由下式定义:
(9)余弦相似度
代码语言:javascript复制select madlib.cosine_similarity('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 cosine_similarity
-------------------
0.974631846197076
(1 row)
代码语言:txt复制 在介绍MADlib的稀疏向量时,曾经举了一个tf/idf算法的例子。当时我们使用了反余弦函数计算文档的角距离,从而以此判断文档的相似度(参见[http://blog.csdn.net/wzy0623/article/details/78874176](http://blog.csdn.net/wzy0623/article/details/78874176))。文档的相似性度量不仅应当像Jaccard度量一样需要忽略0-0匹配,而且还必须能够处理非二元向量。文档相似性最常用的度量之一就是余弦相似度,其定义如下。如果x和y是两个文档向量,则
其中,“.”表示向量点积,
,
是向量x的长度,
。
代码语言:txt复制 余弦相似度实际上是x和y之间夹角(余弦)的度量。这样,如果余弦相似度为1,则x个y之间的夹角为0度,并且除大小(长度)之外,x和y是相同的;如果余弦相似度为0,则x和y之间的夹角为90度,以文档为例,说明它们不包含任何相同的词(术语)。
代码语言:txt复制 余弦相似度公式可以写成下面的形式:
其中,
,而
。x和y被它们的长度除,将它们规范化成具有长度1。这意味着在计算相似度时,余弦相似度不考虑两个数据对象的量值。(当量值是重要的时,欧几里得距离可能是一种更好的选择。)对于长度为1的向量,余弦度量可以通过简单地取点积计算。从而,在需要大量对象之间的余弦相似度时,将对象规范化,使之具有单位长度可以减少计算时间。
(10)角距离
代码语言:javascript复制select madlib.dist_angle('{1,-2,3}','{4,-5,6}');
代码语言:txt复制 结果:
代码语言:javascript复制 dist_angle
-------------------
0.225726128552734
(1 row)
代码语言:txt复制 角距离定义为余弦相似度上的反余弦函数:
代码语言:javascript复制dm=# selectacos(madlib.cosine_similarity('{1,-2,3}', '{4,-5,6}'));
acos
-------------------
0.225726128552734
(1 row)
(11)取矩阵的行列
代码语言:javascript复制drop table if exists matrix;
create table matrix(id integer, mfloat8[]);
insert into matrix values (1,'{{4,5},{3,5},{9,0}}');
select madlib.get_row(m, 1) as row_1,
madlib.get_row(m, 2) as row_2,
madlib.get_row(m, 3) as row_3,
madlib.get_col(m, 1) as col_1,
madlib.get_col(m, 2) as col_2
from matrix;
代码语言:txt复制 结果:
代码语言:javascript复制 row_1 | row_2 | row_3 | col_1 | col_2
------- ------- ------- --------- ---------
{4,5} | {3,5} | {9,0} | {4,3,9} | {5,5,0}
(1 row)
(12)求向量平均值
代码语言:javascript复制drop table if exists vector;
create table vector(id integer, vfloat8[]);
insert into vector values (1, '{4,1}'), (2,'{8,-6}'), (3, '{5,9}');
select madlib.avg(v) from vector;
代码语言:txt复制 结果:
代码语言:javascript复制 avg
-------------------------------------
{5.66666666666667,1.33333333333333}
(1 row)
代码语言:txt复制 平均值函数madlib.avg对一组向量个分量求平均值,返回由分量平均值构成的结果向量。
代码语言:javascript复制dm=# select avg(v[1]),avg(v[2]) fromvector;
avg | avg
------------------ ------------------
5.66666666666667 | 1.33333333333333
(1 row)
(13)求向量的归一化平均值
代码语言:javascript复制select madlib.normalized_avg(v) fromvector;
代码语言:txt复制 结果:
代码语言:javascript复制 normalized_avg
---------------------------------------
{0.974756579834322,0.223270262394468}
(1 row)
代码语言:txt复制 madlib.normalized_avg的计算过程为:
- 将原数据中的向量做标准差归一化。
- 对归一化后的数据求向量平均值。
- 对结果向量再做一次标准差归一化,返回结果向量。
dm=# select c1/sqrt(power(c1,2) power(c2,2)) c1,
dm-# c2/sqrt(power(c1,2) power(c2,2)) c2
dm-# from (select avg(v[1]/sqrt(power(v[1],2) power(v[2],2))) c1,
dm(# avg(v[2]/sqrt(power(v[1],2) power(v[2],2))) c2
dm(# from vector) t;
c1 | c2
------------------- -------------------
0.974756579834322 | 0.223270262394468
(1 row)
代码语言:txt复制 注意madlib.normalized_avg函数只做归一化,不做中心化(均值不为0)。
(14)合并向量
代码语言:javascript复制select madlib.matrix_agg(v) from vector;
代码语言:txt复制 结果:
代码语言:javascript复制 matrix_agg
----------------------
{{4,1},{8,-6},{5,9}}
(1 row)
代码语言:txt复制 madlib.matrix_agg函数将参数中的一组向量合并为一个矩阵(2维数组)。
三、距离度量的中心化和标准化
代码语言:txt复制 距离度量的一个重要问题是当属性具有不同的值域时如何处理。(这种情况通常称作“变量具有不同的尺度。”)例如,基于年龄和收入两个属性来度量人之间的欧几里得距离,除非这两个属性是标准化的,否则两个人之间的距离将被收入所左右。
代码语言:txt复制 当属性具有不同值域(不同的方差)、并且数据分布近似于正太分布时,需要标准化步骤对数据进行预处理。通过中心化和标准化处理,可以得到均值为0,标准差为1的服从正太分布的数据。
代码语言:txt复制 假设样本集X的均值(mean)为m,标准差(Standard Deviation)为s,那么X的标准化变量表示为:
代码语言:txt复制 假设有一组数值
,其算数平均值为m,则标准差的计算公式为:
代码语言:txt复制 标准差是一组数据平均值分散程度的一种度量。较大的标准差表示大部分数值和其平均值之间差异较大,标准差较小,代表这些数值比较接近平均值。
代码语言:txt复制 通过简单的推导可得,两个向量x和y的标准化欧几里得距离的计算公式为:
其中,
是向量x的第k个分量,
向量y的第k个分量,
是第k个分量上的标准差。这样,在计算距离时,不同特征的影响程度就一样了。如果将方差的倒数看成是权重,标准化欧氏距离公式也可以看作是一种加权欧氏距离。
代码语言:txt复制 标准化欧几里得距离解决了不同属性的尺度(值域)不一致的问题,但当某些属性之间相关时,可能需要使用马氏距离。
四、选取正确的邻近度度量
代码语言:txt复制 首先,邻近度度量的类型应该与数据类型相适应。对于稠密的、连续的数据,通常使用距离度量,如欧几里得距离。数据挖掘中,取实数值的数据是连续的数据,而具有有限个值或无限但可数个值的数据称为离散数据。连续属性之间的邻近度通常用属性值的差来表示,并且距离度量提供了一种将这些差组合到总邻近性度量的良好的方法。尽管属性可能有不同的取值范围和不同的重要性,但这些问题通常可以使用标准化或加权的方法处理。
代码语言:txt复制 对于稀疏数据,常常包含非对称的属性,通常使用忽略0-0匹配的相似性度量。从概念上讲,这反映了如下事实:对于一对复杂对象,相似度依赖于它们共同具有的性质数目,而不是依赖于它们都缺失的性质数目。在特殊情况下,对于稀疏的、非对称的数据,大部分对象都只具有少量被属性描述的性质,因此如果考虑它们都不具有的性质的话,它们都高度相似。余弦、Jaccard和广义Jaccard度量对于这类数据是合适的。
代码语言:txt复制 在某些情况下,为了得到合适的相似性度量,数据的变换或规范化是重要的,因为这种变换并非总能在邻近性度量中提供,例如,时间序列数据可能具有显著影响相似性的趋势或周期模式。此外,正确地计算相似度还需要考虑时间延迟。最后,两个时间序列可能只在特定的时间周期上相似,例如,气温与天然气的用量之间存在很强的关联,但是这种联系仅出现在取暖季节。
代码语言:txt复制 实践考虑也是重要的。有时,一种或多种邻近度度量已经在某个特定领域使用,因此,其他人已经回答了应当使用何种邻近度度量的问题;另外,所使用的软件包或聚类算法可能完全限制了选择;如果关心效率,则我们可能希望选择具有某些性质的邻近性度量,这些性质(如三角不等式)可以用来降低邻近度计算量。然而,如果通常的实践或实践限制并未规定某种选择,则正确地选择邻近性度量可能是一项耗时的任务,需要仔细地考虑领域知识和度量使用的目的。可能需要评估许多不同的相似性度量,以确定哪些结果最有意义。