Deep Karmaning

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

JuliaでMatrix Factorizationを実装してPythonと計算速度の比較をしてみたらJuliaのほうが早かった

概要

以前以下の記事でPythonによるMatrix Factorizationを行いました。

rf00.hatenablog.com

これをJuliaでやってみようと言うのが、今回の記事の趣旨です。

そしてJuliaとPythonでMatrix Factorizationの計算速度も比較したところJuliaが早かったので、 その点も最後に示します。

実装

今回使ったJuliaコードとPythonコードは以下です。

julia_mf.ipynb · GitHub

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

grouplens.org

モデル部分

Juliaでのモデルは以下のようにmoduleで実装しました。

module MatrixFactorization

mutable struct MatrixFactorizationModel
    K::Int64
    alpha::Float64
    beta::Float64
    n_user::Int64
    n_item::Int64
    R::Array
    user_factors::Array
    item_factors::Array
end

#学習
function fit(model::MatrixFactorizationModel, n_iter::Int64)
    model.user_factors = rand(Float64, model.n_user, model.K)
    model.item_factors = rand(Float64, model.n_item, model.K)
    
    for i = 1:n_iter
        sgd(model)
    end
    
end

#確率的最急降下法
function sgd(model::MatrixFactorizationModel)
    samples = model.R[shuffle(1:end), :]
    
    for i in 1:size(samples)[1]
        user = samples[i, :][1]
        item = samples[i, :][2]
        
        err = samples[i, :][3] - dot(model.user_factors[user], model.item_factors[item])
        
        model.user_factors[user] += model.alpha * (err * model.item_factors[item] - model.beta * model.user_factors[user])
        model.item_factors[item] += model.alpha * (err * model.user_factors[user] - model.beta * model.item_factors[item])             
    end
    
end


#予測
function predict(model::MatrixFactorizationModel, X::Array)
    rate = zeros(size(X)[1])
    
    for i = 1:size(X)[1]
        user = X[i, :][1]
        item = X[i, :][2]
        rate[i] = dot(model.user_factors[user], model.item_factors[item])
    end
    
    return rate
end


end

Pythonでの実装とかなり似たようにしていて、scikit-learn的に使えるようにしています。

次に学習の実施までを見ていきます。

学習の実施

まずはMovie Lensのデータをmoduleに利用できる形まで持っていきます。

using DataFrames 
using CSV

df = CSV.read("data/ml-100k/u.data", header = false, delim = '\t')

#配列化
df = Array(df[:, 1:3])

#ユニークユーザー、ユニークアイテム
user = length(unique(df[:, 1]))
item = length(unique(df[:, 2]))

#シャッフル
df = df[shuffle(1:end), :]

#学習データとテストデータ分割
N = size(df)[1]
train_size = Int64(N * 0.8)
train_df = df[1:train_size, :]
test_df = df[train_size:N, :]

これで学習データと検証データに分割して準備ができました。

以下で学習して、精度検証をします。

#学習
MF = MatrixFactorization.MatrixFactorizationModel(20, 0.01, 0.5, user, item, train_df, [], [])
MatrixFactorization.fit(MF, 10)

#精度
pred = MatrixFactorization.predict(MF, test_df)

#rmse
sqrt(mean((pred - test_df[:, 3]).^2))

精度が、

1.0777975155497033

でとりあえず学習はできていそうですね!

計算時間

計算時間は以下のように計測しました。

まずはJuliaは、

function main()
    MF = MatrixFactorization.MatrixFactorizationModel(20, 0.01, 0.5, user, item, train_df, [], [])
    MatrixFactorization.fit(MF, 50)
end

@time main()

となり、結果は、

  7.633972 seconds (155.94 M allocations: 3.576 GiB, 32.54% gc time)

という結果になりました。

一方でPythonで独自の実装を行ったものと比較すると(詳しくは上で貼ったコードを見てください)、

import time

start = time.time()

#Matrix Factorization
MF = MatrixFactorization(K = 20, alpha = 0.01, beta = 0.5)

MF.fit(train_df, n_user, n_item, n_iter = 50)

time.time() - start

で実行し、

75.14610314369202

でした。

Juliaのほうが10倍くらい早いです(すごい!)。

Juliaを真面目に使えるようになろうと思います。

まとめ

今回は、JuliaでMatrix Factorizationを実装して、Pythonで以前実装したものとスピードを比較するところまで行いました。

その結果かなりJuliaのほうが10倍程度早いことがわかりました。

これは個人的にはかなりJuliaを使うモチベーションになりましたので、今後積極的に勉強していこうと思います。

間違い等ありましたらご指摘お願いいたします。