recommendWithCosSimilar
(スタンドアローンレコメンドエンジン)

アイテムベースの協調フィルタリングエンジンです。
アイテム間のコサイン類似度を計算して推薦します。








 本ソフトは、スタンドアローンで動くレコメンドエンジンです。
 商品の購買データやコンテンツのブックマークデータから、よく一緒に買われたり、よく一緒にブックマークされたりするアイテム(商品、コンテンツなど)を自動で分析し、どのユーザーにどのアイテムがお薦めか、などの情報を出力します。

 本ソフトは本体(.exe)をダブルクリックすれば実行できます。インストールは不要です。
 よって本ソフトが不要になった場合のアンインストールも不要です。
 ファイル削除のみで問題ありません。

 本ソフトはフリーソフトです。誰でも無料で利用できます。またソースコードについても、オープンソースとします。自由に改変、カスタマイズして頂いて構いません。


▼ダウンロード▼


ダウンロードはこちらから!




★CSVファイルを準備★

csvファイルは、

鈴木 りんご
佐藤 りんご
田中 メロン
鈴木 みかん


のように、一列目にユーザー名(ID可)を、二列目に商品名(ID可)をリストしてください。

「鈴木」さんが「りんご」を買った、
「田中」さんが「メロン」をお気に入り登録した、

など、あるユーザーのある商品に対する肯定的なアクションをリストしてください。

準備したcsvファイルは、.exeファイルと同じディレクトリに置いてください。


★出力されるJSONのみかた★

出力されるファイルは、
recommendForUsers.js と similarItems.js の二つです。 


recommendForUsers.jsは以下のようなJSONが記述されています。

{
   "田中" : [
      "スイカ"
   ],
   "鈴木" : [
      "スイカ"
   ],
   "佐藤" : [
      "マンゴー"
   ]
}

この場合、
「田中」さんに「スイカ」がオススメ、
「鈴木」さんに「スイカ」がオススメ、
「佐藤」さんに「マンゴー」がオススメ、
となります。

similarItems.jsは以下のようなJSONが記述されています。


"パイナップル" : [
      "りんご",
      "マンゴー",
      "キウィ"
   ]

この場合、
「パイナップル」に類似している商品が、
「りんご」「マンゴー」「キウィ」
の三つということになります。


▼コサイン類似度とは?▼

コサイン類似度について簡単に説明します。

ユーザーⅠ~Ⅳが商品(A)(B)を、

 購入した     ⇒ 1
 購入しなかった ⇒ 0

として購買データを作り、下記の行列を得たとします。









このとき商品(A)と商品(B)のコサイン類似度は下記の数式で求まります。









購入したか否かを、1 または 0 の整数で表現する場合、

分子      = 両方を購入したユーザーの数

分母の二乗 = 商品(A)を購入したユーザー数 × 商品(B)を購入したユーザー数

ですから、

1.商品(A)を購入したユーザー数
2.商品(B)を購入したユーザー数
3.両方を購入したユーザー数

の3つがわかれば計算できます。


▼Perlスクリプト▼
#!/usr/bin/perl

use 5.010;
use strict;
use JSON;

print "データファイル(.csv)を指定してください。\n";
chomp(my $dataFile = <STDIN>);
print "データを読み込んでいます。\n";

my $start        = time;
my $dataSize     = 0;
my %favorite = ();
my %favoriteUser = ();
my %favoriteItem = ();
open(IN, $dataFile);
while(<IN>){

    chomp;

    my @d = split(/,/, $_);

if( defined($favorite{$d[0]}{$d[1]}) ){

#skip

}else{

push(@{$favoriteUser{$d[1]}},$d[0]);

push(@{$favoriteItem{$d[0]}},$d[1]);

$favorite{$d[0]}{$d[1]} = 1;

}

}
close(IN);
undef(%favorite);

print "データの読み込みが完了しました。\n";
my $gd = time - $start;
print "ここまでに$gd秒かかりました。\n";
print "コサイン類似度を計算しています。\n";


my %similar  = ();
my @user = keys(%favoriteItem);
my @item     = keys(%favoriteUser);
my $userSize = @user;

for(my $i = 0; $i<=$#item; $i++){

for(my $j = $i + 1;  $j<=$#item; $j++){

my @u = ();

push(@u,@{$favoriteUser{$item[$i]}});
push(@u,@{$favoriteUser{$item[$j]}});

my $inner = 0;

my %count = ();

for (my $k=0; $k<=$#u; $k++ ) {

$count{$u[$k]}++;

if($count{$u[$k]}==2){

$inner++;

}

}

if( $inner > 0 ){

my $k = $inner;
my $x = @{$favoriteUser{$item[$i]}};
my $y = @{$favoriteUser{$item[$j]}};

my $cos      = $inner/($x*$y)**(1/2);

$similar{$item[$i]}{$item[$j]} = $cos;
$similar{$item[$j]}{$item[$i]} = $cos;

}
}
}

print "コサイン類似度の計算が完了しました。\n";
my $cc = time - $start;
print "ここまでに$cc秒かかりました。\n";

print "各ユーザーにアイテムをいくつ推薦しますか?(半角数字)\n";
chomp(my $threshold = int(<STDIN>));
print "了解です。推薦処理を行っています。\n";

my %recommendForUsers = ();

for( my $i = 0; $i < $userSize; $i++){

my @fs = @{$favoriteItem{$user[$i]}};

my %r = ();

my %skip = ();

for (my $k=0; $k<=$#fs; $k++ ) {

$skip{$fs[$k]} = 1;

}


for( my $ j = 0; $j <= $#fs; $j++ ){

my $s = $fs[$j];

for( my $k = 0; $k <=$#item; $k++){

if( $skip{$item[$k]} ){

#skip;

}else{

$r{$item[$k]} = $r{$item[$k]} + $similar{$s}{$item[$k]};

}

}

}

my $recommendNum      = 0;
foreach my $key (sort { $r{$b} <=> $r{$a} } keys %r){

push(@{$recommendForUsers{$user[$i]}},$key);

$recommendNum++;

if($recommendNum >= $threshold){

last;

}

}

}

print "推薦処理が完了しました。\n";
print "各アイテムに類似アイテムをいくつ推薦しますか?(半角数字)\n";
chomp(my $threshold = int(<STDIN>));
print "了解です。推薦処理を行っています。\n";

my %similarItems  = ();

for( my $i = 0; $i <= $#item; $i++){

my %r = %{$similar{$item[$i]}};
my $recommendNum  = 0;

foreach my $key (sort { $r{$b} <=> $r{$a} } keys %r){

push(@{$similarItems{$item[$i]}},$key);

$recommendNum++;

if($recommendNum >= $threshold){

last;

}

}

}

print "推薦処理が完了しました。\n";
print "JSファイルを出力しています。\n";

#出力

unlink './recommendForUsers.js';
open(DATAFILE, ">> ./recommendForUsers.js");

print DATAFILE "var recommendForUsers = \n";
print DATAFILE JSON::XS->new->pretty(1)->encode(\%recommendForUsers);
print DATAFILE ";\n";

close DATAFILE;

unlink './similarItems.js';
open(DATAFILE, ">> ./similarItems.js");

print DATAFILE "var similarItems = \n";
print DATAFILE JSON::XS->new->pretty(1)->encode(\%similarItems);
print DATAFILE ";\n";

close DATAFILE;

exit;



0 件のコメント:

コメントを投稿