2章の残り
今回は、2.7「アイテムベースのフィルタリング」で推薦を行うところと、2.8の「MovieLensのデータセットを使う」ところを実装しました。これで、エクササイズが残っていますが、2章「推薦を行う」が完了しました!
2.8は単にデータファイルを取り込むだけなので省略して、2.7でアイテムベースの推薦を行うgetRecommendedItemsを載せておきます。
# アイテムベースのレコメンドを行う sub getRecommendedItems { my ($prefs, $itemMatch, $user) = @_; # あるユーザの評価したアイテムと点数へのリファレンス my $userRatings = $prefs->{$user}; my %scores = (); my %totalSim = (); # $userに評価されたアイテムをループする foreach my $item (keys %{ $userRatings }) { # そのアイテムの点数 my $rating = $userRatings->{$item}; # アイテム間類似度データの中でこのアイテムに似ているアイテムについて処理 # これはcalculateSimilarItemsで作られたデータでハッシュの配列のハッシュ my $cnt = @{$itemMatch->{$item}}; for( my $i=0; $i<$cnt; $i++) { foreach my $item2 (keys %{ $itemMatch->{$item}->[$i] }) { my $similarity = $itemMatch->{$item}->[$i]{$item2}; # このアイテムに対してユーザが既に評価を行っていたら無視する if( exists $userRatings->{$item2} ) { last; } # 点数と類似度を掛けあわせたものの合計で重み付けする $scores{$item2} += ($similarity * $rating); # すべての類似度の合計 $totalSim{$item2} += $similarity; } } } # 類似度の合計で正規化 my %normScores = (); foreach my $item (keys %scores) { $normScores{$item} = $scores{$item} / $totalSim{$item}; } # 降順に並べたランキングを返す my @rankings = (); foreach my $key (sort {$normScores{$b} <=> $normScores{$a}} keys %normScores) { my $h = { $key => $normScores{$key} }; push(@rankings, $h); } return \@rankings; }
これは、アイテム間の類似度を基に推薦を行う方法で、アイテムの類似度ディクショナリを事前に作成しておくことで、ユーザベースの推薦のように毎回類似度を計算する必要がないため、少ない処理時間で推薦を行うことが出来ます(アイテム間の類似度は、ユーザ間の類似度ほど頻繁には変わらないと仮定)。