LoginSignup
40
30

More than 5 years have passed since last update.

コサイン類似度行列?それNumPyですぐ出せるよ

Posted at

結論

def cos_sim_matrix(matrix):
    """
    item-feature 行列が与えられた際に
    item 間コサイン類似度行列を求める関数
    """
    d = matrix @ matrix.T  # item-vector 同士の内積を要素とする行列

    # コサイン類似度の分母に入れるための、各 item-vector の大きさの平方根
    norm = (matrix * matrix).sum(axis=1, keepdims=True) ** .5

    # それぞれの item の大きさの平方根で割っている(なんだかスマート!)
    return d / norm / norm.T
  • 英語でググれ (「cosine similarity matrix numpy」とかで)
  • 元の数式をよく見ろ

導入

皆さんコサイン類似度行列は好きでしょうか?私は大好きです :innocent:

特に協調フィルタリングなどを行おうとすると作ってみることが多いのではないのでしょうか。

ただ残念なことに、日本語で調べると2つのベクトル間のコサイン類似度の求め方はしばしば見かけるのですが、それらがまとまったコサイン類似度行列の求め方はほとんど見かけませんでした。

もしくはもう少し numpy 力がある人にとっては自明なのであえて明記していないのかもしれません。

ですので自分のへの備忘録も兼ねてここに残しておきます。

それまでにやってしまった過ち

これは失敗作ですので参考にしないでください :cry:
上記の数十倍時間かかります :poop:

@numba.jit('f8(f8[:],f8[:])', nopython=True)
def _cos_sim(v1, v2):
    """
    2つのベクトルのコサイン類似度を返す
    """
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

@numba.jit('f8[:, :](f8[:, :])', nopython=True)
def item_similarities(item_user_matrix):
    """
    アイテム・ユーザー行列が与えられた際に
    アイテム間類似度行列を求める関数
    """
    n = item_user_matrix.shape[0]  # n: item counts
    sims = np.identity(n)  # 同じアイテム同士の類似度は1

    for i in range(n):
        for j in range(i+1, n):
            sim = _cos_sim(item_user_matrix[i], item_user_matrix[j])
            sims[i][j] = sim
            sims[j][i] = sim
    return sims

最後に

I need more power...!! (もっと行列計算力が欲しい)

40
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
30