個性化推薦(二)

本文做者:在線實驗室javascript

數據準備html

 

數據介紹與下載java

 

咱們以 MovieLens 百萬數據集(ml-1m)爲例進行介紹。ml-1m 數據集包含了 6,000 位用戶對 4,000 部電影的 1,000,000 條評價(評分範圍 1~5 分,均爲整數),由 GroupLens Research 實驗室蒐集整理。web

 

Paddle在API中提供了自動加載數據的模塊。數據模塊爲 paddle.dataset.movielens緩存

 

import paddle
movie_info = paddle.dataset.movielens.movie_info()
print movie_info.values()[0]
# Run this block to show dataset's documentation
# help(paddle.dataset.movielens)

 

在原始數據中包含電影的特徵數據,用戶的特徵數據,和用戶對電影的評分。網絡

 

例如,其中某一個電影特徵爲:app

 

movie_info = paddle.dataset.movielens.movie_info()
print movie_info.values()[0]
<MovieInfo id(1), title(Toy Story ), categories(['Animation', "Children's", 'Comedy'])>

 

這表示,電影的id是1,標題是《Toy Story》,該電影被分爲到三個類別中。這三個類別是動畫,兒童,喜劇。ide

 
user_info = paddle.dataset.movielens.user_info()
print user_info.values()[0]
<UserInfo id(1), gender(F), age(1), job(10)>

 

這表示,該用戶ID是1,女性,年齡比18歲還年輕。職業ID是10。函數

 

其中,年齡使用下列分佈oop

  • 1: "Under 18"
  • 18: "18-24"
  • 25: "25-34"
  • 35: "35-44"
  • 45: "45-49"
  • 50: "50-55"
  • 56: "56+"

 

職業是從下面幾種選項裏面選則得出:

 

  • 0: "other" or not specified
  • 1: "academic/educator"
  • 2: "artist"
  • 3: "clerical/admin"
  • 4: "college/grad student"
  • 5: "customer service"
  • 6: "doctor/health care"
  • 7: "executive/managerial"
  • 8: "farmer"
  • 9: "homemaker"
  • 10: "K-12 student"
  • 11: "lawyer"
  • 12: "programmer"
  • 13: "retired"
  • 14: "sales/marketing"
  • 15: "scientist"
  • 16: "self-employed"
  • 17: "technician/engineer"
  • 18: "tradesman/craftsman"
  • 19: "unemployed"
  • 20: "writer"

 

而對於每一條訓練/測試數據,均爲 <用戶特徵> + <電影特徵> + 評分。

 

例如,咱們得到第一條訓練數據:

 

train_set_creator = paddle.dataset.movielens.train()
train_sample = next(train_set_creator())
uid = train_sample[0]
mov_id = train_sample[len(user_info[uid].value())]
print "User %s rates Movie %s with Score %s"%(user_info[uid], movie_info[mov_id], train_sample[-1])
User <UserInfo id(1), gender(F), age(1), job(10)> rates Movie <MovieInfo id(1193), title(One Flew Over the Cuckoo's Nest ), categories(['Drama'])> with Score [5.0]
 

即用戶1對電影1193的評價爲5分。

 

模型配置說明

 

下面咱們開始根據輸入數據的形式配置模型。首先引入所需的庫函數以及定義全局變量。 - IS_SPARSE: embedding中是否使用稀疏更新 - PASS_NUM: epoch數量

 

from __future__ import print_function
import math
import sys
import numpy as np
import paddle
import paddle.fluid as fluid
import paddle.fluid.layers as layers
import paddle.fluid.nets as nets

IS_SPARSE = True
BATCH_SIZE = 256
PASS_NUM = 20

 

 

而後爲咱們的用戶特徵綜合模型定義模型配置

 

def get_usr_combined_features():
    """network definition for user part"""

    USR_DICT_SIZE = paddle.dataset.movielens.max_user_id() + 1

    uid = layers.data(name='user_id', shape=[1], dtype='int64')

    usr_emb = layers.embedding(
        input=uid,
        dtype='float32',
        size=[USR_DICT_SIZE, 32],
        param_attr='user_table',
        is_sparse=IS_SPARSE)

    usr_fc = layers.fc(input=usr_emb, size=32)

    USR_GENDER_DICT_SIZE = 2

    usr_gender_id = layers.data(name='gender_id', shape=[1], dtype='int64')

    usr_gender_emb = layers.embedding(
        input=usr_gender_id,
        size=[USR_GENDER_DICT_SIZE, 16],
        param_attr='gender_table',
        is_sparse=IS_SPARSE)

    usr_gender_fc = layers.fc(input=usr_gender_emb, size=16)

    USR_AGE_DICT_SIZE = len(paddle.dataset.movielens.age_table)
    usr_age_id = layers.data(name='age_id', shape=[1], dtype="int64")

    usr_age_emb = layers.embedding(
        input=usr_age_id,
        size=[USR_AGE_DICT_SIZE, 16],
        is_sparse=IS_SPARSE,
        param_attr='age_table')

    usr_age_fc = layers.fc(input=usr_age_emb, size=16)

    USR_JOB_DICT_SIZE = paddle.dataset.movielens.max_job_id() + 1
    usr_job_id = layers.data(name='job_id', shape=[1], dtype="int64")

    usr_job_emb = layers.embedding(
        input=usr_job_id,
        size=[USR_JOB_DICT_SIZE, 16],
        param_attr='job_table',
        is_sparse=IS_SPARSE)

    usr_job_fc = layers.fc(input=usr_job_emb, size=16)

    concat_embed = layers.concat(
        input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc], axis=1)

    usr_combined_features = layers.fc(input=concat_embed, size=200, act="tanh")

    return usr_combined_features

 

