Rubyで書くアイテムベースの推薦

前回作成したアイテムベースのデータを使って,映画の推薦を行うことができます.

アイテム相関による推薦

アイテム同士の相関値を作成してから,批評値と相関値を掛け合わせます.
あらかじめ作成したアイテム相関のデータを渡せると,計算が早くなるのが嬉しいところ.

module My
  class Recommender
    #
    # アイテム相関と映画評価の積で一番の映画を推薦する
    #
    def get_item_similarity_recommendations(critics, me, options = {})
      # 各アイテムの相関のあるアイテムを計算する
      # 計算済みのデータが指定されている場合はそのまま使う
      item_similarities = options[:tem_similarity] || get_item_similarity(critics)

      # 相関のあるアイテムごとにmeの評価値と相関の積を出す
      ratings = Hash.new(0)
      sum_similarities = Hash.new(0)
      critics[me].each do |movie, critic|
        item_similarities[movie].each do |similarity, similar_movie|
          ratings[similar_movie] += (critic * similarity)
          sum_similarities[similar_movie] += similarity
        end
      end

      # 自分が批評済のものは無駄なので削除
      ratings.delete_if { |movie, similarity|
        critics[me].keys.include?(movie)
      }.map { |movie, rating|
        # 正規化(類似度の合計で割る)
        [movie, rating.quo(sum_similarities[movie])]
      }.sort_by { |movie, normalized_rating|
        normalized_rating
      }.reverse
    end
  end
end

実行結果

まずは,値を計算してみましょう.

recommender = My:Recommender.new
puts recommender.get_item_similarity_recommendations(critics, 'Toby')

いざ,実行!

% ./recommender.rb
The Night Listener
3.18263473053892
Just My Luck
2.59833187006146
Lady in the Water
2.47308781869688

p.27と同じ結果が得られました.


さて...
ここでは更にBenchmarkモジュールを使って,今回得られたアイテム相関値を与えない場合と,与える場合を比較してみましょう.
1000回繰り返して,どのくらい差が出るかみてみます.

require 'benchmark'
items = recommender.get_item_similarity(critics)
Benchmark.bm(15) do |x|
  x.report('items_disabled') { 
    1000.times { recommender.get_item_similarity_recommendations(critics, 'Toby') }
  }
  x.report('items_enabled') { 
    1000.times { recommender.get_item_similarity_recommendations(critics, 'Toby', { :item_similarity => items }) }
  }
end

いざ,実行!

% system_profiler SPHardwareDataType                                                                        
Hardware:

    Hardware Overview:

      Model Name: MacBook Air
      Model Identifier: MacBookAir1,1
      Processor Name: Intel Core 2 Duo
      Processor Speed: 1.6 GHz
      Number Of Processors: 1
      Total Number Of Cores: 2
      L2 Cache: 4 MB
      Memory: 2 GB
      Bus Speed: 800 MHz
      Boot ROM Version: MBA11.00BB.B03
      SMC Version: 1.23f9
      Serial Number: ***********
      Sudden Motion Sensor:
          State: Enabled

% ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
% ./recommender.rb
                     user     system      total        real
items_disabled   4.320000   0.020000   4.340000 (  4.371397)
items_enabled    0.140000   0.000000   0.140000 (  0.141398)

今回はデータの総量が少ないので,そもそも大して時間は掛かりませんでした.
が,それでも30倍くらいの差が出ました.