it-swarm.dev

ما هي أسرع طريقة في Python لحساب تشابه جيب التمام في ضوء بيانات المصفوفة المتناثرة؟

بالنظر إلى قائمة مصفوفة متفرقة ، ما هي أفضل طريقة لحساب تشابه جيب التمام بين كل من الأعمدة (أو الصفوف) في المصفوفة؟ أنا أفضل عدم تكرار ن اختيار مرتين.

قل مصفوفة الإدخال:

A= 
[0 1 0 0 1
 0 0 1 1 1
 1 1 0 1 0]

التمثيل المتناثر هو:

A = 
0, 1
0, 4
1, 2
1, 3
1, 4
2, 0
2, 1
2, 3

في Python ، من السهل العمل باستخدام تنسيق المصفوفة:

import numpy as np
from sklearn.metrics import pairwise_distances
from scipy.spatial.distance import cosine

A = np.array(
[[0, 1, 0, 0, 1],
[0, 0, 1, 1, 1],
[1, 1, 0, 1, 0]])

dist_out = 1-pairwise_distances(A, metric="cosine")
dist_out

يعطي:

array([[ 1.        ,  0.40824829,  0.40824829],
       [ 0.40824829,  1.        ,  0.33333333],
       [ 0.40824829,  0.33333333,  1.        ]])

هذا جيد لإدخال مصفوفة كاملة ، لكنني أريد حقًا أن أبدأ بالتمثيل المتناثر (بسبب حجم المصفوفة الخاصة بي). أي أفكار حول أفضل طريقة لتحقيق ذلك؟ شكرا لك مقدما.

50
zbinsd

يمكنك حساب تشابه جيب التمام الزوجي على صفوف المصفوفة المتناثرة مباشرةً باستخدام sklearn. بدءًا من الإصدار 0.17 ، يدعم أيضًا الإخراج المتناثر:

from sklearn.metrics.pairwise import cosine_similarity
from scipy import sparse

A =  np.array([[0, 1, 0, 0, 1], [0, 0, 1, 1, 1],[1, 1, 0, 1, 0]])
A_sparse = sparse.csr_matrix(A)

similarities = cosine_similarity(A_sparse)
print('pairwise dense output:\n {}\n'.format(similarities))

#also can output sparse matrices
similarities_sparse = cosine_similarity(A_sparse,dense_output=False)
print('pairwise sparse output:\n {}\n'.format(similarities_sparse))

النتائج:

pairwise dense output:
[[ 1.          0.40824829  0.40824829]
[ 0.40824829  1.          0.33333333]
[ 0.40824829  0.33333333  1.        ]]

pairwise sparse output:
(0, 1)  0.408248290464
(0, 2)  0.408248290464
(0, 0)  1.0
(1, 0)  0.408248290464
(1, 2)  0.333333333333
(1, 1)  1.0
(2, 1)  0.333333333333
(2, 0)  0.408248290464
(2, 2)  1.0

إذا كنت تريد التشابه في عمود التمام تمامًا ، فعليك ببساطة تغيير مصفوفة الإدخال مسبقًا:

A_sparse.transpose()
52
Jeff

الطريقة التالية أسرع بنحو 30 مرة من scipy.spatial.distance.pdist. يعمل بسرعة كبيرة على المصفوفات الكبيرة (على افتراض أن لديك ذاكرة RAM كافية)

انظر أدناه لمناقشة كيفية تحسين ل sparsity.

# base similarity matrix (all dot products)
# replace this with A.dot(A.T).toarray() for sparse representation
similarity = numpy.dot(A, A.T)


# squared magnitude of preference vectors (number of occurrences)
square_mag = numpy.diag(similarity)

# inverse squared magnitude
inv_square_mag = 1 / square_mag

# if it doesn't occur, set it's inverse magnitude to zero (instead of inf)
inv_square_mag[numpy.isinf(inv_square_mag)] = 0

# inverse of the magnitude
inv_mag = numpy.sqrt(inv_square_mag)

# cosine similarity (elementwise multiply by inverse magnitudes)
cosine = similarity * inv_mag
cosine = cosine.T * inv_mag

إذا كانت مشكلتك نموذجية بالنسبة لمشكلات التفضيل الثنائي واسعة النطاق ، فلديك المزيد من الإدخالات في بعد واحد عن الآخر. أيضًا ، البعد القصير هو البعد الذي تريد حساب إدخالات التشابه بينها. دعنا نطلق على هذا البعد البعد "العنصر".

إذا كانت هذه هي الحالة ، فقم بإدراج "العناصر" في الصفوف وإنشاء A باستخدام scipy.sparse . ثم استبدل السطر الأول كما هو محدد.

