PaddlePaddle 个性化推荐系统

1.x(i):表示第i部电影的各种类型风格的指标程度。每一个电影都有一个3*1维度特征向量x(i)=[x_0,a,b]T。
	x_0:每一步电影自带的特征偏置项,但该值为不代表属性的固定值x_0=1。
	a:浪漫指数,代表电影的浪漫程度。
	b:动作指数,代表电影的动作程度。
  例如:第一部电影的特征向量x(1)=[1, 0.9, 0.1]T,该部电影的浪漫指数为0.9,动作指数为0.1。

2.θ(j)=[x_0,a,b]T:表示用户j对于各种电影类型的喜好程度的3*1维度特征向量,该向量为模型需要学习的参数。
	x_0:同样该值为不代表属性的固定值x_0=0。
	a:代表用户j对于浪漫类电影的喜好程度。
	b:代表用户j对于动作类电影的喜好程度。
  例如:用户1的特征向量θ(1)=[0, 5, 0]T,用户1对于浪漫类电影的喜好程度为5,对于动作类电影的喜好程度为0。

3.θ(j)T x(i):表示用户j对于第i部电影的评分预测值。
  θ(1)T x(1)=0*1 +5*0.9 + 0*0.1 = 4.5,表示用户1对于第1部电影的评分预测值为4.5。

其他补充:
1.LeNet5网络的特点能够总结为如下几点:
	1.使用多层神经网络(MLP)作为最后的分类器。
	2.。。。。
2.NIN模型主要有以下两个特点:
	1.引入了多层感知卷积网络(Multi-Layer Perceptron Convolution,MLPconv)来代替一层线性卷积网络。
	  MLPconv是通过在线性卷积后增加若干层的卷积而形成的一个微型多层卷积网络,可用于提取高度非线性特征。
	2.一般来说,传统的CNN网络最后几层都是全连接层,包含较多参数。而在NIN模型的设计中,最后一层卷积层包含维度大小等同于类别数量的特征图,
	  并采用全局平均池化层替代全连接层,从而得到类别维度大小的向量,再据此进行分类。这样的设计有利于减少参数数量。

def multilayer_perceptron(img):
    """
    定义多层感知机分类器:
        含有两个隐藏层(即全连接层)的多层感知器
        其中两个隐藏层的激活函数均采用ReLU,输出层的激活函数用Softmax
    Args: img -- 输入的原始图像数据
    Return: predict_image -- 分类的结果
    """
    # 第一个全连接层
    hidden1 = paddle.layer.fc(input=img, size=128, act=paddle.activation.Relu())
    # 第二个全连接层
    hidden2 = paddle.layer.fc(input=hidden1, size=64, act=paddle.activation.Relu())
    # 第三个全连接层,需要注意输出尺寸为10,,对应0-9这10个数字
    predict = paddle.layer.fc(input=hidden2, size=10, act=paddle.activation.Softmax())
    return predict


"""
    使用paddle框架实现个性化电影推荐系统的模型训练和参数输出保存,关键步骤如下:
    1.初始化
    2.配置网络结构和设置参数:
      - 构造用户融合特征模型
      - 构造电影融合特征模型
      - 定义特征相似性度量inference
      - 成本函数cost
      - 创建parameters
      - 定义feeding
    3.定义event_handler
    4.定义trainer
    5.开始训练
    6.展示cost曲线plot_costs()
"""
import os
import matplotlib
#from paddle.v2.plot import Ploter语句引入了PaddlePaddle的绘图工具包,其内部也是调用了matplotlib包。
#在默认设置下,运行绘图代码时可能会遇到“no display name and no$DISPLAY environment variable”的问题。
#这是因为绘图工具函数的默认后端为一些需要图形用户接口(GUI)的后端,而运行的当前环境不满足。
#为此,可加入import matplotlib和matplotlib.use('Agg')语句重新指定不需要GUI的后端,从而解决该问题。
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import paddle.v2 as paddle

WITH_GPU = os.getenv('WITH_GPU', '0') != '0' #False
STEP = 0

