似た人が好きな本をすすめる
レコメンドとは、その人が好きそうな品物を、記録から選んですすめる仕組みです。協調フィルタリングは、好みの似た人たちの評価を使って、まだ見ていない品物の点を予測する方法です。
大学の図書館アプリは、借りた本に1から5の点をつけてもらいます。まだ読んでいない本のうち、どれを見せるかを考えます。
このレッスンのゴール
メモ
似た人の評価から、まだ読んでいない本の点を予測できる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 協調フィルタリング | 似た人の評価から、好みを予測する方法 | |
| 評価表 | 人ごと、品物ごとの点を並べた表 | |
| 類似度 | 2人の好みがどれだけ近いかを表す数 | 0.33 |
| 予測 | まだ評価していない品物の点を見積もること | |
| コールドスタート | 評価がまだなくて、予測できない状態 |
学生が入力するセルは、このプレビューでは空白です。
評価表から始める
4人の学生が、5冊の本に1から5の点をつけました。ゆいは「暗号」をまだ読んでいません。ゆいが何点をつけそうかを予測します。
考え方は単純です。ゆいと好みが近い人は、ゆいと似た点をつけるはずです。近い人の点を重く数えます。
例: 4人の評価表
| 学生 | 統計入門 | Python入門 | データベース | ネットワーク | 暗号 |
|---|---|---|---|---|---|
| ゆい | 5 | 4 | 4 | 1 | ? |
| けん | 5 | 5 | 4 | 2 | 5 |
| あや | 1 | 2 | 1 | 5 | 2 |
| そう | 4 | 4 | 5 | 1 | 4 |
学生が入力するセルは、このプレビューでは空白です。
近さは点の差で測る
2人が同じ本につけた点の差を、すべて足します。差が小さいほど、好みが近いということです。
類似度は 1 ÷ (1 + 差の合計) にします。差が0なら 1 で、差が大きいほど 0 に近づきます。次のコマンドで、ゆいとけんの類似度を求めます。答えは 0.33 です。
ゆいとけんの類似度
docker exec jupyter python3 -c 'a = [5, 4, 4, 1]; b = [5, 5, 4, 2]; print(round(1 / (1 + sum([abs(x - y) for x, y in zip(a, b)])), 2))'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
例: ゆいとほかの3人の類似度
| 学生 | 差の合計 | 類似度 |
|---|---|---|
| けん | 2 | 0.33 |
| あや | 13 | 0.07 |
| そう | 2 | 0.33 |
学生が入力するセルは、このプレビューでは空白です。
近い人の点を重くして平均する
暗号に点をつけたのは、けん(5点)、あや(2点)、そう(4点)です。ふつうの平均は約 3.67 です。けれど、あやはゆいと好みが違います。
類似度を重みにして平均します。(0.33×5 + 0.07×2 + 0.33×4) ÷ (0.33 + 0.07 + 0.33) で、約 4.26 です。次のコマンドも、同じ計算をします。
重みつき平均で予測する
docker exec jupyter python3 -c 'w = [1/3, 1/14, 1/3]; s = [5, 2, 4]; print(round(sum([a * b for a, b in zip(w, s)]) / sum(w), 2))'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
予測をコードで計算する
# 4人の評価表です。ゆいは「暗号」を読んでいません。
ratings = {
"ゆい": {"統計入門": 5, "Python入門": 4, "データベース": 4, "ネットワーク": 1},
"けん": {"統計入門": 5, "Python入門": 5, "データベース": 4, "ネットワーク": 2, "暗号": 5},
"あや": {"統計入門": 1, "Python入門": 2, "データベース": 1, "ネットワーク": 5, "暗号": 2},
"そう": {"統計入門": 4, "Python入門": 4, "データベース": 5, "ネットワーク": 1, "暗号": 4},
}
def similarity(a, b):
gap = 0
for book in a:
if book in b:
gap = gap + abs(a[book] - b[book])
return 1 / (1 + gap)
top = 0
bottom = 0
for other in ratings:
if other != "ゆい" and "暗号" in ratings[other]:
weight = similarity(ratings["ゆい"], ratings[other])
top = top + weight * ratings[other]["暗号"]
bottom = bottom + weight
print("ゆいの予測:", round(top / bottom, 2))
# あやの暗号の点を 2 から 5 に変えて、もう一度実行してください。
# 予測はどう変わりますか。
品物どうしの近さを使う方法もある
似た人を探す代わりに、似た本を探す方法もあります。同じ人たちに好まれる本どうしは、近い本と考えます。
ゆいが好きな本に近い本を、すすめます。人の数が増えても本のほうが変わりにくいので、大きなサービスでよく使われます。
よくある間違い
注意
平均点がいちばん高い本を、全員にすすめればよいと考えがちです。実際は、人気の本が、その人の好みに合うとは限りません。好みの近い人の点を重く数えます。新しい学生は、評価がなく予測できません。