stMind

about Arsenal, Arsene Wenger, Tech, Computer vision and Machine learning

perl de 集合知プログラミング(7) 2章完

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;
}

これは、アイテム間の類似度を基に推薦を行う方法で、アイテムの類似度ディクショナリを事前に作成しておくことで、ユーザベースの推薦のように毎回類似度を計算する必要がないため、少ない処理時間で推薦を行うことが出来ます(アイテム間の類似度は、ユーザ間の類似度ほど頻繁には変わらないと仮定)。

その他

いつものようにgistで公開しております。
gist: 489178 - GitHub

ようやくperlに慣れて、こうしたい!と思ったものが実装できるようになってきました。