Nie można przydzielić tablicy z kształtem i typem danych

116

Mam problem z przydzielaniem ogromnych tablic w Numpy na Ubuntu 18, podczas gdy nie napotykam tego samego problemu na MacOS.

Próbuję przydzielić pamięć dla tablicy numpy z kształtem (156816, 36, 53806) z

np.zeros((156816, 36, 53806), dtype='uint8')

i gdy otrzymuję błąd w systemie Ubuntu OS

>>> import numpy as np
>>> np.zeros((156816, 36, 53806), dtype='uint8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
numpy.core._exceptions.MemoryError: Unable to allocate array with shape (156816, 36, 53806) and data type uint8

Nie dostaję tego na MacOS:

>>> import numpy as np 
>>> np.zeros((156816, 36, 53806), dtype='uint8')
array([[[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       ...,

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]],

       [[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]]], dtype=uint8)

Czytałem gdzieś, co np.zerosnie powinno tak naprawdę przydzielać całej pamięci potrzebnej dla tablicy, ale tylko dla niezerowych elementów. Mimo że maszyna Ubuntu ma 64 GB pamięci, podczas gdy mój MacBook Pro ma tylko 16 GB.

wersje:

Ubuntu
os -> ubuntu mate 18
python -> 3.6.8
numpy -> 1.17.0

mac
os -> 10.14.6
python -> 3.6.4
numpy -> 1.17.0

PS: również nie udało się w Google Colab

Martin Brisiak
źródło
1
Czy w pamięci działają inne procesy?
BlueRine S
nie, próbowałem topi free -mte polecenia, w których wrzucano 60 GB pamięci za darmo i więcej
Martin Brisiak
hmmm. dziwne. To nie powinno zająć tyle pamięci. Ile pamięci zajmowało to na Macosie?
BlueRine S
1
Mało prawdopodobne, ale tak się nie stało, że używasz 32-bitowego interpretera Pythona w Ubuntu, prawda?
jdehesa
1
np.zerosnie tworzy sparsemacierzy. Może wystąpić opóźnienie w wypełnianiu zer. Ale zobacz stackoverflow.com/q/27464039
hpaulj

Odpowiedzi:

116

Jest to prawdopodobnie spowodowane trybem obsługi overcommit w systemie .

W trybie domyślnym 0,

Heurystyczna obsługa overcommitów. Oczywiste przeciążenia przestrzeni adresowej są odrzucane. Używany w typowym systemie. Zapewnia to, że poważnie szalona alokacja nie powiedzie się, jednocześnie umożliwiając nadmierne zaangażowanie w celu zmniejszenia wykorzystania wymiany. root może przydzielić nieco więcej pamięci w tym trybie. To jest ustawienie domyślne.

Dokładna heurystyka nie jest tutaj dobrze wyjaśniona, ale jest to omówione bardziej szczegółowo w Linuksie w porównaniu z heurystyką zatwierdzania i na tej stronie .

Możesz sprawdzić aktualny tryb overcommit, uruchamiając

$ cat /proc/sys/vm/overcommit_memory
0

W tym przypadku przydzielasz

>>> 156816 * 36 * 53806 / 1024.0**3
282.8939827680588

~ 282 GB, a jądro mówi dobrze, oczywiście, że nie ma możliwości, abym był w stanie poświęcić na to tyle fizycznych stron, i odmawia alokacji.

Jeśli (jako root) uruchomisz:

$ echo 1 > /proc/sys/vm/overcommit_memory

Umożliwi to tryb „zawsze overcommit” i przekonasz się, że system rzeczywiście pozwoli ci dokonać alokacji bez względu na to, jak duża jest (przynajmniej w ramach 64-bitowego adresowania pamięci).

Sam to przetestowałem na maszynie z 32 GB RAM-u. Z trybem overcommit 0również dostałem MemoryError, ale po zmianie z powrotem na 1to działa:

>>> import numpy as np
>>> a = np.zeros((156816, 36, 53806), dtype='uint8')
>>> a.nbytes
303755101056

Następnie możesz kontynuować zapisywanie w dowolnym miejscu w tablicy, a system przydzieli fizyczne strony tylko wtedy, gdy jawnie zapiszesz na tej stronie. Możesz więc ostrożnie używać tego do rzadkich tablic.

Iguananaut
źródło
2
Jest to w szczególności cecha jądra Linuksa, więc koniecznie nie ma bezpośredniego odpowiednika na MacOS, choć prawdopodobnie coś podobnego. Nie wydaje mi się, aby na komputerach Mac zmiana ustawień jądra była tak łatwa.
Iguananaut
1
@Iguananaut, jakie jest dokładne znaczenie ostrzeżenia „ostrożnie”? to znaczy. Jaki jest najgorszy scenariusz, w którym coś pójdzie nie tak na serwerze Ubuntu 18 z GPU GTX 1080?
mLstudent33
1
@ mLstudent33 Po pierwsze, nie ma to nic wspólnego z twoim GPU, który ma własną pamięć. Chodzi mi tylko o to, że nadal możesz zapełnić swoją pamięć - za każdym razem, gdy piszesz na jakiejś stronie w pamięci, ta strona (zwykle 4k bajtów) musi zostać zapisana w pamięci fizycznej. Więc najgorszym scenariuszem jest to, że zabraknie Ci pamięci.
Iguananaut
1
Czy ta zmiana zaczyna obowiązywać natychmiast, czy też musimy ponownie uruchomić naszą powłokę lub samą maszynę?
dumbledad
2
Działa natychmiast, ale nie będzie trwać po ponownym uruchomieniu bez dodatkowych środków. Przeszukaj inne pytania dotyczące tego, jak najlepiej zachować /proc/sysustawienia w swojej dystrybucji.
Iguananaut
53

Miałem ten sam problem na Windowsie i natknąłem się na to rozwiązanie. Więc jeśli ktoś napotka ten problem w systemie Windows, rozwiązaniem dla mnie było zwiększenie rozmiaru pliku stronicowania , ponieważ dla mnie był to również problem z nadmiernym zaangażowaniem pamięci.

Windows 8

  1. Na klawiaturze Naciśnij klawisz WindowsKey + X, a następnie kliknij opcję System w menu podręcznym
  2. Naciśnij lub kliknij Zaawansowane ustawienia systemu. Możesz zostać poproszony o podanie hasła administratora lub o potwierdzenie wyboru
  3. Na karcie Zaawansowane w obszarze Wydajność dotknij lub kliknij opcję Ustawienia.
  4. Dotknij lub kliknij kartę Zaawansowane, a następnie w obszarze Pamięć wirtualna dotknij lub kliknij Zmień
  5. Usuń zaznaczenie pola wyboru Automatycznie zarządzaj rozmiarem pliku stronicowania dla wszystkich dysków.
  6. W obszarze Dysk [etykieta woluminu] stuknij lub kliknij dysk zawierający plik stronicowania, który chcesz zmienić
  7. Naciśnij lub kliknij Rozmiar niestandardowy, wprowadź nowy rozmiar w megabajtach w polu Rozmiar początkowy (MB) lub Rozmiar maksymalny (MB), naciśnij lub kliknij opcję Ustaw, a następnie naciśnij lub kliknij przycisk OK
  8. Uruchom ponownie system

Windows 10

  1. Naciśnij klawisz Windows
  2. Wpisz SystemPropertiesAdvanced
  3. Kliknij Uruchom jako administrator
  4. W obszarze Wydajność kliknij opcję Ustawienia
  5. Wybierz kartę Zaawansowane
  6. Wybierz Zmień ...
  7. Usuń zaznaczenie opcji Automatycznie zarządzaj rozmiarem pliku stronicowania dla wszystkich dysków
  8. Następnie wybierz Rozmiar niestandardowy i wpisz odpowiedni rozmiar
  9. Naciśnij przycisk Ustaw, a następnie OK, a następnie zamknij okno Pamięć wirtualna, Opcje wydajności i Właściwości systemu
  10. Uruchom ponownie system

Uwaga: w moim systemie nie było wystarczającej ilości pamięci dla ~ 282 GB w tym przykładzie, ale w moim konkretnym przypadku zadziałało.

EDYTOWAĆ

Od tutaj sugerowane zalecenia dotyczące rozmiaru pliku strony:

Istnieje wzór do obliczania prawidłowego rozmiaru pliku stronicowania. Rozmiar początkowy to półtora (1,5) x ilość całkowitej pamięci systemowej. Maksymalny rozmiar to trzy (3) x rozmiar początkowy. Powiedzmy, że masz 4 GB (1 GB = 1024 MB x 4 = 4096 MB) pamięci. Początkowy rozmiar to 1,5 x 4096 = 6144 MB, a maksymalny 3 x 6144 = 18432 MB.

Niektóre rzeczy, aby pamiętać, ze tutaj :

Jednak nie uwzględnia to innych ważnych czynników i ustawień systemowych, które mogą być unikalne dla twojego komputera. Ponownie pozwól systemowi Windows wybrać, czego użyć, zamiast polegać na jakiejś arbitralnej formule, która działała na innym komputerze.

Również:

Zwiększenie rozmiaru pliku strony może pomóc w zapobieganiu niestabilności i awariom w systemie Windows. Jednak czasy odczytu / zapisu dysku twardego są znacznie dłuższe niż byłyby, gdyby dane znajdowały się w pamięci komputera. Posiadanie większego pliku strony będzie wymagało dodatkowej pracy na dysku twardym, powodując, że wszystko inne będzie działać wolniej. Rozmiar pliku strony powinien być zwiększany tylko w przypadku napotkania błędów braku pamięci i tylko jako rozwiązanie tymczasowe. Lepszym rozwiązaniem jest dodanie większej ilości pamięci do komputera.

recurseuntilfor
źródło
jakie ustawienia rozmiaru niestandardowego (rozmiar początkowy + rozmiar maksymalny) masz teraz? Nie jestem pewien, ile przeznaczyć dla siebie
Azizbro
1
@Azizbro Wróciłem do ustawień domyślnych, ale po prostu dostosowałem wartości, aż zniknął błąd braku pamięci.
powtarzane do
Zrobiłem to i nadal otrzymujęMemoryError: Unable to allocate 10.3 PiB for an array with shape (38137754, 38137754) and data type float64
george.adams1
24

Natknąłem się na ten problem również w systemie Windows. Rozwiązaniem dla mnie było przejście z 32-bitowej na 64-bitową wersję Pythona . Rzeczywiście, oprogramowanie 32-bitowe, podobnie jak 32-bitowy procesor, może adresować maksymalnie 4 GB pamięci RAM (2 ^ 32). Jeśli więc masz więcej niż 4 GB pamięci RAM, wersja 32-bitowa nie może z tego skorzystać.

W przypadku 64-bitowej wersji Pythona (tej oznaczonej x86-64 na stronie pobierania) problem zniknął.

Możesz sprawdzić, którą wersję posiadasz, wpisując tłumacza. Ja, w wersji 64-bitowej, mam teraz Python 3.7.5rc1 (tags/v3.7.5rc1:4082f600a5, Oct 1 2019, 20:28:14) [MSC v.1916 64 bit (AMD64)]:, gdzie [MSC v.1916 64-bitowy (AMD64)] oznacza „64-bitowy Python”.

Uwaga : w chwili pisania tego tekstu (maj 2020 r.) Matplotlib nie jest dostępne na pythonie39, więc zalecam zainstalowanie pythona37 w wersji 64-bitowej.

Źródła:

kotchwane
źródło
2
Jak wpisać tłumacza?
Shayan
Rozwiązał też mój problem. Korzystanie z Pycharm. Odinstalowana wersja 32-bitowa, ponownie zainstalowana wersja 64-bitowa, zmieniony interpreter projektu na nowy 64-bitowy Python.
Jason Goal
3

W moim przypadku dodanie atrybutu dtype zmieniło dtype tablicy na mniejszy typ (z float64 na uint8), zmniejszając rozmiar tablicy na tyle, aby nie zgłaszać MemoryError w systemie Windows (64 bit).

od

mask = np.zeros(edges.shape)

do

mask = np.zeros(edges.shape,dtype='uint8')
Pragya Agrawal
źródło
1

Czasami ten błąd pojawia się, ponieważ jądro osiągnęło swój limit. Spróbuj ponownie uruchomić jądro, powtórz niezbędne kroki.

Antonio Moreno Martín
źródło
4
Zobacz: stackoverflow.com/help/how-to-answer Dobra próba.
Kishan Mehta
1

zmienić typ danych na inny, który zużywa mniej pamięci. U mnie zmieniam typ danych na numpy.uint8:

data['label'] = data['label'].astype(np.uint8)
Mingming Qiu
źródło