我对使用Scala感兴趣,因为它似乎是一种并行化操作的好方法。我需要设计一个利用向量乘法的机器学习算法(和许多一样)。我知道如何做算法,但我想做的是来自HashMaps的稀疏向量实现。几乎所有的向量都存储为HashMaps [Int,Double],其中向量中给定double的索引是键的整数。
使用Pythonish伪代码, < 7,6,5,4> ==> {1:7,2:6,3:5,4:4}
我想使用fold,reduce,map等来定义点积函数,但我不想使用foldLeft,reduceLeft ......因为我希望这可能是可以并行的因为我的向量最多可以达到6000多个维度,对于点数产品,订单无关紧要。
我已经阅读了很多foldLeft和reduceLeft的例子,但我还没有找到如何使用HashMap.fold或HashMap.reduce。
我理解函数式编程,但我不理解Scala中的错误消息。这是我想要或多或少的模板。
object NGramAnalysis {
def main(args: Array[String]) {
val mapped = HashMap(1->1.2, 5->2.4)
println(mapped.fold( .... What goes here ... )
}
}
结论 我想要一个使用HashMap.fold NOT foldLeft 的实例,HashMap.reduce也是如此
提前谢谢你。我一直在苦苦挣扎。
答案 0 :(得分:3)
首先,fold
中reduce
和fold
之间的差异采用了另一个用作初始值的参数,而reduce
采用了第一个元素集合作为这样的初始值,如果集合为空,则抛出异常。因此,fold
比reduce
更为通用,因此从现在开始我将这两个函数称为fold
。
要让fold
正常工作,集合中的元素必须形成半群,也就是说,应该有一个二进制操作,它也必须是 associative ,即,应保留以下身份:(a `op` b) `op` c == a `op` (b `op` c)
。需要关联性,因为fold
未指定操作应用程序顺序,这在并行上下文中尤为重要。此操作用于执行折叠:
a1 `op` a2 `op` a3 `op` ... `op` an
如果reduce
并行运行,它可以拆分集合并减少一个线程的前半部分和另一个线程中的后半部分;然后他们的结果使用相同的操作加入。仅当操作是关联的时,这才能正常工作。
正如我已经说过的,fold
方法有两个参数:初始值和[associative]二元运算符。例如,要并行连接字符串列表,您可以这样做:
val strings = Seq("a", "b", "c", "d", ...)
strings.par.fold("")(_ ++ _) // or strings.par.reduce(_ ++ _) if you know that strings is non-empty
因此,要实现点积,您需要考虑要折叠/缩减的集合以及执行此缩减的二元运算符。
这是两个集合的点积的简单实现:
(c1 zip c2).par.map {
case (e1, e2) => e1 * e2
}.reduce(_ + _)
也就是说,我们将这些集合压缩在一起,使用*
运算符成对地将它们的元素相乘,然后使用+
运算符减少结果。当然,必须在*
和+
的元素上定义c1
和c2
。
但是,HashMap
未排序,因此其迭代顺序未定义。无法保证zip
将使用相同的键加入元素,这使得上述点积的概念不正确。你需要做这样的事情:
c1.par.map {
case (k, v) => v * c2(k)
}.reduce(_ + _)
这里我们不会压缩集合,而是使用第一张地图中的所有键在第二张地图中执行查找。
答案 1 :(得分:0)
我只想添加一个简单的示例实现,因为@Vladimir Matveev介绍了背景。
此处向量支持HashMap
。 apply factory方法确保所有未指定的索引都有一个默认值0
。
这个想法非常简单。我们合并了键集,以便在任一映射中指定所有键。然后我们将相应的值相乘并求它。由于我们有非现有密钥的默认值,因此工作正常。
class SparseVector private (val entries: Map[Int, Double]) {
def **(vec: SparseVector) =
(entries.keySet ++ vec.entries.keySet).par
.map(index => entries(index) * vec.entries(index)).sum
//alternative suggested by @wingedsubmariner
def **(vec: SparseVector) =
(entries.keySet ++ vec.entries.keySet).par
.aggregate(0.0)((sum, index) => sum + entries(index) * vec.entries(index), (_ + _))
}
object SparseVector {
def apply(entries: HashMap[Int, Double]) =
new SparseVector(entries.withDefaultValue(0.0))
}
方法map
,sum
和aggregate
都有并行实现。