2025, Nov 12 21:01
Ошибка 'list' object has no attribute 'to' в PyTorch при S3MapDataset: как нормализовать батч
PyTorch и S3: решаем ошибку 'list' object has no attribute 'to' при работе с S3MapDataset — нормализуйте батч и применяйте .to(device) только к тензору.
Обучение напрямую из S3 удобно, пока первый батч не падает с непонятной ошибкой атрибута. Если конвейер ожидает один Tensor на батч, а источник данных возвращает списокоподобную структуру, простое перенесение на устройство вроде samples.to(device) завершится неудачей. Именно так может случиться при использовании s3torchconnector.S3MapDataset.from_prefix в окружении SageMaker.
Воспроизведение: когда батч — не Tensor
Ниже показана настройка, которая инициализирует датасет по префиксу в S3 и подает его в стандартный DataLoader. Модель и цикл типичные, но важный момент — входной сэмпл не всегда Tensor; при работе с S3 он может прийти как списокоподобный объект, из‑за чего возникает ошибка 'list' object has no attribute 'to'.
from PIL import Image
import torch
import torchvision
from torchvision import transforms
import s3torchconnector
# преобразование изображения, возвращающее идентификатор и тензор float32
def fetch_img(obj_ref):
pic = Image.open(obj_ref)
resizer = transforms.Resize(size=(224, 224))
pic = resizer(pic)
pic = transforms.functional.pil_to_tensor(pic)
return (obj_ref.key, torchvision.transforms.functional.convert_image_dtype(pic, dtype=torch.float32))
# датасет на базе S3
train_ds = s3torchconnector.S3MapDataset.from_prefix(
cfg.IMAGES_URI,
region=cfg.REGION,
transform=fetch_img,
)
# DataLoader
train_loader = torch.utils.data.DataLoader(
train_ds,
sampler=train_sampler,
batch_size=cfg.batch_size,
num_workers=cfg.num_workers,
pin_memory=cfg.pin_mem,
drop_last=True,
)
# Модель
model = models_mae.__dict__[cfg.model](norm_pix_loss=cfg.norm_pix_loss)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# Цикл обучения (фрагмент)
for ep in range(cfg.start_epoch, cfg.epochs):
if cfg.distributed:
train_loader.sampler.set_epoch(ep)
for batch in train_loader:
samples = batch
samples = samples.to(device, non_blocking=True) # ошибка: у объекта 'list' нет атрибута 'to'
Что происходит
Конвейер данных смешивает две разные предпосылки. Шаг обучения рассчитывает получить Tensor, чтобы вызвать .to(device, non_blocking=True). При загрузке через s3torchconnector.S3MapDataset.from_prefix сэмпл может прийти как списокоподобный контейнер вместо Tensor. Как только к этому списку применяется .to, возникает ошибка атрибутов. Тот же цикл работает с локальным датасетом, где сэмпл уже является Tensor, поэтому проблема проявляется только после перехода на S3.
Исправление: нормализуйте батч перед переносом на устройство
Практичное решение — привести входной батч к ожидаемому Tensor. Когда батч — список, нужный Tensor находится по индексу 1; в противном случае это уже Tensor. Такое условное приведение позволяет без изменений запускать обучение как с локальными данными, так и с S3.
for ep in range(cfg.start_epoch, cfg.epochs):
if cfg.distributed:
train_loader.sampler.set_epoch(ep)
for batch in train_loader:
inputs = batch
if type(inputs) == list:
inputs = inputs[1].to(device, non_blocking=True)
else:
inputs = inputs.to(device, non_blocking=True)
# продолжайте прямой/обратный проход, используя `inputs`
Почему это важно
Единообразие интерфейса батча критично при смене источников данных. Шаг обучения обычно зашивает ожидания по форме и типу входа. Если один источник возвращает Tensor, а другой — списокоподобную обертку, тот же код модели будет вести себя по‑разному. Нормализация батча в точке передачи гарантирует, что цикл будет столь же надежно работать с крупным датасетом на S3, как и с локальными файлами в SageMaker.
Выводы
Если после перехода на s3torchconnector обучение падает с ошибкой 'list' object has no attribute 'to', проверьте, что именно приходит в батче, и преобразуйте его к одному Tensor перед вызовом .to(...). Небольшая проверка типа, которая выбирает нужный элемент из списка и переносит его на устройство, возвращает совместимость и для локальных датасетов, и для загрузки из S3, позволяя модели читать и обучаться на выборке, упакованной в список, без дальнейших изменений остального тренировочного цикла.