読者です 読者をやめる 読者になる 読者になる

stMind

You'll never blog alone

perl de 集合知プログラミング(1)

集合知プログラミングを題材に、機械学習perlを学んでいこうと思っています。

ユークリッド距離を基にした類似性判定

今回は、2章の協調フィルタリングの中の2.3.1節をperlで実装してみました。pythonのサンプルコードをそのままperlにしただけです。

#! /usr/bin/perl

use strict;
use warnings;

my %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, 
	'The Night Listener' => 3.0,
	'You, Me and Dupree' => 3.5
    },
    '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, 
	'The Night Listener' => 4.5,
	'Superman Returns' => 4.0, 
	'You, Me and Dupree' => 2.5
    },
    'Mick LaSalle' =>  {
	'Lady in the Water' => 3.0, 
	'Snakes on a Plane' => 4.0,  
	'Just My Luck' => 2.0, 
	'Superman Returns' => 3.0, 
	'The Night Listener' => 3.0, 
	'You, Me and Dupree' => 2.0
    },
    'Jack Matthews' =>  {
	'Lady in the Water' => 3.0, 
	'Snakes on a Plane' => 4.0, 
	'The Night Listener' => 3.0, 
	'Superman Returns' => 5.0, 
	'You, Me and Dupree' => 3.5
    },
    'Toby' =>  {
	'Snakes on a Plane' => 4.5, 
	'Superman Returns' => 4.0, 
	'You, Me and Dupree' => 1.0
    }
    );

# person1とperson2のユークリッド距離を基にした類似性スコアを返す
sub sim_distance {
    my ($prefs, $person1, $person2) = @_;

    # 二人とも評価しているアイテムのリストを得る
    my %si = ();
    foreach my $key (keys %{ $prefs->{$person1} } ) {
	if ($prefs->{$person2}->{$key}) {
	    $si{$key} = 1;
	}
    }

    # 両者ともに評価しているものが一つもなければ0を返す
    my $n = keys %si;
    if ($n == 0) { return 0; }

    # すべての差の平方を足し合わせる
    my $sum_of_squares;
    foreach my $key (keys %{ $prefs->{$person1} } ) {
	if ($prefs->{$person2}->{$key}) {
	    $sum_of_squares += ( ($prefs->{$person1}->{$key} - $prefs->{$person2}->{$key}) ** 2 );
	}
    }

    return (1 / (1 + $sum_of_squares));
}

# person1とperson2のスコアを求める
print "List of Persons:\n";
foreach my $user (keys %critics) {
    print "\t";
    print $user . "\n";
}

print "Enter person1 ==> ";
my $person1 = <STDIN>; 
chomp($person1);

print "Enter person2 ==> ";
my $person2 = <STDIN>;
chomp($person2);

my $sim_score = sim_distance(\%critics, $person1, $person2);

print "Similarity Score == $sim_score" . "\n";

映画を評価したユーザと、各映画への評価スコアのデータセットをハッシュで記述して、あとはユークリッド距離を用いた類似性スコアを返すサブルーチンは本の中と同じです。標準入力から類似性を判定したいユーザを取得する部分は追加してます。

実行すると、

List of Persons:
	Toby
	Michael Phillips
	Gene Seymour
	Lisa Rose
	Claudia Puig
	Mick LaSalle
	Jack Matthews
Enter person1 ==> 

となるのでperson1を入力、続いてperson2の入力も促されるのでそれも同じように入力すると、類似性スコアが出力されます。

Enter person1 ==> Lisa Rose
Enter person2 ==> Gene Seymour
Similarity Score == 0.148148148148148

なんとか動くものがかけてほっとしました。