如上述代碼所示,對於每一個用戶,咱們輸入4維特徵。其中包括user_id,gender_id,age_id,job_id。這幾維特徵均是簡單的整數值。爲了後續神經網絡處理這些特徵方便,咱們借鑑NLP中的語言模型,將這幾維離散的整數值,變換成embedding取出。分別造成usr_emb, usr_gender_emb, usr_age_emb, usr_job_emb。

 

而後,咱們對於全部的用戶特徵,均輸入到一個全鏈接層(fc)中。將全部特徵融合爲一個200維度的特徵。

 

進而,咱們對每個電影特徵作相似的變換,網絡配置爲:

 

def get_mov_combined_features():
    """network definition for item(movie) part"""

    MOV_DICT_SIZE = paddle.dataset.movielens.max_movie_id() + 1

    mov_id = layers.data(name='movie_id', shape=[1], dtype='int64')

    mov_emb = layers.embedding(
        input=mov_id,
        dtype='float32',
        size=[MOV_DICT_SIZE, 32],
        param_attr='movie_table',
        is_sparse=IS_SPARSE)

    mov_fc = layers.fc(input=mov_emb, size=32)

    CATEGORY_DICT_SIZE = len(paddle.dataset.movielens.movie_categories())

    category_id = layers.data(
        name='category_id', shape=[1], dtype='int64', lod_level=1)

    mov_categories_emb = layers.embedding(
        input=category_id, size=[CATEGORY_DICT_SIZE, 32], is_sparse=IS_SPARSE)

    mov_categories_hidden = layers.sequence_pool(
        input=mov_categories_emb, pool_type="sum")

    MOV_TITLE_DICT_SIZE = len(paddle.dataset.movielens.get_movie_title_dict())

    mov_title_id = layers.data(
        name='movie_title', shape=[1], dtype='int64', lod_level=1)

    mov_title_emb = layers.embedding(
        input=mov_title_id, size=[MOV_TITLE_DICT_SIZE, 32], is_sparse=IS_SPARSE)

    mov_title_conv = nets.sequence_conv_pool(
        input=mov_title_emb,
        num_filters=32,
        filter_size=3,
        act="tanh",
        pool_type="sum")

    concat_embed = layers.concat(
        input=[mov_fc, mov_categories_hidden, mov_title_conv], axis=1)

    mov_combined_features = layers.fc(input=concat_embed, size=200, act="tanh")

    return mov_combined_features

 

電影標題名稱(title)是一個序列的整數,整數表明的是這個詞在索引序列中的下標。這個序列會被送入 sequence_conv_pool 層,這個層會在時間維度上使用卷積和池化。由於如此,因此輸出會是固定長度,儘管輸入的序列長度各不相同。

 

最後,咱們定義一個inference_program來使用餘弦類似度計算用戶特徵與電影特徵的類似性。

 