# 构造用户融合特征模型
def get_usr_combined_features():
    """
    构造用户融合特征模型,融合特征包括:
        user_id:用户编号
        gender_id:性别类别编号
        age_id:年龄分类编号
        job_id:职业类别编号
    以上特征信息从数据集中读取后分别变换成对应词向量,再输入到全连接层
    所有的用户特征再输入到一个全连接层中,将所有特征融合为一个200维的特征
    Return: usr_combined_features -- 用户融合特征模型
    """
    # 读取用户编号信息(user_id)
    uid = paddle.layer.data(
        name='user_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_user_id() + 1))

    # 将用户编号变换为对应词向量
    usr_emb = paddle.layer.embedding(input=uid, size=32)

    # 将用户编号对应词向量输入到全连接层
    usr_fc = paddle.layer.fc(input=usr_emb, size=32)

    # 读取用户性别类别编号信息(gender_id)并做处理(同上)
    usr_gender_id = paddle.layer.data(name='gender_id', type=paddle.data_type.integer_value(2))
    usr_gender_emb = paddle.layer.embedding(input=usr_gender_id, size=16)
    usr_gender_fc = paddle.layer.fc(input=usr_gender_emb, size=16)

    # 读取用户年龄类别编号信息(age_id)并做处理(同上)
    usr_age_id = paddle.layer.data(
        name='age_id',
        type=paddle.data_type.integer_value(
            len(paddle.dataset.movielens.age_table)))
    usr_age_emb = paddle.layer.embedding(input=usr_age_id, size=16)
    usr_age_fc = paddle.layer.fc(input=usr_age_emb, size=16)

    # 读取用户职业类别编号信息(job_id)并做处理(同上)
    usr_job_id = paddle.layer.data(
        name='job_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_job_id() + 1))
    usr_job_emb = paddle.layer.embedding(input=usr_job_id, size=16)
    usr_job_fc = paddle.layer.fc(input=usr_job_emb, size=16)

    # 所有的用户特征再输入到一个全连接层中,完成特征融合
    usr_combined_features = paddle.layer.fc(
        input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc],
        size=200,
        act=paddle.activation.Tanh())

    return usr_combined_features

# 构造电影融合特征模型
def get_mov_combined_features():
    """
    构造电影融合特征模型,融合特征包括:
        movie_id:电影编号
        category_id:电影类别编号
        movie_title:电影名
    以上特征信息经过相应处理后再输入到一个全连接层中,
    将所有特征融合为一个200维的特征
    Return: mov_combined_features -- 电影融合特征模型
    """
    movie_title_dict = paddle.dataset.movielens.get_movie_title_dict()

    # 读取电影编号信息(movie_id)
    mov_id = paddle.layer.data(
        name='movie_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_movie_id() + 1))

    # 将电影编号变换为对应词向量
    mov_emb = paddle.layer.embedding(input=mov_id, size=32)

    # 将电影编号对应词向量输入到全连接层
    mov_fc = paddle.layer.fc(input=mov_emb, size=32)

    # 读取电影类别编号信息(category_id)
    mov_categories = paddle.layer.data(
        name='category_id',
        type=paddle.data_type.sparse_binary_vector(
            len(paddle.dataset.movielens.movie_categories())))

    # 将电影编号信息输入到全连接层
    mov_categories_hidden = paddle.layer.fc(input=mov_categories, size=32)

    # 读取电影名信息(movie_title)
    mov_title_id = paddle.layer.data(
        name='movie_title',
        type=paddle.data_type.integer_value_sequence(len(movie_title_dict)))

    # 将电影名变换为对应词向量
    mov_title_emb = paddle.layer.embedding(input=mov_title_id, size=32)

    # 将电影名对应词向量输入到卷积网络生成电影名时序特征
    mov_title_conv = paddle.networks.sequence_conv_pool(
        input=mov_title_emb, hidden_size=32, context_len=3)

    # 所有的电影特征再输入到一个全连接层中,完成特征融合
    mov_combined_features = paddle.layer.fc(
        input=[mov_fc, mov_categories_hidden, mov_title_conv],
        size=200,
        act=paddle.activation.Tanh())

    return mov_combined_features

# 配置网络结构
def network_config():
    """
    配置网络结构
    Return:
        inference -- 相似度
        cost -- 损失函数
        parameters -- 模型参数
        feeding -- 数据映射,python字典
    """
    # 构造用户融合特征,电影融合特征
    usr_combined_features = get_usr_combined_features()
    mov_combined_features = get_mov_combined_features()

    # 计算用户融合特征和电影融合特征的余弦相似度
    inference = paddle.layer.cos_sim(
        a=usr_combined_features, b=mov_combined_features, size=1, scale=5)

    # 定义成本函数为均方误差函数
    cost = paddle.layer.square_error_cost(
        input=inference,
        label=paddle.layer.data(
            name='score', type=paddle.data_type.dense_vector(1)))

    # 利用cost创建parameters
    parameters = paddle.parameters.create(cost)

    # 数据层和数组索引映射,用于trainer训练时读取数据
    feeding = {
        'user_id': 0,
        'gender_id': 1,
        'age_id': 2,
        'job_id': 3,
        'movie_id': 4,
        'category_id': 5,
        'movie_title': 6,
        'score': 7
    }

    result = [cost, parameters, feeding]
    return result

# 展示模型训练测试曲线
def plot_costs(train_costs, train_step, test_costs, test_step):
    """
    利用costs展示模型的训练测试曲线
    Args:
        train_costs -- 记录了训练过程的cost变化的list,每100次迭代记录一次
        train_step -- 记录了训练过程迭代次数的list
        test_costs -- 记录了测试过程的cost变化的list,每3500次迭代记录一次
        test_step -- 记录了测试过程迭代次数的list
    """
    train_costs = np.squeeze(train_costs)
    test_costs = np.squeeze(test_costs)

    plt.figure()
    plt.plot(train_step, train_costs, label="Train Cost")
    plt.plot(test_step, test_costs, label="Test Cost")

    plt.ylabel('cost')
    plt.xlabel('iterations (step)')
    plt.title("train-test-cost")

    plt.legend()
    plt.show()
    plt.savefig('train_test_cost.png')


def main():
    """
    定义神经网络结构,训练网络
    """
    # 初始化,设置为不使用GPU
    paddle.init(use_gpu=WITH_GPU)

    # 配置网络结构
    cost, parameters, feeding = network_config()

    # 记录cost和step
    train_costs = []
    test_costs = []
    train_step = []
    test_step = []

    # 定义模型训练器,配置三个参数
    # cost:成本函数
    # parameters:参数
    # update_equation:更新公式(模型采用Adam方法优化更新,并初始化学习率)
    trainer = paddle.trainer.SGD(
        cost=cost,
        parameters=parameters,
        update_equation=paddle.optimizer.Adam(learning_rate=1e-4))

    # 事件处理模块
    def event_handler(event):
        """
        事件处理器,可以根据训练过程的信息作相应操作
        Args: event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
        """
        global STEP
        if isinstance(event, paddle.event.EndIteration):
            # 每100个batch输出一条记录,分别是当前的迭代次数编号,batch编号和对应损失值
            if event.batch_id % 100 == 0:
                print "Pass %d Batch %d Cost %.2f" % (
                    event.pass_id, event.batch_id, event.cost)
                # 添加训练数据的cost绘图数据
                train_costs.append(event.cost)
                train_step.append(STEP)
            STEP += 1

        if isinstance(event, paddle.event.EndPass):
            # 保存参数至文件
            with open('params_pass_%d.tar' % event.pass_id, 'w') as param_f:
                trainer.save_parameter_to_tar(param_f)

            # 利用测试数据进行测试
            result = trainer.test(reader=paddle.batch(paddle.dataset.movielens.test(), batch_size=128))
            print "Test with Pass %d, Cost %f" % (event.pass_id, result.cost)
            # 添加测试数据的cost绘图数据
            test_costs.append(result.cost)
            test_step.append(STEP)

    # 模型训练
    # paddle.batch(reader(), batch_size=256):表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
    # paddle.reader.shuffle(train(), buf_size=8192):表示trainer从train()这个reader中读取了buf_size=8192大小的数据并打乱顺序
    # event_handler:事件管理机制,可以自定义event_handler,根据事件信息作相应的操作下方代码中选择的是event_handler_plot函数
    # feeding:用到了之前定义的feeding索引,将数据层信息输入trainer
    # num_passes:定义训练的迭代次数
    trainer.train(
        reader=paddle.batch(
            paddle.reader.shuffle(
                paddle.dataset.movielens.train(), buf_size=8192),
            batch_size=256),
        event_handler=event_handler,
        feeding=feeding,
        num_passes=10)

    # 展示学习曲线
    plot_costs(train_costs, train_step, test_costs, test_step)

if __name__ == '__main__':
    main()

"""
    使用paddle框架实现个性化电影推荐系统的结果预测,
    无需重新训练模型,只需加载模型文件。关键步骤如下:
    1.初始化
    2.配置网络结构
      - 构造用户融合特征模型
      - 构造电影融合特征模型
      - 定义特征相似性度量inference
      - 定义feeding
    3.从parameters文件直接获取模型参数
    4.根据模型参数和测试数据来预测结果
"""
import copy
import os
import paddle.v2 as paddle
PARAMETERS = None

# 构造用户融合特征模型
def get_usr_combined_features():
    """
    构造用户融合特征模型,融合特征包括:
        user_id:用户编号
        gender_id:性别类别编号
        age_id:年龄分类编号
        job_id:职业类别编号
    以上特征信息从数据集中读取后分别变换成对应词向量,再输入到全连接层
    所有的用户特征再输入到一个全连接层中,将所有特征融合为一个200维的特征
    Return:usr_combined_features -- 用户融合特征模型
    """
    # 读取用户编号信息(user_id)
    uid = paddle.layer.data(
        name='user_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_user_id() + 1))

    # 将用户编号变换为对应词向量
    usr_emb = paddle.layer.embedding(input=uid, size=32)

    # 将用户编号对应词向量输入到全连接层
    usr_fc = paddle.layer.fc(input=usr_emb, size=32)

    # 读取用户性别类别编号信息(gender_id)并做处理(同上)
    usr_gender_id = paddle.layer.data(
        name='gender_id', type=paddle.data_type.integer_value(2))
    usr_gender_emb = paddle.layer.embedding(input=usr_gender_id, size=16)
    usr_gender_fc = paddle.layer.fc(input=usr_gender_emb, size=16)

    # 读取用户年龄类别编号信息(age_id)并做处理(同上)
    usr_age_id = paddle.layer.data(
        name='age_id',
        type=paddle.data_type.integer_value(
            len(paddle.dataset.movielens.age_table)))
    usr_age_emb = paddle.layer.embedding(input=usr_age_id, size=16)
    usr_age_fc = paddle.layer.fc(input=usr_age_emb, size=16)

    # 读取用户职业类别编号信息(job_id)并做处理(同上)
    usr_job_id = paddle.layer.data(
        name='job_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_job_id() + 1))
    usr_job_emb = paddle.layer.embedding(input=usr_job_id, size=16)
    usr_job_fc = paddle.layer.fc(input=usr_job_emb, size=16)

    # 所有的用户特征再输入到一个全连接层中,完成特征融合
    usr_combined_features = paddle.layer.fc(
        input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc],
        size=200,
        act=paddle.activation.Tanh())

    return usr_combined_features


# 构造电影融合特征模型
def get_mov_combined_features():
    """
    构造电影融合特征模型,融合特征包括:
        movie_id:电影编号
        category_id:电影类别编号
        movie_title:电影名
    以上特征信息经过相应处理后再输入到一个全连接层中,
    将所有特征融合为一个200维的特征
    Return:mov_combined_features -- 电影融合特征模型
    """
    movie_title_dict = paddle.dataset.movielens.get_movie_title_dict()

    # 读取电影编号信息(movie_id)
    mov_id = paddle.layer.data(
        name='movie_id',
        type=paddle.data_type.integer_value(
            paddle.dataset.movielens.max_movie_id() + 1))

    # 将电影编号变换为对应词向量
    mov_emb = paddle.layer.embedding(input=mov_id, size=32)

    # 将电影编号对应词向量输入到全连接层
    mov_fc = paddle.layer.fc(input=mov_emb, size=32)

    # 读取电影类别编号信息(category_id)
    mov_categories = paddle.layer.data(
        name='category_id',
        type=paddle.data_type.sparse_binary_vector(
            len(paddle.dataset.movielens.movie_categories())))

    # 将电影编号信息输入到全连接层
    mov_categories_hidden = paddle.layer.fc(input=mov_categories, size=32)

    # 读取电影名信息(movie_title)
    mov_title_id = paddle.layer.data(
        name='movie_title',
        type=paddle.data_type.integer_value_sequence(len(movie_title_dict)))

    # 将电影名变换为对应词向量
    mov_title_emb = paddle.layer.embedding(input=mov_title_id, size=32)

    # 将电影名对应词向量输入到卷积网络生成电影名时序特征
    mov_title_conv = paddle.networks.sequence_conv_pool(
        input=mov_title_emb, hidden_size=32, context_len=3)

    # 所有的电影特征再输入到一个全连接层中,完成特征融合
    mov_combined_features = paddle.layer.fc(
        input=[mov_fc, mov_categories_hidden, mov_title_conv],
        size=200,
        act=paddle.activation.Tanh())

    return mov_combined_features


# 配置网络结构
def netconfig():
    """
    配置网络结构
    Return:
        inference -- 相似度
        feeding -- 数据映射,python字典
    """
    # 构造用户融合特征,电影融合特征
    usr_combined_features = get_usr_combined_features()
    mov_combined_features = get_mov_combined_features()

    # 计算用户融合特征和电影融合特征的余弦相似度
    inference = paddle.layer.cos_sim(
        a=usr_combined_features, b=mov_combined_features, size=1, scale=5)

    # 数据层和数组索引映射,用于trainer训练时读取数据
    feeding = {
        'user_id': 0,
        'gender_id': 1,
        'age_id': 2,
        'job_id': 3,
        'movie_id': 4,
        'category_id': 5,
        'movie_title': 6,
        'score': 7
    }

    data = [inference, feeding]
    return data


def main():
    """
    读取模型参数并预测结果
    """
    global PARAMETERS
    paddle.init(use_gpu=False)

    # 配置网络结构
    inference, feeding = netconfig()

    # 判断参数文件是否存在
    if not os.path.exists('params_pass_9.tar'):
        print "Params file doesn't exists."
        return

    # 从文件中读取参数
    with open('params_pass_9.tar', 'r') as param_f:
        PARAMETERS = paddle.parameters.Parameters.from_tar(param_f)

    # 定义用户编号值和电影编号值
    user_id = 234
    movie_id = 345

    # 根据已定义的用户、电影编号值从movielens数据集中读取数据信息
    user = paddle.dataset.movielens.user_info()[user_id]
    movie = paddle.dataset.movielens.movie_info()[movie_id]

    # 存储用户特征和电影特征
    feature = user.value() + movie.value()

    # 复制feeding值,并删除序列中的得分项
    infer_dict = copy.copy(feeding)
    del infer_dict['score'] #删除序列中的得分项

    # 预测指定用户对指定电影的喜好得分值
    prediction = paddle.infer(
        output_layer=inference,
        parameters=PARAMETERS,
        input=[feature],
        feeding=infer_dict)
    score = (prediction[0][0] + 5.0) / 2
    print "[Predict] User %d Rating Movie %d With Score %.2f" % (
        user_id, movie_id, score)

if __name__ == '__main__':
    main()


jupyter文件中的内容

import paddle.v2 as paddle
paddle.init(use_gpu=False)

#在原始数据中包含电影的特征数据,用户的特征数据,和用户对电影的评分。
#例如,其中某一个电影特征为 <MovieInfo id(1), title(Toy Story ), categories(['Animation', "Children's", 'Comedy'])>
#这表示,电影的id是1,标题是《Toy Story》,该电影被分为到三个类别中。这三个类别是动画,儿童,喜剧。
movie_info = paddle.dataset.movielens.movie_info()
print movie_info.values()[0]

user_info = paddle.dataset.movielens.user_info()
#<UserInfo id(1), gender(F), age(1), job(10)>
print user_info.values()[0]
"""  
	这表示,该用户ID是1,女性,年龄比18岁还年轻。职业ID是10。
	其中,年龄使用下列分布
	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"
	而对于每一条训练/测试数据,均为 <用户特征> + <电影特征> + 评分。
"""

#例如,我们获得第一条训练数据 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分。
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])


#模型配置说明,下面我们开始根据输入数据的形式配置模型。
#如上述代码所示,对于每个用户,我们输入4维特征。其中包括user_id,gender_id,age_id,job_id。这几维特征均是简单的整数值。
#为了后续神经网络处理这些特征方便,我们借鉴NLP中的语言模型,将这几维离散的整数值,变换成embedding取出。
#分别形成usr_emb, usr_gender_emb, usr_age_emb, usr_job_emb。
uid = paddle.layer.data(
    name='user_id',
    type=paddle.data_type.integer_value(
		paddle.dataset.movielens.max_user_id() + 1))
usr_emb = paddle.layer.embedding(input=uid, size=32)
usr_fc = paddle.layer.fc(input=usr_emb, size=32)

usr_gender_id = paddle.layer.data(name='gender_id', type=paddle.data_type.integer_value(2))
usr_gender_emb = paddle.layer.embedding(input=usr_gender_id, size=16)
usr_gender_fc = paddle.layer.fc(input=usr_gender_emb, size=16)

usr_age_id = paddle.layer.data(
    name='age_id',
    type=paddle.data_type.integer_value(
        len(paddle.dataset.movielens.age_table)))
usr_age_emb = paddle.layer.embedding(input=usr_age_id, size=16)
usr_age_fc = paddle.layer.fc(input=usr_age_emb, size=16)

usr_job_id = paddle.layer.data(
    name='job_id',
    type=paddle.data_type.integer_value(
        paddle.dataset.movielens.max_job_id() + 1))
usr_job_emb = paddle.layer.embedding(input=usr_job_id, size=16)
usr_job_fc = paddle.layer.fc(input=usr_job_emb, size=16)

usr_combined_features = paddle.layer.fc(
        input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc],
        size=200,
        act=paddle.activation.Tanh())


