アイテム間のコサイン類似度を計算して推薦します。
本ソフトは、スタンドアローンで動くレコメンドエンジンです。
商品の購買データやコンテンツのブックマークデータから、よく一緒に買われたり、よく一緒にブックマークされたりするアイテム(商品、コンテンツなど)を自動で分析し、どのユーザーにどのアイテムがお薦めか、などの情報を出力します。
本ソフトは本体(.exe)をダブルクリックすれば実行できます。インストールは不要です。
よって本ソフトが不要になった場合のアンインストールも不要です。
ファイル削除のみで問題ありません。
本ソフトはフリーソフトです。誰でも無料で利用できます。またソースコードについても、オープンソースとします。自由に改変、カスタマイズして頂いて構いません。
ダウンロードはこちらから!
のように、一列目にユーザー名(ID可)を、二列目に商品名(ID可)をリストしてください。
「鈴木」さんが「りんご」を買った、
「田中」さんが「メロン」をお気に入り登録した、
など、あるユーザーのある商品に対する肯定的なアクションをリストしてください。
準備したcsvファイルは、.exeファイルと同じディレクトリに置いてください。
recommendForUsers.js と similarItems.js の二つです。
recommendForUsers.jsは以下のようなJSONが記述されています。
{
"田中" : [
"スイカ"
],
"鈴木" : [
"スイカ"
],
"佐藤" : [
"マンゴー"
]
}
この場合、
このとき商品(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;
本ソフトはフリーソフトです。誰でも無料で利用できます。またソースコードについても、オープンソースとします。自由に改変、カスタマイズして頂いて構いません。
▼ダウンロード▼
ダウンロードはこちらから!
★CSVファイルを準備★
csvファイルは、鈴木 | りんご |
佐藤 | りんご |
田中 | メロン |
鈴木 | みかん |
「鈴木」さんが「りんご」を買った、
「田中」さんが「メロン」をお気に入り登録した、
など、あるユーザーのある商品に対する肯定的なアクションをリストしてください。
準備したcsvファイルは、.exeファイルと同じディレクトリに置いてください。
★出力されるJSONのみかた★
出力されるファイルは、recommendForUsers.js と similarItems.js の二つです。
recommendForUsers.jsは以下のようなJSONが記述されています。
{
"田中" : [
"スイカ"
],
"鈴木" : [
"スイカ"
],
"佐藤" : [
"マンゴー"
]
}
この場合、
「田中」さんに「スイカ」がオススメ、
「鈴木」さんに「スイカ」がオススメ、
「佐藤」さんに「マンゴー」がオススメ、
となります。
similarItems.jsは以下のようなJSONが記述されています。
となります。
similarItems.jsは以下のようなJSONが記述されています。
"パイナップル" : [
"りんご",
"マンゴー",
"キウィ"
]
この場合、
「パイナップル」に類似している商品が、
「りんご」「マンゴー」「キウィ」
の三つということになります。
▼コサイン類似度とは?▼
ユーザーⅠ~Ⅳが商品(A)(B)を、
"りんご",
"マンゴー",
"キウィ"
]
この場合、
「パイナップル」に類似している商品が、
「りんご」「マンゴー」「キウィ」
の三つということになります。
▼コサイン類似度とは?▼
コサイン類似度について簡単に説明します。
購入した ⇒ 1
購入しなかった ⇒ 0
として購買データを作り、下記の行列を得たとします。
このとき商品(A)と商品(B)のコサイン類似度は下記の数式で求まります。

購入したか否かを、1 または 0 の整数で表現する場合、
分子 = 両方を購入したユーザーの数
分母の二乗 = 商品(A)を購入したユーザー数 × 商品(B)を購入したユーザー数
ですから、
1.商品(A)を購入したユーザー数
2.商品(B)を購入したユーザー数
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 件のコメント:
コメントを投稿