fastai動かしてみたLesson5②

公開日:2019-05-25
最終更新:2019-05-25

今回はLesson5 https://course.fast.ai/videos/?lesson=5

notebookはこちら https://github.com/fastai/course-v3/tree/master/nbs/dl1

協調フィルタリングのnotebookに戻る

今回はMovielens 100kというデータセットの全部を利用する
http://files.grouplens.org/datasets/movielens/ml-100k.zip
今回はここからデータを持ってきて以下でPathを指定してください

path = Path('path/to/files')  

ファイルの読み込み

user,item,title = 'userId','movieId','title'  
ratings = pd.read_csv(path/'u.data', delimiter='\t', header=None,  
                      names=[user,item,'rating','timestamp'])  
ratings.head()  

movies = pd.read_csv(path/'u.item',  delimiter='|', encoding='latin-1', header=None,  
                    names=[item, 'title', 'date', 'N', 'url', *[f'g{i}' for i in range(19)]])  
movies.head()  

ちなみにratingsの長さは100000

rating_movie = ratings.merge(movies[[item, title]])  
rating_movie.head()  


databunchを作って中身を見るとこんな感じ

data = CollabDataBunch.from_df(rating_movie, seed=42, valid_pct=0.1, item_name=title)  
data.show_batch()  

よりよい結果を得るための小ネタ

y_range = [0,5.5]  

実際のratingは1~5を取るが、ここでは取りうる値を0~5.5に設定する

learn = collab_learner(data, n_factors=40, y_range=y_range, wd=1e-1)  
learn.lr_find()  
learn.recorder.plot(skip_end=15)  


いつも通りLearnerを定義して最適な学習率を探す
ここで、n_factorsというのはembedding行列の幅の事である
なぜここでembedding行列の"size"を使わないのだろうか
それは協調フィルタリングの世界ではその言葉を使わないからである
ここでは"latent factors"という考え方から"factors"が来ているということもあるし、協調フィルタリングというのはmatrix factorizationとも呼ばれるからである
つまり、今回学ぶことは実はmatrix factorizationについてなのである

なぜ、n_factors=40なのだろうか、これは自分自身で構造を決定しなければいけない物の一つである
そこで、Fastaiを作っているJeremy氏は10、20、40、80を試したところ、40が今回は最も良いとわかったので、今回は40に設定する

動かしてみるとこんな感じ

learn.fit_one_cycle(5, 5e-3)  

Biasの解釈

映画をあまり高く評価していなくても、あるいは映画が高評価をつくような要素を持っていなくても、movieバイアスを見た時に、最高の映画はなんであるか、どんな映画がウケるかといったことがわかる
バイアスを使う事で、偏りの無い映画の評価を得ることが出来るようになる

実際にBiasを使ってみよう

さっきとLearnerは同じ

learn.model  


今回のデータセットは1998年までしかないので、多くの人が見ているものを上位1000位までを利用する
多く見られている物上位10位を見てみる

g = rating_movie.groupby(title)['rating'].count()  
top_movies = g.sort_values(ascending=False).index.values[:1000]  
top_movies[:10]  


ここでis_itemをTrueにすると、itemのbias、つまりここではMovieのbiasを得る
Falseにすると、userのbiasを得る

movie_bias = learn.bias(top_movies, is_item=True)  
movie_bias.shape  


movie1000件に対するbiasを得ることが出来た

item0は映画名を入れるとバイアスを返す関数
sortedでitem0を使って、バイアスの小さい物順に並び替える

mean_ratings = rating_movie.groupby(title)['rating'].mean()  
movie_ratings = [(b, i, mean_ratings.loc[i]) for i,b in zip(top_movies,movie_bias)]  

item0 = lambda o:o[0]  

sorted(movie_ratings, key=item0)[:15]  

(bias, item, rating)の順で表示されている
ratingが低い物ばかりを得ることが出来た

sorted(movie_ratings, key=lambda o: o[0], reverse=True)[:15]  

ちゃんとratingの高い物を得ることが出来た

重み(Weight)の解説

n_factors=40でLearnerを設定したので、40×1000の形のWeightになる
n_factorsは分類でいうと分類のカテゴリ数であったが、実際の嗜好に関わる40のlatent factorは存在しないため、あまり40といわれてもピンとこない
ここでやりたいことは、40の要素を丁度3に落とし込むことだ

movie_w = learn.weight(top_movies, is_item=True)  
movie_w.shape  

torch.Size([1000, 40])

movie_wはPytorchのtensorであり、PCAメソッドを追加する

movie_pca = movie_w.pca(3)  
movie_pca.shape  

torch.Size([1000, 3])

これは主成分分析(PCA)であり、主成分分析は入力行列を使い、元の行列の多くのスぺースをカバーする、より少ない列数を見つける線形変換である

