余弦相似度-矩阵比向量计算性能比较

背景

比如在推荐系统,通过算法计算出数据在返回向量时,就需要比较向量之间的距离,来判断相似度。 比如用tfidf计算两个文件之间的相似度。调用机器学习库会返回两个文件对应的关键词向量,需要计算两个向量之间的距离来判断两个文本的相似度。

在实际应用中可能是上千或上万个文档。我们可能需要指定一篇文档与其他文档的相似度,按分数从高到低排序,选出关联最强的文档作为推荐

先给出结论

矩阵计算性能要远远大于向量计算
以下实例均采用pyhon演示

向量表示方法

密集向量与稀疏向量
给定向量 (0.5,0,1)
密集向量表示方法 [0.5,0,1]
稀疏向量表示方法 (3,[0,2],[0.5,1])第一个数字3 表示向量维度3,[0,2]表示向量在0,和2上有数据,[0.5,1] 表示数据并且与[0,2]对应。也就是说0,5在0号位,2在二号位。未表示出来的位置的数值是0

向量初始化方法

from pyspark.ml.linalg import SparseVector
SparseVector(3, [0,2], [0.5,1])

矩阵表示方法

矩阵有很多种表示方法 这里只介绍csr_matrix表示方法
用网上的一个图来说明


image.png

图左边Matrix 是一个常规表示矩阵
图右边csr表示矩阵。
csr矩阵表示法中 只表示矩阵的非0数值及位置,在矩阵中0值较多的情况下能节约空间。
values 表示矩阵中的非0数值。
row indices 表示列标:row indices数组的第一个位置的值表示values第一个数值列标,row indices数组的第二个位置的值表示values第二个数值列标 依次类推
column indices 表示行标 :column indices数组的第一个位置的值表示values第一个数值行标 ,column indices数组的第二个位置的值表示values第二个数值行标 依次类推
对数值1 行标为0,列标也为0 所以矩阵00位置就是1
同理数值7 行标1 列标为0 所以矩阵的01位置就是7

from scipy.sparse import csr_matrix
mat = csr_matrix((data, (row_ind, col_ind)), shape=(M, N))

例如

from scipy.sparse import csr_matrix
mat = csr_matrix(([1,2,3,4], ([0,0,1,1],[0,1,0,1])), shape=(2, 2))
# mat.toarray() 结果:
array([[1, 2],
       [3, 4]], dtype=int64)

shape=(2,2) 表示 2x2的矩阵

SparseVector 向量转csr_matrix 矩阵

当然不会一个向量转成矩阵 肯定是一组向量转矩阵

# 假如有一组向量
vertor_list = []
data = []
row_ind = []
col_ind = []
M = len(vertor_list)
N = 0

for i, v in enumerate(vertor_list):
    N = v.size # 向量的维度 也就是矩阵的列数 所有向量的维度应该一致
    row_ind.extend(N * [i]) # 列下表 每一行来说列下标一致 并且都是 偏移量i
    col_ind.extend(v.indices) # 行下标 与向量的行下标一致
    data.extend(v.values)

mat = csr_matrix((data, (row_ind, col_ind)), shape=(M, N))

这组向量中的维度一定要一致,比如都是2维向量。假如一组向量有些是2维向量有些是3维向量,不仅数据转换会出错 并且二维向量与三维向量算相似度也没有意义

上述代码是把一组向量转成矩阵的方法 比如
(0,1)
(0,2)
(1,2)
转成矩阵后:
[[0,1],
[0,2],
[1,2]]

如何用矩阵来代替向量的点乘呢?

比如有一组向量
(0,1)
(0,2)
(1,2)
现有向量(1,1)分别于这组向量计算点乘
(1,1) * (0,1) = 1 * 0 + 1 * 1
(1,1) * (0,2) = 1 * 0 + 1 * 2
(1,1) * (1,2) = 1 * 1 + 1 * 2
实际上这就是矩阵

[1,1] * [[0,0,1]
           [1,2,2]]

而矩阵

 [[0,0,1]
  [1,2,2]]

正好是矩阵

[[0,1],
[0,2],
[1,2]]

的转置
好了 基础知识介绍完毕 下面开始完成的测试代码了

测试代码

from pyspark.ml.linalg import SparseVector
import random
from scipy.sparse import csr_matrix
import time
# num 表示生产多少组向量,v表示向量的维度
def build_data(num, v):
  vertor_list = []
  for i in range(num):
    data = [random.random() for i in range(v)]
    verctor = SparseVector(v, [i for i in range(v)], data)
    vertor_list.append(verctor)

  data = []
  row_ind = []
  col_ind = []
  M = len(vertor_list)
  N = 0

  for i, v in enumerate(vertor_list):
    N = v.size
    row_ind.extend(N * [i])
    col_ind.extend(v.indices)
    data.extend(v.values)

  mat = csr_matrix((data, (row_ind, col_ind)), shape=(M, N))

  return vertor_list, mat

# 性能比较
v1_list, mat1 = build_data(100, 200)
v2_list, mat2 = build_data(2, 200)

result = []
start = time.time() * 1000
for v2 in v2_list:
  for v1 in v1_list:
    result.append(v2.dot(v1))
end = time.time() * 1000
print('向量计算耗时:' + str(end - start))

start = time.time() * 1000
a = mat2.dot(mat1.T)
end = time.time() * 1000
print('矩阵计算耗时:' + str(end - start))

一组比较结果

向量计算耗时:10.74658203125
矩阵计算耗时:0.9091796875

另一组比较

v1_list, mat1 = build_data(5000, 200)
v2_list, mat2 = build_data(500, 200)

向量计算耗时:152447.44384765625
矩阵计算耗时:1628.77685546875

5000个数据200维向量在任何公司都不能有这么小的数据量 直接向量点成要2.5分钟,而矩阵相乘只需要1.6秒。直接向量点成不仅仅是效率低。而是一个不能接受的超长时间

余弦相似度说明

假如有向量A,B |A|=向量A的摸 |B|=向量B的摸
cos(A,B) = A * B / (|A|*|B|)
当|A| = 1 切 |B| = 1时 有 cos(A,B) = A * B
所以在求的向量时 先对向量进行正则化 正则化后向量的摸为1,向量的点乘即可代表向量的余弦值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349