Scalaで集合知プログラミング その2
集合知プログラミングの第2章に入っていきますよー。第2章はAmazonのような嗜好データを基にした推薦をテーマに集合知を取り扱っていきますねー
第2章からは具体的なコードが出てきますので、このPythonコードをなんとかScalaのコードに置き換えていきたいと思いますよ(`・ω・´)
協調フィルタリング
大規模な集団の中から好みの似た人々を見つけることで、好みの近い人同士の嗜好パターンから推薦リストを作成するのが協調フィルタリングという技術ですね。
協調フィルタリングのイメージとしては次のような感じになりますデス(`・ω・´)
- とある集団内でAさんとBさんは同じような商品を購入する傾向がある
- AさんとBさんは同じような嗜好があると推測できる
- Aさんが新しく購入した商品はBさんも興味があるに違いないので推薦するぜ !
上記方法はいかに嗜好の似たユーザ同士を発見できるか?ということが肝になるわけで、そのためには母集団となるユーザ数は多ければ多いほど良い、と言われておりますね(´・ω・`)
なお、協調フィルタリングを行うためには主に次の2つの要素が肝になるみたいです(他にもあるのですが…目立つところということで)
- ユーザの行動からの嗜好の分析
- 類似しているユーザの選択方法
嗜好の収集
では、実際に嗜好の収集方法を試してみましょう…ということで分析対象となるのが次のようなPythonで書かれたデータセットですネ。
データセットには評論家、観た映画、映画の評点(1-5)が記録されていますデス(´・ω・`)
- Pythonコード
# 映画の評者別の映画への評点のディクショナリを # recommendation.pyとして保存しますです critics = {'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You Me and Dupree': 2.5, 'The Night Listener': 3.0}, 'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5, 'Just My Luck': 1.5, 'Superman Returns': 5.0, 'You Me and Dupree': 3.5, 'The Night Listener': 3.0}, 'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0, 'Superman Returns': 3.5, 'The Night Listener': 4.0}, 'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'Superman Returns': 4.0, 'You Me and Dupree': 2.5, 'The Night Listener': 4.5}, 'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'You Me and Dupree': 2.0, 'The Night Listener': 3.0}, 'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Superman Returns': 5.0, 'You Me and Dupree': 3.5, 'The Night Listener': 3.0}, 'Toby': {'Snakes on a Plane': 4.5, 'Superman Returns': 4.0, 'You Me and Dupree': 1.0}}
今回はScalaで本書のコードを書きなおす修行も行うので、上記PythonコードをScalaで書きなおしてみますデスよ。Pythonの辞書型だからScalaだったらMapにすればいいかしら?ついでに静的なデータだけだからobjectの要素として定義してしまいますかネ(´・ω・`)
- Scalaコード
package org.plasticscafe.collective.recommend // Recommendation.scalaとして保存しますデス object Recommendation { val critics = Map("Lisa Rose" -> Map("Lady in the Water" -> 2.5, "Snakes on a Plane" -> 3.5, "Just My Luck" -> 3.0, "Superman Returns" -> 3.5, "You Me and Dupree" -> 2.5, "The Night Listener" -> 3.0), "Gene Seymour" -> Map("Lady in the Water" -> 3.0, "Snakes on a Plane" -> 3.5, "Just My Luck" -> 1.5, "Superman Returns" -> 5.0, "You Me and Dupree" -> 3.5, "The Night Listener" -> 3.0), "Michael Phillips" -> Map("Lady in the Water" -> 2.5, "Snakes on a Plane" -> 3.0, "Superman Returns" -> 3.5, "The Night Listener" -> 4.0), "Claudia Puig" -> Map("Snakes on a Plane" -> 3.5, "Just My Luck" -> 3.0, "Superman Returns" -> 4.0, "You Me and Dupree" -> 2.5, "The Night Listener" -> 4.5), "Mick LaSalle" -> Map("Lady in the Water" -> 3.0, "Snakes on a Plane" -> 4.0, "Just My Luck" -> 2.0, "Superman Returns" -> 3.0, "You Me and Dupree" -> 2.0, "The Night Listener" -> 3.0), "Jack Matthews" -> Map("Lady in the Water" -> 3.0, "Snakes on a Plane" -> 4.0, "Superman Returns" -> 5.0, "You Me and Dupree" -> 3.5, "The Night Listener" -> 3.0), "Toby" -> Map("Snakes on a Plane" -> 4.5, "Superman Returns" -> 4.0, "You Me and Dupree" -> 1.0)) }
うん、とりあえずこんな感じで定義するのがヨサゲですな(`・ω・´)
今回は映画に対する評価(嗜好)を1-5の数字で表現していますが、必ずしも各ユーザ(評価者)が明示的に数字で評価を付ける必要はなく、例えばショッピングサイト等では次のようにユーザの行動から暗黙的に点数化することも可能になるのですね
- 購入:2
- 閲覧:1
- アクション無し:0 の
…と世の中のシステムの多くはこちらを使っているような気も(´・ω・`)
対話コンソールからの呼び出し
基本的に各コードは対話コンソールを使って試行錯誤するのがよさそうなので、上記の各コードを対話コンソールから読み込めるようにしておきますデス
Pythonでは保存したrecommendation.pyを動的に読み込むことができるので、次のようにimportして使ってやることにしますデス
# importでモジュールとして読み込みます >>> from recommendation import critics # 要素にアクセスします >>> critics['Toby'] {'Snakes on a Plane': 4.5, 'You Me and Dupree': 2.0, 'Superman Returns': 5.0} >>> critics['Toby']['Snakes on a Plane'] 4.5 # 要素数でもとってみます >>> len(critics) 7
それではScalaでも同じようにやってみますデスヨ。まずはRecommendation.scalaをコンパイルしてやります
% scalac Recommendation.scala
コンパイルしたものをモジュールとしてimportしてやりますよ
// インポートしますデス scala> import org.plasticscafe.collective.recommend.Recommendation.critics import org.plasticscafe.collective.recommend.Recommendation.critics // 要素にアクセスしてやります scala> critics("Toby") res10: scala.collection.immutable.Map[java.lang.String,Double] = Map((Snakes on a Plane,4.5), (Superman Returns,5.0), (You Me and Dupree,2.0)) scala> critics("Toby")("Snakes on a Plane") res11: Double = 4.5 // 要素数を取得しますデス scala> critics.size res12: Int = 7