def inference_program():
    """the combined network"""

    usr_combined_features = get_usr_combined_features()
    mov_combined_features = get_mov_combined_features()

    inference = layers.cos_sim(X=usr_combined_features, Y=mov_combined_features)
    scale_infer = layers.scale(x=inference, scale=5.0)

    return scale_infer

 

進而,咱們定義一個train_program來使用inference_program計算出的結果,在標記數據的幫助下來計算偏差。咱們還定義了一個optimizer_func來定義優化器。

 
def train_program():
    """define the cost function"""

    scale_infer = inference_program()

    label = layers.data(name='score', shape=[1], dtype='float32')
    square_cost = layers.square_error_cost(input=scale_infer, label=label)
    avg_cost = layers.mean(square_cost)

    return [avg_cost, scale_infer]


def optimizer_func():
    return fluid.optimizer.SGD(learning_rate=0.2)


訓練模型

定義訓練環境

定義您的訓練環境,能夠指定訓練是發生在CPU仍是GPU上。

 

定義數據提供器

 

下一步是爲訓練和測試定義數據提供器。提供器讀入一個大小爲 BATCH_SIZE的數據。paddle.dataset.movielens.train 每次會在亂序化後提供一個大小爲BATCH_SIZE的數據,亂序化的大小爲緩存大小buf_size

 

train_reader = paddle.batch(
    paddle.reader.shuffle(
        paddle.dataset.movielens.train(), buf_size=8192),
    batch_size=BATCH_SIZE)

test_reader = paddle.batch(
    paddle.dataset.movielens.test(), batch_size=BATCH_SIZE)

 

構造訓練過程(trainer)

 

咱們這裏構造了一個訓練過程,包括訓練優化函數。

 

提供數據

feed_order用來定義每條產生的數據和paddle.layer.data之間的映射關係。好比,movielens.train產生的第一列的數據對應的是user_id這個特徵。

 

feed_order = [
    'user_id', 'gender_id', 'age_id', 'job_id', 'movie_id', 'category_id',
    'movie_title', 'score'
]

 

構建訓練程序以及測試程序

 

分別構建訓練程序和測試程序,並引入訓練優化器。

 

main_program = fluid.default_main_program()
star_program = fluid.default_startup_program()
[avg_cost, scale_infer] = train_program()

test_program = main_program.clone(for_test=True)
sgd_optimizer = optimizer_func()
sgd_optimizer.minimize(avg_cost)
exe = fluid.Executor(place)

def train_test(program, reader):
    count = 0
    feed_var_list = [
        program.global_block().var(var_name) for var_name in feed_order
    ]
    feeder_test = fluid.DataFeeder(
    feed_list=feed_var_list, place=place)
    test_exe = fluid.Executor(place)
    accumulated = 0
    for test_data in reader():
        avg_cost_np = test_exe.run(program=program,
                                               feed=feeder_test.feed(test_data),
                                               fetch_list=[avg_cost])
        accumulated += avg_cost_np[0]
        count += 1
    return accumulated / count

 

構建訓練主循環並開始訓練

 

咱們根據上面定義的訓練循環數(PASS_NUM)和一些別的參數,來進行訓練循環,而且每次循環都進行一次測試,當測試結果足夠好時退出訓練並保存訓練好的參數。

 

# Specify the directory path to save the parameters
params_dirname = "recommender_system.inference.model"

from paddle.utils.plot import Ploter
train_prompt = "Train cost"
test_prompt = "Test cost"

plot_cost = Ploter(train_prompt, test_prompt)

def train_loop():
    feed_list = [
        main_program.global_block().var(var_name) for var_name in feed_order
    ]
    feeder = fluid.DataFeeder(feed_list, place)
    exe.run(star_program)

    for pass_id in range(PASS_NUM):
        for batch_id, data in enumerate(train_reader()):
            # train a mini-batch
            outs = exe.run(program=main_program,
                               feed=feeder.feed(data),
                               fetch_list=[avg_cost])
            out = np.array(outs[0])

            # get test avg_cost
            test_avg_cost = train_test(test_program, test_reader)

            plot_cost.append(train_prompt, batch_id, outs[0])
            plot_cost.append(test_prompt, batch_id, test_avg_cost)
            plot_cost.plot()

            if batch_id == 20:
                if params_dirname is not None:
                    fluid.io.save_inference_model(params_dirname, [
                                "user_id", "gender_id", "age_id", "job_id",
                                "movie_id", "category_id", "movie_title"
                        ], [scale_infer], exe)
                return
            print('EpochID {0}, BatchID {1}, Test Loss {2:0.2}'.format(
                            pass_id + 1, batch_id + 1, float(test_avg_cost)))

            if math.isnan(float(out[0])):
                sys.exit("got NaN loss, training failed.")

 

