A Novel Matrix Completion Approach for Rating Predictio
作者:禅与计算机程序设计艺术
1.简介
在互联网发展的早期阶段,人们主要通过搜索引擎、即时通信工具以及门户网站(如新浪微博、QQ空间)等方式进行信息的获取、传递与交流。随着信息量的爆炸式增长,传播速度的加快、社交网络的形成、网民之间的沟通渠道的不断拓展等原因,人们逐渐认识到,获取有效的信息并将其转化为价值创造力至关重要。而互联网上提供的各种服务功能也越来越丰富、高度个性化,例如购物、电影观看、论坛吐槽等。基于以上原因,用户对网络资源的需求已经超出了传统媒体形式所能及的范围。 然而,在这些服务中,最受欢迎的莫过于电子商务网站了。电子商务网站是一个完全线上模式的平台,用户可以在线下实体店购买商品、在线上购物车中放入商品、结算后付款、甚至还可以提供免邮政策,用户所产生的消费行为都被记录下来,用于形成对商品的品牌推广、商业分析、营销策略的优化等。因此,对于电子商务网站来说,对用户评分预测一直是非常重要的一个环节。因为评分预测直接影响着用户购买行为、以及用户对产品质量的满意程度等,具有十分重要的作用。
然而,由于电子商务网站用户数量庞大、商品种类多样、商品属性复杂等特点,评分预测面临着巨大的计算问题。传统的基于协同过滤的方法往往无法很好地适应这种规模的数据,而深度学习模型又耗费大量的训练时间,导致实施难度极高。因此,基于矩阵分解的评分预测方法被提出来,该方法通过学习用户-商品矩阵中的潜在因素来预测用户对商品的评分。
本文将详细介绍一种基于矩阵分解的评分预测方法——A Novel Matrix Completion Approach for Rating Prediction in Social Networks,该方法能够在一定程度上缓解评分预测任务中的计算复杂度。具体地,文章首先介绍了该方法的基本原理、核心概念以及相关术语;然后,依据具体的实现细节,介绍了该方法的具体操作步骤、数学公式以及代码实例;最后,结合实际应用场景,讨论了该方法未来的发展方向以及挑战。
2.核心概念
2.1 矩阵分解
矩阵分解(matrix decomposition)是指将一个矩阵分解为两个正交矩阵的乘积,同时保持原始矩阵的某些性质或特性,如秩、特征向量、主成分等。在评分预测领域,利用矩阵分解有助于对用户-商品矩阵进行更好的解释,进而达到推荐系统的目的。
2.2 欧拉正交化
欧拉正交化(Orthogonalization)是指从一个矩阵的任意向量出发,旋转这个向量使得它变成单位向量,再继续沿着其他向量旋转,直到所有向量都变成正交的。在评分预测领域,根据用户-商品矩阵的属性,如无偏性、稀疏性、半正定性等,可以使用欧拉正交化对其进行处理。
2.3 模型估计
模型估计(model estimation)是指按照已知数据集的参数估计模型参数的过程。在评分预测领域,利用模型估计可以得到用户-商品矩阵中隐含的潜在因素,从而进行评分预测。
3.评分预测方法
3.1 方法描述
在电子商务网站的评分预测问题中,用户-商品矩阵的每个元素代表了一个用户对某件商品的评分,其中0表示尚未评级的商品,而非0则表示用户对商品的真实评分。目前存在很多基于矩阵分解的评分预测方法,包括SVD、NMF、ALS等。本文将介绍的评分预测方法是一种新的基于用户-商品矩阵的评分预测方法——A Novel Matrix Completion Approach for Rating Prediction in Social Networks (ANCMARP),其基本思路如下:
-
使用欧拉正交化对用户-商品矩阵进行去噪,得到无偏的、稀疏的、半正定的矩阵W;
-
使用ALS算法估计矩阵W的因子,得到W=U*V^T作为模型的估计值;
-
在已有的评分预测模型基础上,加入用户的个人信息和商品的属性信息,形成更丰富的输入特征;
-
根据计算出的用户-商品矩阵的预测值,结合额外的特征,训练机器学习模型预测用户对未评级商品的评分。
在以上四步中,第3步使用额外的特征构造特征向量,其由两个部分组成:用户信息部分(包含个人特征、浏览历史、收藏夹等)和商品信息部分(包含商品名称、类别、价格、属性等)。接着,第4步使用机器学习模型对评分预测值进行训练。
为了验证该方法的有效性,作者从三个方面进行了实验研究。第一,作者比较了ANCMARP方法与SVD、NMF方法在三个数据集上的性能表现;第二,作者针对不同数据集的样本缺失情况,验证了ANCMARP方法在矩阵分解过程中对缺失数据进行补全;第三,作者应用ANCMARP方法预测不同评级体系下的用户对商品的评分,并与其它方法进行比较。结果表明,ANCMARP方法在三个数据集上的性能优于SVD、NMF方法,并且在样本缺失情况下也能取得较佳的效果。
4.具体操作步骤及数学公式解析
4.1 数据准备
- 用户-商品矩阵:先将用户对商品的评分保存到csv文件中,例如:
| User_ID | Item_ID | Rating | Time stamp |
|---|---|---|---|
| 1 | 1 | 5 | 2021-07-19 |
| 1 | 2 | 3 | 2021-07-19 |
| ... | ... | ... | ... |
用户信息:用户的信息可以从用户的注册信息中提取,也可以从用户的浏览记录、评论、购买记录等中获得。为了方便比较,需要将信息转换为数字形式。例如:
| User_ID | Gender | Age | Occupation | Purchased items |
|---|---|---|---|---|
| 1 | Male | Adult | Student | [item_id1, item_id2] |
| 2 | Female | Teenager | Professional | [item_id1, item_id3] |
| ... | ... | ... | ... | ... |
商品信息:商品信息可以从商品详情页中获取,也可以从商品的标签、描述等中获得。为了方便比较,需要将信息转换为数字形式。例如:
| Item_ID | Name | Category | Price | Attributes |
|---|---|---|---|---|
| 1 | Toy Car | Vehicle | $300 | [Expensive, Handmade] |
| 2 | Bird House | Household Goods | $1000 | [Trendy, Festive] |
| ... | ... | ... | ... | ... |
4.2 矩阵分解
矩阵W可以通过两种方式获得,即SVD和ALS。下面分别介绍两种方法:
SVD
SVD(singular value decomposition)是一种基于奇异值的分解法,用于求解一个矩阵W=UDV^T。其基本思想是将矩阵W分解为三个矩阵的乘积,即:
其中U是一个m\times k的矩阵,每列是一个正交基,且满足UU^T=\mathbb{I};D是一个对角阵,每项的值都是奇异值,奇异值按照大小排序;V^T是一个n\times k的矩阵,每行是一个正交基,且满足VV^T=\mathbb{I}。对角阵D中除第一个奇异值之外的其他值都为0。此处,k表示矩阵W的秩。当m>>n或者n<
那么如何对矩阵W进行欧拉正交化?假设有一列向量u,要使得Wu是单位向量,只需将u投影到\frac{W(W^{\top}W)}{(W^{\top}W)u}\hat{\mathbf{w}}上即可。即:
因此,可以采用以下步骤进行欧拉正交化:
-
对矩阵W进行SVD分解,得到U, D, V^T;
-
如果存在缺失元素,则用平均值代替;
-
对每个列向量u_i进行标准化:
-
将标准化后的列向量投影到低秩矩阵W上:
最终得到的向量就是经过欧拉正交化之后的Wu,即:
这里,(())表示矩阵的迹;\cdot表示矩阵相乘;\cdot表示向量点积;v_j表示V^T的第j列。最终得到的Wu_{norm}^{({})}称作标准化后的列向量。
ALS
ALS(alternating least squares)是一种迭代方法,用于求解矩阵W的最小二乘解。其基本思想是在每次迭代中更新某个矩阵元素,使得整个模型的参数估计不断逼近真实参数。ALS算法的求解步骤如下:
-
初始化两个矩阵:一个随机矩阵R,另一个初始化为0的矩阵P。
-
使用规则更新法,依次更新R和P:
a. 更新R:
b. 更新P:
其中,Q为由用户-商品矩阵的左半部分构成的矩阵,即:
c. 判断是否停止:如果max(|\Delta R|, |\Delta P|)
4.3 特征构造
特征构造(feature construction)是指根据用户的个人信息、商品的属性信息等进行特征向量的构建。特征向量由两部分组成:用户信息部分和商品信息部分。下面对这两部分进行详细介绍。
用户信息部分
用户信息部分由五个维度组成:Gender、Age、Occupation、Purchased Items、Browsing History。下面对这几个维度进行具体说明:
Gender
用户的性别属于分类变量,可转换为[Male,Female]的二元变量。
Age
用户的年龄属于连续变量,可转换为[Young,Adult,Senior]的三元变量。
Occupation
用户的职业属于分类变量,可转换为[Student,Professional,Worker]的三元变量。
Purchased Items
用户购买过的商品列表属于标量变量,可转换为由多个one-hot编码组成的向量。例如,若用户购买了商品1和商品2,则对应的向量为:[1,0,0,1,0]。
Browsing History
用户的浏览记录属于标量变量,可转换为由多个one-hot编码组成的向量。例如,若用户曾经查看过商品1和商品2,则对应的向量为:[1,0,0,1,0]。
商品信息部分
商品信息部分由六个维度组成:Name、Category、Price、Attributes、Item Popularity、Item Recency。下面对这几种维度进行具体说明:
Name
商品名属于标量变量,不可忽略。
Category
商品类目属于标量变量,可转换为由多个one-hot编码组成的向量。例如,若商品属于Vehicle和Household Goods两个类目,则对应的向量为:[1,1,0,...,0].
Price
商品价格属于连续变量,可转换为[Low,>High]的两元变量。
Attributes
商品属性属于标量变量,可转换为由多个one-hot编码组成的向量。例如,若商品拥有[Expensive,Handmade]两个属性,则对应的向量为:[1,1,0,...,0].
Item Popularity
商品流行度属于连续变量,可转换为一个标量变量。
Item Recency
商品最新的时间戳属于标量变量,可转换为一个标量变量。
4.4 评分预测模型
考虑到之前的评分预测模型都存在着计算复杂度的问题,因此采用神经网络模型作为评分预测模型。具体地,建立一个简单的神经网络结构,输入特征向量,输出用户对未评级商品的评分。
4.5 训练和测试
在训练阶段,利用训练集中的数据训练神经网络模型;在测试阶段,利用测试集中的数据验证模型的性能。
5.代码实例及注释说明
Python实现
import pandas as pd
from sklearn.decomposition import TruncatedSVD
from scipy.sparse import csr_matrix
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
def prepare_data():
ratings = pd.read_csv('ratings.csv')
user_info = pd.get_dummies(pd.concat([
ratings['user'],
pd.DataFrame({'age': ratings['user'].map(lambda x: int(x[-1]))}),
pd.DataFrame({'occupation': ratings['user'].map(lambda x:'student' if'student' in x else ('professional' if 'professor' in x or 'teacher' in x else 'worker'))})], axis=1))
item_info = pd.get_dummies(pd.concat([
ratings['item'],
pd.DataFrame({'price': ratings['item'].map(lambda x: '$low' if float(x[1:-1]) < 500 else '$high'),
'popularity': ratings['item'].map(len),
'recency': pd.to_datetime(ratings['timestamp']).dt.date - max(pd.to_datetime(ratings['timestamp']).dt.date)},
index=ratings['item']),
ratings['category']], axis=1)).astype('float32')
users = csr_matrix((np.ones_like(ratings['rating']), (ratings['user'], ratings['item'])))
return users, user_info, item_info
def matrix_completion(users, svd_components):
U, D, V = TruncatedSVD(n_components=svd_components).fit_transform(users).T[:svd_components,:].T #Perform singular value decomposition on the transposed user-item matrix to obtain U and V matrices of size svd_components*m and n*svd_components respectively
W = np.dot(U, V.T)
#Find missing values by computing mean rating per user/movie pair
row_mean = np.array([users.getrow(i).mean() for i in range(users.shape[0])])
col_mean = np.array([users[:,i].mean() for i in range(users.shape[1])])
mask = W == 0
W[mask] = np.where(mask, row_mean[mask.nonzero()[0]], col_mean[mask.nonzero()[1]])
#Normalize each column vector to be unit norm
W /= np.linalg.norm(W, axis=0)[None,:]
return W
def ANCMARP_prediction(W, user_info, item_info):
inputs = np.concatenate([user_info.values, item_info.values]).astype('float32')
model = Sequential()
model.add(Dense(1024, input_dim=inputs.shape[1], activation='relu', kernel_initializer='normal'))
model.add(Dropout(0.2))
model.add(Dense(1024, activation='relu', kernel_initializer='normal'))
model.add(Dropout(0.2))
model.add(Dense(1, activation='sigmoid', kernel_initializer='normal'))
optimizer = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
history = model.fit(inputs, W, epochs=100, batch_size=1024, verbose=0, validation_split=0.2)
predicted = model.predict(inputs)
return predicted
if __name__=='__main__':
print("Loading data...")
users, user_info, item_info = prepare_data()
print("Matrix completion using SVD with %d components..." % svd_components)
W = matrix_completion(users, svd_components)
print("Predicting ratings using ANCMARP approach...")
predictions = ANCMARP_prediction(W, user_info, item_info)
mse = ((predictions - W)**2).mean()
rmse = np.sqrt(mse)
print("MSE is %.4f" % mse)
print("RMSE is %.4f" % rmse)
代码解读
