Я обучаю сквозную модель на видео задаче. В качестве кодировщика я использовал Pytorch ResNet50, а входная форма — (1,seq_length,3,224,224), где seq_length — количество кадров в каждом видео. Например, если видео 1 имеет 1500 кадров, входная форма будет (1,1500,3,224,224), если видео 2 имеет 2000 кадров, входная форма будет (1,2000,3,224,224). Однако когда я передаю входные данные в Resnet, CUDA исчерпает память при прохождении через первый слой свертки в прямом проходе.
Я пробовал:
Есть ли какие-нибудь хаки, которые могут помочь с этой проблемой? Любая помощь приветствуется
Ниже приведено полное сообщение об ошибке
Traceback (most recent call last):
File "Path/E2E.py", line 143, in <module>
p_classes1, phase_preds = model1(long_feature)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1553, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1562, in _call_impl
return forward_call(*args, **kwargs)
File "Path/E2E.py", line 47, in forward
x = self.resnet_lstm(x)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1553, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1562, in _call_impl
return forward_call(*args, **kwargs)
File "Path/train_embedding.py", line 226, in forward
x = self.share.forward(x)
File "Path/lib/python3.9/site-packages/torch/nn/modules/container.py", line 219, in forward
input = module(input)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1553, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1562, in _call_impl
return forward_call(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/container.py", line 219, in forward
input = module(input)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1553, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1562, in _call_impl
return forward_call(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torchvision/models/resnet.py", line 155, in forward
out = self.bn3(out)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1553, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1562, in _call_impl
return forward_call(*args, **kwargs)
File "Path/lib/python3.9/site-packages/torch/nn/modules/batchnorm.py", line 176, in forward
return F.batch_norm(
File "Path/lib/python3.9/site-packages/torch/nn/functional.py", line 2512, in batch_norm
return torch.batch_norm(
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 614.00 MiB. GPU 0 has a total capacity of 23.65 GiB of which 353.38 MiB is free. Including non-PyTorch memory, this process has 23.28 GiB memory in use. Of the allocated memory 22.84 GiB is allocated by PyTorch, and 3.77 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation. See documentation for Memory Management (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)
Вот код модели Resnet50, слой self.fc существует просто потому, что я хочу иметь возможность загружать предварительно обученную модель.
class resnet_lstm(torch.nn.Module):
def __init__(self):
super(resnet_lstm, self).__init__()
resnet = models.resnet50(pretrained=True)
self.share = torch.nn.Sequential()
self.share.add_module("conv1", resnet.conv1)
self.share.add_module("bn1", resnet.bn1)
self.share.add_module("relu", resnet.relu)
self.share.add_module("maxpool", resnet.maxpool)
self.share.add_module("layer1", resnet.layer1)
self.share.add_module("layer2", resnet.layer2)
self.share.add_module("layer3", resnet.layer3)
self.share.add_module("layer4", resnet.layer4)
self.share.add_module("avgpool", resnet.avgpool)
self.fc = nn.Sequential(nn.Linear(2048, 512),
nn.ReLU(),
nn.Linear(512, 7))
def forward(self, x):
x = x.view(-1, 3, 224, 224)
x = self.share.forward(x)
x = x.view(1,-1, 2048)
return x
Вот реализация набора данных, file_paths — это список путей к изображениям длиной seq_length:
class CustomDataset(Dataset):
def __init__(self, file_paths, file_labels, transform=None,
loader=pil_loader):
self.file_paths = file_paths
self.file_labels_phase = file_labels
self.transform = transform
self.loader = loader
def __getitem__(self, index):
img_names_list = self.file_paths[index]
labels_phase = self.file_labels_phase[index]
imgs = [self.loader(img_name) for img_name in img_names_list]
if self.transform is not None:
imgs = [self.transform(img) for img in imgs]
return imgs, labels_phase, index
def __len__(self):
return len(self.file_paths)
class SeqSampler(RandomSampler):
def __init__(self, data_source, seed=1):
super().__init__(data_source)
self.data_source = data_source
self.seed = seed
def __iter__(self):
if self.seed is not None:
random.seed(self.seed)
# Generate a list of indices and shuffle them
indices = list(range(len(self.data_source)))
random.shuffle(indices)
return iter(indices)
def __len__(self):
return len(self.idx)
train_loaders = DataLoader(
train_dataset_80,
batch_size=1,
sampler=SeqSampler(train_dataset_80),
num_workers=1,
pin_memory=True,
)
Вот как я подаю входные данные, это довольно регулярно:
for data,labels_phase in tqdm(train_loaders,desc=f"Epoch {epoch+1}/{max_epochs}"):
long_feature = torch.tensor(np.array(data)).to(device)
labels_phase = np.asarray(labels_phase).squeeze()
optimizer1.zero_grad()
labels_phase = torch.LongTensor(labels_phase).to(device)
#Allocated: 2239.18 MB
#Cached: 2248.00 MB
p_classes = model1(long_feature)
Вот вывод nvidia-smi:
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 545.23.08 Driver Version: 545.23.08 CUDA Version: 12.3 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 NVIDIA RTX A6000 On | 00000000:25:00.0 Off | Off |
| 30% 24C P8 17W / 300W | 12MiB / 49140MiB | 0% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| 0 N/A N/A 7388 G /usr/lib/xorg/Xorg 4MiB |
+---------------------------------------------------------------------------------------+
Привет. Спасибо за ответ, надеюсь, с кодом будет понятнее
Основные вопросы: сколько у вас памяти графического процессора? Вы проверили nvidia-smi, чтобы узнать, сколько памяти доступно? Делаете ли вы в сценарии что-нибудь еще, что может выделять память графического процессора?
Я думаю, что памяти еще достаточно (47 ГБ), но позвольте мне проверить, есть ли другие вещи, которые потребляют память.
Хорошо, я только что проверил: перед тем, как входные данные передаются в модель, выделяется только 2G и кэшируется 2G (как показано в коде), поэтому я не думаю, что другие реализации являются проблемой.
Написал свои наблюдения в качестве ответа. Будем рады обсудить подробности в комментариях.
По сути, ваш подход заключается в том, чтобы рассматривать каждый кадр видео как отдельное изображение.
Когда ты это делаешь
for data,labels_phase in tqdm(train_loaders,desc=f"Epoch {epoch+1}/{max_epochs}"):
Я не знаю, как инициализируется ваш train_loaders
, но если он инициализируется с размером пакета b
, то когда вы делаете x = x.view(-1, 3, 224, 224)
в forward()
, вы обрабатываете каждый кадр видео как отдельное изображение, поэтому эффективный размер пакета становится b * seq_length
, что может получиться довольно большим.
Я думаю, что это основная причина ваших проблем с памятью. Одним из очевидных решений было бы разбить видео на несколько частей, чтобы seq_length
эффективно снижался. Чтобы создать одно вложение для каждого видео, вы можете объединить вложения всех фрагментов (например, усреднив их).
Альтернативные подходы
Ваш подход не только неэффективен с точки зрения памяти, но и не использует временную семантику видео (поскольку вы считаете, что видео имеет огромный набор отдельных несвязанных кадров изображения).
Вместо этого вы можете попробовать:
forward()
).Привет. Спасибо, что ответили мне. Я пробовал разбить видео на несколько частей, например, если у меня есть видео из 1000 кадров, я группирую каждые 100 кадров в одну группу данных, создавая входные данные (1, 100, 3, 224, 224). Но это приведет к значительному снижению производительности. И я только что обновил реализацию своего набора данных в своем посте, если вам интересно.
О каком спектакле идет речь? Показатели качества модели или производительность памяти? Если первое, вам нужно показать, как вы обучаете модель (с применением функции потерь). То, что вы показали, — это всего лишь прямой проход (он же вывод).
Извините за поздний ответ, проблему я уже решил. Большое спасибо!
Предоставьте базовый код, показывающий, как вы используете RestNet50 для работы с данными формы
(1,seq_length,3,224,224)
. Ренеты — это модели изображений, и они не требуют временного измерения, поэтому мне хотелось бы посмотреть, как вы их адаптируете.