在 docker 个容器之间共享 gensim 的 KeyedVectors 对象的内存
Sharing memory for gensim's KeyedVectors objects between docker containers
在 之后,我创建了 docker 容器,它在 docker 容器中加载 GoogleNews-vectors-negative300 KeyedVector 并将其全部加载到内存中
KeyedVectors.load(model_path, mmap='r')
word_vectors.most_similar('stuff')
我还有另一个 Docker 容器,它提供 REST API 用
加载这个模型
KeyedVectors.load(model_path, mmap='r')
而且我观察到满载的容器占用超过 5GB 的内存,每个 gunicorn worker 占用 1.7GB 的内存。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
acbfd080ab50 vectorizer_model_loader_1 0.00% 5.141GiB / 15.55GiB 33.07% 24.9kB / 0B 32.9MB / 0B 15
1a9ad3dfdb8d vectorizer_vectorizer_1 0.94% 1.771GiB / 15.55GiB 11.39% 26.6kB / 0B 277MB / 0B 17
但是,我希望所有这些进程为 KeyedVector 共享相同的内存,因此它只需要 5.4 GB 在所有容器之间共享。
有人尝试过并成功吗?
编辑:
我尝试了以下代码片段,它确实在不同容器之间共享相同的内存。
import mmap
from threading import Semaphore
with open("data/GoogleNews-vectors-negative300.bin", "rb") as f:
# memory-map the file, size 0 means whole file
fileno = f.fileno()
mm = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ)
# read whole content
mm.read()
Semaphore(0).acquire()
# close the map
mm.close()
所以KeyedVectors.load(model_path, mmap='r')
不共享内存的问题
编辑2:
研究 gensim 的源代码我看到 np.load(subname(fname, attrib), mmap_mode=mmap)
被调用来打开 memmaped 文件。以下代码示例跨多个容器共享内存。
from threading import Semaphore
import numpy as np
data = np.load('data/native_format.bin.vectors.npy', mmap_mode='r')
print(data.shape)
# load whole file to memory
print(data.mean())
Semaphore(0).acquire()
我不确定容器化是否允许容器共享相同的内存映射文件 – 但即使它允许,无论您使用什么实用程序来测量每个容器的内存使用情况,都可能将内存计算两次甚至如果它是共享的。您使用什么工具来监视内存使用情况,您确定它会指示真正的共享吗? (如果在 gensim 之外,您尝试使用 Python 的 mmap.mmap()
在两个容器中打开同一个巨型文件,会发生什么?您看到与 gensim 中相同、更多或更少的内存使用吗案例?)
而且:为了执行 most_similar()
,KeyedVectors
将创建一个 second 词向量数组,归一化为单位长度, 在 属性 vectors_norm
中。 (这只在第一次需要时完成一次。)这个范数数组 没有 保存,因为它总是可以重新计算。因此,对于您的使用,每个容器都将创建自己的、非共享的 vectors_norm
数组——撤消共享内存映射文件可能节省的任何内存。
您可以通过以下方式解决此问题:
在加载模型之后但在触发自动归一化之前,用一个特殊的参数明确地强制自己就地破坏原始原始向量。然后保存这个预先规范的版本:
word_vectors = KeyedVectors.load(model_path)
word_vectors.init_sims(replace=True)
word_vectors.save(normed_model_path)
稍后以内存映射方式重新加载模型时,手动将 vectors_norm
属性 设置为与 vectors
相同,以防止赋范数组的冗余重新创建:
word_vectors = KeyedVectors.load(normed_model_path, mmap='r')
word_vectors.vectors_norm = word_vectors.vectors
如果规范阻碍了您看到预期的内存节省,此方法可能会有所帮助。
经过大量调试后,我发现 mmap 对 KeyedVectors
对象中的 numpy 数组按预期工作。
但是,KeyedVectors 具有其他属性,例如 self.vocab
、self.index2word
和 self.index2entity
,这些属性不共享并且每个对象消耗约 1.7 GB 的内存。
在
KeyedVectors.load(model_path, mmap='r')
word_vectors.most_similar('stuff')
我还有另一个 Docker 容器,它提供 REST API 用
加载这个模型KeyedVectors.load(model_path, mmap='r')
而且我观察到满载的容器占用超过 5GB 的内存,每个 gunicorn worker 占用 1.7GB 的内存。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
acbfd080ab50 vectorizer_model_loader_1 0.00% 5.141GiB / 15.55GiB 33.07% 24.9kB / 0B 32.9MB / 0B 15
1a9ad3dfdb8d vectorizer_vectorizer_1 0.94% 1.771GiB / 15.55GiB 11.39% 26.6kB / 0B 277MB / 0B 17
但是,我希望所有这些进程为 KeyedVector 共享相同的内存,因此它只需要 5.4 GB 在所有容器之间共享。
有人尝试过并成功吗?
编辑: 我尝试了以下代码片段,它确实在不同容器之间共享相同的内存。
import mmap
from threading import Semaphore
with open("data/GoogleNews-vectors-negative300.bin", "rb") as f:
# memory-map the file, size 0 means whole file
fileno = f.fileno()
mm = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ)
# read whole content
mm.read()
Semaphore(0).acquire()
# close the map
mm.close()
所以KeyedVectors.load(model_path, mmap='r')
不共享内存的问题
编辑2:
研究 gensim 的源代码我看到 np.load(subname(fname, attrib), mmap_mode=mmap)
被调用来打开 memmaped 文件。以下代码示例跨多个容器共享内存。
from threading import Semaphore
import numpy as np
data = np.load('data/native_format.bin.vectors.npy', mmap_mode='r')
print(data.shape)
# load whole file to memory
print(data.mean())
Semaphore(0).acquire()
我不确定容器化是否允许容器共享相同的内存映射文件 – 但即使它允许,无论您使用什么实用程序来测量每个容器的内存使用情况,都可能将内存计算两次甚至如果它是共享的。您使用什么工具来监视内存使用情况,您确定它会指示真正的共享吗? (如果在 gensim 之外,您尝试使用 Python 的 mmap.mmap()
在两个容器中打开同一个巨型文件,会发生什么?您看到与 gensim 中相同、更多或更少的内存使用吗案例?)
而且:为了执行 most_similar()
,KeyedVectors
将创建一个 second 词向量数组,归一化为单位长度, 在 属性 vectors_norm
中。 (这只在第一次需要时完成一次。)这个范数数组 没有 保存,因为它总是可以重新计算。因此,对于您的使用,每个容器都将创建自己的、非共享的 vectors_norm
数组——撤消共享内存映射文件可能节省的任何内存。
您可以通过以下方式解决此问题:
在加载模型之后但在触发自动归一化之前,用一个特殊的参数明确地强制自己就地破坏原始原始向量。然后保存这个预先规范的版本:
word_vectors = KeyedVectors.load(model_path) word_vectors.init_sims(replace=True) word_vectors.save(normed_model_path)
稍后以内存映射方式重新加载模型时,手动将
vectors_norm
属性 设置为与vectors
相同,以防止赋范数组的冗余重新创建:word_vectors = KeyedVectors.load(normed_model_path, mmap='r') word_vectors.vectors_norm = word_vectors.vectors
如果规范阻碍了您看到预期的内存节省,此方法可能会有所帮助。
经过大量调试后,我发现 mmap 对 KeyedVectors
对象中的 numpy 数组按预期工作。
但是,KeyedVectors 具有其他属性,例如 self.vocab
、self.index2word
和 self.index2entity
,这些属性不共享并且每个对象消耗约 1.7 GB 的内存。