Pytorch: Memori CPU secara bertahap bocor ketika num_workers > 0 di DataLoader

Dibuat pada 29 Okt 2018  ·  79Komentar  ·  Sumber: pytorch/pytorch

Catatan editor: Ada solusi yang diketahui lebih jauh tentang masalah ini, yaitu TIDAK menggunakan daftar Python, tetapi menggunakan sesuatu yang lain, misalnya, array numpy atau tensor secara langsung.

Bug

Memori CPU akan bocor jika DataLoader num_workers > 0 .

untuk mereproduksi

Jalankan cuplikan berikut:

from torch.utils.data import Dataset, DataLoader
from PIL import Image
from torchvision import transforms
import os

class DataIter(Dataset):
    def __init__(self):
        path = "path/to/data"
        self.data = []

        for cls in os.listdir(path):
            for img in os.listdir(os.path.join(path, cls)):
                self.data.append(os.path.join(path, cls, img))

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        with Image.open(self.data[idx]) as img:
            img = img.convert('RGB')
            return transforms.functional.to_tensor(img)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 200 == 0:
        print(i)

Perilaku yang diharapkan

Memori CPU secara bertahap akan mulai meningkat, akhirnya mengisi seluruh RAM. Misalnya, proses dimulai dengan sekitar 15GB dan mengisi seluruh 128GB yang tersedia di sistem.
Ketika num_workers=0 , penggunaan RAM konstan.

Lingkungan

PyTorch version: 1.0.0.dev20181028
Is debug build: No
CUDA used to build PyTorch: 9.0.176

OS: Ubuntu 16.04.4 LTS
GCC version: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
CMake version: version 3.5.1

Python version: 3.5
Is CUDA available: Yes
CUDA runtime version: 9.0.176
GPU models and configuration: 
GPU 0: GeForce GTX 1080 Ti
GPU 1: GeForce GTX 1080 Ti
GPU 2: GeForce GTX 1080 Ti

Nvidia driver version: 390.67
cuDNN version: Probably one of the following:
/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.4

Versions of relevant libraries:
[pip] Could not collect
[conda] Could not collect

PIL.__version__
'5.3.0'

informasi tambahan

Ada sekitar 24 juta gambar dalam kumpulan data dan semua jalur gambar dimuat ke dalam satu daftar seperti yang disajikan dalam cuplikan kode di atas.

Saya juga telah mencoba beberapa versi Pytorch (0.4.0 dan 0.4.1) dan efeknya sama.

cc @ezyang @gchanan @zou3519 @SsnL

high priority dataloader memory usage molly-guard multiprocessing triaged

Komentar yang paling membantu

Setelah beberapa penyelidikan lebih lanjut, saya telah menemukan skenario yang tepat ketika kebocoran terjadi. Perhatikan contoh kode di bawah ini:

from torch.utils.data import Dataset, DataLoader
import numpy as np
import torch


class DataIter(Dataset):
    def __init__(self):
        self.data_np = np.array([x for x in range(24000000)])
        self.data = [x for x in range(24000000)]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        data = np.array([data], dtype=np.int64)
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

Jika kita menggunakan variabel self.data yang merupakan daftar int standar Python, kebocoran data akan terjadi. Namun, jika variabel self.data_np digunakan, yang menyimpan data yang sama tetapi dalam bentuk array Numpy, kebocoran tidak akan terjadi.
Pengamatan lain adalah bahwa kebocoran secara signifikan kurang parah jika shuffle=False di DataLoader .

Semua 79 komentar

Apakah Anda melihat penggunaan memori meningkat saat melakukan iterasi, atau bahkan sebelum Anda mulai melakukan iterasi?

@SsnL Selama iterasi saja.

Saat kami memperbaiki #13243, kami harus memeriksa apakah yang ini juga diperbaiki.

Saya telah mengalami hal serupa di mana penggunaan memori terus meningkat hingga OOM dipicu saat menggunakan batch_sampler dengan num_workers>0 .

Untuk Mereproduksi

import math

from torch.utils.data import DataLoader


class Sampler:
    def __init__(self, n=100000, batch_size=32):
        self.n = n
        self.batch_size = batch_size

    def __len__(self):
        return math.ceil(float(self.n)/self.batch_size)

    def __iter__(self):
        batch = []
        for i in range(self.n):
            batch.append(i)
            if len(batch) == self.batch_size:
                yield batch
                batch = []
        if batch:
            yield batch


N = 100000000
train_data = list(range(N))


def ok():
    train_sampler = Sampler(len(train_data))
    train_loader = DataLoader(train_data,
                              num_workers=0,
                              batch_sampler=train_sampler)

    for i, item in enumerate(train_loader):
        if i % 10000 == 0:
            print(i)


def leaky():
    train_sampler = Sampler(len(train_data))
    train_loader = DataLoader(train_data,
                              num_workers=8,
                              batch_sampler=train_sampler)

    for i, item in enumerate(train_loader):
        if i % 10000 == 0:
            print(i)


print('Starting ok')
ok()
print('ok done, starting leaky()')
leaky()
print('leaky done')

Lingkungan

$ python3 collect_env.py
Collecting environment information...
PyTorch version: 0.4.0
Is debug build: No
CUDA used to build PyTorch: 9.1.85

OS: Ubuntu 16.04.5 LTS
GCC version: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
CMake version: version 3.5.1

Python version: 3.5
Is CUDA available: Yes
CUDA runtime version: 9.1.85
GPU models and configuration: GPU 0: GeForce GTX 1050 Ti with Max-Q Design
Nvidia driver version: 390.77
cuDNN version: Probably one of the following:
/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.2
/usr/lib/x86_64-linux-gnu/libcudnn_static_v7.a

Versions of relevant libraries:
[pip] Could not collect
[conda] Could not collect

@ezyang

Saat kami memperbaiki #13243, kami harus memeriksa apakah yang ini juga diperbaiki.

Masalahnya masih ada di 1.0.0.dev20181105 , di mana #13243 diperbaiki.

Setelah beberapa penyelidikan lebih lanjut, saya telah menemukan skenario yang tepat ketika kebocoran terjadi. Perhatikan contoh kode di bawah ini:

