有没有一种内存有效的方法来应用大型(>4gb)模型来触发Dataframe,而不会出现内存问题?
我们最近将一个定制的管道框架移植到spark上(使用python和pyspark),在将word2vec和autoencoders等大型模型应用到标记化文本输入时遇到了问题。首先,我非常天真地将转换调用转换为 udf
s(Pandas和spark“本地”的),只要所使用的模型/实用程序足够小,可以广播,也可以反复示例化:
@pandas_udf("array<string>")
def tokenize_sentence(sentences: pandas.Series):
return sentences.map(lambda sentence: tokenize.word_tokenize(sentence))
在大型模型上尝试同样的方法(例如,通过word2vec将这些标记嵌入向量空间)导致了糟糕的性能,我明白了原因:
@pandas_udf("array<array<double>>")
def rows_to_lists_of_vectors(rows):
model = api.load('word2vec-google-news-300')
def words_to_vectors(words) -> List[List[float]]:
vectors = []
for word in words:
if word in model:
vec = model[word]
vectors.append(vec.tolist())
return vectors
return rows.map(words_to_vectors)
上面的代码会反复示例化~4gbword2vec模型,将其从磁盘加载到ram中,速度非常慢。我可以通过使用 mapPartition
,每个分区至少只加载一次。但更重要的是,如果我不严格限制任务的数量,这会导致内存相关的问题(至少在我的开发人员机器上)崩溃,这反过来又会使小型udf非常慢。例如,将任务数限制为2可以解决内存崩溃问题,但会使标记化变得非常缓慢。
我知道spark中有一个完整的管道框架,可以满足我们的需求,但在承诺之前,我想了解我遇到的问题是如何在那里得到解决的。也许我们可以使用一些关键的实践来代替重写我们的框架。
因此,我的实际问题有两个:
假设我们为spark开箱即用(比如tokenizers和word2vec)没有涵盖的步骤编写了定制的估计器和转换器,那么使用spark管道框架是否可以解决我们在性能和内存方面的问题呢。
spark是如何解决这些问题的?我是否可以改进当前的方法,或者这是否不可能使用python(据我所知,进程不共享内存空间)。
如果以上任何一点让你认为我错过了spark的核心原则,请指出,毕竟我只是刚刚开始使用spark。
1条答案
按热度按时间sczxawaw1#
这取决于各种因素(模型、集群资源、管道),但试图回答您的主要问题:
1). 如果spark pipeline符合您在标记器、words2vec等方面的需求,那么它可能会解决您的问题。但是,这些工具并没有现成的工具集和加载的工具集强大
api.load
. 您可能还想看看deeplearning4j,它将这些功能引入java/apachespark,并了解它如何实现相同的功能:标记化、word2vec等2). 按照当前的方法,我将看到在
foreachParition
或者mapPartition
并确保模型能够适合每个分区的内存。您可以根据集群资源将分区大小缩小到一个更合理的数字,以避免内存问题(例如,如果不是为每一行创建一个db连接,而是为每一个分区创建一个db连接,则情况相同)。通常,spark UDF在应用一种spark友好且不与第三方混合的业务逻辑时是很好的。