#python #c #python-3.x #numpy #out-of-memory
#python #c #python-3.x #numpy #нехватка памяти
Вопрос:
Я работаю над проектом, в котором мне нужно найти ближайших соседей встраиваемого вектора. Недавно я пытаюсь использовать новый инструмент Google ANN для сканирования github. Я смог создать объект searcher и сериализовать его для небольшого набора данных (~ 200 тыс. строк с 512 значениями) с помощью следующего кода
import numpy as np
import scann
data = np.random.random((200k,512))
data = data / np.linalg.norm(data, axis=1)[:, np.newaxis]
searcher = scann.scann_ops_pybind.builder(data, 10, "dot_product").tree(
num_leaves=2000, num_leaves_to_search=100, training_sample_size=250000).score_ah(
2, anisotropic_quantization_threshold=0.2).reorder(100).build()
searcher.serialize('./scann')
Но когда я попробовал с реальным набором данных (~ 48 млн строк с 512 значениями), я получил:
In [11]: searcher.serialize('scann/')
---------------------------------------------------------------------------
MemoryError Traceback (most recent call last)
<ipython-input-11-71a5ef71c81f> in <module>
----> 1 searcher.serialize('scann/')
~/.local/lib/python3.6/site-packages/scann/scann_ops/py/scann_ops_pybind.py in serialize(self, artifacts_dir)
70
71 def serialize(self, artifacts_dir):
---> 72 self.searcher.serialize(artifacts_dir)
73
74
MemoryError: std::bad_alloc
Размер .npy
файла для набора данных составляет ~ 90 ГБ, и на моем компьютере осталось не менее 500 ГБ свободной оперативной памяти и 1 ТБ свободного диска:
Я использую Ubuntu 18.04.5 LTS и Python 3.6.9. Модуль Scann был установлен с помощью Pip.
Есть идеи о том, что может происходить?
Спасибо за помощь
[править / править код] После комментария @MSalters я провел некоторое тестирование и выяснил, что если подлежащий сериализации набор данных содержит более 16777220 байт (2 ^ 24 4), он завершается ошибкой с bad_alloc
сообщением. Я до сих пор не знаю, почему это происходит…
[edit2] Я создаю SCANN из исходного кода и добавляю в него несколько отладочных отпечатков. Ошибка, похоже, в этой строке:
vector<uint8_t> storage(hash_dim * expected_size);
и если я напечатаю это так:
std::cout << hash_dim << " " << expected_size <<"n" << std::flush;
std::cout << hash_dim * expected_size <<"n" << std::flush;
vector<uint8_t> v2;
std::cout << v2.max_size() << "n" << std::flush;
vector<uint8_t> storage(hash_dim * expected_size);
std::cout << "after storage creationn" << std::flush;
Тогда я получаю;
256 8388608
-2147483648
9223372036854775807
Комментарии:
1. Один очевидный вопрос. Используете ли вы 32-битную или 64-битную версию Python? 32-разрядная версия может использовать только 2 ГБ оперативной памяти.
2. Вывод
platform.architecture()
есть('64bit', 'ELF')
. Я думал, что это может быть проблемой, но мне только что удалось сериализовать набор данных с 5 млн строк и 11 ГБ, но не удалось с 10 млн строк и 21 ГБ3. Получить a
bad_alloc
в Linux довольно сложно. Linux обычно делает вид, что памяти достаточно, даже если ее на самом деле нет. Это называется чрезмерной ответственностью, контролируемойproc/sys/vm/overcommit_memory
. Результатом использования недоступной памяти является то, что Linux сначала замедляется (замена), а затем убивает случайный процесс. Abad_alloc
больше намекает на ошибку программирования, такую как попытка выделить -1 байт.4. @MSalters Я провел некоторое тестирование, и кажется, что если размер моего набора данных меньше 16777126 (2 ^ 24), он работает просто отлично. Итак, я думаю, вы правы, и где-то в коде СКАНИРОВАНИЯ размер выделяемой памяти переполняет переменную, а затем пытается выделить отрицательный объем памяти. Теперь мне нужно просто выяснить, где
5. Похоже на ошибку в SCANN или способ ее использования (не эксперт в этом, поэтому не могу сказать наверняка). Тип обоих
hash_dim
иexpected_size
естьint
, поэтомуhash_dim * expected_size
переполняется.size_t
илиint64_t
работал бы лучше.
Ответ №1:
Похоже, в ScaNN есть отчет о существующей проблеме # 427 с аналогичной ошибкой.
Основываясь на выводе -2147483648
for std::cout << hash_dim * expected_size
, мы можем сделать вывод, что hash_dim * expected_size
переполняется.
Глядя на источник, мы видим тип обоих hash_dim
и expected_size
есть int
.
Так что, вероятно, тип хотя бы одного из них должен был быть int64_t
, long long
или, что еще лучше, size_t
.
Глядя на источник ScaNN, кажется, что может быть больше мест, которые могли бы извлечь выгоду из типа данных, специально предназначенного для хранения size ( size_t
) вместо an int
.