Эта проблема
Я написал классификатор нейронной сети, который принимает большие изображения (~ 1-3 ГБ за штуку), исправляет их и передает патчи по сети индивидуально. Обучение шло очень медленно, поэтому я провел сравнительный анализ и обнаружил, что для загрузки патчей из одного изображения в память (с использованием библиотеки Openslide) требуется ~ 50 с, а для прохождения их через модель - ~ 0,5 с.
Тем не менее, я работаю над суперкомпьютером с 1,5 ТБ ОЗУ, из которых используется только ~ 26 ГБ. Набор данных составляет ~ 500 Гб. Я думаю, что если бы мы могли загрузить весь набор данных в память, это значительно ускорило бы обучение. Но я работаю с исследовательской группой, и мы проводим эксперименты на нескольких скриптах Python. Поэтому в идеале я хотел бы загрузить весь набор данных в память одним сценарием и иметь доступ к нему для всех сценариев.
Больше деталей:
- Мы проводим наши индивидуальные эксперименты в отдельных контейнерах Docker (на одной машине), поэтому набор данных должен быть доступен для нескольких контейнеров.
- Набор данных - это набор данных Camelyon16; изображения хранятся в формате
.tif
. - Нам просто нужно прочитать изображения, не нужно писать.
- Нам нужно только получить доступ к небольшим частям набора данных одновременно.
Возможные решения
Я нашел много постов о том, как делиться объектами Python или необработанными данными в памяти между несколькими скриптами Python:
Обмен данными Python между скриптами
Серверные процессы с SyncManager и BaseManager в многопроцессорном модуле | Пример 1 | Пример 2 | Документы - Серверные процессы | Документы - SyncManager
- Положительные стороны: могут совместно использоваться процессами на разных компьютерах по сети (могут ли они использоваться несколькими контейнерами?)
- Возможная проблема: медленнее, чем использование общей памяти, в соответствии с документацией. Если мы разделяем память между несколькими контейнерами, используя клиент/сервер, будет ли это быстрее, чем все сценарии, читающие с диска?
- Возможная проблема: в соответствии с этим ответом объект
Manager
выбирает объекты перед отправкой, что может привести к замедлению работы.
- Возможная проблема:
mmap
отображает файл в виртуальную память, а не в физическую память - он создает временный файл. - Возможная проблема: поскольку мы используем только небольшую часть набора данных за один раз, виртуальная память помещает весь набор данных на диск, мы сталкиваемся с серьезными проблемами и ошибками программы.
Pyro4 (клиент-сервер для объектов Python) | Документы
Модуль sysv_ipc для Python. Это демо выглядит многообещающе.
- Возможные проблемы: возможно только нижний уровень экспозиция вещей доступны во встроенном в
multi-processing
модуле?
Я также нашел этот список опций для IPC/сетей в Python.
Некоторые обсуждают настройки сервера-клиента, некоторые обсуждают сериализацию/десериализацию, которая, боюсь, займет больше времени, чем просто чтение с диска. Ни один из ответов, которые я нашел, не отвечает на мой вопрос о том, приведут ли они к повышению производительности ввода-вывода.
Совместное использование памяти через контейнеры Docker
Нам нужно не только разделять объекты/память Python между скриптами; нам нужно поделиться ими через контейнеры Docker.
Docker документация объясняет --ipc
флаг очень хорошо. Что имеет смысл для меня по документации работает:
docker run -d --ipc=shareable data-server
docker run -d --ipc=container:data-server data-client
Но когда я запускаю свой клиент и сервер в отдельных контейнерах с подключением --ipc
настроенным, как описано выше, они не могут общаться друг с другом. Вопросы, которые я прочитал (1, 2, 3, 4), не касаются интеграции разделяемой памяти между скриптами Python в отдельных контейнерах Docker.
Мои вопросы:
- 1: Предоставит ли какой-либо из них более быстрый доступ, чем чтение с диска? Разумно ли даже думать, что совместное использование данных в памяти между процессами/контейнерами повысит производительность?
- 2: Какое решение будет наиболее подходящим для совместного использования данных в памяти между несколькими контейнерами докеров?
- 3: Как интегрировать решения для совместного использования памяти из Python с
docker run --ipc=<mode>
? (Является ли общее пространство имен IPC даже лучшим способом разделения памяти между контейнерами Docker?) - 4: есть ли лучшее решение, чем это, чтобы решить нашу проблему больших накладных расходов ввода-вывода?
Минимальный рабочий пример - Обновлено. Не требует никаких внешних зависимостей!
Это мой наивный подход к разделению памяти между скриптами Python в отдельных контейнерах. Это работает, когда скрипты Python запускаются в одном и том же контейнере, но не когда они запускаются в отдельных контейнерах.
server.py
from multiprocessing.managers import SyncManager
import multiprocessing
patch_dict = {}
image_level = 2
image_files = ['path/to/normal_042.tif']
region_list = [(14336, 10752),
(9408, 18368),
(8064, 25536),
(16128, 14336)]
def load_patch_dict():
for i, image_file in enumerate(image_files):
# We would load the image files here. As a placeholder, we just add '1' to the dict
patches = 1
patch_dict.update({'image_{}'.format(i): patches})
def get_patch_dict():
return patch_dict
class MyManager(SyncManager):
pass
if __name__ == "__main__":
load_patch_dict()
port_num = 4343
MyManager.register("patch_dict", get_patch_dict)
manager = MyManager(("127.0.0.1", port_num), authkey=b"password")
# Set the authkey because it doesn't set properly when we initialize MyManager
multiprocessing.current_process().authkey = b"password"
manager.start()
input("Press any key to kill server".center(50, "-"))
manager.shutdown
client.py
from multiprocessing.managers import SyncManager
import multiprocessing
import sys, time
class MyManager(SyncManager):
pass
MyManager.register("patch_dict")
if __name__ == "__main__":
port_num = 4343
manager = MyManager(("127.0.0.1", port_num), authkey=b"password")
multiprocessing.current_process().authkey = b"password"
manager.connect()
patch_dict = manager.patch_dict()
keys = list(patch_dict.keys())
for key in keys:
image_patches = patch_dict.get(key)
# Do NN stuff (irrelevant)
Эти сценарии прекрасно работают для обмена изображениями, когда сценарии запускаются в одном и том же контейнере. Но когда они запускаются в отдельных контейнерах, вот так:
# Run the container for the server
docker run -it --name cancer-1 --rm --cpus=10 --ipc=shareable cancer-env
# Run the container for the client
docker run -it --name cancer-2 --rm --cpus=10 --ipc=container:cancer-1 cancer-env
Я получаю следующую ошибку:
Traceback (most recent call last):
File "patch_client.py", line 22, in <module>
manager.connect()
File "/usr/lib/python3.5/multiprocessing/managers.py", line 455, in connect
conn = Client(self._address, authkey=self._authkey)
File "/usr/lib/python3.5/multiprocessing/connection.py", line 487, in Client
c = SocketClient(address)
File "/usr/lib/python3.5/multiprocessing/connection.py", line 614, in SocketClient
s.connect(address)
ConnectionRefusedError: [Errno 111] Connection refused