إذا كانت مشكلتك غير نمطية ، فستحتاج إلى مزيد من التعديلات. يجب أن تكون هذه بدائل مباشرة لعمليات numpy الأساسية مع scipy.sparse ما يعادلها.

40
Waylon Flinn

لقد جربت بعض الأساليب أعلاه. ومع ذلك ، فإن التجربة التي كتبهاzbinsd لها حدودها. تباين المصفوفة المستخدم في التجربة منخفض للغاية بينما يكون التباين الحقيقي أكثر من 90٪. في حالتي ، فإن المتناثر هو في شكل (7000 ، 25000) وتناثر 97 ٪. الطريقة الرابعة بطيئة للغاية ولا يمكنني تحمل النتائج. أنا استخدم الطريقة 6 التي انتهت في 10 ثانية. بشكل مثير للدهشة ، أجرب الطريقة أدناه وانتهت في 0.247 ثانية فقط.

import sklearn.preprocessing as pp

def cosine_similarities(mat):
    col_normed_mat = pp.normalize(mat.tocsc(), axis=0)
    return col_normed_mat.T * col_normed_mat

هذه الطريقة الفعالة مرتبطة بـ أدخل وصف الرابط هنا

11
tianshi miao

أخذت كل هذه الإجابات وكتبت نصًا برمجيًا إلى 1. تحقق من صحة كل من النتائج (انظر التأكيد أدناه) و 2. انظر ما هو الأسرع. الكود والنتائج أدناه:

# Imports
import numpy as np
import scipy.sparse as sp
from scipy.spatial.distance import squareform, pdist
from sklearn.metrics.pairwise import linear_kernel
from sklearn.preprocessing import normalize
from sklearn.metrics.pairwise import cosine_similarity

# Create an adjacency matrix
np.random.seed(42)
A = np.random.randint(0, 2, (10000, 100)).astype(float).T

# Make it sparse
rows, cols = np.where(A)
data = np.ones(len(rows))
Asp = sp.csr_matrix((data, (rows, cols)), shape = (rows.max()+1, cols.max()+1))

print "Input data shape:", Asp.shape

# Define a function to calculate the cosine similarities a few different ways
def calc_sim(A, method=1):
    if method == 1:
        return 1 - squareform(pdist(A, metric='cosine'))
    if method == 2:
        Anorm = A / np.linalg.norm(A, axis=-1)[:, np.newaxis]
        return np.dot(Anorm, Anorm.T)
    if method == 3:
        Anorm = A / np.linalg.norm(A, axis=-1)[:, np.newaxis]
        return linear_kernel(Anorm)
    if method == 4:
        similarity = np.dot(A, A.T)

        # squared magnitude of preference vectors (number of occurrences)
        square_mag = np.diag(similarity)

        # inverse squared magnitude
        inv_square_mag = 1 / square_mag

        # if it doesn't occur, set it's inverse magnitude to zero (instead of inf)
        inv_square_mag[np.isinf(inv_square_mag)] = 0

        # inverse of the magnitude
        inv_mag = np.sqrt(inv_square_mag)

        # cosine similarity (elementwise multiply by inverse magnitudes)
        cosine = similarity * inv_mag
        return cosine.T * inv_mag
    if method == 5:
        '''
        Just a version of method 4 that takes in sparse arrays
        '''
        similarity = A*A.T
        square_mag = np.array(A.sum(axis=1))
        # inverse squared magnitude
        inv_square_mag = 1 / square_mag

        # if it doesn't occur, set it's inverse magnitude to zero (instead of inf)
        inv_square_mag[np.isinf(inv_square_mag)] = 0

        # inverse of the magnitude
        inv_mag = np.sqrt(inv_square_mag).T

        # cosine similarity (elementwise multiply by inverse magnitudes)
        cosine = np.array(similarity.multiply(inv_mag))
        return cosine * inv_mag.T
    if method == 6:
        return cosine_similarity(A)

# Assert that all results are consistent with the first model ("truth")
for m in range(1, 7):
    if m in [5]: # The sparse case
        np.testing.assert_allclose(calc_sim(A, method=1), calc_sim(Asp, method=m))
    else:
        np.testing.assert_allclose(calc_sim(A, method=1), calc_sim(A, method=m))

# Time them:
print "Method 1"
%timeit calc_sim(A, method=1)
print "Method 2"
%timeit calc_sim(A, method=2)
print "Method 3"
%timeit calc_sim(A, method=3)
print "Method 4"
%timeit calc_sim(A, method=4)
print "Method 5"
%timeit calc_sim(Asp, method=5)
print "Method 6"
%timeit calc_sim(A, method=6)

