\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)

例子

  1. // Read input data set from a csv file val inputDS: DataSet[(Int, Int, Double)] = env.readCsvFile[(Int, Int, Double)](
  2. pathToTrainingFile)
  3. // Setup the ALS learner val als = ALS()
  4. .setIterations(10)
  5. .setNumFactors(10)
  6. .setBlocks(100)
  7. .setTemporaryPath("hdfs://tempPath")
  8. // Set the other parameters via a parameter map val parameters = ParameterMap()
  9. .add(ALS.Lambda, 0.9)
  10. .add(ALS.Seed, 42L)
  11. // Calculate the factorization als.fit(inputDS, parameters)
  12. // Read the testing data set from a csv file val testingDS: DataSet[(Int, Int)] = env.readCsvFile[(Int, Int)](pathToData)
  13. // Calculate the ratings according to the matrix factorization val predictedRatings = als.predict(testingDS)