LoginSignup
11
9

pythonで「君とはベクトルが合わない」を数値で出そう

Last updated at Posted at 2019-08-25

#ベクトルが合わない?
趣味、思考、方向性が違うってときに、君とはベクトルが合わないわーとかで一言で片付けられません?
そう言われるとなんかちょっとイラってします・・

そんなベクトルが合うとか合わないとかいう曖昧な表現を、数値で正確に表す方法として”コサイン類似度”というものがあるので紹介します!
(( かんたんに言えば数Ⅱで習う内積の公式を使った類似度計算です ))

#コサイン類似度とは
コサイン類似度の話をする前にまず内積の復習をしますね
##内積

コサイン類似度.jpeg

α = (a,b)
β = (c,d)
α,βをベクトルとすると、これらの内積は

α・β = ac + bd

このα,βの内積は、この2つのベクトルの間の角度をθとすると

α・β = |α||β|cosθ

っていう超有名な関係式が成り立ちます
(ちなみに、|α| = √(a^2 + b^2), |β| = √(c^2 + d^2) ですね!)

##コサイン類似度

この関係式をcosθ基準に考えると

cosθ = (α・β)/|α||β|

IMG_0144.JPG

θは2つのベクトルの間の角度だから、2つのベクトルが近いとθ→0に近づき、cosθ→1に近づく。

α、βの要素がすべてなら 0 ≦ cosθ ≦ 1

つまりα君とβ君のベクトルが合う(近い)ときcosθ =1、合わない(遠い)ときcosθ = 0


#Pythonで書いてみる

####環境

ubuntu 18.04
python3


分かりやすいようにコサイン類似度を返すモジュール作ってimportして使います!
まずモジュール制作

module_cos.py

import math
import numpy as np

def calc_cos(list1, list2):
    list1 = np.array(list1) # list ではなく np.array を使う
    list2 = np.array(list2)
    sum1 = sum(list1**2) # ループを使う必要がない(速い)
    sum2 = sum(list2**2)
    length1 = math.sqrt(sum1)
    length2 = math.sqrt(sum2)
    inner_product = sum(list1*list2) # ループを使う必要がない(速い)
    if (length1 != 0 and length2 != 0):
        cos = inner_product/(length1*length2)
    else:
        cos = 0
    return cos

簡単に説明すると
sum は、ベクトルの各要素の2乗を足したやつ
length は、sumの平方根 つまり|sum|ですね

length = 0 だと分母が0になって発散しちゃうんで、その時だけ例外処理してます

このモジュールを使うときは同じフォルダに置いて、import module_cos で呼んで、module_cos.cos(list1,list2) みたいに使います
(返り値はコサイン類似度)


###例えば、音楽の嗜好ベクトルの類似度を求めてみよう

個人的に音楽を聞くのが趣味なので、音楽のベクトルの類似性を考えてみよう!

架空の6人を創造します

1 = 好き
0 = そこまで

A B C D E F
クリープハイプ 1 1 0 1 0 1
RADWIMPS 1 1 0 1 1 0
Mr.Children 0 0 1 0 1 1
GreeeeN 0 0 1 0 1 0
サカナクション 0 1 1 0 0 0
ONE OK ROCK 0 1 1 1 0 0
あいみょん 1 1 0 1 1 1
BUMP OF CHICKEN 1 0 1 1 1 0

さて、A〜Fまでの中で音楽性のベクトルが合うor合わないを求めます。

cos_sim.py
import module_cos
import numpy as np

A = [1,1,0,0,0,0,1,1]
B = [1,1,0,0,1,1,1,0]
C = [0,0,1,1,1,1,0,1]
D = [1,1,0,0,0,1,1,1]
E = [0,1,1,1,0,0,1,1]
F = [1,0,1,0,0,0,1,0]
people = [A,B,C,D,E,F]
tag = ["A","B","C","D","E","F"]


chemistry_list = []
cos_list = [] # cos だけを記録するリスト
n = len(people)
for i in range(n-1):
    for j in range(i+1, n):
        cos0 = module_cos.calc_cos(people[i], people[j])
        temp = tag[i]+""+tag[j]+""+str(cos0)
        chemistry_list.append(temp)
        cos_list.append(cos0)
        print(temp)
cos_list = np.array(cos_list)
max2 = cos_list.argmax() # 最大値は何番目の要素かを求める
min2 = cos_list.argmin() # 最小値は何番目の要素かを求める
print("\nベクトルが最も合うのは", chemistry_list[max2])
print("ベクトルが最も合わないのは", chemistry_list[min2])


結果↓
AとB:0.6708203932499369
AとC:0.22360679774997896
AとD:0.8944271909999159
AとE:0.6708203932499369
AとF:0.5773502691896258
BとC:0.3999999999999999
BとD:0.7999999999999998
BとE:0.3999999999999999
BとF:0.5163977794943222
CとD:0.3999999999999999
CとE:0.5999999999999999
CとF:0.2581988897471611
DとE:0.5999999999999999
DとF:0.5163977794943222
EとF:0.5163977794943222

ベクトルが最も合うのは AとD:0.8944271909999159
ベクトルが最も合わないのは AとC:0.22360679774997896


実際のところ、
A君「C君とはベクトル合わないわー」
      ↓↓↓↓
A君「C君とはコサイン類似度が0.223だから合わないわー」

って言ったらまあ多分嫌われます()

11
9
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
11
9