#然后,我们对于所有的用户特征,均输入到一个全连接层(fc)中。将所有特征融合为一个200维度的特征。
#电影ID和电影类型分别映射到其对应的特征隐层。对于电影标题名称(title),一个ID序列表示的词语序列,
#在输入卷积层后,将得到每个时间窗口的特征(序列特征),然后通过在时间维度降采样得到固定维度的特征,整个过程在sequence_conv_pool实现。
#进而,我们对每一个电影特征做类似的变换,网络配置如下
mov_id = paddle.layer.data(
    name='movie_id',
    type=paddle.data_type.integer_value(
        paddle.dataset.movielens.max_movie_id() + 1))
mov_emb = paddle.layer.embedding(input=mov_id, size=32)
mov_fc = paddle.layer.fc(input=mov_emb, size=32)

mov_categories = paddle.layer.data(
    name='category_id',
    type=paddle.data_type.sparse_binary_vector(
        len(paddle.dataset.movielens.movie_categories())))
mov_categories_hidden = paddle.layer.fc(input=mov_categories, size=32)

movie_title_dict = paddle.dataset.movielens.get_movie_title_dict()
mov_title_id = paddle.layer.data(
    name='movie_title',
    type=paddle.data_type.integer_value_sequence(len(movie_title_dict)))