النتائج:

Input data shape: (100, 10000)
Method 1
10 loops, best of 3: 71.3 ms per loop
Method 2
100 loops, best of 3: 8.2 ms per loop
Method 3
100 loops, best of 3: 8.6 ms per loop
Method 4
100 loops, best of 3: 2.54 ms per loop
Method 5
10 loops, best of 3: 73.7 ms per loop
Method 6
10 loops, best of 3: 77.3 ms per loop
5
zbinsd

يجب عليك مراجعة scipy.sparse ( الرابط ). يمكنك تطبيق العمليات على تلك المصفوفات المتناثرة تمامًا مثل طريقة استخدام المصفوفة العادية.

1
cheeyos

مرحبا يمكنك أن تفعل ذلك بهذه الطريقة

    temp = sp.coo_matrix((data, (row, col)), shape=(3, 59))
    temp1 = temp.tocsr()

    #Cosine similarity
    row_sums = ((temp1.multiply(temp1)).sum(axis=1))
    rows_sums_sqrt = np.array(np.sqrt(row_sums))[:,0]
    row_indices, col_indices = temp1.nonzero()
    temp1.data /= rows_sums_sqrt[row_indices]
    temp2 = temp1.transpose()
    temp3 = temp1*temp2
1
Vaali
def norm(vector):
    return sqrt(sum(x * x for x in vector))    

def cosine_similarity(vec_a, vec_b):
        norm_a = norm(vec_a)
        norm_b = norm(vec_b)
        dot = sum(a * b for a, b in Zip(vec_a, vec_b))
        return dot / (norm_a * norm_b)

يبدو أن هذه الطريقة أسرع إلى حد ما من استخدام تطبيق sklearn في حالة تمرير زوج واحد من المتجهات في وقت واحد.

0
Daniel

بناء حل فالي:

def sparse_cosine_similarity(sparse_matrix):
    out = (sparse_matrix.copy() if type(sparse_matrix) is csr_matrix else
           sparse_matrix.tocsr())
    squared = out.multiply(out)
    sqrt_sum_squared_rows = np.array(np.sqrt(squared.sum(axis=1)))[:, 0]
    row_indices, col_indices = out.nonzero()
    out.data /= sqrt_sum_squared_rows[row_indices]
    return out.dot(out.T)

هذا يأخذ مصفوفة متفرق (ويفضل أن يكون csr_matrix) وإرجاع csr_matrix. ينبغي أن تفعل الأجزاء الأكثر كثافة باستخدام حسابات متفرق مع الحد الأدنى من الذاكرة حمولة جميلة. لم أختبرها على نطاق واسع ، لذا التحذير من الامبراطور (التحديث: أشعر بالثقة في هذا الحل الآن بعد أن قمت باختباره وتقييمه)

أيضًا ، يوجد هنا إصدار متفرق من حلول Waylon في حال كان يساعد أي شخص ، غير متأكد من الحل الأفضل فعليًا.

def sparse_cosine_similarity_b(sparse_matrix):
    input_csr_matrix = sparse_matrix.tocsr()
    similarity = input_csr_matrix * input_csr_matrix.T
    square_mag = similarity.diagonal()
    inv_square_mag = 1 / square_mag
    inv_square_mag[np.isinf(inv_square_mag)] = 0
    inv_mag = np.sqrt(inv_square_mag)
    return similarity.multiply(inv_mag).T.multiply(inv_mag)

يبدو أن كلا الحلين يتماثلان مع sklearn.metrics.pairwise.cosine_similarity

:-د

تحديث:

لقد اختبرت كلاً من الحلين في مقابل تطبيق Cython الحالي: https://github.com/davidmashburn/sparse_dot/blob/master/test/benchmarks_v3_output_table.txt ويبدو أن الخوارزمية الأولى تؤدي الأفضل من الثلاثة معظم الوقت.

0
David

أقترح تشغيل في خطوتين:

1) إنشاء التعيين A الذي يعين A: فهرس الأعمدة> الكائنات غير الصفر

2) لكل كائن i (صف) مع تكرارات غير صفرية (أعمدة) {k1 ، .. kn} لحساب تشابه جيب التمام فقط للعناصر في مجموعة الاتحاد A [k1] U A [k2] U .. A [kn]

بافتراض وجود مصفوفة متناثرة كبيرة بتناثر عالٍ ، فإن هذا سيكسب دفعة كبيرة للقوة الغاشمة

0
moshik