В фрагменте PyTorch: for data,label in loader: optimizer.zero_grad(); output=model(data); loss=criterion(output,label); loss.backward(); — студенты жалуются, что модель не обучается; предложите распространённые ошибки (включая использование detach(), неправильный режим model.eval()/train(), проблемы с нормализацией) и способ диагностировать их
Коротко — перечислю распространённые ошибки, почему модель «не учится», и как это быстро диагностировать и исправить. 1) Нет шага оптимизатора / неправильный порядок - Ошибка: забыли `optimizer.step()` или вызываете его до `loss.backward()`, или переинициализируете оптимизатор внутри цикла. - Диагностика: убедитесь, что в цикле порядок: `optimizer.zero_grad(); output=...; loss=...; loss.backward(); optimizer.step()`. - Проверка параметров: сохранить копию весов до и после шага и сравнить (см. пункт "проверка обновления"). 2) Использование detach() / .data / torch.no_grad() - Ошибка: `output.detach()` или `some_tensor.detach()` или операции в `with torch.no_grad():` ломают граф и градиенты не проходят. - Диагностика: поиск `detach()`, `.data` и `no_grad()` в коде. Убедиться, что они не окружают прямой путь от параметров к loss. 3) Режимы model.train()/model.eval() - Ошибка: модель в режиме `eval()` при обучении — BatchNorm и Dropout не ведут себя правильно. - Диагностика: проверить `model.training` (ожидается `True` при обучении). Перед обучением явно вызвать `model.train()`. 4) Неправильная нормализация данных - Ошибка: данные не нормализованы или нормализованы другим образом, чем ожидалось при обучении предобученных частей сети. - Диагностика: убедиться, что используемые mean/std совпадают с теми, на которых обучалась база. Попробуйте подавать данные без нормализации и посмотреть на поведение (или с правильной нормализацией). 5) Неправильный loss / неправильные метки - Ошибка: используете `nn.CrossEntropyLoss` но подаёте one-hot вектор (нужно целые классы), или, наоборот, применяете softmax в модели перед `CrossEntropyLoss`. - Диагностика: проверить формы `output` и `label`. Для `CrossEntropyLoss` output = (N,C), label = (N,) с классами 0..C-1. 6) Параметры оптимизатора не те / lr слишком мал/велик - Ошибка: оптимизатор создан не для параметров модели, lr очень маленький или огромный. - Диагностика: убедиться, что `optimizer = optim.SGD(model.parameters(), lr=1e−31e-31e−3)` (пример). Посмотреть lr: `for pg in optimizer.param_groups: print(pg['lr'])`. Попробовать lr порядка 1e−31e-31e−3-1e−21e-21e−2. 7) Параметры заморожены (requires_grad=False) - Ошибка: случайно выставили `param.requires_grad = False`. - Диагностика: `for n,p in model.named_parameters(): print(n, p.requires_grad)`. 8) Градиенты нулевые / NaN - Ошибка: градиенты обнуляются, либо становятся NaN (например, из-за слишком большого lr или лог(0)). - Диагностика: после `loss.backward()` напечатать нормы градиентов: `for n,p in model.named_parameters(): print(n, p.grad.norm())`. Если всё примерно 000 — градиенты отсутствуют. Если `nan` — уменьшите lr, проверьте входы, используйте градиентную нормализацию/клиппинг. 9) Ошибка расположения device (CPU vs CUDA) - Ошибка: данные на CPU, модель на GPU (или наоборот) — операции не выполняются правильно. - Диагностика: `print(next(model.parameters()).device, data.device, label.device)`. 10) Потеря графа через inplace-операции или неправильную архитектуру - Ошибка: inplace-операции (e.g. `x += y`) могут повредить граф; замените на не-inplace. - Диагностика: включить `torch.autograd.set_detect_anomaly(True)` и смотреть traceback при `loss.backward()`. 11) Проблемы с батч-сайзом / шифтом меток / датасетом - Ошибка: метки сдвинуты, класс всегда один, dataset возвращает постоянные тензоры. - Диагностика: проверить несколько батчей: распечатать распределение меток, визуализировать входы. Практическая быстрая диагностика (шаги, которые быстро выявят проблему) 1) Попытаться переобучить на одном батче (overfit single batch): - Взять один батч и запускать обучение много эпох; loss должен быстро падать к низкому значению. - Если не падает — проблема в модели/потоке градиентов/функции потерь. 2) Проверка обновления весов: - Сохраняете веса одного параметра до и после `optimizer.step()`; сравните. Если не меняются — optimizer или grad проблема. 3) Проверка градиентов: - После `loss.backward()` выполнить: - `for n,p in model.named_parameters(): print(n, p.grad is None, p.grad.norm() if p.grad is not None else None)` 4) Проверка режима: - `print("training?", model.training)`; принудительно `model.train()` перед циклом. 5) Включить диагностику аномалий: - `torch.autograd.set_detect_anomaly(True)` — поможет найти операцию, порождающую NaN/разрыв графа. 6) Проверка device и dtype: - `print(next(model.parameters()).device, data.device)`; убедиться, что dtype совпадают (float32). 7) Минимальный воспроизводимый пример: - Попробовать простую модель (один Linear), простую loss и SGD на одном батче — если это работает, проблема в архитектуре/данных. Короткий чеклист (в порядке проверки) - Есть `optimizer.step()` после `backward()`? Да/Нет. - Нет `detach()` / `no_grad()` на пути к loss? Да/Нет. - model.train() включён? Да/Нет. - Параметры имеют `requires_grad=True`? Да/Нет. - Градиенты после backward не равны нулю/NaN? Да/Нет. - Optimizer настроен на `model.parameters()` и lr адекватен? Да/Нет. - BatchNorm/Dropout работают как ожидалось? Да/Нет. - Данные корректно нормализованы и имеют правильные метки? Да/Нет. - Нет device-mismatch? Да/Нет. Если нужно, могу прислать короткий диагностический скрипт (код), который автоматически проверит пункты выше.
1) Нет шага оптимизатора / неправильный порядок
- Ошибка: забыли `optimizer.step()` или вызываете его до `loss.backward()`, или переинициализируете оптимизатор внутри цикла.
- Диагностика: убедитесь, что в цикле порядок: `optimizer.zero_grad(); output=...; loss=...; loss.backward(); optimizer.step()`.
- Проверка параметров: сохранить копию весов до и после шага и сравнить (см. пункт "проверка обновления").
2) Использование detach() / .data / torch.no_grad()
- Ошибка: `output.detach()` или `some_tensor.detach()` или операции в `with torch.no_grad():` ломают граф и градиенты не проходят.
- Диагностика: поиск `detach()`, `.data` и `no_grad()` в коде. Убедиться, что они не окружают прямой путь от параметров к loss.
3) Режимы model.train()/model.eval()
- Ошибка: модель в режиме `eval()` при обучении — BatchNorm и Dropout не ведут себя правильно.
- Диагностика: проверить `model.training` (ожидается `True` при обучении). Перед обучением явно вызвать `model.train()`.
4) Неправильная нормализация данных
- Ошибка: данные не нормализованы или нормализованы другим образом, чем ожидалось при обучении предобученных частей сети.
- Диагностика: убедиться, что используемые mean/std совпадают с теми, на которых обучалась база. Попробуйте подавать данные без нормализации и посмотреть на поведение (или с правильной нормализацией).
5) Неправильный loss / неправильные метки
- Ошибка: используете `nn.CrossEntropyLoss` но подаёте one-hot вектор (нужно целые классы), или, наоборот, применяете softmax в модели перед `CrossEntropyLoss`.
- Диагностика: проверить формы `output` и `label`. Для `CrossEntropyLoss` output = (N,C), label = (N,) с классами 0..C-1.
6) Параметры оптимизатора не те / lr слишком мал/велик
- Ошибка: оптимизатор создан не для параметров модели, lr очень маленький или огромный.
- Диагностика: убедиться, что `optimizer = optim.SGD(model.parameters(), lr=1e−31e-31e−3)` (пример). Посмотреть lr: `for pg in optimizer.param_groups: print(pg['lr'])`. Попробовать lr порядка 1e−31e-31e−3-1e−21e-21e−2.
7) Параметры заморожены (requires_grad=False)
- Ошибка: случайно выставили `param.requires_grad = False`.
- Диагностика: `for n,p in model.named_parameters(): print(n, p.requires_grad)`.
8) Градиенты нулевые / NaN
- Ошибка: градиенты обнуляются, либо становятся NaN (например, из-за слишком большого lr или лог(0)).
- Диагностика: после `loss.backward()` напечатать нормы градиентов: `for n,p in model.named_parameters(): print(n, p.grad.norm())`. Если всё примерно 000 — градиенты отсутствуют. Если `nan` — уменьшите lr, проверьте входы, используйте градиентную нормализацию/клиппинг.
9) Ошибка расположения device (CPU vs CUDA)
- Ошибка: данные на CPU, модель на GPU (или наоборот) — операции не выполняются правильно.
- Диагностика: `print(next(model.parameters()).device, data.device, label.device)`.
10) Потеря графа через inplace-операции или неправильную архитектуру
- Ошибка: inplace-операции (e.g. `x += y`) могут повредить граф; замените на не-inplace.
- Диагностика: включить `torch.autograd.set_detect_anomaly(True)` и смотреть traceback при `loss.backward()`.
11) Проблемы с батч-сайзом / шифтом меток / датасетом
- Ошибка: метки сдвинуты, класс всегда один, dataset возвращает постоянные тензоры.
- Диагностика: проверить несколько батчей: распечатать распределение меток, визуализировать входы.
Практическая быстрая диагностика (шаги, которые быстро выявят проблему)
1) Попытаться переобучить на одном батче (overfit single batch):
- Взять один батч и запускать обучение много эпох; loss должен быстро падать к низкому значению.
- Если не падает — проблема в модели/потоке градиентов/функции потерь.
2) Проверка обновления весов:
- Сохраняете веса одного параметра до и после `optimizer.step()`; сравните. Если не меняются — optimizer или grad проблема.
3) Проверка градиентов:
- После `loss.backward()` выполнить:
- `for n,p in model.named_parameters(): print(n, p.grad is None, p.grad.norm() if p.grad is not None else None)`
4) Проверка режима:
- `print("training?", model.training)`; принудительно `model.train()` перед циклом.
5) Включить диагностику аномалий:
- `torch.autograd.set_detect_anomaly(True)` — поможет найти операцию, порождающую NaN/разрыв графа.
6) Проверка device и dtype:
- `print(next(model.parameters()).device, data.device)`; убедиться, что dtype совпадают (float32).
7) Минимальный воспроизводимый пример:
- Попробовать простую модель (один Linear), простую loss и SGD на одном батче — если это работает, проблема в архитектуре/данных.
Короткий чеклист (в порядке проверки)
- Есть `optimizer.step()` после `backward()`? Да/Нет.
- Нет `detach()` / `no_grad()` на пути к loss? Да/Нет.
- model.train() включён? Да/Нет.
- Параметры имеют `requires_grad=True`? Да/Нет.
- Градиенты после backward не равны нулю/NaN? Да/Нет.
- Optimizer настроен на `model.parameters()` и lr адекватен? Да/Нет.
- BatchNorm/Dropout работают как ожидалось? Да/Нет.
- Данные корректно нормализованы и имеют правильные метки? Да/Нет.
- Нет device-mismatch? Да/Нет.
Если нужно, могу прислать короткий диагностический скрипт (код), который автоматически проверит пункты выше.