mov_title_emb = paddle.layer.embedding(input=mov_title_id, size=32)
mov_title_conv = paddle.networks.sequence_conv_pool(
    input=mov_title_emb, hidden_size=32, context_len=3)

mov_combined_features = paddle.layer.fc(
    input=[mov_fc, mov_categories_hidden, mov_title_conv],
    size=200,
    act=paddle.activation.Tanh())

#最后再将电影的特征融合进mov_combined_features中,进而我们使用余弦相似度计算用户特征与电影特征的相似性。
inference = paddle.layer.cos_sim(a=usr_combined_features, b=mov_combined_features, size=1, scale=5)
#并将这个相似性拟合(回归)到用户评分上。至此,我们的优化目标就是这个网络配置中的cost了。
cost = paddle.layer.square_error_cost(
        input=inference,
        label=paddle.layer.data(
            name='score', type=paddle.data_type.dense_vector(1)))

#训练模型,定义参数
#神经网络的模型,我们可以简单的理解为网络拓朴结构+参数。之前一节,我们定义出了优化目标cost。这个cost即为网络模型的拓扑结构。
#我们开始训练模型,需要先定义出参数。定义方法如下
parameters = paddle.parameters.create(cost)
#parameters是模型的所有参数集合。他是一个python的dict。我们可以查看到这个网络中的所有参数名称。
#因为之前定义模型的时候,我们没有指定参数名称,这里参数名称是自动生成的。当然,我们也可以指定每一个参数名称,方便日后维护。
print parameters.keys()
#[u'___fc_layer_2__.wbias', u'___embedding_layer_3__.w0', u'___fc_layer_1__.wbias', u'___fc_layer_3__.w0', u'___fc_layer_1__.w0',
# u'___fc_layer_6__.wbias', u'___fc_layer_5__.w0', u'___fc_layer_4__.wbias', u'___fc_layer_6__.w0', u'___fc_layer_0__.w0', 
# u'___fc_layer_0__.wbias', u'___fc_layer_2__.w0', u'___embedding_layer_0__.w0', u'___embedding_layer_5__.w0', u'___fc_layer_7__.w2',
# u'___fc_layer_7__.w1', u'___fc_layer_3__.wbias', u'___fc_layer_5__.wbias', u'___sequence_conv_pool_0___conv_fc.w0',
# u'___sequence_conv_pool_0___conv_fc.wbias', u'___fc_layer_7__.wbias', u'___fc_layer_7__.w0', u'___embedding_layer_2__.w0',
# u'___embedding_layer_1__.w0', u'___fc_layer_4__.w2', u'___fc_layer_4__.w3', u'___fc_layer_4__.w0', u'___fc_layer_4__.w1', u'___embedding_layer_4__.w0']

#构造训练(trainer)。下面,我们根据网络拓扑结构和模型参数来构造出一个本地训练(trainer)。
#在构造本地训练的时候,我们还需要指定这个训练的优化方法。这里我们使用Adam来作为优化算法。
trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=paddle.optimizer.Adam(learning_rate=1e-4))

#训练,下面我们开始训练过程。
#我们直接使用Paddle提供的数据集读取程序。paddle.dataset.movielens.train()和paddle.dataset.movielens.test()分别做训练和预测数据集。
#并且通过feeding来指定每一个数据和data_layer的对应关系。
#例如,这里的feeding表示的是,对于数据层 user_id,使用了reader中每一条数据的第0个元素。gender_id数据层使用了第1个元素。以此类推。
feeding = {
    'user_id': 0,
    'gender_id': 1,
    'age_id': 2,
    'job_id': 3,
    'movie_id': 4,
    'category_id': 5,
    'movie_title': 6,
    'score': 7
}

#训练过程是完全自动的。我们可以使用event_handler与event_handler_plot来观察训练过程,或进行测试等。
#这里我们在event_handler_plot里面绘制了训练误差曲线和测试误差曲线。并且保存了模型。
def event_handler(event):
    if isinstance(event, paddle.event.EndIteration):
        if event.batch_id % 100 == 0:
            print "Pass %d Batch %d Cost %.2f" % (
                event.pass_id, event.batch_id, event.cost)


