我有一个存储在S3中的weka模型,大小约为400MB。 现在,我有一些记录,我想在其上运行模型并执行预测。
为了进行预测,我试过的是,
在驱动程序上下载并加载模型作为静态对象,将其广播给所有执行程序。对预测RDD执行映射操作。 ---->不工作,如在Weka中执行预测,模型对象需要修改,广播需要只读副本。
在驱动程序上下载并加载模型作为静态对象,并在每个映射操作中将其发送给执行程序。 ----->工作(效率不高,如在每个地图操作中,我传递400MB对象)
在驱动程序上下载模型并将其加载到每个执行程序上并将其缓存在那里。 (不知道该怎么做)
有人知道如何在每个执行程序上加载模型一次并将其缓存,以便其他记录我不再加载它?
答案 0 :(得分:26)
您有两种选择:
object WekaModel {
lazy val data = {
// initialize data here. This will only happen once per JVM process
}
}
然后,您可以在map
函数中使用lazy val。 lazy val
确保每个工作者JVM初始化他们自己的数据实例。不会对data
执行序列化或广播。
elementsRDD.map { element =>
// use WekaModel.data here
}
<强>优点强>
<强>缺点强>
mapPartition
(或foreachPartition
)方法,而不仅仅是map
。这允许您初始化整个分区所需的任何内容。
elementsRDD.mapPartition { elements =>
val model = new WekaModel()
elements.map { element =>
// use model and element. there is a single instance of model per partition.
}
}
<强>优点强>:
<强>缺点强>
答案 1 :(得分:1)
这对我来说比懒惰的初始化程序更有用。我创建了一个初始化为null的对象级指针,让每个执行器初始化它。在初始化块中,您可以使用一次性运行代码。请注意,每个处理批处理将重置局部变量,但不会重置对象级变量。
object Thing1 {
var bigObject : BigObject = null
def main(args: Array[String]) : Unit = {
val sc = <spark/scala magic here>
sc.textFile(infile).map(line => {
if (bigObject == null) {
// this takes a minute but runs just once
bigObject = new BigObject(parameters)
}
bigObject.transform(line)
})
}
}
这种方法每个执行器只创建一个大对象,而不是其他方法的每个分区的一个大对象。
如果将 var bigObject:BigObject = null 放在主函数名称空间中,它的行为会有所不同。在这种情况下,它在每个分区的开头运行bigObject构造函数(即批处理)。如果你有内存泄漏,那么这最终会杀死执行程序。垃圾收集还需要做更多的工作。
答案 2 :(得分:0)
这是我们通常要做的
定义执行此类工作的单例客户端,以确保每个执行器中仅存在一个客户端
有一个getorcreate方法来创建或获取客户信息,通常让您拥有一个要为多个不同模型提供服务的通用服务平台,然后我们就可以像并发映射一样使用以确保线程安全和可计算性
getorcreate方法将在RDD级别(如transform或foreachpartition)中调用,因此请确保init在执行程序级别发生
答案 3 :(得分:0)