コラム 人と星とともにある数学 数学

NumPyでベクトル <後編>|Pythonで数学を学ぼう! 第21回

はじめに|レポート

私が毎月開催している桜井進のPython・UNIX教室【数学プログラミングコース】3月と桜井進の数学浪漫紀行5月のテーマが「コサイン類似度」でした。

前回はPythonのNumPyライブラリによるベクトルを扱う方法を紹介しました。今回はベクトルの応用としてコサイン類似度を紹介します。

2つのベクトルのなす角

2つのベクトルに対して、その角を考えることができます。2つのベクトルのなす角をθとすると、その取り得る範囲は0°≦θ≦180°です。

余弦cosθを2つのベクトルの成分を用いて次の図のように求めることができます。よってθはアークコサイン(逆余弦関数)を用いて得られます。

この計算をPythonでも行ってみます。

>>>  import numpy as np
>>>  x = np.array([2, -1, 3, -2])
>>>  y = np.array([-3, 2, 1, 5])
>>>  theta = np.dot(x, y)/(np.linalg.norm(x) * np.linalg.norm(y))
>>>  print(np.degrees(np.arccos(theta)))
>>>  124.48138937198158

たしかに同じ結果です。この計算に登場するnp.dot(x, y)がベクトルの内積です。

ベクトルの内積とコサイン類似度

ベクトルxとyの内積をx・yのように表します。次の図のように内積x・yは2通りで表すことができます。

その1はベクトルのノルム(大きさ)となす角を用いた式、その2はベクトルの成分による式です。

これら2つの内積の式およびベクトルのノルムの式よりcosθが成分だけで表されます。

このcosθがコサイン類似度の正体です。

データをベクトル化することで、2つのデータの比較をベクトルのなす角によって行えるようになります。

θが0°(cos0°=1)の場合すなわち2つのベクトルが平行の場合は、2つのデータは“完全に似ている”とし、θが180°(cos180°=-1)の場合すなわち2つのベクトルが真反対の場合は、2つのデータは“全く似ていない”とする解釈です。

実際にはθではなくcosθで比べます。

cos0°が最大値1である場合が、データは“完全に似ている”、cos0°が最小値-1である場合が、データは“全く似ていない”と対応します。

つまり類似度はcosθの大小に一致します。これをコサイン類似度(Cosine Similarity)と呼び、cos(x,y)と表したりします。

コサイン類似度の実例

6人それぞれに10の音楽ジャンルごとに好き嫌いのアンケートをとり、そのデータをもとに音楽の趣味が合う2人と逆に趣味が合わない2人を探し出してみます。

好きは○、嫌いは●、どちらでもないは空欄とします。6人の結果を数値化します。

○は1、●は-1、そして空間は0として、まず一人一人リスト化します。コサイン類似度を計算するために、このリストをベクトル化(1次元配列)します。例えば、A(1佐藤さん)の場合には、A = [1,1,-1,0,1,-1,0,-1,1,-1]とします。

プログラム「cossim.py」コサイン類似度の計算プログラム

>>>  import numpy as np
>>>
>>>  # 好き嫌いデータ
>>>  A = [1,1,-1,0,1,-1,0,-1,1,-1]
>>>  B = [-1,1,-1,-1,1,1,1,-1,1,1]
>>>  C = [1,0,0,0,0,0,-1,1,0,0]
>>>  D = [0,1,1,1,0,-1,0,1,0,1]
>>>  E = [-1,-1,-1,-1,-1,-1,1,-1,-1,-1]
>>>  F = [1,1,1,0,1,1,1,1,-1,1]
>>>  people = [A, B, C, D, E, F]
>>>  name = ["1佐藤さん", "2鈴木さん", "3武田さん", "4加藤さん", "5田中さん", "6岡田さん"]
>>>
>>>  # コサイン類似度の関数定義
>>>  def cossim(list1, list2):
>>>   vec1 = np.array(list1)
>>>   vec2 = np.array(list2)
>>>   if (np.linalg.norm(vec1) != 0 and np.linalg.norm(vec2) != 0):
>>>    cos_theta = np.dot(vec1, vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2))
>>>   else:
>>>    cos_theta = 0
>>>   return cos_theta
>>>
>>>  people_ijcos_list = [] # people[i]とpeople[j]:コサイン類似度 cos_theta のリスト
>>>  cos_theta_list = [] # cos_theta のリスト
>>>  n = len(people) # 人数
>>>
>>>  for i in range(n-1):
>>>   for j in range(i+1, n):
>>>     cos_theta = cossim(people[i], people[j]) # コサイン類似度cos_theta
>>>     pair_ijcos = name[i]+"と"+name[j]+":コサイン類似度 "+str(round(cos_theta, 4))
>>>     people_ijcos_list.append(pair_ijcos)
>>>     cos_theta_list.append(cos_theta)
>>>     print(pair_ijcos)
>>>
>>>  maxpair = cos_theta_list.index(max(cos_theta_list)) # コサイン類似度最大値のペアのインデックス
>>>  minpair = cos_theta_list.index(min(cos_theta_list)) # コサイン類似度最小値のペアのインデックス
>>>  print("\n音楽の趣味が最も類似する二人 ", people_ijcos_list[maxpair])
>>>  print("音楽の趣味が最も類似しない二人", people_ijcos_list[minpair])

