#ベクトルが合わない?
趣味、思考、方向性が違うってときに、君とはベクトルが合わないわーとかで一言で片付けられません?
そう言われるとなんかちょっとイラってします・・
そんなベクトルが合うとか合わないとかいう曖昧な表現を、数値で正確に表す方法として”コサイン類似度”というものがあるので紹介します!
(( かんたんに言えば数Ⅱで習う内積の公式を使った類似度計算です ))
#コサイン類似度とは
コサイン類似度の話をする前にまず内積の復習をしますね
##内積
α = (a,b)
β = (c,d)
α,βをベクトルとすると、これらの内積は
α・β = ac + bd
このα,βの内積は、この2つのベクトルの間の角度をθとすると
α・β = |α||β|cosθ
っていう超有名な関係式が成り立ちます
(ちなみに、|α| = √(a^2 + b^2), |β| = √(c^2 + d^2) ですね!)
##コサイン類似度
この関係式をcosθ基準に考えると
cosθ = (α・β)/|α||β|
θは2つのベクトルの間の角度だから、2つのベクトルが近いとθ→0に近づき、cosθ→1に近づく。
α、βの要素がすべて正なら 0 ≦ cosθ ≦ 1
つまりα君とβ君のベクトルが合う(近い)ときcosθ =1、合わない(遠い)ときcosθ = 0
#Pythonで書いてみる
####環境
ubuntu 18.04
python3
分かりやすいようにコサイン類似度を返すモジュール作ってimportして使います!
まずモジュール制作
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合わないを求めます。
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だから合わないわー」
って言ったらまあ多分嫌われます()