背景
比如在推薦系統(tǒng)亲雪,通過算法計(jì)算出數(shù)據(jù)在返回向量時(shí),就需要比較向量之間的距離,來判斷相似度品擎。 比如用tfidf計(jì)算兩個(gè)文件之間的相似度。調(diào)用機(jī)器學(xué)習(xí)庫會(huì)返回兩個(gè)文件對(duì)應(yīng)的關(guān)鍵詞向量备徐,需要計(jì)算兩個(gè)向量之間的距離來判斷兩個(gè)文本的相似度萄传。
在實(shí)際應(yīng)用中可能是上千或上萬個(gè)文檔。我們可能需要指定一篇文檔與其他文檔的相似度蜜猾,按分?jǐn)?shù)從高到低排序秀菱,選出關(guān)聯(lián)最強(qiáng)的文檔作為推薦
先給出結(jié)論
矩陣計(jì)算性能要遠(yuǎn)遠(yuǎn)大于向量計(jì)算
以下實(shí)例均采用pyhon演示
向量表示方法
密集向量與稀疏向量
給定向量 (0.5,0,1)
密集向量表示方法 [0.5,0,1]
稀疏向量表示方法 (3,[0,2]蹭睡,[0.5,1])第一個(gè)數(shù)字3 表示向量維度3衍菱,[0,2]表示向量在0,和2上有數(shù)據(jù)肩豁,[0.5,1] 表示數(shù)據(jù)并且與[0,2]對(duì)應(yīng)脊串。也就是說0,5在0號(hào)位,2在二號(hào)位清钥。未表示出來的位置的數(shù)值是0
向量初始化方法
from pyspark.ml.linalg import SparseVector
SparseVector(3, [0,2], [0.5,1])
矩陣表示方法
矩陣有很多種表示方法 這里只介紹csr_matrix表示方法
用網(wǎng)上的一個(gè)圖來說明
圖左邊Matrix 是一個(gè)常規(guī)表示矩陣
圖右邊csr表示矩陣琼锋。
csr矩陣表示法中 只表示矩陣的非0數(shù)值及位置,在矩陣中0值較多的情況下能節(jié)約空間祟昭。
values 表示矩陣中的非0數(shù)值缕坎。
row indices 表示列標(biāo):row indices數(shù)組的第一個(gè)位置的值表示values第一個(gè)數(shù)值列標(biāo),row indices數(shù)組的第二個(gè)位置的值表示values第二個(gè)數(shù)值列標(biāo) 依次類推
column indices 表示行標(biāo) :column indices數(shù)組的第一個(gè)位置的值表示values第一個(gè)數(shù)值行標(biāo) 篡悟,column indices數(shù)組的第二個(gè)位置的值表示values第二個(gè)數(shù)值行標(biāo) 依次類推
對(duì)數(shù)值1 行標(biāo)為0谜叹,列標(biāo)也為0 所以矩陣00位置就是1
同理數(shù)值7 行標(biāo)1 列標(biāo)為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() 結(jié)果:
array([[1, 2],
[3, 4]], dtype=int64)
shape=(2,2) 表示 2x2的矩陣
SparseVector 向量轉(zhuǎn)csr_matrix 矩陣
當(dāng)然不會(huì)一個(gè)向量轉(zhuǎn)成矩陣 肯定是一組向量轉(zhuǎn)矩陣
# 假如有一組向量
vertor_list = []
data = []
row_ind = []
col_ind = []
M = len(vertor_list)
N = 0
for i, v in enumerate(vertor_list):
N = v.size # 向量的維度 也就是矩陣的列數(shù) 所有向量的維度應(yīng)該一致
row_ind.extend(N * [i]) # 列下表 每一行來說列下標(biāo)一致 并且都是 偏移量i
col_ind.extend(v.indices) # 行下標(biāo) 與向量的行下標(biāo)一致
data.extend(v.values)
mat = csr_matrix((data, (row_ind, col_ind)), shape=(M, N))
這組向量中的維度一定要一致匾寝,比如都是2維向量。假如一組向量有些是2維向量有些是3維向量荷腊,不僅數(shù)據(jù)轉(zhuǎn)換會(huì)出錯(cuò) 并且二維向量與三維向量算相似度也沒有意義
上述代碼是把一組向量轉(zhuǎn)成矩陣的方法 比如
(0,1)
(0很钓,2)
(1董栽,2)
轉(zhuǎn)成矩陣后:
[[0,1],
[0,2],
[1,2]]
如何用矩陣來代替向量的點(diǎn)乘呢锭碳?
比如有一組向量
(0,1)
(0推汽,2)
(1歹撒,2)
現(xiàn)有向量(1,1)分別于這組向量計(jì)算點(diǎn)乘
(1,1) * (0,1) = 1 * 0 + 1 * 1
(1,1) * (0,2) = 1 * 0 + 1 * 2
(1,1) * (1,2) = 1 * 1 + 1 * 2
實(shí)際上這就是矩陣
[1,1] * [[0,0,1]
[1,2,2]]
而矩陣
[[0,0,1]
[1,2,2]]
正好是矩陣
[[0,1],
[0,2],
[1,2]]
的轉(zhuǎn)置
好了 基礎(chǔ)知識(shí)介紹完畢 下面開始完成的測試代碼了
測試代碼
from pyspark.ml.linalg import SparseVector
import random
from scipy.sparse import csr_matrix
import time
# num 表示生產(chǎn)多少組向量暖夭,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('向量計(jì)算耗時(shí):' + str(end - start))
start = time.time() * 1000
a = mat2.dot(mat1.T)
end = time.time() * 1000
print('矩陣計(jì)算耗時(shí):' + str(end - start))
一組比較結(jié)果
向量計(jì)算耗時(shí):10.74658203125
矩陣計(jì)算耗時(shí):0.9091796875
另一組比較
v1_list, mat1 = build_data(5000, 200)
v2_list, mat2 = build_data(500, 200)
向量計(jì)算耗時(shí):152447.44384765625
矩陣計(jì)算耗時(shí):1628.77685546875
5000個(gè)數(shù)據(jù)200維向量在任何公司都不能有這么小的數(shù)據(jù)量 直接向量點(diǎn)成要2.5分鐘,而矩陣相乘只需要1.6秒邪码。直接向量點(diǎn)成不僅僅是效率低闭专。而是一個(gè)不能接受的超長時(shí)間
余弦相似度說明
假如有向量A,B |A|=向量A的摸 |B|=向量B的摸
cos(A,B) = A * B / (|A|*|B|)
當(dāng)|A| = 1 切 |B| = 1時(shí) 有 cos(A,B) = A * B
所以在求的向量時(shí) 先對(duì)向量進(jìn)行正則化 正則化后向量的摸為1,向量的點(diǎn)乘即可代表向量的余弦值