Глубокое обучение имеет репутацию входа за семью печатями: годы математики, кластеры с видеокартами и горы кода ради одной сети. Библиотека fastai рождена из курса её авторов, чтобы этот вход сократить: типовая задача классификации картинок решается пятью строками, а табличная модель стартует без единой строчки ручной архитектуры. Под капотом остаётся честный PyTorch, и в любой момент можно спуститься на его уровень. Актуальная версия 2.8.12 требует Python 3.10 и новее. Разберём путь от папки с файлами до готовой модели в продакшене, с рабочим кодом и разбором каждого шага. Материал следует духу исходного курса: минимум теории, максимум практики, которая работает.
Библиотека fastai надстраивается над PyTorch для быстрого результата
fastai не конкурирует с PyTorch, он сидит на нём сверху. Каждый цикл обучения, слой и функция потерь остаются обычными объектами PyTorch, а высокоуровневые обёртки собирают из них пайплайн по проверенным практикам: передача переноса, однодорожечная скорость обучения, аугментации на графическом процессоре и смешанная точность включаются по умолчанию. Философия простая: лучшие находки исследований должны работать из коробки, а не требовать трёх страниц настроек.
История у проекта учебная: курс авторов стартовал в конце 2010-х, первая версия библиотеки вышла в 2018 году, вторая в 2020-м переписала API поверх PyTorch и закрепилась в книге для практиков. Такой подход дал библиотеке двойную жизнь. Студенты проходят на ней курс от распознавания кошек до развёртывания, а инженеры берут за основу прототипов, потому что первый результат на новом наборе ценнее идеальной чистоты. Когда прототип надо доработать, слои PyTorch доступны напрямую, и библиотека не превращается в стеклянный потолок.
Набор доменов при этом не ограничивается картинками: зрение, текст, таблицы и рекомендательные задачи имеют свои готовые заготовки, и все они проходят через одни и те же три шага: собрать загрузчик, создать ученика, запустить обучение.
DataBlock описывает путь от папки с файлами до пакетного загрузчика
Центральная идея fastai в сборке конвейера значений: из сырых файлов нужно получить типизированные пакеты, где каждый элемент уже приведён к нужному виду. Конструктор DataBlock описывает этот путь декларативно, одна инструкция на каждый шаг:
- blocks задаёт типы входа и цели, например картинку и категорию;
- get_items говорит, откуда брать список файлов;
- splitter отделяет обучающую и проверочную части;
- get_y достаёт метку класса из имени или структуры пути;
- item_tfms приводит каждый элемент к единому размеру до сбора в пакет;
- batch_tfms применяет аугментации к целому пакету на графическом процессоре.
from fastai.vision.all import *
db = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(seed=42),
get_y=parent_label,
item_tfms=Resize(256),
batch_tfms=aug_transforms(size=224),
)
dls = db.dataloaders(path, bs=64)
dls.show_batch(max_n=6)
Вызов show_batch рисует горстку элементов прямо из конвейера, и это первое, что делает практик: глаз дешевле метрик ловит перепутанные метки, чёрные квадраты и нечитаемые файлы. Словарь классов лежит в атрибуте vocab, и по нему же проверяют порядок меток в предсказаниях, а не гадают по числам. Разделение на аугментации по элементам и по пакету не каприз: первые работают с одиночной картинкой на процессоре, вторые крутят и переворачивают целый пакет параллельно на видеокарте, и на это уходит доля времени эпохи.
Для структурированных хранилищ есть и короткий путь: ImageDataLoaders из имени папки или функции, TextDataLoaders из текстовых файлов, а для таблиц собственный конвейер, о котором ниже. Оба входа сходятся в один и тот же тип загрузчика, и ученик не различает их происхождение.
Обучение передачей переноса стартует с одной строкой fine_tune
Обучение с нуля на скромной коллекции картинок обречено на переобучение. fastai ставит во главу угла передачу переноса: берётся сеть, натренированная на миллионах изображений, её выученные фильтры замораживаются, и сначала доучивается только голова под новые классы. Метод fine_tune делает оба этапа одной строкой:
learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(3)
Первая эпоха замораживает ствол и настраивает финальный слой, следующие размораживают сеть целиком с малой скоростью. Для стандартной задачи с десятками тысяч картинок и тридцатью классами такой запуск даёт рабочую точность за считанные минуты на одной видеокарте, а(resnet) резерва хватает и для более тяжёлых стволов вроде resnet50 или более современных архитектур.
Метрика error_rate выводится по каждой эпохе вместе с потерями, и прогресс читается без досмотра журналов. Смешанная точность включается методом to_fp16 и экономит память видеокарты почти вдвое, что на скромных картах разница между влезанием и невлезанием.
Порядок работы на новом наборе устоялся годами. Сначала show_batch и быстрая пара эпох, чтобы конвейер не падал. Затем подбор скорости обучения и полный прогон. Затем диагностика ошибок, чистка разметки и повтор. Именно цикл разметка, обучение, диагностика, а не слепой одноразовый запуск, даёт прирост качества.
Подбор скорости обучения и расписания меняет качество на порядок
Скорость обучения остаётся той ручкой, которая чаще всего отделяет хороший результат от неудовлетворительного. fastai умеет предложить значение сама: метод lr_find гоняет сеть с экспоненциально растущей скоростью и рисует кривую потерь, а выбирается точка на спаде перед взлётом:
learn.lr_find()
Дальше в дело вступает расписание одинарного цикла: скорость растёт к середине и плавно падает к концу, момент держится в пути. Метод fit_one_cycle реализует его целиком:
learn.fit_one_cycle(6, 3e-3)
Практика устойчива: стартовая скорость из окна lr_find, шесть эпох с расписанием и замораживанием дают базу, от которой уже отталкиваются размораживания послойно и подбор аугментаций. Сохранение промежуточных состояний делается методом save, а финальная модель для встраивания экспортируется отдельно, о чём ниже.
Отдельного упоминания стоит постепенное размораживание: после обучения головы ствол отпускается по слоям с дифференцированными скоростями, и каждая новая порция параметров учится в десять раз медленнее предыдущей. Диапазон задаётся срезом slice, ранням слоям достаётся меньшая скорость:
learn.unfreeze()
learn.fit_one_cycle(4, lr_max=slice(1e-5, 1e-3))
На маленьких наборах это добавляет пару процентов точности, что на практике нередко означает переход от сомнительного прототипа к рабочему решению.
Табличные и текстовые задачи закрывают те же три шага
Зрения в библиотеке больше всего, но табличные задачи в быту встречаются чаще. Прогноз оттока, скоринг заявки, оценка риска: всё это таблица, часть колонок категориальные, часть числовые, и цель одна. Конвейер строится той же тройкой шагов:
from fastai.tabular.all import *
splits = RandomSplitter(seed=42)(df)
to = TabularPandas(
df,
procs=[Categorify, FillMissing, Normalize],
cat_names=['region', 'channel'],
cont_names=['age', 'income'],
y_names='churn',
y_block=CategoryBlock,
splits=splits,
)
dls = to.dataloaders()
learn = tabular_learner(dls, metrics=accuracy)
learn.fit_one_cycle(5)
Процессоры в списке procs делают за конвейер то, что обычно пишут руками: категории получают числовые коды, пропуски помечаются отдельной колонкой, числовые колонки нормируются. Для текста путь похож и кодируется так же коротко:
from fastai.text.all import *
dls = TextDataLoaders.from_folder(path, valid_pct=0.2)
learn = language_model_learner(
dls, AWD_LSTM, drop_mult=0.5,
metrics=[accuracy, Perplexity()],
)
learn.fit_one_cycle(1, 1e-2)
Языковая модель доучивается на собственном корпусе, затем классификатор донастраивается поверх кодировок, и для тональности отзывов или разбора тем такой конвейер выручает без развёртывания больших языковых моделей. Показатель недоумения Perplexity подскажет, насколько модель предсказывает следующий токен, а точность на отложенной части скажет про пригодность для задачи.
Рекомендательные задачи закрыты коллаборативным модулем, где по матрице оценок достраиваются предпочтения. Для витрины из товаров и пользователей это готовая связка, а точность оценивается по доле угаданных оценок на отложенной части.
Диагностика ошибок через интерпретатор и очистку выборки
Обученная модель без диагностики это чёрный ящик. Интерпретатор классификации собирает из ученика разбор ошибок: матрица путаницы, худшие предсказания, топ потерь. Одного взгляда на worst cases часто хватает, чтобы понять природу проблем:
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(figsize=(8, 8))
interp.plot_top_losses(9, nrows=3)
Матрица покажет, какие классы сливаются, топ потерь подсветит элементы, где модель уверенно ошибается, и в половине случаев там окажется кривая разметка: чужая картинка в папке, обрезанный файл, дубликат с другой меткой. Для уборки есть виджет, который листает худшие элементы прямо в ноутбуке и позволяет менять метку или удалять файл:
from fastai.vision.widgets import ImageClassifierCleaner
cleaner = ImageClassifierCleaner(learn)
Очистка выборки по этому списку даёт больше, чем тройная смена архитектуры, потому что сеть честно выучивает то, что ей показали.
Проверочная часть обязана оставаться нетронутой: если подчищать и её, метрика начинает отражать не качество модели, а усердие по подгонке. Граница проходит по обучающей части, а проверочная открывается только в конце, для финальной оценки.
Для поиска гиперпараметров по сетке в экосистеме есть обёртки, но честный порядок остаётся прежним: сначала чистая выборка и разумная скорость обучения, и только потом опыты с архитектурой. Скучно, зато воспроизводимо.
Экспорт модели и перенос в продакшен на практике
Модель живёт, пока она отвечает на запросы. Метод export сериализует ученика вместе с конвейером предобработки, и файл моделирует все шаги от сырого файла до предсказания:
learn.export('churn_model.pkl')
from fastai.learner import load_learner
model = load_learner('churn_model.pkl')
pred_class, pred_idx, probs = model.predict(row)
Файл модели переносится в сервис на FastAPI или Flask, обрастает эндпоинтом и живёт как обычный микросервис. Для массовых предсказаний работает batching: пакеты собираются на входе и исполняются группой, что разгружает процессор на порядок по сравнению с одиночными вызовами.
Модель в продакшене нуждается в обслуживании. Версионирование файлов, контроль качества на отложенной свежей выборке и переобучение по расписанию или по дрейфу закрывают жизненный цикл. Дрейф ловится простым сравнением: свежая партия размечается вручную, модель прогоняется по ней, и падение точности против контрольной цифры служит триггером пересборки. fastai здесь не добавляет ничего своего, но и не мешает: экспортированный файл это обычный pickle с объектами PyTorch, и любые внешние инструменты мониторинга приживаются без трений.
Итог для практики. fastai закрывает расстояние от идеи до работающего прототипа за вечер: конвейер описывается декларативно, обучение начинается одной строкой, диагностика встроена, экспорт работает в обои... в обычный сервис. Для исследований и экзотических архитектур рано или поздно спускаются на чистый PyTorch, но быстрая проверка гипотез, табличные задачи и первые версии компьютерного зрения остаются территорией, где библиотека экономит недели. Порог входа при этом один из самых низких в глубоком обучении, и это осознанное решение авторов, а не побочный эффект. Главное, что она при этом остаётся честной: каждый слой обёртки приоткрывается, и по мере роста навыка практик забирает себе управление ровно столько, сколько готов держать.