\newcommand{\R}{\mathbb{R}} \newcommand{\E}{\mathbb{E}} \newcommand{\x}{\mathbf{x}} \newcommand{\y}{\mathbf{y}} \newcommand{\wv}{\mathbf{w}} \newcommand{\av}{\mathbf{\alpha}} \newcommand{\bv}{\mathbf{b}} \newcommand{\N}{\mathbb{N}} \newcommand{\id}{\mathbf{I}} \newcommand{\ind}{\mathbf{1}} \newcommand{\0}{\mathbf{0}} \newcommand{\unit}{\mathbf{e}} \newcommand{\one}{\mathbf{1}} \newcommand{\zero}{\mathbf{0}} \newcommand\rfrac[2]{^{#1}!/_{#2}} \newcommand{\norm}[1]{\left\lVert#1\right\rVert}
交替最小二乘
说明
交替最小二乘(ALS)算法将给定矩阵 $R$ 分解为两个因子 $U$ 和 $V$,使得 $R 约等于 U^TV$。未知行维度作为算法的参数给出,称为潜在因子。由于矩阵分解可以在推荐的上下文中使用,因此矩阵$U$和$V$可以分别称为user和item矩阵。user矩阵的第$i$t列用$u_i$表示,item矩阵的$i$t列用$v_i$表示。矩阵$R$可以称为评级矩阵。
为了找到user和item 矩阵,下面的问题就解决了:
其中$\lambda$是正则化因子,表示用户$i$已评级的项数,以及项$j$已评级的次数。这种避免过度拟合的正则化方案称为加权-$\lambda$-正则化。详细信息可以在Zhou et al.的著作中找到。
通过固定其中一个矩阵$U$或$V$,我们得到了一个可以直接求解的二次形式。修正问题的解保证了总成本函数的单调递减。通过将此步骤交替应用于矩阵$U$和$V$,我们可以迭代地改进矩阵分解。
矩阵$R$的稀疏表示形式是$(i,j, R)$的元组,其中$i$表示行索引,$j$表示列索引,$R$表示位置$(i,j)$的矩阵值。
操作
ALS是一个预测因子。因此,它支持“拟合”和“预测”操作。
拟合
ALS针对评分矩阵的稀疏表示进行训练:
fit: DataSet[(Int, Int, Double)] => Unit
预测
ALS预测每一行和列索引的元组的评级:
predict: DataSet[(Int, Int)] => DataSet[(Int, Int, Double)]
参数
交替最小二乘实现可由以下参数控制:
参数 | 描述 |
---|---|
NumFactors | 用于基础模型潜在因素的数量。它等价于计算user和item向量的维数。(默认值: 10) |
Lambda | 正则化因子。调整此值,以避免过度拟合或由于强泛化而导致性能低下。 (默认值: 1) |
Iterations | 最大迭代次数 (默认值: 10) |
Blocks | 将user和item矩阵分组到其中的块数。使用的块越少,冗余发送的数据就越少。然而,更大的块意味着必须存储在堆上的更新消息更大。如果算法因为OutOfMemoryException而失败,则尝试增加块的数量。(默认值:None) |
Seed | 用于生成算法初始item矩阵的随机种子。(默认值: 0) |
TemporaryPath | 存储中间结果的临时目录的路径。如果设置了这个值,那么算法将分为两个预处理步骤,ALS迭代和一个后处理步骤,后者计算最后一个ALS半步。预处理步骤计算给定评级矩阵的“OutBlockInformation”和“InBlockInformation”。各个步骤的结果存储在指定的目录中。通过将算法分割成多个较小的步骤,Flink不必在太多的操作之间分割可用内存。这允许系统处理更大的单个消息,并提高整体性能 (默认值:None) |
例子
// Read input data set from a csv file val inputDS: DataSet[(Int, Int, Double)] = env.readCsvFile[(Int, Int, Double)](4322d17c4d707ef2c7e55469e0ce227e)
// Setup the ALS learner val als = ALS()
.setIterations(10)
.setNumFactors(10)
.setBlocks(100)
.setTemporaryPath("hdfs://tempPath")
// Set the other parameters via a parameter map val parameters = ParameterMap()
.add(ALS.Lambda, 0.9)
.add(ALS.Seed, 42L)
// Calculate the factorization als.fit(inputDS, parameters)
// Read the testing data set from a csv file val testingDS: DataSet[(Int, Int)] = env.readCsvFile[(Int, Int)](docs_1.7_pathToData)
// Calculate the ratings according to the matrix factorization val predictedRatings = als.predict(testingDS)