from torch.utils.data import Dataset, DataLoader
import numpy as np
import torch


class DataIter(Dataset):
    def __init__(self):
        self.data_np = np.array([x for x in range(24000000)])
        self.data = [x for x in range(24000000)]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        data = np.array([data], dtype=np.int64)
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

Jika kita menggunakan variabel self.data yang merupakan daftar int standar Python, kebocoran data akan terjadi. Namun, jika variabel self.data_np digunakan, yang menyimpan data yang sama tetapi dalam bentuk array Numpy, kebocoran tidak akan terjadi.
Pengamatan lain adalah bahwa kebocoran secara signifikan kurang parah jika shuffle=False di DataLoader .

Saya menghadapi masalah serupa, tetapi dalam kasus saya itu terjadi dengan array numpy juga. Saya menggunakan rilis malam Python 3.7 dan PyTorch.

Saya tidak tahu bagaimana multiprocessing benar-benar bekerja di bawah kap pytorch, tetapi kami telah banyak membahas masalah "Kebocoran Memori" ini (yang mungkin bukan kebocoran memori!) di forum fast.ai (https://forums. fast.ai/t/runtimeerror-dataloader-worker-is-killed-by-signal/31277/55?u=marcmuc). Temuan awal yang semoga menambah wawasan di sini (jika ini TIDAK berlaku, beri komentar!):

Python Multiprocessing: Tidak ada cara untuk menyimpan objek python sewenang-wenang (bahkan daftar sederhana) dalam memori bersama di Python tanpa memicu perilaku copy-on-write karena penambahan refcounts, setiap kali sesuatu membaca dari objek ini. Penghitungan ulang ditambahkan halaman memori demi halaman memori, itulah sebabnya konsumsi tumbuh perlahan. Proses (pekerja) pada akhirnya akan memiliki semua/sebagian besar memori disalin sedikit demi sedikit, itulah sebabnya kami mendapatkan masalah kelebihan memori. Deskripsi terbaik dari perilaku ini ada di sini (SO).

Kemungkinan Solusi:
Menggunakan Multiprocessing seperti sekarang: agar multiprocessing python berfungsi tanpa efek refcount ini, objek harus dibuat "kompatibel dengan" dan dibungkus dengan multiprocessing.Array sebelum kumpulan proses dibuat dan pekerja bercabang. Ini seharusnya memastikan, bahwa memori akan benar-benar dibagikan dan tidak ada copy-on-write yang terjadi. Ini menjelaskan bagaimana melakukannya untuk array numpy dan ini menjelaskan alasan di baliknya lagi. Jangan bingung dengan beberapa pernyataan palsu bahkan oleh penulis jawaban bagus ini yang menyatakan bahwa copy-on-write membuat semua ini tidak perlu, yang tidak benar. Satu komentar juga menunjukkan ini:

“Hanya untuk dicatat, pada Python fork() sebenarnya berarti copy on access (karena hanya mengakses objek akan mengubah ref-count-nya).”

Saya tidak terbiasa dengan penggantian drop-in torch.multiprocessing yang saya pahami menggunakan pytorch, tetapi saya akan menganggap itu juga tidak akan dapat menghapus masalah refcount python inti.

@mprostock torch.multiprocessing hanyalah multiprocessing Python, dengan pickler khusus. Pickler khusus, setiap kali menemukan torch.tensor , akan secara otomatis memindahkannya ke memori bersama, dan karenanya setidaknya pada objek torch.tensor , tidak ada copy-on-write yang terjadi.

Terima kasih atas penjelasannya! Saya telah bereksperimen dengan contoh reproduksi @bfreskura dan saya pikir saya sekarang dapat menunjukkan masalahnya:

Contoh reproduksi oleh bfreskura di atas menunjukkan perbedaan antara daftar python biasa dan array numpy. Tetapi masalahnya bukan (hanya) daftar python itu sendiri, hal yang sama terjadi pada array tipe objek numpy. Daftar Python hanya menyimpan referensi ke objek, objek disimpan secara terpisah di memori. Setiap objek memiliki refcount, oleh karena itu setiap item dalam daftar memiliki refcount.

Array numpy (dari tipe np standar) disimpan sebagai blok kontinu dalam memori dan hanya SATU objek dengan satu refcount.

Ini berubah jika Anda membuat array numpy secara eksplisit dari objek tipe, yang membuatnya mulai berperilaku seperti daftar python biasa (hanya menyimpan referensi ke objek (string)). "Masalah" yang sama dengan konsumsi memori sekarang muncul.

Ini akan menjelaskan, mengapa dengan daftar reguler (atau array numpy dari objek tipe) kita melihat "kebocoran memori", yang sebenarnya merupakan masalah penyalinan akses dari proses python bercabang karena perubahan penghitungan ulang, bukan kebocoran memori.

Jadi masalahnya mungkin (sering) tidak ada hubungannya dengan tensor atau objek obor yang sebenarnya, melainkan dengan daftar nama file dan dikte label, yang umumnya digunakan dalam pemuat data/set data.

Saya telah membuat notebook Gist , jika seseorang ingin cepat mencobanya.
Lihatlah konsumsi memori (mem cepat dan kotor dari total sistem, sehingga pengaruh kecil oleh proses lain, mencoba menjaga sistem tetap bersih)

Konsumsi Memori dalam GB dengan larik string panjang tetap:
image

Konsumsi Memori dalam GB dengan array objek (hanya berubah!)
image

Saya menghadapi masalah yang sama. Ini mengisi RAM saya dengan sangat cepat jika num_workers > 0.
Saya menghapus variabel yang saya rasa tidak lagi diperlukan dalam kode saya, juga memanggil gc.collect() pada setiap iterasi, tetapi tidak ada yang membantu.
Ada solusi?

Beralih dari dict ke panda dan dari daftar ke array numpy membantu saya

Saya menghadapi masalah yang sama. Ini mengisi RAM saya dengan sangat cepat jika num_workers > 0.
Saya menghapus variabel yang saya rasa tidak lagi diperlukan dalam kode saya, juga memanggil gc.collect() pada setiap iterasi, tetapi tidak ada yang membantu.
Ada solusi?

Terima kasih balasannya. Saya akan mencobanya dan semoga berhasil.

Bolehkah saya meminta solusi untuk masalah ini? Saya mencoba kode @samgd pada pytorch yang dibuat harian terakhir, dan masih bocor.

@Godricly Lihat komentar @mprostock dan @soumith di atas. Ini sebenarnya bukan kebocoran, tetapi perilaku yang tidak menguntungkan menggunakan daftar asli python. Menggunakan tensor obor atau array np akan menyelesaikan masalah memori ini.

@mprostock Apakah maksud Anda itu adalah salinan yang dibuat oleh akses-salin yang menghabiskan memori, bukan yang lain? Dan bukankah salinannya keluar setelah digunakan?

Seseorang perlu meningkatkan dan menulis operasi augmentasi yang tepat untuk set data gambar setidaknya. Seluruh alasan untuk semua kekacauan multiprosesing ini adalah karena kumpulan data visi _memiliki_ untuk memecahkan kode dan memotong gambar pada banyak inti. Jika ada operasi yang menangani decoding dan transformasi gambar geometris (resize, crop flip, shear, affine), dan menghasilkan tensor batch secara langsung, tidak perlu menggunakan multiprocessing sama sekali, dan selanjutnya, langkah-langkah augmentasi non-geometris (warna, pemutihan/normalisasi, noise) dapat menggunakan paralelisme intra-op untuk merobek seluruh tensor. Perhatian harus dilakukan saat merancang operasi semacam itu untuk mengekspos parameter transformasi untuk setiap sampel di tensor ke luar, untuk memungkinkan transformasi paralel anotasi (kotak pembatas, topeng, titik kunci, dll).
Atau lebih baik lagi, jadikan ini server, sehingga banyak proses (serta kerangka kerja DL lainnya) dapat menggunakannya juga.

@mprostock terima kasih atas penjelasan yang bagus!

Namun, belum ada solusi yang ditawarkan. Menyimpan daftar nama file di objek Dataset tampaknya adil, jadi bagaimana cara menggunakannya? Apakah ada yang mengetahuinya?

@1e100 Saya percaya @fmassa sedang bekerja untuk menambahkan lebih banyak operasi augmentasi gambar asli ke torchvision , yang akan membantu mengatasi masalah ini.

Adakah pembaruan untuk masalah ini?

Menggunakan banyak memori bersama memecahkan masalah bagi saya. Berikut adalah retasan untuk mengatur memori bersama di dalam skrip jika Anda menjalankan kode di dalam wadah buruh pelabuhan dan tidak dapat mengatur memori bersama jika tidak:

os.system(f"mount -o remount,size={args.shared_memory_size} /dev/shm")

Ukuran memori bersama bisa misalnya setengah dari total RAM, katakanlah `80G' untuk mesin biiig.

Saya menemukan solusi untuk lemparan kesalahan unable to open shared memory object </torch_22291_1137042840> in read-write mode yang terkait dengan masalah ini dengan mengubah jumlah deskriptor file yang diizinkan, meskipun memori _still_ merayap ke titik tertentu.

Untuk memeriksa jumlah deskriptor file yang diizinkan, masukkan ulimit -a ke dalam bash dan itu akan berada di bawah tag -n . Untuk meningkatkan batas ini untuk shell Anda saat ini (yaitu jika Anda tidak memiliki izin untuk server), jalankan perintah berikut:
BASH: ulimit -n NEW_VALUE

Untuk mengubahnya untuk seluruh sistem, lihat di sini .

Jadi jika saya mengerti dengan benar, proses pekerja membuat salinan daftar panjang jalur file setiap kali mereka mengakses daftar? Tetapi bukankah salinan sementara ini keluar dari ruang lingkup (dan akibatnya dihancurkan) segera setelah fungsi __getitem__ untuk proses itu kembali? Mengapa konsumsi RAM meningkat tanpa batas?

Alangkah baiknya jika seseorang membuat panduan singkat dengan beberapa praktik terbaik tentang cara menghindari masalah ini. Dengan nilai numerik, mudah untuk mengganti daftar Python dengan array NumPy, tetapi tidak sepenuhnya jelas bagaimana mengurangi masalah dengan string (berukuran variabel).

Dalam kasus saya, saya memiliki daftar objek kelas khusus yang dibuat/diisi dalam konstruktor. Ini pada dasarnya hanya berisi set jalur file. Kemudian di dalam __getitem__ saya memuat gambar-gambar itu, melakukan beberapa praproses, mengonversi ke Tensor obor dan kemudian secara eksplisit memanggil del pada gambar yang dimuat sebelum kembali. Masalahnya adalah menambahkan beberapa langkah pra-pemrosesan tambahan yang tampaknya tidak berbahaya menimbulkan masalah penggunaan memori di luar batas ini.

mp.shared_memory Py 3.8 dapat memberikan solusi yang cukup baik untuk berbagi banyak objek non-tensor/nparray, misalnya, daftar bersama: https://docs.python.org/3.8/library/multiprocessing.shared_memory.html #multiprocessing.shared_memory.ShareableList. :)

penafian: saya sebenarnya tidak membaca dokumen dengan cermat.

Apakah ada tindakan yang bisa kita lakukan di sini? Apakah kami memiliki cukup transformasi gambar yang didukung di torchvision untuk mendokumentasikan pemindahan beberapa kasus penggunaan ke sana?

Hanya untuk memperjelas intinya di sini: mengimplementasikan apa yang diusulkan @1e100 adalah sesuatu yang kami miliki di peta jalan torchvision, tetapi itu tidak ada di daftar teratas kami dan mungkin memerlukan dukungan tensor bersarang terlebih dahulu.

Meskipun demikian, ini tidak akan menjadi perbaikan umum untuk masalah ini: itu hanya akan mengabaikan kebutuhan multi-pemrosesan dalam pemuatan data dengan menggunakan pendekatan yang berbeda (misalnya, transformasi pada GPU).

cc @cpuhrsch ketika saya melihat seseorang menyebutkan tensor bersarang. (Omong-omong, @cpuhrsch , dapatkah Anda membuat label modul untuk tensor bersarang dan menambahkannya sendiri di https://github.com/pytorch/pytorch/issues/24422 ?)

Mengapa bug ini tidak terpecahkan dalam setahun?

@IMLHF Lihat baris pertama dari deskripsi masalah ini atau diskusi di atas. Karena ini sebenarnya bukan kebocoran tetapi desain python yang tidak menguntungkan, yang berada di luar kendali kita. Baik pytorch dan numpy telah mencoba mengatasi ini dengan menerapkan serialisasi khusus untuk tensor dan ndarrays. Namun kami tidak dapat benar-benar menjelaskan struktur data umum. Ini terbuka karena kami menerapkan lebih banyak utilitas bagi pengguna untuk mengatasi masalah ini.

Menambahkan torch.cuda.empty_cache() di setiap akhir iterasi membantu saya memecahkan masalah ini. Penggunaan memori berfluktuasi bukannya terakumulasi setelah menambahkan ini.

mungkin kita harus menambahkan peringatan.

@VitalyFedyunin apakah Anda memiliki bandwidth untuk melihat ini? Setidaknya, dapatkah kami mengetahui apakah ini masalah yang sama dengan https://github.com/pytorch/pytorch/issues/17499?

Saya pikir saya memecahkan masalah ini dengan menggunakan ndarray daripada menggunakan tensor di proyek saya.

Kode saya sebelumnya adalah

def df2var(x):
    return (torch.LongTensor(token2id(x['Query'], max_char = max_length_char)), 
            torch.tensor(coll2id[x['Agg_Coll']], dtype = torch.long))

class Making_Dataset(Dataset):
    def __init__(self, input_dataframe):
        self.dataset = input_dataframe.apply(lambda x : df2var(x), axis = 1)

    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, data_index):
        return self.dataset[data_index]

Dan saya mereformasi kodenya sebagai

class Making_Dataset(Dataset):
    def __init__(self, input_dataframe):
        self.text = np.array([token2id(q, max_char = max_length_char) for q in input_dataframe.Query])
        self.labels = np.array([coll2id[coll] for coll in input_dataframe.Agg_Coll])

    def __len__(self):
        return len(self.text)

    def __getitem__(self, data_index):
        return self.text[data_index], self.labels[data_index]

Setelah memperbaiki kode, masalah peningkatan memori di setiap zaman hilang di proyek saya.
Karena saya tidak tahu persis apa yang menyebabkan masalah ini, komentar apa pun tentang ini dipersilakan!

Saya melihat masalah serupa dengan Torch 1.3.0 dengan CUDA 10 di Ubuntu 18.04. Ini bukan masalah pada mesin AWS dengan RAM 64GB, tetapi pada mesin lokal dengan RAM 128GB dan swap 128GB, saya bahkan tidak dapat melewati 150 zaman - penggunaan memori terus meningkat dari beberapa GB (diharapkan) menjadi 128 + GB.

Memperbarui

Masalah saya adalah bug kecil yang berbahaya - saat merekam statistik pelatihan, saya menyimpan informasi gradien selain nilai murni, yang keduanya tidak perlu dan menambah jejak memori Anda di setiap zaman.

mp.shared_memory Py 3.8 dapat memberikan solusi yang cukup baik untuk berbagi banyak objek non-tensor/nparray, misalnya, daftar bersama
https://github.com/pytorch/pytorch/issues/13246#issuecomment -513480017

Menggunakan banyak memori bersama memecahkan masalah bagi saya
https://github.com/pytorch/pytorch/issues/13246#issuecomment -487042977

Hai, agak terlambat untuk topik ini, tetapi saya menghadapi masalah yang sama tetapi dengan kamus.
Adakah yang berhasil dengan peretasan ini?

Ini masih masalah yang valid. Bisakah seseorang memberikan daftar praktik terbaik cara menggunakan banyak pekerja di DataLoaders tanpa menyebabkan kebocoran memori?

@marrrcin Saya pikir taruhan terbaik adalah menganggap tensor mahal, jadi Anda harus berhati-hati dengan seberapa banyak Anda menggunakannya, terutama jika ada kemungkinan mereka akan memiliki informasi gradien.

Misalnya, mungkin ada baiknya menyimpan semuanya sebagai daftar atau numpy.ndarray s sampai Anda perlu melakukan operasi torch

@AudreyBeard terima kasih atas jawabannya. Dalam kode Dataset saya, saya menyimpan semuanya sebagai numpy/lists/strings/int dan satu-satunya bagian di mana saya menggunakan tensor adalah __getitem__ dan kemudian di collate_fn (menerapkan padding). Haruskah saya membuat tensor dengan requires_grad disetel ke false di sana? Setelah kode saya masuk ke num_workers> 0 memori mulai bocor.

Maaf untuk format yang buruk, saya menggunakan ponsel.

@marrrcin Saya biasanya hanya memasukkan data input saya (gambar atau sinyal atau apa pun) sebagai tensor di __getitem__ . Label saya dan semacamnya biasanya dikembalikan sebagai daftar. Saya tidak yakin jenis data apa yang Anda gunakan atau jika Anda melakukan jenis padding khusus, tetapi saya biasanya menggunakan torchvision.transforms di __getitem__ saya. Untuk apa nilainya, saya sangat jarang menerapkan custom collate_fn .

Sebuah pemikiran: Saya awalnya memposting di sini karena saya mengalami apa yang saya pikir adalah kebocoran memori. Ternyata saya bergantung pada data yang tidak perlu setiap zaman, dan itu memiliki gejala kebocoran ketika itu benar-benar hanya manajemen variabel yang sangat halus di pihak saya. Butuh beberapa saat untuk mencari tahu apa yang sebenarnya terjadi.

@AudreyBeard kasus saya tidak terkait dengan gambar / torchvision, saya telah menggunakan bantalan untuk token yang diekstraksi dari teks dengan panjang variabel, itu sebabnya saya perlu menggunakan collate_fn .

class PaddingCollateFn:
    def __call__(self, batch):
        sorted_batch = sorted(batch, key=lambda x: x[0].shape[0], reverse=True)
        sequences = [x[0] for x in sorted_batch]
        sequences_padded = torch.nn.utils.rnn.pad_sequence(sequences, batch_first=True)

        attention_masks = [torch.tensor([1 for _ in x[0]]) for x in sorted_batch]

        attention_masks_padded = torch.nn.utils.rnn.pad_sequence(
            attention_masks, batch_first=True
        )
        lengths = torch.tensor([len(x) for x in sequences])
        labels = torch.tensor([x[1] for x in sorted_batch])

        return (sequences_padded, lengths, attention_masks_padded), labels

Haruskah saya menjatuhkan tensor asli setelah mengisinya (misalnya menggunakan del )? Saya pikir setelah collate_fn selesai, mereka akan keluar dari ruang lingkup dan dihapus karena tidak akan ada referensi untuk mereka.

Saya bertemu ini di Pytorch versi 1.3.1....Ketika saya melatih ImageNet....Apakah ada yang punya ide?
Dalam kasus saya, saya menggunakan num_workers dari 24, biasanya biayanya sekitar 110G memori di Epoch 1, tetapi ketika saya melatih epoch kedua, itu akan menghabiskan semua memori saya, dan sistem akan mematikan dataloader .....Saya benar-benar tidak' gk tau kenapa....

Bagi saya masalahnya adalah saya sudah mengonversi array numpy ke tensor obor di dataloader __getitem__

Array numpy hanya boleh dikonversi ke tensor obor di loop pelatih, tepat sebelum dikirim ke model. Jika tidak, tensor akan membuat memori bersama tumbuh di luar batas.

Anda dapat memantau memori bersama dengan menjalankan perintah watch -n .3 df -h
Memori bersama sesuai dengan baris /dev/shm
Jumlah yang digunakan tidak boleh meningkat setelah setiap epoch.

Saya memiliki masalah yang sama

bug ini tidak diselesaikan di pytorch 1.4.0

Saya juga memiliki masalah yang sama

Saya juga menghadapi masalah yang sama, bahkan setelah:
1) menghapus semua variabel yang tidak perlu
2) menggunakan array numpy alih-alih daftar
3) menggunakan gc.collect()

@annukkaa dan lainnya: hanya menggunakan np.array(list_of_paths) tidak cukup karena menyimpan daftar string sebagai banyak objek. Gunakan np.array(list_of_paths).astype(np.string_) untuk memasukkan array ke dalam array byte berbentuk persegi (dan pastikan untuk mengonversi dari byte ke str saat benar-benar menggunakan string). Itu akan membantu. Juga mengatur memori bersama ke nilai tinggi, katakanlah, 100GB.

Saya belum melihatnya disebutkan secara eksplisit di utas ini, jadi saya pikir saya akan membagikan solusi saya.
Dalam kasus saya, saya memiliki objek kelas khusus dan daftar string dalam dataset saya yang diakses setiap iterasi dan dengan cepat menghabiskan memori CPU saya.
Dengan membungkus kelas dan daftar menggunakan objek Manajer multiproses yang menangani status bersama, saya dapat menghilangkan kebocoran memori.

Untuk mengikat dengan contoh minimal kode akan terlihat seperti ini.

from torch.utils.data import Dataset, DataLoader
import torch
from multiprocessing import Manager


class DataIter(Dataset):
    def __init__(self):
        manager = Manager()
        self.data = manager.list([x for x in range(24000000)])

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

Ada beberapa overhead karena objek diasamkan, tetapi merupakan alternatif yang baik untuk membuat memori meledak.

Apakah hal ini setiap akan diperbaiki????

Sepertinya masalahnya masih terbuka.

Menggunakan ndarrays juga tidak membantu. Ini meningkatkan RAM CPU kira-kira 4 kali dengan pekerja sebagai nol.
Mencoba del namun tidak ada peningkatan yang signifikan.

Halo semua,

Saya baru saja mencoba solusi untuk ini dan ini bekerja seperti keindahan mutlak.

Bagi saya, saya menyimpan data imagenet sebagai array numpy yang disimpan secara lokal.

Saya telah menulis kumpulan data khusus saya sebagai ---

`impor obor
dari data impor obor.utils
impor numpy sebagai np

kelas DataSetBuilder(data.Dataset):
"""Dataset TinyImagenet."""

def __init__(self, rootpath, train=True, transform=None):
    """
    Args:
        rootpath: Path to the pytorch file with annotations.
        root_dir (string): Directory with all the images.
        transform (callable, optional): Optional transform to be applied
            on a sample.
    """
    self.path = rootpath
    self.transform = transform
    self.train = train
    # Load input data
    if self.train:
        self.X_ = np.load(self.path +'x_train.npy')
    else:
        self.X_ = np.load(self.path +'x_test.npy')
    # Load target data
    if self.train:
        self.y_ = np.load(self.path +'y_train.npy')
    else:
        self.y_ = np.load(self.path +'y_test.npy')

def __len__(self):
    if self.train:
        dataFile = self.path + 'x_train.npy'
    else:
        dataFile = self.path + 'x_test.npy'

    data = np.load(dataFile)
    return data.shape[0]

def __getitem__(self, idx):
    if torch.is_tensor(idx):
        idx = idx.tolist()
    X = self.X_[idx, :, :, :]
    y = self.y_[idx]
    if self.transform is not None:
        X = self.transform(X)
    return X, torch.from_numpy(y).type(torch.LongTensor)`

Alih-alih memuat data di __getitem__ saya memuatnya pada saat pembuatan objek yang berarti tidak memuat setiap kali array numpy yang sama ke dalam memori melainkan memuatnya sekaligus pada waktu pembuatan objek.

Semoga ini membantu!

Tinggalkan komentar jika ini berhasil untuk Anda ... :-)

Hai @varinder-singh,
Senang Anda menemukan solusi. Saya tidak melihat bagaimana ini berbeda dari contoh numpy yang diberikan oleh @bfreskura sebelumnya ? __getitem__ Anda juga mengiris data dari array numpy.
Mungkin saya salah membaca kode, maukah Anda menjelaskan kepada saya mengapa mereka akan memengaruhi konsumsi memori secara berbeda?

Setelah mengalami masalah ini dalam proyek saya saat ini, dan membaca utas ini, saya merasa mungkin berguna untuk menambahkan pemikiran saya sendiri dan memberikan solusi yang agak serbaguna.

Hal pertama yang pertama:

1) Mempertimbangkan semua yang dapat saya amati di sini, diagnosis @mprostock benar. Pekerjaan Anda menyelamatkan saya banyak waktu menggali sendiri.
2) Tentu saja respons @soumith juga benar, tetapi tidak berlaku dalam kasus ini karena alasan yang dinyatakan oleh posting selanjutnya @mprostock pada array objek.

Ini bukan masalah pyTorch. Ini adalah masalah Python dan, dengan demikian, harus diselesaikan di sana. Tetapi karena masalahnya disebabkan oleh penghitungan referensi, yang merupakan bagian integral dari manajemen memori Python, ini mungkin tidak akan terjadi dalam waktu dekat. Beberapa solusi yang diusulkan di atas menarik, tetapi mengapa sampai sejauh itu? Dengan asumsi tugasnya adalah untuk bersama-sama mengakses sejumlah urutan panjang variabel seperti nama file, Anda tidak perlu menemukan sesuatu yang baru. Cukup gunakan numpy untuk mengemas urutan dan melakukan pencarian tidak langsung. Untuk memahami apa yang saya maksud, lihat kode di bawah ini, yang sepenuhnya menghindari masalah yang dibahas di utas ini.

@mprostock dan @smolendawid String pada dasarnya hanyalah urutan bilangan bulat, yang merupakan tipe yang dapat ditangani dengan mudah di numpy. Contoh di bawah ini disesuaikan untuk berbagi daftar string (misalnya nama file gambar) di antara beberapa pemuat data.
@marrrcin Anda meminta praktik terbaik. Ini kuat dan berfungsi untuk daftar urutan panjang variabel apa pun. Dalam proyek saya saat ini, saya menggunakan varian yang sedikit lebih canggih dari ini untuk data multi-dimensi di mana setiap dimensi memiliki panjang variabel.
@SsnL Ini secara implisit memecahkan masalah yang Anda diskusikan dengan @zhiweifang di /issues/20433 tanpa menggunakan konstruksi Python 3.8 yang mewah.

import numpy as np
import torch
from typing import Union

# --- UTILITY FUNCTIONS ---
def string_to_sequence(s: str, dtype=np.int32) -> np.ndarray:
    return np.array([ord(c) for c in s], dtype=dtype)

def sequence_to_string(seq: np.ndarray) -> str:
    return ''.join([chr(c) for c in seq])

def pack_sequences(seqs: Union[np.ndarray, list]) -> (np.ndarray, np.ndarray):
    values = np.concatenate(seqs, axis=0)
    offsets = np.cumsum([len(s) for s in seqs])
    return values, offsets

def unpack_sequence(values: np.ndarray, offsets: np.ndarray, index: int) -> np.ndarray:
    off1 = offsets[index]
    if index > 0:
        off0 = offsets[index - 1]
    elif index == 0:
        off0 = 0
    else:
        raise ValueError(index)
    return values[off0:off1]


# --- OUR DATASET CODE STARTS HERE ---
class MyDataset(torch.utils.data.Dataset):

    def __init__(self):
        strings = [
            'I like', # You can use np.int8 for ASCII strings.
            'chocolate',
            '我喜欢', # If you use anything that is not standard ASCII,
            '巧克力', # need to use np.int16, or even np.int32.
        ]

        # Convert each string to sequence of codepoints (integer),
        # and then pack them into a numpy array.
        seqs = [string_to_sequence(s) for s in strings]
        self.strings_v, self.strings_o = pack_sequences(seqs)

    def __len__(self): return 4

    def __getitem__(self, i):
        # Use indirect lookup to fetch the i-th sequence. This only uses integer numpy
        # array lookups, which avoids that the objects are subsequently replicated by
        # child processes.
        seq = unpack_sequence(self.strings_v, self.strings_o, i)
        string = sequence_to_string(seq)
        # ACTION NEEDED: You probably do not want to return the string itself ;-).
        return string


m = MyDataset()
for i in range(len(m)):
    print(i, '=', m[i])

# Output
# -------
# 0 = I like
# 1 = chocolate
# 2 = 我喜欢
# 3 = 巧克力

Saya dapat memecahkan masalah ini dan ingin membagikan 2 sen saya. Saya pada dasarnya mengikuti ide-ide yang ditunjukkan oleh @harpone dan yang lainnya bahwa string adalah masalahnya. Saya memiliki 2 argumen bermasalah di kelas Dataset saya:

  1. array string yang numpy (casting menggunakan .astype(str) tidak membantu)
  2. kamus dari string ke vektor numpy

Saya harus memperbaiki 1 dan 2 untuk menghentikan kebocoran memori. Untuk 1, string saya sebenarnya adalah hash untuk mengakses vektor numpy dalam kamus, jadi saya mengonversi semua string menjadi bilangan bulat karena saya memiliki kamus ukuran tetap.

Untuk 2, saya mengonversi kamus untuk menggunakan kunci integer, namun kebocoran memori masih ada. Apa yang berhasil sebenarnya tidak meneruskan kamus ke kelas Dataset sama sekali, tetapi hanya mengembalikan kunci interger di __getitem___ dan melakukan pengindeksan/pormotion kamus ke tensor/promosi Pytorch ke GPU di loop kereta saya.

Adakah cara untuk membuat proses pemuat data menginisialisasi ulang diri mereka sendiri setiap zaman dan membersihkan semua memori yang bocor itu?

@Pozimek mereka sudah menginisialisasi ulang setiap zaman.

Jadi apa praktik terbaik SEKARANG?

@wangchust : solusi yang diusulkan oleh @bashimao telah bekerja dengan baik untuk saya, bahkan pada kumpulan data yang cukup besar (25 juta+ urutan teks).

@wangchust : solusi yang diusulkan oleh @bashimao telah bekerja dengan baik untuk saya, bahkan pada kumpulan data yang cukup besar (25 juta+ urutan teks).

Gerakan mengungkap kekerasan seksual demi menghapuskannya. Solusi dari @bashimao bekerja dengan sangat baik.

Halo semuanya, saya di sini lagi. Adakah yang memenuhi "OverflowError: tidak dapat membuat serial objek byte yang lebih besar dari 4 GiB" ketika pekerja pemuat data mengambil dari proses utama?

Halo semuanya, saya di sini lagi. Adakah yang memenuhi "OverflowError: tidak dapat membuat serial objek byte yang lebih besar dari 4 GiB" ketika pekerja pemuat data mengambil dari proses utama?

@wangchust Jika Anda membuat serial, Anda mungkin melakukan sesuatu yang salah. Setiap proses akan melakukan deserialize 4 atau seberapa besar objek Anda dalam Gigbytes dan merekonstruksi objek serial. Oleh karena itu, Anda akan mereplikasi memori dan akhirnya kehabisan memori jika ada banyak proses paralel. Inti dari tindakan yang diusulkan oleh saya sendiri dan juga orang lain di utas ini adalah untuk menghindari replikasi memori. Seperti yang dikatakan dalam kalimat pertama saya, saya yakin Anda melakukan sesuatu yang salah, mungkin pada tingkat yang cukup mendasar.

Array string yang didukung tensor khusus tampaknya membantu https://Gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 :

import torch

class TensorBackedImmutableStringArray:
    def __init__(self, strings, encoding = 'utf-8'):
        encoded = [torch.ByteTensor(torch.ByteStorage.from_buffer(s.encode(encoding))) for s in strings]
        self.cumlen = torch.cat((torch.zeros(1, dtype = torch.int64), torch.as_tensor(list(map(len, encoded)), dtype = torch.int64).cumsum(dim = 0)))
        self.data = torch.cat(encoded)
        self.encoding = encoding

    def __getitem__(self, i):
        return bytes(self.data[self.cumlen[i] : self.cumlen[i + 1]]).decode(self.encoding)

    def __len__(self):
        return len(self.cumlen) - 1

    def __list__(self):
        return [self[i] for i in range(len(self))]

Mungkin sth seperti ini bahkan layak untuk dimasukkan ke dalam inti PyTorch

Adakah yang beruntung membuat kamus berfungsi dan tidak bocor?
Saya melihat posting ini di atas , tetapi saya ingin akses ke beberapa jenis tabel hash di dalam pekerja alih-alih melakukan pekerjaan di luarnya seperti yang disarankan oleh komentar itu.

Saya sedang mempertimbangkan salah satu dari berikut ini:

  • Dikte Manajer multiprosesing
  • memori bersama atau mmap
  • tabel hash berbasis numpy buatan sendiri tanpa objek objek apa pun.

Memori bersama terlihat seperti opsi python yang paling menjanjikan dan asli. Saya ingin tahu mengapa Anda menggunakan dict? Pola umum di sini adalah memiliki daftar item (biasanya string) dan mengindeksnya.

Bagi saya itu awalnya daftar dicts (daftar contoh metadata, setiap contoh adalah dict)

Mengerti. Umumnya dict membuatnya lebih sulit, karena pola akses memori tidak berurutan. Saya sedang berpikir untuk menambahkan dukungan struktur data Fork-safe (tidak yakin apakah membangun atau memisahkan repo).

Memori bersama terlihat seperti opsi python yang paling menjanjikan dan asli. Saya ingin tahu mengapa Anda menggunakan dict? Pola umum di sini adalah memiliki daftar item (biasanya string) dan mengindeksnya.

@VitalyFedyunin Terima kasih atas tipnya. Saya dapat mencoba memori bersama terlebih dahulu.
Alasan untuk dict saat ini adalah untuk O(1) pencarian elemen untuk fungsi pengambilan sampel acak dalam langkah pembuatan data. Lebih khusus lagi, "penambangan triplet" di mana dict dikunci pada user_id, dan nilainya adalah daftar contoh positif yang terkait dengan pengguna itu. Lihat di sini untuk contoh.

@marrrcin Saya biasanya hanya memasukkan data input saya (gambar atau sinyal atau apa pun) sebagai tensor di __getitem__ . Label saya dan semacamnya biasanya dikembalikan sebagai daftar. Saya tidak yakin jenis data apa yang Anda gunakan atau jika Anda melakukan jenis padding khusus, tetapi saya biasanya menggunakan torchvision.transforms di __getitem__ saya. Untuk apa nilainya, saya sangat jarang menerapkan custom collate_fn .

Sebuah pemikiran: Saya awalnya memposting di sini karena saya mengalami apa yang saya pikir adalah kebocoran memori. Ternyata saya bergantung pada data yang tidak perlu setiap zaman, dan itu memiliki gejala kebocoran ketika itu benar-benar hanya manajemen variabel yang sangat halus di pihak saya. Butuh beberapa saat untuk mencari tahu apa yang sebenarnya terjadi.

@AudreyBeard terima kasih. ini membantu dan menyelesaikan masalah saya.

Sesuatu yang saya ingin tahu adalah (1) mengapa shuffle sangat memengaruhi konsumsi memori dan (2) mengapa total penggunaan memori tampaknya jauh lebih banyak daripada jumlah proses * ukuran atribut data.

Dalam contoh oleh @bfreskura ukuran self.data adalah bilangan bulat 24e7, yang kira-kira 1.83GB. Jika kita menurunkannya ke 24e5 (agar skrip dapat berjalan dengan cepat hingga selesai), maka ukuran objek datanya kira-kira 18,92MB.

Dalam kasus daftar Python, jika set shuffle=False, saya mengukur bahwa proses tersebut menghabiskan 298,17 MB. Saya kemudian mengatur shuffle=True, saya mengukur proses mengkonsumsi 1,44GB.

Jadi, lebih dari 18 pekerja + 1 proses induk utama, bahkan jika semua data disalin ke setiap proses, itu hanya paling banyak 359,48 MB tambahan RAM. Bagaimana kasusnya ketika shuffle=True saya mendapatkan hampir 4 kali lipat dari jumlah itu? Saya membayangkan itu pasti ada hubungannya dengan akses memori sekuensial vs acak dan kesalahan halaman yang dihasilkan, tapi saya ingin tahu apakah ada yang bisa lebih tepat menggambarkan apa yang terjadi di sini.

Untuk referensi modifikasi saya (api CLI + pelaporan konsumsi memori) ke skrip @bfreskura ada di sini:

https://Gist.github.com/Erotemic/3f017de31529dc64c1a54948f37da1d5

Akses acak akan memaksa Python untuk menulis penghitung objek kembali ke memori, menyebabkan copy-on-write frame memori. Akses berurutan berpotensi dapat dioptimalkan dengan tidak menulis penghitung yang tidak berubah (kemungkinan tergantung pada siklus GC). Juga jauh lebih aman (sejauh ini) untuk memperkirakan dengan penggunaan puncak yaitu jumlah pekerja * (semua ukuran objek + jumlah objek * penunjuk objek python + ukuran penghitung). Kami sedang mengerjakan solusi untuk mencegah salinan memori penuh, tetapi memerlukan arsitektur ulang yang signifikan dan akan memakan waktu.

@VitalyFedyunin terima kasih atas penjelasannya, tapi saya khawatir saya belum mengerti :senyum:

Saya telah berhasil menyelesaikan masalah di atas dengan menggunakan array numpy alih-alih daftar, dan misalnya array numpy byte tipe persegi np.string_ , tetapi sekarang saya menghadapi masalah yang tampaknya serupa dengan webdataset (https:/ /github.com/tmbdev/webdataset/issues/24#issuecomment-709101119). Sepertinya saya tidak kehabisan shm, tetapi seperti yang ditunjukkan @tmbdev sebelumnya di utas webdataset, masalahnya bisa jadi _number_ segmen memori bersama ...

Apakah Anda memiliki kiat tentang cara men-debug masalah ini dan/atau peretasan sementara di sekitarnya? Saya sudah mencoba ipcs tetapi itu tidak menunjukkan sesuatu yang berguna bagi saya (saya pikir). lsof /dev/shm menunjukkan beberapa info tentang objek dan ukuran shm, tapi saya tidak yakin apa artinya...

Bagi saya, mengukur proportional set size (pss di psutil) membantu mengukur ukuran masalah. Saya mengatasinya dengan kelas StringArray dan DictArray khusus: https://Gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 yang mengemas string/dicts dalam tensor datar (tidak ada NumPy yang digunakan)

@wangchust : solusi yang diusulkan oleh @bashimao telah bekerja dengan baik untuk saya, bahkan pada kumpulan data yang cukup besar (25 juta+ urutan teks).

Maaf mungkin saya melewatkan sesuatu tentang penggunaan github tetapi saya tidak melihat solusi dari @bashimao di utas ini, hanya komentar. Adakah yang bisa mengarahkan saya ke sana?

Jauh lebih sederhana untuk hanya dilemparkan ke np.string_ (perhatikan BUKAN str dll). Katakanlah kamu punya

strings = ['hello', 'world']

lalu lakukan

strings_byte = np.array(strings).astype(np.string_)

Hasilnya kemudian akan menjadi array byte persegi tunggal (perhatikan tipe d):

array([b'hello', b'world'], dtype='|S5')

Anda kemudian harus menyandikan kembali ke string ketika memilih string dari itu, misalnya str(strings_byte[0], encoding='utf-8') .

Perhatikan bahwa ini tidak akan berhasil:

strings_byte = np.array(strings).astype(str)

perhatikan tipe d:

array(['hello', 'world'], dtype='<U5')

Itu bukan array byte persegi yaitu bukan objek tunggal.

Akan sangat membantu mengingat kegigihan masalah ini dan berapa kali saya atau kolega saya mengalami masalah ini jika kami dapat memiliki beberapa resep untuk menentukan apakah ini penyebabnya atau tidak. Setelah membaca utas ini dengan saksama, sepertinya ada saran bagus untuk mengurangi masalah (https://github.com/pytorch/pytorch/issues/13246#issuecomment-436632186, https://github.com/pytorch/pytorch/issues /13246#issuecomment-612396143), tetapi juga beberapa perilaku yang membingungkan (https://github.com/pytorch/pytorch/issues/13246#issuecomment-708067670).

  1. Apakah cukup menjalankan pemuat data sendirian dalam beberapa saat True loop dan mengamati penggunaan memori untuk mengesampingkan masalah ini? Saya kira jika memori tumbuh saat loop berjalan maka kita dapat menyimpulkan bahwa objek dataset kita memiliki beberapa perilaku patologis yang mengakumulasi objek atau bahwa kita mengalami masalah ini?
  2. Hal yang benar-benar tidak dapat saya pahami dari utas ini adalah mengapa ini menjadi masalah jika Anda memiliki kelas kumpulan data yang hanya menyimpan beberapa MB data. Jika saya mengerti dengan benar, masalah yang diuraikan di sini adalah bahwa objek python apa pun yang diakses oleh kumpulan data pada akhirnya akan disalin ke memori utas pekerja. Jika saya memiliki kelas kumpulan data sederhana yang memuat video dari daftar jalur yang disimpan sebagai bidang di kelas kumpulan data, mengapa itu menjadi masalah? Data yang dibagikan sangat kecil sehingga dapat diabaikan. Mengapa penggunaan shuffle=True dalam pemuat data menghasilkan penggunaan memori yang lebih tinggi seperti yang dijelaskan dalam https://github.com/pytorch/pytorch/issues/13246#issuecomment -708067670?

Solusi yang cocok untuk saya - https://t.me/snakers4/2577

Solusi yang cocok untuk saya - https://t.me/snakers4/2577

Ini bagus! Saya kira satu-satunya keuntungan dari metode saya di https://Gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 adalah bahwa objek yang dikemas dengan tensor dapat dibagikan antara pekerja DDP dengan primitif DDP (yaitu kita membaca objek dataset raksasa hanya dalam satu utas, lalu sebarkan objek dataset paket tensor ke peringkat DDP lainnya). Dengan cara yang sama, pekerja master DDP dapat mengumpulkan beberapa larik string yang dikemas dengan tensor dari peringkat DDP.

Kejadian dunia nyata lain dari bug ini: https://github.com/NVIDIA/NeMo/issues/1467

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

Coderx7 picture Coderx7  ·  3Komentar

ikostrikov picture ikostrikov  ·  3Komentar

negrinho picture negrinho  ·  3Komentar

kdexd picture kdexd  ·  3Komentar

SeparateReality picture SeparateReality  ·  3Komentar