このプログラムファイルは次からダウンロードできます。
https://drive.google.com/file/d/1KIs09O3TfoUcmNioFsZ_F19ZbXjKj1CH/view?usp=sharing

プログラム実行結果

>>>  1佐藤さんと2鈴木さん:コサイン類似度 0.2236
>>>  1佐藤さんと3武田さん:コサイン類似度 0.0
>>>  1佐藤さんと4加藤さん:コサイン類似度 -0.1443
>>>  1佐藤さんと5田中さん:コサイン類似度 0.0
>>>  1佐藤さんと6岡田さん:コサイン類似度 -0.2357
>>>  2鈴木さんと3武田さん:コサイン類似度 -0.5477
>>>  2鈴木さんと4加藤さん:コサイン類似度 -0.2582
>>>  2鈴木さんと5田中さん:コサイン類似度 0.0
>>>  2鈴木さんと6岡田さん:コサイン類似度 0.1054
>>>  3武田さんと4加藤さん:コサイン類似度 0.2357
>>>  3武田さんと5田中さん:コサイン類似度 -0.5477
>>>  3武田さんと6岡田さん:コサイン類似度 0.1925
>>>  4加藤さんと5田中さん:コサイン類似度 -0.5164
>>>  4加藤さんと6岡田さん:コサイン類似度 0.4082
>>>  5田中さんと6岡田さん:コサイン類似度 -0.527
>>>
>>>  音楽の趣味が最も類似する二人  4加藤さんと6岡田さん:コサイン類似度 0.4082
>>>  音楽の趣味が最も類似しない二人 2鈴木さんと3武田さん:コサイン類似度 -0.5477

6人から2人選ぶすべての組合せにおいて、コサイン類似度を計算します。そして、最大値と最小値を検索します。

結果は、4加藤さんと6岡田さんの相性が最も良く、2鈴木さんと3武田さんの相性が最も良くないとなりました。

それぞれの組合せを比べてみると、それなりに納得できる結果のようにも思えます。冒頭の「# 好き嫌いデータ部分」をいじってみてコサイン類似度がどのように変化するのかをためしてみましょう。

  • この記事を書いた人
  • 最新記事

桜井進(さくらいすすむ)様

1968年山形県生まれ。 サイエンスナビゲーター®。株式会社sakurAi Science Factory 代表取締役CEO。 (略歴) 東京工業大学理学部数学科卒、同大学大学院院社会理工学研究科博士課程中退。 東京理科大学大学院非常勤講師。 理数教育研究所Rimse「算数・数学の自由研究」中央審査委員。 高校数学教科書「数学活用」(啓林館)著者。 公益財団法人 中央教育研究所 理事。 国土地理院研究評価委員会委員。 2000年にサイエンスナビゲーターを名乗り、数学の驚きと感動を伝える講演活動をスタート。東京工業大学世界文明センターフェローを経て現在に至る。 子どもから大人までを対象とした講演会は年間70回以上。 全国で反響を呼び、テレビ・新聞・雑誌など様々なメディアに出演。 著書に『感動する!数学』『わくわく数の世界の大冒険』『面白くて眠れなくなる数学』など50冊以上。 サイエンスナビゲーターは株式会社sakurAi Science Factoryの登録商標です。

あわせて読みたい

-コラム, 人と星とともにある数学, 数学
-,