Deep Karmaning

技術系の話から日常のことまで色々と書きます

Bayesian Probabilistic Matrix FactorizationをJuliaで実装

概要

リブセンスさんの以下記事で実装されている、Bayesian Probabilistic Matrix Factorizationに興味が湧いたので実装してみました。

analytics.livesense.co.jp

とはいえ全く同じことをやっても面白くないので、プラスαとしてJuliaで実装しPythonと速度を比較してみます。

Bayesian Probabilistic Matrix Factorizationに関しては、ここでは詳しく数式は見ませんが、Probabilistic Matrix Factorizationという手法を拡張したものになります*1

論文は以下になります。

Bayesian Probabilistic Matrix Factorization using Markov Chain Monte Carlo

検証

コードは以下です*2

Bayesian_Probabilistic_Matrix_Factorization.ipynb · GitHub

データはおなじみのMovie Lensを使います。

grouplens.org

細かいところは実際のコードを見てもらうとして*3、Juliaのコードは以下のように実行しました。

また今回は簡易のため、学習データと精度検証用データを分ける等の処理はしていないです*4

#時間計測
function main()
    #hyper hyper parameter
    beta_0 = 2
    mu_0 = 0
    D = 10 #分解後の潜在変数の次元
    nu_0 = D 
    tmp_matrix = rand(D, D)
    W_0 = Float64.(Int.(inv(tmp_matrix) * tmp_matrix .>= 0.9)) #逆行列かけて型変換をして単位行列に変換
    alpha = 2
    T = 50 #サンプリング回数

    N = user #ユーザー数
    M = item #アイテム数

    R = df#今回は簡易的な実験のため学習と精度検証用のデータはわけない

    #学習
    BPMF = BayesianProbabilisticMatrixFactorization.BayesianProbabilisticMatrixFactorizationModel(D, alpha, beta_0, mu_0, nu_0,
        W_0, N, M, T, R, [], [])
    BayesianProbabilisticMatrixFactorization.fit(BPMF)

    #予測
    R_P = BayesianProbabilisticMatrixFactorization.predict_all(BPMF, 10)
    
    return R_P
end

@time R_P = main()

計算時間は、

209.223477 seconds (212.96 M allocations: 304.652 GiB, 9.71% gc time)

という結果です。

一方でPythonは、

np.random.seed(1)

import time
start = time.time()


D = 10
n_sample = 50

U = np.zeros((n_sample, N, D))
V = np.zeros((n_sample, D, M))
U[0, :, :] = np.random.rand(N, D)
V[0, :, :] = np.random.rand(D, M)
U, V = bpmf_gibbs_sampling(r, U, V, N, M, D, n_sample)

burn_in = 10
p = np.empty((N, M))
for u in range(N):
    for i in range(M):
        p[u, i] = np.mean(np.sum(U[burn_in:, u, :] * V[burn_in:, :, i], axis=1))

#print(p)

time.time() - start

こんな感じで、リブセンスさんと全く一緒のコードです。

比較のためパラメータ等条件はJuliaでの実行と同じです。

計算時間はというと、

4599.483886003494

という結果でした。

Juliaのほうが20倍くらい早いですね、Juliaやばいです。

最後に一応RMSEも見ておくとJuliaが、

sqrt(mean((df[:, 3] - BayesianProbabilisticMatrixFactorization.predict(df, R_P)) .^ 2))

で実行し、

0.7190330856378937

でした。

一方Pythonは、

#精度確認
new_data = np.array(df)
ret = np.zeros(new_data.shape[0])

for i in range(len(ret)):
    ret[i] = p[new_data[i, :][0], new_data[i, :][1]]
    
np.sqrt(np.mean(pow((new_data[:, 2] - ret), 2)))

で実行、

0.71530658114895218

とほぼ同等な結果が出ました*5

まとめ

Juliaはやっぱ早いですね!

もはやデータサイエンスに実務で関わる人で使わない理由はあまり無い気がします。

また今回数式を自分で読んで自分で実装する力はかなり大事だなと言うこと改めて感じました*6

次はRobust Bayesian Matrix Factorisationという、Bayesian Matrix Factorisationを変分推論(variational inference)でやっている論文を見つけたのでこちらをJuliaで実装していきたいと思います*7

それでは間違い等ありました指摘お願いいたします。

*1:リブセンスさんの記事の解説がかなりわかりやすいのでぜひご覧ください

*2:2つのjupyter notebookをアップロードしていて、上にあるのがJuliaで下にあるのがPythonです

*3:コード上でmoduleを定義しているのでぜひ見てみてください

*4:計算時間の比較がメインなので問題ないはず

*5:乱数seedの違い等で精度に違いが出たと思われます

*6:今回はリブセンスさんの実装を参考にできたので助かりましたが、そういうものがなくてもきちんと実装できるように力はつけておきたいところです

*7:流し読みしかしていないので間違ってたらすみません