Posted on Aug. 1, 2018, 7:50 a.m.
recommender systemについて記載します。今回は、映画に対するユーザ評価のrecommender systemです。
CourseraのMachine Learningコースを元にしています。
商品の特徴(X)とユーザの嗜好(θ)からユーザの商品に対する評価を求めます。また、recommender systemでは、θの初期値を決めることで、θ⇒X⇒θ⇒X⇒θ...というように、θとXでお互いに最適解を求めることができます。
まず、データを読み込みます。Yは、映画数(m)とユーザ数(j)のmatrixです。Rは、matrix Yにratingがされている場合にr=1(ratingされていない場合はr=0)とするbinary matrixです。
load ('ex8_movies.mat');
続いて、movieListを作成します。作成元となるmovie_ids.txtファイルは下記の様な形です。
1 Toy Story (1995)
2 GoldenEye (1995)
3 Four Rooms (1995)
function movieList = loadMovieList()
fid = fopen('movie_ids.txt');
n = 1682; % Total number of movies
movieList = cell(n, 1);
for i = 1:n
% Read line
line = fgets(fid);
% Word Index (can ignore since it will be = i)
[idx, movieName] = strtok(line, ' ');
% Actual Word
movieList{i} = strtrim(movieName);
endfor
fclose(fid);
end
movieList = loadMovieList();
ex8_movies.matをロードした際に既に、rating表(縦:映画作品、横:ユーザ)が読み込まれますが、ユーザを新規に追加し、幾つかの映画に対してratingを行います。ここでは、1682作品中11作品に対してratingを行います。
my_ratings = zeros(1682, 1);
my_ratings(1) = 4;
my_ratings(98) = 2;
my_ratings(7) = 3;
my_ratings(12)= 5;
my_ratings(54) = 4;
my_ratings(64)= 5;
my_ratings(66)= 3;
my_ratings(69) = 5;
my_ratings(183) = 4;
my_ratings(226) = 5;
my_ratings(355)= 5;
fprintf('\n\nNew user ratings:\n');
for i = 1:length(my_ratings)
if my_ratings(i) > 0
fprintf('Rated %d for %s\n', my_ratings(i), ...
movieList{i});
endif
endfor
新規ユーザのratingデータを既存のrating表に追加します。
Y = [my_ratings Y];
R = [(my_ratings ~= 0) R];
num_users = size(Y, 2);
num_movies = size(Y, 1);
num_features = 10;
rating表を0を中心にnormalizationします。
function [Ynorm, Ymean] = normalizeRatings(Y, R)
[m, n] = size(Y);
Ymean = zeros(m, 1);
Ynorm = zeros(size(Y));
for i = 1:m
idx = find(R(i, :) == 1);
Ymean(i) = mean(Y(i, idx));
Ynorm(i, idx) = Y(i, idx) - Ymean(i);
endfor
end
[Ynorm, Ymean] = normalizeRatings(Y, R);
続いて、XとThetaの初期値を設定します。ランダムな数値を割り当てます。Xは映画数(num_movies)と特徴数(num_features)のmatrixです。Thetaは、ユーザ数(num_users)と特徴数(num_features)のmatrixです。
後述のfmincg実行時に引数を1つしか指定できないので、XとThetaを一つのベクトルに纏めます。
X = randn(num_movies, num_features);
Theta = randn(num_users, num_features);
initial_parameters = [X(:); Theta(:)];
gradientを実行する際に使用する目的関数を定義します。この関数を実行することで、XとThetaのGradientを一つのベクトルに纏めたgradを求めます。
function [J, grad] = cofiCostFunc(params, Y, R, num_users, num_movies, num_features, lambda)
X = reshape(params(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(params(num_movies*num_features+1:end), num_users, num_features);
J = 0;
X_grad = zeros(size(X));
Theta_grad = zeros(size(Theta));
Rdiff= (X*Theta' - Y).*R;
Jreg = lambda/2 * sum(sum(Theta.^2)) + lambda/2 * sum(sum(X.^2));
J = 1/2 * sum(sum(Rdiff.^2)) + Jreg;
Xreg = lambda * X;
X_grad = Rdiff*Theta + Xreg;
Thetareg = lambda * Theta;
Theta_grad = Rdiff'*X + Thetareg;
grad = [X_grad(:); Theta_grad(:)];
end
fmincgでGradientを実行します。iteration回数は100回、lambdaの値は10としています。
options = optimset('GradObj', 'on', 'MaxIter', 100);
lambda = 10;
theta = fmincg (@(t)(cofiCostFunc(t, Ynorm, R, num_users, num_movies, num_features, lambda)), initial_parameters, options);
上記で求めたthetaを、XとThetaに分解します。
X = reshape(theta(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(theta(num_movies*num_features+1:end), num_users, num_features);
以上でXとThetaの最適解まで求まりました。
上で求めたXとThetaを元に、ratingを推測します。
なお、Yの値はnormalizationしていたので、pの値には、Ymeanを加算します。
p = X * Theta';
my_predictions = p(:,1) + Ymean;
my_predictionsの値を評価の高い順でSortし、上位10作品をrecommendします。
[r, ix] = sort(my_predictions, 'descend');
fprintf('\nTop recommendations for you:\n');
for i=1:10
j = ix(i);
fprintf('Predicting rating %.1f for movie %s\n', my_predictions(j), ...
movieList{j});
end
以上です。