Почему ResNet18 переобучается: точность 97% на обучении против 77% на валидации

    При обучении нейросети ResNet18 для классификации 6 видов животных по следам вы столкнулись с типичной проблемой глубокого обучения: отличные показатели на тренировочной выборке (97% accuracy) и скромные 77% на валидации. Разрыв в 20% говорит о переобучении - модель запомнила шумы и детали обучающих фото, вместо того чтобы выделять общие признаки следа. Рассмотрим причины и способы решения.

    Почему возникает переобучение на ResNet18?

    Исходная архитектура ResNet18 предобучена на ImageNet (1,2 млн изображений), а ваш датасет - всего 3000 снимков следов. Модель слишком мощная для такого объёма данных: полносвязный слой из 256 нейронов добавляет более 2 млн параметров, которые легко запоминают выборку. Оптимизатор Adam с lr=1e-3 и 20 эпох усиливает эффект - градиенты быстро сходятся к локальному минимуму на обучающих данных, не обобщаясь на новые примеры.

    5 шагов для уменьшения переобучения и повышения macro F1

    1. Усилите аугментацию данных

    Текущие аугментации (RandomResizedCrop, HorizontalFlip, Rotation, ColorJitter) - хороший старт, но для следов животных этого мало. Добавьте:

    • RandomGrayscale(0.1) - имитация разной освещённости
    • GaussianBlur(kernel_size=(3,3), sigma=(0.1, 1.0)) - смазывание от движения
    • RandomPerspective(distortion_scale=0.2, p=0.3) - искажение перспективы

    Это заставит сеть учиться по форме и текстуре следа, а не по цвету или чёткости краёв.

    2. Уменьшите сложность головы модели

    Замените два полносвязных слоя (256 → 6) на один слой с Dropout. Пример:

    model_ft.fc = nn.Sequential( nn.Dropout(0.5), nn.Linear(num_ftrs, num_classes) )

    Dropout 0.5 отключает половину нейронов случайным образом - это один из самых эффективных методов борьбы с переобучением.

    3. Используте L2-регуляризацию (weight decay)

    В оптимизатор Adam добавьте weight_decay=1e-4 или 1e-5. Это штрафует большие веса и не даёт модели подстраиваться под шум:

    optimizer = torch.optim.Adam(model_ft.parameters(), lr=1e-3, weight_decay=1e-4)

    4. Настройте learning rate и early stopping

    Уменьшите learning rate до 1e-4 и добавьте планировщик ReduceLROnPlateau - он снижает lr в 2 раза, если валидационная loss перестаёт падать. Также используйте EarlyStopping (терпение 3-5 эпох) - обучение остановится, когда метрики на валидации не улучшаются.

    5. Примените fine-tuning с заморозкой первых слоёв

    Заморозьте первые 6-7 блоков ResNet18 (слои, отвечающие за базовые признаки вроде краёв и текстур) и дообучайте только последние 3-4 блока + голову. Это уменьшит количество обучаемых параметров и сохранит общие представления из ImageNet. Пример:

    for name, param in model_ft.named_parameters(): if 'layer4' not in name and 'fc' not in name: param.requires_grad = False

    Дополнительные советы для классификации следов

    Попробуйте более лёгкую архитектуру - например, MobileNetV3-Small или EfficientNet-B0. Они имеют меньше параметров и менее склонны к переобучению на маленьких датасетах. Также проверьте разметку: возможно, в валидации есть классы с малым числом примеров (дисбаланс), из-за чего macro F1 занижен. Используйте weighted sampler для балансировки батчей.

    После внедрения этих методов ожидайте повышение macro F1 до 85-88% при сохранении accuracy на обучении на уровне 90-92% - здоровый разрыв в 5-10% считается нормальным для задач с 3000 изображений.

    Часто задаваемые вопросы