(ここら辺よくわかんない)
3つの要素をfac0,fac1,fac2とし、movieの構成物を使って並び替えてみる
今中身を見ても意味は分からないが、嗜好と映画の特徴である
そこで、それらの最上部と最下部を表示させることで、最も高いランクの物を見つけることが出来る

fac0,fac1,fac2 = movie_pca.t()  
movie_comp = [(f, i) for f,i in zip(fac0, top_movies)]  

sorted(movie_comp, key=itemgetter(0), reverse=True)[:10]  

[(tensor(1.0922), 'When We Were Kings (1996)'),
(tensor(1.0887), 'Lawrence of Arabia (1962)'),
(tensor(1.0852), 'Casablanca (1942)'),
(tensor(1.0616), 'Close Shave, A (1995)'),
(tensor(0.9945), 'Wrong Trousers, The (1993)'),
(tensor(0.9832), 'Some Folks Call It a Sling Blade (1993)'),
(tensor(0.9830), 'Chinatown (1974)'),
(tensor(0.9829), 'Third Man, The (1949)'),
(tensor(0.9722), 'Godfather, The (1972)'),
(tensor(0.9655), 'Apocalypse Now (1979)')]

sorted(movie_comp, key=itemgetter(0))[:10]  

[(tensor(-1.0692), 'Casablanca (1942)'),
(tensor(-1.0523), 'Close Shave, A (1995)'),
(tensor(-1.0142), 'When We Were Kings (1996)'),
(tensor(-1.0075), 'Lawrence of Arabia (1962)'),
(tensor(-1.0034), 'Wrong Trousers, The (1993)'),
(tensor(-0.9905), 'Chinatown (1974)'),
(tensor(-0.9692), 'Ran (1985)'),
(tensor(-0.9541), 'Apocalypse Now (1979)'),
(tensor(-0.9523), 'Wallace & Gromit: The Best of Aardman Animation (1996)'),
(tensor(-0.9369), 'Some Folks Call It a Sling Blade (1993)')]

movie_comp = [(f, i) for f,i in zip(fac1, top_movies)]  
sorted(movie_comp, key=itemgetter(0), reverse=True)[:10]  

[(tensor(1.1547), 'Braveheart (1995)'),
(tensor(1.1277), 'Raiders of the Lost Ark (1981)'),
(tensor(1.0513), 'Titanic (1997)'),
(tensor(0.8897), 'Pretty Woman (1990)'),
(tensor(0.8810), "It's a Wonderful Life (1946)"),
(tensor(0.8417), 'Hunt for Red October, The (1990)'),
(tensor(0.8389), 'Return of the Jedi (1983)'),
(tensor(0.8274), 'Forrest Gump (1994)'),
(tensor(0.8240), 'Independence Day (ID4) (1996)'),
(tensor(0.8207), 'Back to the Future (1985)')]

sorted(movie_comp, key=itemgetter(0))[:10]  

[(tensor(-0.9327), 'Keys to Tulsa (1997)'),
(tensor(-0.8720), 'Trainspotting (1996)'),
(tensor(-0.8708), 'Ready to Wear (Pret-A-Porter) (1994)'),
(tensor(-0.7839), 'Nosferatu (Nosferatu, eine Symphonie des Grauens) (1922)'),
(tensor(-0.7407), 'Big Night (1996)'),
(tensor(-0.7266), 'Very Brady Sequel, A (1996)'),
(tensor(-0.7155), 'Jude (1996)'),
(tensor(-0.7116), 'Clockwork Orange, A (1971)'),
(tensor(-0.7096), 'Stupids, The (1996)'),
(tensor(-0.6879), 'Lost Highway (1997)')]

idxs = np.random.choice(len(top_movies), 50, replace=False)  
idxs = list(range(50))  
X = fac0[idxs]  
Y = fac2[idxs]  
plt.figure(figsize=(15,15))  
plt.scatter(X, Y)  
for i, x, y in zip(top_movies[idxs], X, Y):  
    plt.text(x,y,i, color=np.random.rand(3)*0.7, fontsize=11)  
plt.show()  

Embeddingはマジで凄い

KaggleのRossmanに対するコンペの2番目にいた人たちの考え方は凄い面白いらしい

ニューラルネットを当時は表形式のデータに使い、embeddingも行った

ニューラルネットはもちろんすごくいい結果を出したが、entity embeddingでは、training後に全ての異なるtaskに追加したところ、結果がより良くなった

その人たちはドイツのスーパーマーケットチェーンが私たちと同じ方法で2次元にEmbeddingを表示させたので、表示させてみた
そうした事で、Embeddingと実際の地理が似ている事が分かった
つまり、下図のように近い物でクラスタリングしてみると、実際の位置関係と似たものになるという事だ

(ちょっと飛ばします)

次の記事はWeightDecayについて触れます

記事が少しでもいいなと思ったらクラップを送ってみよう!
17
+1
大学生の書きなぐりブログ 間違ってる事も書いてるので自己責任で勉強しましょう

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする