共计 16483 个字符,预计需要花费 42 分钟才能阅读完成。
💡 作者:韩信子 @ShowMeAI
📘 深度学习实战系列:https://www.showmeai.tech/tutorials/42
📘 TensorFlow 实战系列:https://www.showmeai.tech/tutorials/43
📘 本文地址:https://www.showmeai.tech/article-detail/310
📢 申明:版权所有,转载请分割平台与作者并注明出处
📢 珍藏 ShowMeAI 查看更多精彩内容
举荐零碎 是预测用户对多种产品的偏好的模型,互联网时代,它在各种畛域大放异彩,从视频音乐多媒体举荐、到电商购物举荐、社交关系举荐,无处不在地晋升用户体验。
最常见的举荐零碎办法包含:基于产品特色(基于内容)、用户相似性(协同过滤等近邻算法)、个人信息(基于常识)。当然,随着神经网络的日益遍及,很多公司的业务中应用到的举荐算法曾经是上述所有办法联合的混合举荐零碎。
在本篇内容中,ShowMeAI 将给大家一一道来,从传统举荐零碎算法到前沿的旧式举荐零碎,解说原理并手把手教大家如何用代码实现。
本篇内容应用到的 🏆MovieLens 电影举荐数据集,大家能够在 ShowMeAI 的百度网盘地址下载。
🏆 实战数据集下载(百度网盘):公众号『ShowMeAI 钻研核心』回复『实战 』,或者点击 这里 获取本文 [[19] 基于 TensorFlow 搭建混合神经网络举荐零碎](https://www.showmeai.tech/art…)『MovieLens 电影举荐数据集』
⭐ ShowMeAI 官网 GitHub:https://github.com/ShowMeAI-Hub
数据集蕴含观众对电影的评分后果,有不同规模的数据集大小,咱们本篇内容中的代码通用,大家能够依据本人的计算资源状况抉择适合的数据集。
- 小数据集为 600 名观众对 9000 部电影的 10w 个打分,也包含电影标签特色。
- 大数据集为 280000 名观众对 110w 部电影的 2700w 评分。
本文波及的内容板块如下:
- 根本设置 & 数据预处理
- 冷启动问题 & 解决
- 基于内容的办法(tensorflow 和 numpy 实现)
- 传统协同过滤和神经协同过滤模型(tensorflow/keras 实现)
- 混合模型模型(上下文感知,tensorflow/keras 实现)
💡 根本设置 & 数据预处理
📌 工具库导入
首先,咱们导入所需工具库:
# 数据读取与解决
import pandas as pd
import numpy as np
import re
from datetime import datetime
# 绘图
import matplotlib.pyplot as plt
import seaborn as sns
# 评估与预处理
from sklearn import metrics, preprocessing
# 深度学习
from tensorflow.keras import models, layers, utils #(2.6.0)
📌 读取数据
接下来咱们读取数据。
dtf_products = pd.read_csv("movie.csv")
movie 电影文件中,每一行代表一部电影,右侧的两列蕴含其特色(题目与题材)。让咱们读取用户数据:
dtf_users = pd.read_csv("ratings.csv").head(10000)
这个 ratings 表的每一行都是观众电影对,并显示观众对电影的评分(即 指标变量)。当然啦,并不是每个观众都看过所有的电影,所以咱们能够给他们举荐没有看过的电影。这里的一种思路就是预估观众对于没有看过的电影的评分,再基于评分高下进行举荐。
📌 数据分析 & 特色工程
在理论开掘与建模之前,咱们先做一些 数据清理 和特色工程 的工作,让数据更洁净和适宜建模应用。
数据分析局部波及的工具库,大家能够参考 ShowMeAI 制作的工具库速查表和教程进行学习和疾速应用。
📘数据迷信工具库速查表 | Pandas 速查表
📘 图解数据分析:从入门到精通系列教程
# 电影数据处理
# 题材字段缺失解决
dtf_products = dtf_products[~dtf_products["genres"].isna()]
dtf_products["product"] = range(0,len(dtf_products))
# 电影名称解决
dtf_products["name"] = dtf_products["title"].apply(lambda x: re.sub("[([].*?[)]]", "", x).strip())
# 日期
dtf_products["date"] = dtf_products["title"].apply(lambda x: int(x.split("(")[-1].replace(")","").strip())
if "(" in x else np.nan)
dtf_products["date"] = dtf_products["date"].fillna(9999)
# 判断老电影
dtf_products["old"] = dtf_products["date"].apply(lambda x: 1 if x < 2000 else 0)
# 观众 / 用户数据处理
dtf_users["user"] = dtf_users["userId"].apply(lambda x: x-1)
dtf_users["timestamp"] = dtf_users["timestamp"].apply(lambda x: datetime.fromtimestamp(x))
# 白天时段
dtf_users["daytime"] = dtf_users["timestamp"].apply(lambda x: 1 if 6<int(x.strftime("%H"))<20 else 0)
# 周末
dtf_users["weekend"] = dtf_users["timestamp"].apply(lambda x: 1 if x.weekday() in [5,6] else 0)
# 电影与用户表合并
dtf_users = dtf_users.merge(dtf_products[["movieId","product"]], how="left")
dtf_users = dtf_users.rename(columns={"rating":"y"})
# 荡涤数据
dtf_products = dtf_products[["product","name","old","genres"]].set_index("product")
dtf_users = dtf_users[["user","product","daytime","weekend","y"]]
上述过程中有一些很贴合场景的特色工程和数据生成工作,比方咱们从工夫戳中生成了 2 个新的字段:『是否白天』和『是否周末』。
dtf_context = dtf_users[["user","product","daytime","weekend"]]
下一步咱们构建 Moives-Features 矩阵:
# 电影题材候选统计
tags = [i.split("|") for i in dtf_products["genres"].unique()]
columns = list(set([i for lst in tags for i in lst]))
columns.remove('(no genres listed)')
# 题材可能有多个,切分进去作为标签
for col in columns:
dtf_products[col] = dtf_products["genres"].apply(lambda x: 1 if col in x else 0)
咱们失去的这个『电影 - 题材』矩阵是稠密的(很好了解,个别一部电影只归属于无限的几个题材)。咱们做一点可视化以更好地理解状况,代码如下:
# 构建热力求并可视化
fig, ax = plt.subplots(figsize=(20,5))
sns.heatmap(dtf_products==0, vmin=0, vmax=1, cbar=False, ax=ax).set_title("Products x Features")
plt.show()
上面是咱们的『观众 / 用户 - 电影』评分矩阵,咱们发现它更为稠密(每位用户理论只看过几部电影,但总电影量很大)
tmp = dtf_users.copy()
dtf_users = tmp.pivot_table(index="user", columns="product", values="y")
missing_cols = list(set(dtf_products.index) - set(dtf_users.columns))
for col in missing_cols:
dtf_users[col] = np.nan
dtf_users = dtf_users[sorted(dtf_users.columns)]
同样的热力求后果如下:
在特色工程局部,咱们须要做一些典型的数据预处理过程,比方咱们会在后续用到神经网络模型,而这种计算型模型,咱们对数据做幅度缩放是十分必要的。
对于机器学习特色工程,大家能够参考 ShowMeAI 整顿的特色工程最全解读教程。
📘机器学习实战 | 机器学习特色工程最全解读
# 数据幅度缩放
dtf_users = pd.DataFrame(preprocessing.MinMaxScaler(feature_range=(0.5,1)).fit_transform(dtf_users.values),
columns=dtf_users.columns, index=dtf_users.index)
📌 数据切分
简略解决完数据之后,就像任何典型的机器学习工作一样,咱们须要对数据进行划分,在这里划分为 训练集 和测试集 。如果联合上述『 用户 - 电影』矩阵,咱们会做相似下图的垂直切分,这样训练集和测试集都会尽量笼罩所有用户:
# 数据切分
split = int(0.8*dtf_users.shape[1])
dtf_train = dtf_users.loc[:, :split-1]
dtf_test = dtf_users.loc[:, split:]
💡 冷启动问题 & 解决
📌 冷启动问题
设想一下,相似于『抖音』这样的利用,对于新用户提供举荐,其实是不太精确的(只能基于一些策略,如热度排行等进行举荐),咱们对用户的信息积攒太少,用户画像的工作无奈进行。这就是任何一个举荐零碎产品都会遇到的 冷启动问题(即因为没有足够的历史数据,零碎无奈在用户和产品之间建设任何关联)。
📌 冷启动解决办法
针对冷启动问题,有一些典型的解决形式,例如 基于常识的办法:在首次进入 APP 时询问用户的偏好,构建根本信息与常识,再基于常识进行举荐(比方不同『年龄段』和『性别』青睐的媒体产品等)。
另外一种解决办法是 基于内容的办法。即基于产品的属性(比方咱们以后的场景下,电影的题材、演员、主题等)进行匹配举荐。
💡 基于内容的举荐办法
📌 核心思想
咱们来介绍一下 基于内容的办法。
这个办法是基于产品属性进行关联和举荐的,例如,如果『用户 A 喜爱产品 1』,并且『产品 2 与产品 1 从属性上看类似』,那么『用户 A 可能也会喜爱产品 2』。简略地说,这个想法是『用户实际上对产品的性能 / 属性而不是产品自身进行评分』。
换句话说,如果我喜爱与音乐和艺术相干的产品,那是因为我喜爱那些性能 / 属性(音乐和艺术)。咱们能够基于这个信息做举荐。
📌 代码实现
咱们随机从数据中筛选一个『观众 / 用户』作为咱们的新用户的示例,该订阅者当初曾经应用了足够多的产品,让咱们创立训练和测试向量。
# 选一个 user
i = 1
train = dtf_train.iloc[i].to_frame(name="y")
test = dtf_test.iloc[i].to_frame(name="y")
# 把所有测试集的电影评分清空后拼接
tmp = test.copy()
tmp["y"] = np.nan
train = train.append(tmp)
上面咱们估测『观众 / 用户』对每个特色的权重,回到咱们后面整顿完的 User-Products 矩阵和 Products-Features 矩阵。
# 数据维度
usr = train[["y"]].fillna(0).values.T
prd = dtf_products.drop(["name","genres"],axis=1).values
print("Users", usr.shape, "x Products", prd.shape)
咱们把这 2 个矩阵相乘,咱们取得了一个『用户 - 特色 』矩阵,它蕴含这个用户对每个特色的预计权重。咱们进而把这些权重应从新利用于『 产品 - 特色』矩阵就能够取得测试集后果。
# usr_ft(users,fatures) = usr(users,products) x prd(products,features)
usr_ft = np.dot(usr, prd)
# 归一化
weights = usr_ft / usr_ft.sum()
# 预估打分 rating(users,products) = weights(users,fatures) x prd.T(features,products)
pred = np.dot(weights, prd.T)
test = test.merge(pd.DataFrame(pred[0], columns=["yhat"]), how="left", left_index=True, right_index=True).reset_index()
test = test[~test["y"].isna()]
test
下面是一个十分非常简单的思路,咱们用 numpy 对它进行了实现。其实这个过程也能够在原始数据张量上进行:
# 基于 tensorflow 更高效的实现
import tensorflow as tf
# usr_ft(users,fatures) = usr(users,products) x prd(products,features)
usr_ft = tf.matmul(usr, prd)
# normalize
weights = usr_ft / tf.reduce_sum(usr_ft, axis=1, keepdims=True)
# rating(users,products) = weights(users,fatures) x prd.T(features,products)
pred = tf.matmul(weights, prd.T)
仅仅实现预估步骤还不够,咱们须要对预测举荐进行无效 评估 ,怎么做呢,在以后这个举荐场景下,咱们能够应用 准确性 和均匀倒数排名(MRR,一种针对排序成果的统计度量)。
# 评估指标
def mean_reciprocal_rank(y_test, predicted):
score = []
for product in y_test:
mrr = 1 / (list(predicted).index(product) + 1) if product
in predicted else 0
score.append(mrr)
return np.mean(score)
有时候,在全副排序后果列表上评估,成果个别且计算量太大,咱们能够抉择标准答案的 top k 进行评估(上面代码中 k 取值为 5)。
print("--- user", i, "---")
top = 5
y_test = test.sort_values("y", ascending=False)["product"].values[:top]
print("y_test:", y_test)
predicted = test.sort_values("yhat", ascending=False)["product"].values[:top]
print("predicted:", predicted)
true_positive = len(list(set(y_test) & set(predicted)))
print("true positive:", true_positive, "("+str(round(true_positive/top*100,1))+"%)")
print("accuracy:", str(round(metrics.accuracy_score(y_test,predicted)*100,1))+"%")
print("mrr:", mean_reciprocal_rank(y_test, predicted))
上图显示在 user1 上,咱们预估后果和 top5 实在后果,有 4 个后果是重叠的。(不过因为咱们预估后果的序并不齐全和标准答案一样,所以指标上看 accuracy 和 mrr 会低一点)
# 查看预估后果细节
test.merge(dtf_products[["name","old","genres"]], left_on="product",
right_index=True
).sort_values("yhat", ascending=False)
💡 协同过滤举荐算法
📌 核心思想
协同过滤 是一类典型的『近邻』举荐算法,基于用户和用户的相似性,或者产品和产品的相似性来构建举荐。比方 user-based collaborative filtering(基于用户的协同过滤)中,咱们认为『用户 A 喜爱产品 1』,而基于 用户行为 计算断定『用户 B 和用户 A 类似』,那么『用户 B 可能也会喜爱产品 1』。
留神到协同过滤算法中,很重要的步骤是咱们须要基于用户历史的行为来构建类似度度量(user-user 或 item-item 类似度)。
协同过滤和下面提到的基于内容的举荐算法不同,咱们不须要产品属性来建模,而是基于大量用户的历史行为来计算和构建类似度量(例如在本例中,咱们能够基于不同的观众历史上在一批电影上的评分类似度来构建)。
📌 根底协同过滤算法
协同过滤是 『基于用户行为』 的举荐算法,咱们会『通过群体的行为来找到某种相似性』(用户之间的相似性或者物品之间的相似性),通过相似性来为用户做决策和举荐。协同过滤细分一下,有以下基于邻域的、基于隐语义模型 2 大类办法。
基于近邻的协同过滤
基于近邻的协同过滤蕴含 user-based cf(基于用户的协同过滤)和 item-based cf(基于物品的协同过滤)两种形式,核心思想如下图所示:
外围步骤为以下 3 步:
① 依据历史数据收集用户偏好(比方本例中的打分,比方)。
② 找到类似的用户(基于用户)或物品(基于物品)。
③ 基于相似性计算和举荐。
其中的 similarity 类似度计算局部,能够基于一些度量准则来实现,比方最罕用到的类似度度量是余弦类似度:
$$
\text{cosine similarity}=S_{C}(A, B):=\cos (\theta)=\frac{A \cdot \mathbf{B}}{\|\mathbf{A}\|\|\mathbf{B}\|}=\frac{\sum_{i=1}^{n} A_{i} B_{i}}{\sqrt{\sum_{i=1}^{n} A_{i}^{2} \sqrt{\sum_{i=1}^{n} B_{i}^{2}}}}
$$
在本例中 A 和 B 能够是两个用户的独特电影对应的打分向量,或者两部电影的独特打分用户的打分向量,也就是打分矩阵中的两行或者两列。
当然,咱们也能够基于聚类等其余办法来发现类似用户和物品。
基于隐语义模型的协同过滤
协同过滤的另外一种实现,是基于矩阵合成的办法,在本例中通过这个办法能够预测用户对某个产品的评估,矩阵合成办法将大的『用户 - 物品 』 打分矩阵分成两个较小的因子矩阵,别离是『用户 - 因子』矩阵和『产品 - 因子』矩阵,再基于这两个矩阵对于未打分的『用户 - 物品』对打分。
具体来说,每个因子可能代表某一个属性维度的水平,如下如,咱们如果确定 2 个属性『年龄段』『题材娱乐性』,那咱们能够基于打分矩阵对这两个维度进行合成。
代码实现
在 Python 中,要实现上述提到的 2 类协同过滤算法,最不便的工具库之一是 📘scikit-surprise(从名字大家能够看出,它借助了 scikit-learn 的一些底层算法来实现下层的协同过滤)。它蕴含了上述提到的基于近邻的协同过滤和基于隐语义模型的协同过滤。
不过矩阵实现办法也是各种深度学习模型所善于的,咱们在这里应用 tensorflow/keras 来做一点实现。
咱们先筹备好『用户 - 物品』数据(本例中的用户是观众,物品是电影):
train = dtf_train.stack(dropna=True).reset_index().rename(columns={0:"y"})
train.head()
咱们会利用神经网络的 嵌入层 来创立『用户 - 因子』和『产品 - 因子』矩阵,这里特地适宜用神经网络的 embedding 层来实现映射矩阵构建,咱们为用户和产品别离构建 embedding 矩阵。Embedding 矩阵的维度就是咱们这个中央设定的因子的个数。上面咱们应用 tensorflow 来实现这个过程。
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1]
# 用户 Users 维度(1,embedding_size)
xusers_in = layers.Input(name="xusers_in", shape=(1,))
xusers_emb = layers.Embedding(name="xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
xusers = layers.Reshape(name='xusers', target_shape=(embeddings_size,))(xusers_emb)
# 产品 Products 维度(1,embedding_size)
xproducts_in = layers.Input(name="xproducts_in", shape=(1,))
xproducts_emb = layers.Embedding(name="xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
xproducts = layers.Reshape(name='xproducts', target_shape=(embeddings_size,))(xproducts_emb)
# 矩阵乘法,即咱们咱们下面提到的因子矩阵相乘 维度(1)
xx = layers.Dot(name='xx', normalize=True, axes=1)([xusers, xproducts])
# 预测得分 维度(1)
y_out = layers.Dense(name="y_out", units=1, activation='linear')(xx)
# 编译
model = models.Model(inputs=[xusers_in,xproducts_in], outputs=y_out, name="CollaborativeFiltering")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])
在本例中呢,因为咱们最终是对电影的评分去做预测,所以咱们把这个问题视作一个回归的问题应用模型来解决,咱们会应用均匀绝对误差作为最终的损失函数。当然咱们理论在解决举荐这个问题的时候,可能并不需要失去准确的得分,而是心愿基于这些得分去实现一个排序和最终的举荐。
咱们把构建进去的模型示意图和中间层的维度打印一下,不便大家查看,如下
utils.plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)
接下来咱们就能够在咱们的数据下来训练、评估和测试咱们的模型了。
# 训练
training = model.fit(x=[train["user"], train["product"]], y=train["y"], epochs=100, batch_size=128, shuffle=True, verbose=0, validation_split=0.3)
model = training.model
# 测试
test["yhat"] = model.predict([test["user"], test["product"]])
test
在这个模型最终的预估后果上,大家能够看到模型曾经可能对没有见过的新的电影进行打分的预测了。咱们能够基于这个得分进行排序和实现最终的举荐。以咱们第 1 个用户为例,咱们能够看到对于他进行基于预测得分的举荐,评估后果如下。
📌 神经协同过滤算法
模型介绍
大家在后面看到的协同过滤模型,学习能力绝对比拟弱,对于咱们的信息做的是初步的开掘,而古代的很多新的举荐零碎实际上都应用了深度学习。也能够把深度学习和协同过滤联合,例如 Neural Collaborative Filtering (2017) 联合了来自神经网络的非线性和矩阵合成。该模型旨在充分利用嵌入空间,不仅将其用于传统的协同过滤,还用于齐全连贯的深度神经网络,新增加的模型组成部分会捕捉矩阵合成可能脱漏的模式和特色,如下图所示:
代码实现
上面咱们来实现一下这个模型的一个繁难版本:
# 用户与产品的 embedding 维度,相当于协同过滤中的因子数
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1]
# 输出层
xusers_in = layers.Input(name="xusers_in", shape=(1,))
xproducts_in = layers.Input(name="xproducts_in", shape=(1,))
# A) 模型左侧:Matrix Factorization 矩阵合成
# embeddings 与 reshape
cf_xusers_emb = layers.Embedding(name="cf_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
cf_xusers = layers.Reshape(name='cf_xusers', target_shape=(embeddings_size,))(cf_xusers_emb)
# embeddings 与 reshape
cf_xproducts_emb = layers.Embedding(name="cf_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
cf_xproducts = layers.Reshape(name='cf_xproducts', target_shape=(embeddings_size,))(cf_xproducts_emb)
# 产品 product
cf_xx = layers.Dot(name='cf_xx', normalize=True, axes=1)([cf_xusers, cf_xproducts])
# B) 模型右侧:Neural Network 神经网络
# embeddings 与 reshape
nn_xusers_emb = layers.Embedding(name="nn_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
nn_xusers = layers.Reshape(name='nn_xusers', target_shape=(embeddings_size,))(nn_xusers_emb)
# embeddings 与 reshape
nn_xproducts_emb = layers.Embedding(name="nn_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
nn_xproducts = layers.Reshape(name='nn_xproducts', target_shape=(embeddings_size,))(nn_xproducts_emb)
# 拼接与全连贯解决
nn_xx = layers.Concatenate()([nn_xusers, nn_xproducts])
nn_xx = layers.Dense(name="nn_xx", units=int(embeddings_size/2), activation='relu')(nn_xx)
# 合并 A 和 B
y_out = layers.Concatenate()([cf_xx, nn_xx])
y_out = layers.Dense(name="y_out", units=1, activation='linear')(y_out)
# 编译
model = models.Model(inputs=[xusers_in,xproducts_in], outputs=y_out, name="Neural_CollaborativeFiltering")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])
咱们也同样能够对模型进行构造的绘制,如下所示。
utils.plot_model(model, to_file=’model.png’, show_shapes=True, show_layer_names=True)
咱们再基于当初这个神经网络的模型,去对咱们最终的电影打评分进行预测,并且依据预测的得分进行排序和举荐,那评估的后果如下所示。
💡 混合网络模型
📌 模型介绍
咱们在后面展现了如何联合咱们的用户和产品(在以后场景下是电影举荐的场景)的打分数据来构建协同过滤算法和根底的神经网络算法,实现最终打分的预测和举荐,但理论咱们的数据当中有着更丰盛的信息。
- 用户行为 : 以后场景下是电影的打分,它是一种显式用户反馈;有些场景下咱们会应用隐式的用户反馈,比如说用户的点击或者深度浏览和完播等行为。
- 产品信息 : 产品的标签和形容(这里的电影题材、题目等),次要用于基于内容的办法。
- 用户信息 : 人口统计学信息(即性别和年龄)或行为(即偏好、屏幕上的均匀工夫、最频繁的应用工夫),次要用于基于常识的举荐。
- 上下文 : 对于评分状况的附加信息(如何时、何地、搜寻历史),通常也蕴含在基于常识的举荐中。
古代举荐零碎为了更精准的给大家进行举荐,会尽量的联合所有咱们可能收集到的信息。大家日常应用的抖音或者 B 站,它们在给大家举荐视频类的内容的时候,会更加全面的应用咱们下面提及到的所有的信息,甚至蕴含 APP 能采集到的更丰盛的信息。
📌 代码实现
上面联合本例,咱们也把这些更丰盛的信息(次要是上下文信息)联合到网络中来构建一个混合模型,以实现更精准的预估和举荐。
# 根底特色
features = dtf_products.drop(["genres","name"], axis=1).columns
print(features)
# 上下文特色(时间段、工作日、周末等)context = dtf_context.drop(["user","product"], axis=1).columns
print(context)
根底特色和上下文特色如下
接下来咱们把这些额定信息增加到 训练集 中:
train = dtf_train.stack(dropna=True).reset_index().rename(columns={0:"y"})
# 增加特色
train = train.merge(dtf_products[features], how="left", left_on="product", right_index=True)
# 增加上下文信息
train = train.merge(dtf_context, how="left")
留神咱们这里并没有对测试集间接去执行雷同的操作,因为理论的生产环境当中,咱们可能没有方法提前的去获知一些上下文的信息,所以咱们会为上下文的信息去插入一个动态的值去做填充。
当然咱们在理论预估的时候,是能够比拟精确的去做填充的。比方咱们在星期一早晨为咱们平台的用户进行预测,则上下文变量应为
daytime=0
和week=0
。
上面咱们来构建 上下文感知混合模型,神经网络的构造非常灵活,咱们能够在网络中增加任何咱们想要补充的信息,咱们把上下文等额定信息也通过网络组件的模式补充到神经协同过滤网络结构中,如下所示。
这个过程就相当于在方才的神经协同过滤模型根底上,增加一些新的组块。下列实现代码看起来比拟宏大,但实际上它只是在之前的实现根底上增加了一些行而已:
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1]
feat = len(features)
ctx = len(context)
################## 神经协同过滤 ########################
# 输出层
xusers_in = layers.Input(name="xusers_in", shape=(1,))
xproducts_in = layers.Input(name="xproducts_in", shape=(1,))
# A) 模型左侧:Matrix Factorization 矩阵合成
# embeddings 与 reshape
cf_xusers_emb = layers.Embedding(name="cf_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
cf_xusers = layers.Reshape(name='cf_xusers', target_shape=(embeddings_size,))(cf_xusers_emb)
# embeddings 与 reshape
cf_xproducts_emb = layers.Embedding(name="cf_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
cf_xproducts = layers.Reshape(name='cf_xproducts', target_shape=(embeddings_size,))(cf_xproducts_emb)
# 产品 product
cf_xx = layers.Dot(name='cf_xx', normalize=True, axes=1)([cf_xusers, cf_xproducts])
# B) 模型右侧:Neural Network 神经网络
# embeddings 与 reshape
nn_xusers_emb = layers.Embedding(name="nn_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
nn_xusers = layers.Reshape(name='nn_xusers', target_shape=(embeddings_size,))(nn_xusers_emb)
# embeddings 与 reshape
nn_xproducts_emb = layers.Embedding(name="nn_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
nn_xproducts = layers.Reshape(name='nn_xproducts', target_shape=(embeddings_size,))(nn_xproducts_emb)
# 拼接与全连贯解决
nn_xx = layers.Concatenate()([nn_xusers, nn_xproducts])
nn_xx = layers.Dense(name="nn_xx", units=int(embeddings_size/2), activation='relu')(nn_xx)
######################## 根底信息 ############################
# 电影特色
features_in = layers.Input(name="features_in", shape=(feat,))
features_x = layers.Dense(name="features_x", units=feat, activation='relu')(features_in)
####################### 上下文特色 ###########################
# 上下文特色
contexts_in = layers.Input(name="contexts_in", shape=(ctx,))
context_x = layers.Dense(name="context_x", units=ctx, activation='relu')(contexts_in)
######################### 输入 ##################################
# 合并所有信息
y_out = layers.Concatenate()([cf_xx, nn_xx, features_x, context_x])
y_out = layers.Dense(name="y_out", units=1, activation='linear')(y_out)
# 编译
model = models.Model(inputs=[xusers_in,xproducts_in, features_in, contexts_in], outputs=y_out, name="Hybrid_Model")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])
咱们也绘制一下整个模型的构造
utils.plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)
混合模型的输出数据源更多,理论训练时咱们要把这些数据都送入模型:
# 训练
training = model.fit(x=[train["user"], train["product"], train[features], train[context]], y=train["y"],
epochs=100, batch_size=128, shuffle=True, verbose=0, validation_split=0.3)
model = training.model
# 预测
test["yhat"] = model.predict([test["user"], test["product"], test[features], test[context]])
最终基于混合模型的预测得分进行举荐,评估指标如下:
咱们独自看 user1 这个用户,混合模型在多种信息的撑持下,取得了最高的准确度。
💡 论断
本文解说了举荐零碎的基础知识,以及不同的举荐零碎搭建办法,咱们对各种办法进行了实现和成果改良,包含基于内容的举荐实现,基于协同过滤的举荐实现,咱们把更丰盛的产品信息和上下文信息退出网络实现了混合网络模型。大家能够参考实现流程利用在本人的场景中。
参考资料
- 📘 数据迷信工具库速查表 | Pandas 速查表:https://www.showmeai.tech/article-detail/101
- 📘 图解数据分析:从入门到精通系列教程:https://www.showmeai.tech/tutorials/33
- 📘 机器学习实战 | 机器学习特色工程最全解读:https://www.showmeai.tech/article-detail/208
- 📘 scikit-surprise:https://pypi.org/project/scikit-surprise/