from paddle.v2.plot import Ploter
train_title = "Train cost"
test_title = "Test cost"
cost_ploter = Ploter(train_title, test_title)

step = 0

def event_handler_plot(event):
    global step
    if isinstance(event, paddle.event.EndIteration):
        if step % 10 == 0:  # every 10 batches, record a train cost
            cost_ploter.append(train_title, step, event.cost)

        if step % 1000 == 0: # every 1000 batches, record a test cost
            result = trainer.test(
                reader=paddle.batch(
                    paddle.dataset.movielens.test(), batch_size=256),
                feeding=feeding)
            cost_ploter.append(test_title, step, result.cost)

        if step % 100 == 0: # every 100 batches, update cost plot
            cost_ploter.plot()
        step += 1


trainer.train(
    reader=paddle.batch(
            paddle.reader.shuffle(
            paddle.dataset.movielens.train(), buf_size=8192),
                            batch_size=256),
    event_handler=event_handler_plot,
    feeding=feeding,
    num_passes=2)

 

#应用模型,在训练了几轮以后,您可以对模型进行推断。我们可以使用任意一个用户ID和电影ID,来预测该用户对该电影的评分。
#示例程序 
import copy
user_id = 234
movie_id = 345

user = user_info[user_id]
movie = movie_info[movie_id]

feature = user.value() + movie.value()

infer_dict = copy.copy(feeding)
del infer_dict['score']

prediction = paddle.infer(inference, parameters=parameters, input=[feature], feeding=infer_dict)
score = (prediction[0][0] + 5.0) / 2
print "[Predict] User %d Rating Movie %d With Score %.2f"%(user_id, movie_id, score)

#总结
#本章介绍了传统的推荐系统方法和YouTube的深度神经网络推荐系统,并以电影推荐为例,使用PaddlePaddle训练了一个个性化推荐神经网络模型。
#推荐系统几乎涵盖了电商系统、社交网络、广告推荐、搜索引擎等领域的方方面面,而在图像处理、自然语言处理等领域已经发挥重要作用的深度学习技术,
#也将会在推荐系统领域大放异彩。

发布了225 篇原创文章 · 获赞 111 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/zimiao552147572/article/details/104084438