開始訓練

 
train_loop()


應用模型

 

生成測試數據

 

使用 create_lod_tensor(data, lod, place) 的API來生成細節層次的張量。data是一個序列,每一個元素是一個索引號的序列。lod是細節層次的信息,對應於data。好比,data = [[10, 2, 3], [2, 3]] 意味着它包含兩個序列,長度分別是3和2。因而相應地 lod = [[3, 2]],它代表其包含一層細節信息,意味着 data 有兩個序列,長度分別是3和2。

 

在這個預測例子中,咱們試着預測用戶ID爲1的用戶對於電影'Hunchback of Notre Dame'的評分

 
infer_movie_id = 783
infer_movie_name = paddle.dataset.movielens.movie_info()[infer_movie_id].title
user_id = fluid.create_lod_tensor([[1]], [[1]], place)
gender_id = fluid.create_lod_tensor([[1]], [[1]], place)
age_id = fluid.create_lod_tensor([[0]], [[1]], place)
job_id = fluid.create_lod_tensor([[10]], [[1]], place)
movie_id = fluid.create_lod_tensor([[783]], [[1]], place) # Hunchback of Notre Dame
category_id = fluid.create_lod_tensor([[10, 8, 9]], [[3]], place) # Animation, Children's, Musical
movie_title = fluid.create_lod_tensor([[1069, 4140, 2923, 710, 988]], [[5]],
                                      place) # 'hunchback','of','notre','dame','the'


構建預測過程並測試

 

與訓練過程相似,咱們須要構建一個預測過程。其中, params_dirname是以前用來存放訓練過程當中的各個參數的地址。

 
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
exe = fluid.Executor(place)

inference_scope = fluid.core.Scope()

 

測試

 

如今咱們能夠進行預測了。咱們要提供的feed_order應該和訓練過程一致。

 

總結

 

本章介紹了傳統的個性化推薦系統方法和YouTube的深度神經網絡個性化推薦系統,並以電影推薦爲例,使用PaddlePaddle訓練了一個個性化推薦神經網絡模型。個性化推薦系統幾乎涵蓋了電商系統、社交網絡、廣告推薦、搜索引擎等領域的方方面面,而在圖像處理、天然語言處理等領域已經發揮重要做用的深度學習技術,也將會在個性化推薦系統領域大放異彩。

 

參考文獻

 

  1. P. Resnick, N. Iacovou, etc. 「GroupLens: An Open Architecture for Collaborative Filtering of Netnews」, Proceedings of ACM Conference on Computer Supported Cooperative Work, CSCW 1994. pp.175-186.
  2. Sarwar, Badrul, et al. "Item-based collaborative filtering recommendation algorithms.Proceedings of the 10th international conference on World Wide Web. ACM, 2001.
  3. Kautz, Henry, Bart Selman, and Mehul Shah. "Referral Web: combining social networks and collaborative filtering." Communications of the ACM 40.3 (1997): 63-65. APA
  4. Peter Brusilovsky (2007). The Adaptive Web. p. 325.
  5. Robin Burke , Hybrid Web Recommender Systems, pp. 377-408, The Adaptive Web, Peter Brusilovsky, Alfred Kobsa, Wolfgang Nejdl (Ed.), Lecture Notes in Computer Science, Springer-Verlag, Berlin, Germany, Lecture Notes in Computer Science, Vol. 4321, May 2007, 978-3-540-72078-2.
  6. Yuan, Jianbo, et al. "Solving Cold-Start Problem in Large-scale Recommendation Engines: A Deep Learning Approach." arXiv preprint arXiv:1611.05480 (2016).
  7. Covington P, Adams J, Sargin E. Deep neural networks for youtube recommendations[C]//Proceedings of the 10th ACM Conference on Recommender Systems. ACM, 2016: 191-198.


知識共享許可協議
本教程 由 PaddlePaddle 創做,採用 知識共享 署名-相同方式共享 4.0 國際 許可協議進行許可。

原文連接地址:https://developer.baidu.com/topic/show/290073

相關文章
相關標籤/搜索