我正在研究一种需要对大矩阵进行数学运算的算法。基本上,该算法涉及以下步骤:
输入:两个向量u和v的大小为n
对于每个向量,计算向量中元素之间的成对欧几里德距离。返回两个矩阵E_u和E_v
对于两个矩阵中的每个条目,应用函数f。返回两个矩阵M_u,M_v
求出M_u的特征值和特征向量。返回e_i,ev_i表示i = 0,...,n-1
计算每个特征向量的外积。返回矩阵O_i = e_i * transpose(e_i),i = 0,...,n-1
用e_i = e_i + delta_i调整每个特征值,其中delta_i =求和所有元素(O_i和M_v的元素乘积)/ 2 * mu,其中mu是参数
最终返回矩阵A =元素和(e_i * O_i)i = 0,...,n-1
我面临的问题主要是当n很大(15000或更多)时的内存,因为这里的所有矩阵都是密集矩阵。我目前实现这一目标的方式可能不是最好的,而且部分有效。
我使用RowMatrix作为M_u并使用SVD进行特征分解。
得到的SVD的U因子是一个行矩阵,其列是ev_i' s,因此我必须手动转置它,使其行变为ev_i。得到的e矢量是特征值e_i。
由于先前尝试将每行ev_i直接映射到O_i因内存不足而失败,我现在正在做
R = U.map{
case(i,ev_i) => {
(i, ev_i.toArray.zipWithIndex)
}
}//add index for each element in a vector
.flatMapValues(x=>x)}
.join(U)//eigen vectors column is appended
.map{case(eigenVecId, ((vecElement,elementId), eigenVec))=>(elementId, (eigenVecId, vecElement*eigenVec))}
为了在上面的步骤5中计算调整的e_i,将M_v存储为元组的rdd(i,denseVector)。然后
deltaRdd = R.join(M_v)
.map{
case(j,((i,row_j_of_O_i),row_j_of_M_v))=>
(i,row_j_of_O_i.t*DenseVector(row_j_of_M_v.toArray)/(2*mu))
}.reduceByKey(_+_)
最后,为了计算A,再次由于内存问题,我必须先从不同的rdds加入行,然后按键减少。具体来说,
R_rearranged = R.map{case(j, (i, row_j_of_O_i))=>(i,(j,row_j_of_O_i))}
termsForA = R_rearranged.join(deltaRdd)
A = termsForA.map{
case(i,(j,row_j_of_O_i), delta_i)) => (j, (delta_i + e(i))*row_j_of_O_i)
}
.reduceByKey(_+_)
上述实现适用于termsForA的步骤,这意味着如果我在termsForA上执行一个actionForA.take(1).foreach(println),它就成功了。但是如果我在A上执行一个动作,比如A.count(),那么驱动程序就会出现OOM错误。
我尝试调整sparks配置以增加驱动程序内存以及并行级别,但都失败了。
答案 0 :(得分:0)
使用IndexedRowMatrix而不是RowMatrix,它将有助于转换和转置。 假设您的IndexedRowMatrix是Irm
svd = Irm.computeSVD(k, True)
U = svd.U
U = U.toCoordinateMatrix().transpose().toIndexedRowMatrix()
您可以将Irm转换为BlockMatrix,以便与另一个分布式BlockMatrix进行相乘。
答案 1 :(得分:0)
我想在某些时候Spark决定不需要对执行程序进行操作,并且在驱动程序上做所有的工作。实际上,termsForA会像count一样失败。不知怎的,我通过广播deltaRdd和e来实现它。