Практический гид по PyTorch: от таблиц до BERT

freeCodeCamp.org 130 тыс. 5 ч 48 мин 36 мин 06.03.2025
Главное

Обучение нейронных сетей в PyTorch часто напоминает магию, пока не разберешься в механике того, как данные превращаются в тензоры, а случайные веса — в высокоточные предсказания. От удаления лишних столбцов в таблицах до тонкой настройки трансформеров вроде BERT: этот путь показывает, что работа с искусственным интеллектом — это не столько написание кода, сколько кропотливая подготовка данных и управление архитектурой.

🍚 Подготовка данных для классификации риса 0:41

Работа над любым проектом в области машинного обучения начинается с подготовки данных. В данном курсе мы используем открытый набор данных с платформы Kaggle для классификации типов риса. Это задача бинарной классификации, где модель должна определить сорт риса («Jasmine» или «Gonen») на основе набора физических характеристик.

Анализ и очистка датасета 2:25

Перед началом обучения необходимо привести данные в порядок. Хотя на данном этапе мы не проводим глубокую предобработку, некоторые шаги являются обязательными:

После очистки наш датасет содержит более 18 000 строк и 11 столбцов. Важно отметить, что классы в данных распределены неравномерно: одна категория встречается около 10 000 раз, а другая — около 8 200, однако этот дисбаланс не является критическим для данной учебной задачи.

Нормализация признаков 14:33

Нормализация — критически важный этап подготовки данных для нейронных сетей. Признаки в исходном виде имеют разные масштабы, и зачастую эти значения довольно велики. Это может привести к нескольким проблемам:

  1. Вычислительная сложность: Большие значения входных данных замедляют процесс обучения.
  2. Веса нейросети: Веса модели обычно инициализируются небольшими числами. При умножении на крупные входные значения вычисления могут стать нестабильными.
  3. Потеря точности: В процессе расчета функции потерь (loss function) значения могут округляться, если они слишком велики.

Для решения этих задач мы приводим все значения к диапазону от 0 до 1, деля каждый столбец на его максимальное значение. Мы также сохраняем копию исходного датафрейма в переменную original_data_frame, чтобы иметь доступ к реальным значениям на этапе финального инференса.

Разделение данных и подготовка к PyTorch 18:22

Чтобы правильно оценить качество обучения, мы разделяем данные на три части: обучающую (для тренировки), валидационную (для мониторинга процесса) и тестовую (для финальной оценки). Мы используем соотношение 70% для обучения и по 15% для валидации и тестирования.

На этом этапе мы работаем преимущественно с библиотеками Pandas и NumPy. Однако для работы с PyTorch нам необходимо перевести данные в специфический формат — объект Dataset. PyTorch не умеет работать напрямую с объектами Pandas или NumPy, поэтому мы создаем класс, который наследует базовый класс Dataset из библиотеки PyTorch. В этом классе мы определяем конструктор __init__, который будет принимать наши входные данные (признаки) и целевые значения (метки), адаптируя их для эффективной подачи в модель. Ранее в разговоре автор упоминал, что создание архитектуры модели и сам цикл обучения будут рассматриваться подробно в следующих главах.

🏗️ Фундамент данных: проектирование кастомных классов Dataset в PyTorch 25:12

В экосистеме PyTorch работа с данными начинается задолго до того, как первая итерация обучения коснется нейронной сети. Ключевым этапом здесь является создание кастомных объектов, которые преобразуют сырые структуры данных в формат, понятный библиотеке. В отличие от более автоматизированных фреймворков, таких как TensorFlow, PyTorch требует от разработчика явного определения того, как данные будут загружаться, индексироваться и передаваться на вычислительное устройство.

Конструктор класса: трансформация данных и привязка к устройству 25:12

Создание кастомного набора данных начинается с наследования базового класса Dataset. В методе-конструкторе __init__ происходит критически важная подготовка: трансформация входных признаков (X) и меток (Y) в тензоры. Это фундаментальное требование библиотеки: «Если вы хотите работать с PyTorch, ваши данные обязаны быть тензорами».

Процесс инициализации включает в себя несколько обязательных шагов:

Инструктор подчеркивает, что хотя синтаксис может показаться сложным на первый взгляд, он логичен: мы просто создаем объект, который хранит в себе тензоры, уже оптимизированные для вычислений на конкретном железе (будь то NVIDIA GPU или процессоры Apple Silicon с поддержкой MPS).

Системные методы: управление индексацией и размером выборки 27:27

Для полноценной интеграции с внутренними механизмами PyTorch кастомный класс должен переопределить два системных метода: __len__ и __getitem__. Эти функции не вызываются разработчиком напрямую, но они критически важны для работы загрузчиков данных.

Метод __len__ выполняет тривиальную, но необходимую задачу — он сообщает системе общее количество образцов в наборе данных. Это позволяет алгоритмам обучения понимать границы итераций.

Метод __getitem__ отвечает за логику извлечения конкретного примера по его индексу. Когда алгоритму требуется, например, «строка номер три» из таблицы, этот метод возвращает кортеж, состоящий из входных признаков и соответствующей им метки. Такая архитектура позволяет PyTorch работать с данными любого типа — от простых чисел до тяжелых видеофайлов, — абстрагируя процесс доступа к ним. После того как логика класса описана, создаются три отдельных объекта: для обучения, валидации и тестирования.

DataLoader и концепция батчей: оптимизация передачи данных 30:40

После того как объекты Dataset сформированы, они передаются в DataLoader. Это специализированный инструмент, который управляет тем, как именно данные будут «скармливаться» модели. Основная проблема обучения нейросетей заключается в том, что мы не можем обрабатывать данные по одному образцу — это крайне неэффективно для GPU.

Концепция DataLoader строится вокруг трех ключевых параметров:

  1. Batch Size (размер пакета): Данные группируются в небольшие наборы (батчи). В рассматриваемом примере используется размер батча, равный 8. Это означает, что за один проход модель видит сразу 8 строк данных и 8 соответствующих меток.
  2. Shuffle (перемешивание): Для тренировочного набора данных критически важно устанавливать shuffle=True. Это предотвращает смещение модели (bias), гарантируя, что порядок подачи данных меняется каждую эпоху и сеть не «заучивает» последовательность строк.
  3. Итерация: DataLoader превращает набор данных в итерируемый объект. При запуске цикла обучения он автоматически выдает следующую порцию (батч) тензоров, уже находящихся на нужном устройстве (например, Cuda:0).

«TensorFlow скрывает множество процессов «под капотом», что удобно для новичков. PyTorch же заставляет вас делать много работы вручную, но это дает полное понимание того, как именно движутся данные внутри вашей системы».

Ранее в разговоре лекторы касались подготовки данных для классификации риса, а далее, на основе созданных объектов данных, будет выстроена архитектура модели и запущен цикл обучения. Подготовка функций потерь (таких как Binary Cross Entropy) и оптимизаторов Adam также является необходимым условием перед началом первой эпохи.

🧠 Анатомия цикла обучения в PyTorch: от прямого прохода до оптимизации 50:23

Борьба с размерностями и расчет потерь 50:23

Приступая к фазе обучения нейросети, разработчики часто сталкиваются с классической технической преградой — несоответствием размерностей тензоров. Ранее в разговоре авторы уже затрагивали основы создания объектов PyTorch, но теперь пришло время объединить эти элементы в полноценную систему. В процессе передачи данных в функцию потерь (Criterion) может возникнуть ошибка несоответствия целевого размера батча (например, 8 элементов) и размера предсказания модели (матрица 8x1). PyTorch не выравнивает эти структуры автоматически, из-за чего выполнение кода прерывается.

Эффективным решением здесь выступает функция squeeze(1), которая удаляет лишнюю единичную размерность, приводя данные к идентичному одномерному виду без изменения самого содержимого. Это чисто косметическое изменение представления данных, необходимое для корректного сравнения предсказаний с истинными метками.

После успешного согласования форм начинается поэтапный расчет ошибки на уровне батчей. Для отслеживания глобального прогресса используется переменная накопления общей ошибки обучения:

total_loss_train += batch_loss.item()

Метод .item() здесь критически важен, так как он извлекает чистое числовое значение (скаляр) из тензора, предотвращая утечки памяти и избыточное сохранение всей градиентной истории. Этот процесс укладывается в концепцию вложенных циклов. На уровне батчей модель совершает прямой проход, вычисляет локальную ошибку для небольшой группы строк, аккумулирует ее, а по завершении прохода по всей выборке сбрасывает локальные метрики и переходит к следующей эпохе.

Вычисление метрик и магия градиентного спуска 56:04

Оценка точности (accuracy) требует перевода непрерывных дробных предсказаний модели в бинарный формат. Для этого применяется функция округления round(), которая превращает вероятности в нули или единицы, после чего они сравниваются с реальными метками класса. Результатом такого сравнения становится массив булевых значений (True/False). Чтобы узнать точное количество верных ответов в текущем батче, разработчики используют связку .sum().item(), преобразующую тензор с логическими значениями в стандартное целое число. Полученное значение непрерывно добавляется к суммарной точности текущей эпохи.

Когда прямой проход завершен и ошибка зафиксирована, в дело вступает ключевая триада градиентного спуска:

Без регулярного сброса градиентов через zero_grad() перед каждым новым шагом модель попросту потеряет способность к сходимости и прогрессу, накапливая старые значения и застревая в них.

Валидация модели и управление контекстом градиентов 1:00:40

После успешной настройки тренировочного контура наступает этап проверки качества — валидация. Главное отличие этой фазы заключается в том, что модель тестируется непосредственно в процессе обучения, но без изменения ее внутренних весов. Для этого используется специальный контекстный менеджер with torch.no_grad(). Эта конструкция переводит PyTorch в режим инференса, полностью отключая расчет и отслеживание градиентов, что существенно экономит вычислительные ресурсы и ускоряет выполнение программы.

Логика кода внутри валидационного цикла практически полностью дублирует тренировочный этап:

Такая архитектура наглядно демонстрирует преимущества экосистемы PyTorch. Использование стандартизированных даталоадеров и объектов наборов данных делает код гибким, интуитивно понятным и полностью кастомизируемым под нужды конкретного проекта.

Агрегация эпох, логирование и первый запуск 1:04:29

Выходя из уровня батчей во внешний цикл эпох, разработчики приступают к агрегации собранных метрик для их последующего анализа. Перед добавлением значений в списки для графиков применяется нормирование данных: например, суммарная ошибка делится на 1000 и округляется до четырех знаков после запятой. Расчет финальной точности в процентах реализуется по классической формуле: накопленная точность делится на общую длину набора данных (получаемую через метод len()) и умножается на 100.

Красиво отформатированный вывод в консоль позволяет наглядно отслеживать динамику обучения по каждой эпохе. На начальном этапе сборка может показать слабую динамику из-за неоптимального размера батча. Экспериментальное изменение размера батча с 8 до 32 элементов мгновенно демонстрирует колоссальный сдвиг в обучении, поднимая точность модели до внушительных 98.71%. На данном этапе важнее всего детально усвоить сам синтаксис PyTorch, нежели тратить часы на идеальную оптимизацию архитектуры. Завершив цикл валидации, код плавно переходит к финальной стадии — тестированию на абсолютно независимом наборе данных.

🛠️ Отладка процесса и подготовка инфраструктуры для новых задач 1:15:20

Культура отладки: почему высокая точность — это не финал

На этапе завершения первого проекта точность модели достигла впечатляющих 98,79%. Однако опытный разработчик знает: цифры в консоли — это лишь половина правды. Для глубокого понимания поведения нейросети необходим визуальный контроль, который ранее в разговоре упоминался как важный этап анализа, детально рассматриваемый в контексте визуализации обучения.

В процессе работы с графиками часто всплывают скрытые ошибки кода. Инструктор наглядно демонстрирует типичную ситуацию: при попытке отрисовать кривые обучения обнаружилось, что линия валидации на графике просто отсутствует. Причиной стала банальная опечатка — отсутствие метода .append() для списка total_accuracy_validation_plot. Это важный урок для любого новичка: если модель кажется «слишком идеальной» или графики выглядят странно, в первую очередь стоит проверить логику наполнения массивов данных. «Это была моя вина, просто опечатка, но, к счастью, это не стало большой проблемой», — резюмирует автор после исправления бага и перезапуска цикла.

Практический совет для тех, кто работает с библиотеками вроде Matplotlib: не нужно зазубривать названия всех параметров. Гораздо важнее понимать концепцию построения сетки (например, использование plt.subplots с одной строкой и двумя колонками) и уметь быстро найти нужную команду в документации или Google. Главное — выдерживать структуру и дисциплину кода, переписывая упражнения самостоятельно, даже если готовое решение доступно на GitHub.

Протокол инференса: подготовка данных «на лету»

Когда модель обучена и проверена, встает вопрос её практического применения — инференса. Это процесс использования нейросети в реальных условиях, будь то мобильное приложение или серверная часть веб-сервиса. Ключевой момент здесь — строгая идентичность обработки данных. Любое число, которое пользователь вводит в систему (например, площадь или геометрические параметры объекта), должно пройти через ту же цепочку нормализации, что и тренировочный набор.

В PyTorch процесс инференса имеет свои особенности, отличающие его от Keras или TensorFlow. В частности, здесь не обязательно использовать метод .predict(); чаще всего данные просто передаются напрямую в объект модели. Основные этапы подготовки входного сигнала включают:

Если на выходе получается десятичное число (например, 0.73), для получения финального класса используется стандартное округление round(), превращающее вероятность в конкретный индекс. Этот «инженерный» этап является мостом между теоретическим обучением и работающим программным продуктом.

Инструментарий и детекция GPU: подготовка к масштабированию

Завершив вводный проект, курс переходит к подготовке инфраструктуры для более сложных задач, таких как работа с изображениями, о чем подробнее пойдет речь в следующих главах, посвященных мультиклассовой классификации. Новый этап требует расширения набора инструментов. Помимо базового torch, в стек добавляются:

Критически важным аспектом остается управление вычислительными ресурсами. PyTorch не переключается на видеокарту автоматически. Разработчик должен явно объявить переменную device, проверив доступность CUDA: torch.cuda.is_available(). В рамках данного руководства используется GPU T4 в среде Google Colab, что позволяет значительно ускорить обработку визуальных данных. Инструктор подчеркивает, что при локальной работе крайне важно корректно установить драйверы CUDA, хотя для обучения работа в облаке остается предпочтительной.

Логика парсинга данных: превращение папок в датафреймы

Работа с реальными датасетами, такими как Animal Faces с Kaggle, начинается не с архитектуры сети, а с грамотного маппинга файлов. Часто данные организованы в виде вложенных папок: тренировочные и валидационные выборки, внутри которых лежат категории с изображениями. Задача программиста — превратить эту древовидную структуру в плоский список путей и соответствующих им меток.

Для автоматизации этого процесса используется каскад циклов os.listdir(). Логика обхода выглядит следующим образом:

  1. Вход в корневую директорию датасета (например, afhq).
  2. Перебор папок выборок (Train/Val).
  3. Извлечение меток классов из названий подпапок.
  4. Сбор полных путей к каждому конкретному изображению.

Такой подход позволяет сформировать структуру данных (Pandas DataFrame), где каждому пути к файлу сопоставлен его правильный ответ (label). Это фундамент, на котором будет строиться создание кастомных объектов Dataset и DataLoader в PyTorch. «Вы должны думать об этом как о слоях», — объясняет автор, демонстрируя, как скрипт последовательно «проваливается» от названия архива до конкретной фотографии собаки или кошки. Несмотря на кажущуюся сложность вложенных циклов, это стандартный паттерн для подготовки данных в задачах компьютерного зрения.

📊 Визуализация данных и подготовка инфраструктуры инференса 1:40:37

Стратегия разбиения данных для валидации и контроля 1:40:37

Разделение исходного набора данных на обучающую, валидационную и тестовую выборки — это важнейший фундамент, без которого невозможно адекватное тестирование обученной модели на новых данных. В рассматриваемом фрагменте лектор отказывается от стандартной функции train_test_split в пользу прямого случайного семплирования через средства библиотеки Pandas. Такой подход позволяет избежать генерации промежуточных списков и сразу оперировать готовыми объектами датафреймов.

Распределение исходного набора данных выглядит следующим образом:

Выделение тестовой части происходит путем сброса индексов обучающего набора, после чего остаток делится ровно пополам. Следующим шагом является преобразование текстовых меток классов (wild, cat, dog) в числовой формат (0, 1, 2) с помощью инструмента LabelEncoder.

Для бесшовной интеграции данных в экосистему PyTorch создается кастомный класс CustomImageDataset, наследуемый от базового Dataset. Как отмечает спикер, PyTorch представляет собой гибкую среду, где все компоненты должны идеально понимать синтаксис друг друга. В созданном классе переопределяются стандартные методы управления данными, включая конструктор и метод получения элемента по индексу __getitem__.

Внутри метода __getitem__ к каждому изображению применяется строго определенный пайплайн трансформаций:

После выполнения всех трансформаций тензоры автоматически отправляются на графический ускоритель CUDA для ускорения вычислений. Корректность работы созданной структуры проверяется тестовым вызовом: элемент под индексом 2 успешно возвращает тензор изображения и числовую метку 0, которая через инвертированное преобразование декодируется обратно в класс «cat».

Визуальный аудит датасета с помощью Matplotlib 1:55:20

Прежде чем переходить к этапам долгосрочного обучения нейросети и построению результирующих графиков функции потерь или точности, критически важно провести первичную визуальную инспекцию распределения данных. Автор видео подчеркивает, что этот шаг является полностью опциональным, однако пренебрегать им не стоит, ведь он позволяет оценить реальное качество картинок перед запуском инференса. Для реализации этой задачи привлекается популярная графическая библиотека Matplotlib.

Процесс визуального контроля организуется в виде компактной координатной сетки. Сначала с помощью функции plt.subplots инициализируется матрица подграфиков размером 3 на 3 ячейки. Затем программа запускает вложенный цикл, последовательно проходящий по каждой строке и каждому столбцу заданной структуры. На каждой итерации цикла из исходного датафрейма извлекается случайный путь к файлу изображения посредством метода .sample(n=1). Наличие флага n=1 указывает на выборку конкретного единичного элемента вместо долевого фракционного разделения.

Выбранное изображение открывается, преобразуется в цветовое пространство RGB и отрисовывается в соответствующей ячейке сетки с помощью метода imshow. Для того чтобы техническая информация, такая как разметка осей X и Y, не загромождала графическое окно и не отвлекала аналитика, отображение координатных осей полностью отключается командой axis('off'). Главное преимущество данного интерактивного скрипта заключается в его динамичности: при каждом новом запуске ячейки заполняются абсолютно уникальным набором случайных фотографий животных. Это обеспечивает быструю и наглядную верификацию корректности считывания файлов из дисковой памяти.

Формирование загрузчиков и подготовка архитектуры нейросети 1:58:34

После завершения визуального аудита наступает фаза окончательной настройки гиперпараметров среды и развертывания потоковых загрузчиков. На данном этапе фиксируются ключевые константы: скорость обучения (learning rate) устанавливается равной $1 \cdot 10^{-4}$, размер обрабатываемого пакета (batch size) ограничивается 16 элементами, а общий лимит учебных циклов составляет 10 эпох. Указанные метрики в дальнейшем послужат отправной точкой для построения аналитических графиков.

Для итеративного перебора данных пакетами создаются специализированные объекты DataLoader для всех трех ранее подготовленных выборок. Стоит помнить, что ранее в обучении модели лекторы касались общих принципов создания объектов, но теперь фокус смещен на обеспечение стабильного инференса. Для обучающего загрузчика параметр shuffle в обязательном порядке переводится в состояние True — это необходимо для предотвращения нежелательного смещения (biasing) модели при последовательном прохождении эпох. Для валидационного и тестового потоков перемешивание не требуется.

Завершается подготовка к инференсу проектированием базовой архитектуры модели. Создается класс Network, наследующий функционал от корневого модуля nn.Module. В конструкторе последовательно объявляются три сверточных слоя. Первый слой conv1 принимает на вход 3 стандартных канала изображения (RGB) и генерирует 32 выходных канала. Второй слой conv2 принимает полученные 32 канала и расширяет их до 64 признаков. Третий сверточный слой conv3 увеличивает глубину до 128 каналов.

Лектор проводит краткий теоретический экскурс, объясняя внутреннюю механику работы создаваемых слоев:

Операция пулинга позволяет уменьшить геометрический размер обрабатываемых карт признаков, полностью сохраняя при этом их информационный объем и количество признаков. Это значительно снижает нагрузку на вычислительную систему, оптимизирует прохождение сигналов и подготавливает внутренние векторы к операции уплощения (flatten) для последующего вычисления итоговой точности предсказаний.

🧠 Мультиклассовая классификация изображений 2:05:44

Для реализации мультиклассовой классификации, способной различать несколько типов объектов (например, котов, собак и диких животных), архитектура нейронной сети требует специфической настройки выходного слоя и функции потерь. В отличие от бинарной классификации, где результат предсказывается как «да» или «нет» (используется бинарная кросс-энтропия), мультиклассовая модель должна выдавать вероятности для каждой категории.

Проектирование архитектуры модели 2:05:44

Процесс начинается с формирования последовательности слоев. После сверточных (convolutional) и пулинг-слоев (pooling) данные проходят через функцию активации (рекомендуется ReLU) и слой flatten, который преобразует матричное представление данных в плоский вектор.

Критически важным является динамическое определение выходного слоя:

Для поддержания размерности тензоров при прохождении через сверточные слои рекомендуется добавлять padding=1, что предотвращает потерю признаков на краях изображений.

Обучение и валидация: цикл и метрики 2:16:18

Обучение происходит в рамках цикла по эпохам, где для каждого батча выполняются стандартные шаги PyTorch: обнуление градиентов (zero_grad), прогон данных через модель, вычисление ошибки и обратное распространение (backward).

Для оценки качества работы модели используются списки, хранящие значения функции потерь и точности для каждой эпохи:

Ранее в разговоре они касались подготовки данных и использования устройств (Cuda/GPU) для ускорения вычислений.

Валидация выполняется в режиме torch.no_grad(), что отключает вычисление градиентов, так как на этом этапе веса модели не обновляются — модель лишь проверяется на данных, которые она не видела в процессе обучения. По завершении каждой эпохи собранные данные (Loss и Accuracy для обучения и валидации) усредняются и выводятся для визуализации, что позволяет отслеживать процесс сходимости и вовремя заметить признаки переобучения.

🧠 Основы сверточных нейронных сетей (CNN) 2:47:12

Сверточные нейронные сети (CNN) представляют собой архитектуру, специально разработанную для эффективного анализа визуальных данных. В отличие от полносвязных сетей, CNN используют иерархический подход к извлечению признаков, что делает их золотым стандартом в задачах компьютерного зрения.

Механика свертки и пулинга 2:47:12

Фундамент CNN строится на двух ключевых операциях, которые позволяют сети «видеть» структуру изображения:

Визуально работа сверточных сетей напоминает прохождение «фильтра» через матрицу пикселей. Хотя архитектура требует правильной настройки гиперпараметров, именно такие слои позволяют модели трансформировать «сырые» пиксели в высокоуровневые смысловые концепции.


Анализ эффективности модели 2:32:00

После завершения этапа обучения важно оценить, как именно сеть справляется с данными, которые она не видела ранее. В процессе тестирования модели крайне важно использовать конструкцию torch.no_grad(), чтобы отключить вычисление градиентов — это экономит ресурсы и предотвращает случайное изменение весов модели во время оценки.

Ключевым этапом здесь является расчет метрик на тестовой выборке:

  1. Точность (Accuracy): Вычисляется путем сравнения предсказанных моделью меток (predictions) с истинными метками (labels). Ошибки при вычислении, такие как получение нулевой точности, часто связаны с отсутствием накопления значений точности по батчам.
  2. Потери (Loss): Индикатор ошибки, который помогает понять уверенность модели.
  3. Визуализация прогресса: Построение графиков обучения позволяет наглядно увидеть динамику изменения точности и функции потерь на обучающей и валидационной выборках.

При анализе графиков можно заметить признаки переобучения (overfitting), когда точность на тренировочных данных растет, а на валидационных начинает стагнировать или падать. Для борьбы с этим эффектом разработчики могут экспериментировать с добавлением Dropout-слоев, изменением количества эпох или упрощением архитектуры модели.


Практический инференс: работа с «дикими» данными 2:40:11

Инференс — это этап применения обученной модели к новым данным, например, к изображениям из интернета. Процесс требует осторожности при подготовке входных данных:

Ранее в разговоре они касались темы подготовки данных и использования различных GPU в среде Colab для ускорения вычислений.

🛠 Настройка и подготовка данных в PyTorch 2:55:58

На этом этапе работы с данными мы переходим от теоретической подготовки к практической настройке пайплайна в PyTorch. Первым делом необходимо верифицировать категории данных: в рассматриваемом примере мы работаем с тремя классами, обозначаемыми как 0, 1 и 2, которые соответствуют состояниям «здоровый», «пятнистость» и «ржавчина».

Анализ структуры данных показывает, что мы имеем 1000 изображений для обучения и 133 изображения для валидации. Распределение классов по датасету сбалансировано, что является положительным фактором для обучения. Однако общее количество данных относительно невелико — около 350 изображений на каждый из трех классов. В таких ситуациях, когда данных для полноценного обучения с нуля недостаточно, наиболее эффективным подходом является использование методов переноса обучения (transfer learning).

Трансформации и препроцессинг изображений 2:58:51

Для подготовки изображений к подаче в нейронную сеть создается специальный объект transform, который обеспечивает единообразие данных. Этот объект выполняет три ключевые функции, последовательно объединяемые через transforms.Compose:

Важным этапом является нормализация: так как исходные значения пикселей варьируются от 0 до 255, мы делим их на 255.0. Это позволяет работать с числами в диапазоне от 0 до 1, что способствует более стабильной сходимости модели.

Создание кастомного датасета 3:02:17

PyTorch предоставляет высокую степень гибкости благодаря наследованию от базового класса Dataset. Для работы с нашими данными мы создаем класс CustomImageDataset. Основная задача здесь — переопределить методы __len__ и __getitem__:

В процессе инициализации мы также перемещаем метки классов в тензоры PyTorch и переносим их на выбранное устройство (Cuda), чтобы избежать задержек при передаче данных во время цикла обучения.

Инициализация модели GoogLeNet 3:18:33

Для решения задачи классификации мы используем предопределенную архитектуру GoogLeNet, которая импортируется из torchvision.models. Поскольку мы планируем дообучать модель на наших данных, мы активируем обновление весов, установив param.requires_grad = True для всех параметров модели.

Это позволяет «разморозить» веса предобученной нейросети и позволить ей адаптироваться к специфике наших данных, опираясь на те знания, которые она получила при первоначальном обучении на гораздо более крупном наборе данных. Мы также определяем гиперпараметры обучения: коэффициент обучения (learning rate) установлен на $1 \times 10^{-3}$, размер пакета (batch size) — 4, а количество эпох — 15. Использование DataLoader для обоих наборов данных (обучающего и валидационного) с включенным перемешиванием (shuffle) завершает подготовку инфраструктуры для начала итерационного процесса обучения.

🚀 Тонкая настройка GoogLeNet и переход к анализу аудиоданных 3:21:08

На текущем этапе работы с предобученными моделями ключевой задачей становится адаптация универсальной архитектуры под специфические нужды конкретного датасета. Ранее в курсе уже рассматривались основы построения сверточных нейронных сетей (глава 7), однако здесь акцент смещается на практическую модификацию выходных слоев и сравнение различных стратегий обучения.

🏗️ Кастомизация последнего слоя и запуск цикла обучения 3:21:08

Когда мы используем архитектуру GoogLeNet, по умолчанию модель настроена на классификацию 1000 различных объектов. Однако для текущей задачи классификации риса нам требуется всего три класса. Чтобы реализовать это, необходимо вмешаться в структуру модели и заменить финальный полносвязный слой (fc).

Первым шагом разработчик проверяет количество уникальных категорий в тренировочном наборе данных, используя метод unique(). Убедившись, что классов действительно три, производится замена слоя: вместо стандартного fc устанавливается новый объект torch.nn.Linear. Важно сохранить количество входных признаков (in_features), которое для данной модели составляет 1024, и указать целевое количество выходных признаков, равное трем.

После модификации архитектуры необходимо подготовить инфраструктуру для обучения:

Цикл обучения строится классическим образом, включая обнуление градиентов (optimizer.zero_grad), прямой проход для получения предсказаний и обратное распространение ошибки (loss.backward). Для мониторинга прогресса в реальном времени рассчитывается накопленная точность и потери, которые округляются до четырех знаков после запятой и сохраняются для последующей визуализации.

📊 Оценка результатов и эксперимент с «заморозкой» весов 3:31:00

После завершения тренировочного цикла проводится валидация на тестовом наборе данных с использованием контекстного менеджера torch.no_grad(), что отключает вычисление градиентов и экономит память. Первоначальный подход, при котором вся модель дообучалась на новых данных, показал впечатляющий результат — 86% точности. Это достойный показатель, учитывая относительно небольшой объем данных.

Однако возникает вопрос: можно ли добиться большего, применив стратегию «чистого» трансферного обучения? Для этого был проведен второй эксперимент, где логика обучения изменилась:

  1. Все веса предобученной модели были «заморожены» через установку requires_grad = False.
  2. Разблокированным остался только последний, заново созданный линейный слой.
  3. Обучение проводилось только для параметров этого финального слоя.

Результаты эксперимента оказались неожиданными для новичков: точность упала до 56%. Такое резкое снижение эффективности объясняется тем, что при полной заморозке модель пытается использовать свои старые знания об общих объектах, не адаптируя внутренние фильтры под специфику структуры зерен риса. В первом же случае, когда обучалась вся сеть, модель использовала предобученные веса лишь как удачную стартовую точку, позволяя всем слоям подстроиться под новую задачу. Это доказывает, что для специфических медицинских или сельскохозяйственных данных полное дообучение (fine-tuning) часто предпочтительнее простого извлечения признаков.

🎙️ Новый проект: Инфраструктура для классификации аудио 3:38:26

Завершив блок с изображениями, курс переходит к совершенно иной модальности данных — звуку. В рамках нового проекта ставится задача классификации аудиозаписей. В качестве датасета выбрана коллекция чтений Корана, где необходимо идентифицировать чтеца по его голосу. Это классическая задача распознавания диктора, требующая особого набора инструментов.

Подготовка среды начинается с импорта библиотек, которые ранее не использовались:

При первичной проверке данных выяснилось, что датасет содержит 12 уникальных классов (чтецов). Файлы представлены в формате .wav, а метаданные хранятся в CSV-файле. Одной из технических особенностей, с которой сразу сталкивается разработчик, является структура путей к файлам в CSV: они начинаются с точки (относительные пути), что требует программной корректировки для корректного чтения файлов в среде Google Colab. Этот этап подготавливает почву для глубокого погружения в обработку звуковых волн, которое последует в следующих разделах.

🎧 Обработка аудиоданных и создание датасетов 3:54:44

Для подготовки аудиоданных к обучению модели необходимо пройти путь от сырых файлов до структурированных тензоров. Работа начинается с создания класса CustomAudioDataset, который берет на себя всю логику обработки. В инициализатор класса передается DataFrame с путями к файлам. Здесь же происходит кодирование текстовых меток классов в числовые значения с помощью LabelEncoder и их преобразование в torch.LongTensor, которые затем переносятся на GPU.

Центральным элементом является преобразование аудиосигналов в спектрограммы — визуальное представление звука, где по оси X отложено время, а цвет определяет интенсивность частот. Этот процесс инкапсулирован в функции get_spectogram:

Ранее в разговоре они касались подготовки и разбиения данных на тренировочную, валидационную и тестовую выборки.

⚙️ Оптимизация процесса инференса и обучения 4:08:17

Важным шагом для обеспечения консистентности данных является перенос процесса кодирования меток LabelEncoder за пределы инициализации датасета. Если выполнять fit_transform напрямую внутри датасета, можно столкнуться с несоответствиями при разделении данных на части. Правильный подход — сначала закодировать метки во всем исходном DataFrame, а затем передавать в CustomAudioDataset уже готовые числовые значения.

Метод __getitem__ в классе датасета обеспечивает автоматическую загрузку данных в процессе обучения: он извлекает путь к файлу и соответствующую ему метку, а также формирует тензор спектрограммы с помощью unsqueeze, чтобы обеспечить нужную размерность для передачи в модель. Стоит учитывать, что инициализация датасета может занимать время, так как функция get_spectogram выполняется для каждого файла в наборе данных (всего более 6,600 файлов в данном проекте) в момент создания объектов train_dataset, val_dataset и test_dataset.

🎵 Классификация аудиоданных через глубокое обучение 4:14:01

При работе с аудиоданными, представленными в виде спектрограмм, мы фактически имеем дело с визуальными структурами, напоминающими изображения. Это позволяет нам адаптировать мощные инструменты компьютерного зрения для анализа звука. В данном контексте мы строим сверточную нейронную сеть (CNN) для классификации записей по стилю исполнения. Поскольку спектрограммы в данном случае имеют один канал, архитектура сети настраивается на работу с одноканальными входными данными, сохраняя при этом эффективность классической сверточной модели. Ранее в разговоре уже затрагивались основы работы с данными и архитектура моделей, поэтому здесь мы фокусируемся на специфике звуковых признаков.

Архитектура сверточной сети для аудио 4:14:29

Для построения сети мы создаем класс, наследуемый от nn.Module. Конструктор модели включает последовательность сверточных слоев (nn.Conv2d), где каждый последующий слой увеличивает количество выходных каналов (фильтров), позволяя сети извлекать всё более сложные признаки из спектрограммы.

Итоговый размер выходного слоя динамически определяется на основе количества уникальных классов в наборе данных, что делает код более гибким и профессиональным.

Обучение модели и цикл обработки данных 4:14:01

После определения структуры сети необходимо настроить гиперпараметры и подготовить загрузчики данных (DataLoader). Мы устанавливаем скорость обучения ($10^{-4}$), размер батча (16) и количество эпох (25). Для обучения используется оптимизатор Adam и функция потерь nn.CrossEntropyLoss, что является стандартным выбором для задач мультиклассовой классификации.

Цикл обучения включает итерацию по батчам, прогон данных через модель (forward pass) и вычисление градиентов (backward pass). Accuracy рассчитывается путем сравнения индексов максимальных значений в выходных тензорах с истинными метками классов.

Важным аспектом является работа с устройством вычислений: все компоненты модели и входные данные должны быть явно перенесены на GPU (Cuda), если это возможно, для ускорения процесса вычислений. Общее количество обучаемых параметров в данной конфигурации составляет около 138 миллионов, что требует значительного времени для полноценного обучения модели.

-

🛠️ Финал текущего пайплайна и подготовка инфраструктуры для анализа текстов 4:36:35

Отладка логирования и завершение эпох обучения 4:36:35

В процессе настройки вывода результатов обучения спикер сталкивается с классическими трудностями форматирования строк в Python. Попытка сделать логирование более наглядным приводит к синтаксической ошибке при вызове встроенной функции округления round() для вывода потерь на валидационной выборке. Автор демонстрирует процесс живой отладки кода прямо перед аудиторией, детально разбираясь с расстановкой скобок и запятых внутри многострочного строкового литерала. Чтобы визуально разделить логи между итерациями, в код добавляется эстетичный разделитель, состоящий ровно из 30 знаков равенства.

Для точного мониторинга производительности спикер интегрирует стандартный модуль time. Зафиксировав начальную точку с помощью метода time.time(), он вычисляет чистую продолжительность вычислений и округляет полученное значение секунд до четырех знаков после запятой. В процессе тестирования обнаруживается еще один мелкий косметический нюанс: нумерация эпох на экране начинается с нуля. Чтобы сделать отчетность более привычной для восприятия, значение переменной корректируется на выражение epoch + 1. В конечном итоге весь запланированный цикл из 25 эпох успешно завершается, занимая суммарно около 500 секунд реального времени.

Метрики эффективности и диагностика переобучения 4:43:43

После успешного завершения фазы обучения наступает этап валидации модели на изолированном тестовом наборе данных. На этом шаге спикер обязательно отключает вычисление градиентов с помощью стандартного контекстного менеджера with torch.no_grad(), что позволяет существенно сэкономить память GPU. Внутри итератора по тестовому загрузчику test_loader вычисляются предсказания сети, а финальный предсказанный класс извлекается с помощью функции torch.argmax(). Метрика точности аккумулируется по каждому обрабатываемому батчу, после чего делится на общий объем тестового датасета. Итоговый показатель точности (accuracy score) достигает стабильных 92–93%. Автор положительно оценивает данный результат, называя его весьма хорошим для текущей конфигурации модели.

Ранее в разговоре спикеры уже детально разбирали общие принципы визуализации обучения и инференса, однако здесь строятся графические кривые для детальной диагностики конкретного процесса. С помощью библиотеки matplotlib инициализируется координатная сетка, состоящая из одной строки и двух колонок, с общим размером полотна 15 на 5 дюймов. Первая область графика выделяется под отображение динамики потерь (loss) на тренировочной и валидационной выборках, а вторая — под графики изменения точности. Для повышения читаемости правого графика диапазон оси Y принудительно ограничивается значениями от 0 до 100 процентов. Изучая полученные кривые, спикер констатирует наличие умеренного переобучения (overfitting), поскольку показатели точности на валидации закономерно оказываются ниже тренировочных. Тем не менее, данная проблема не носит критического характера, так как итоговый тест показывает высокую обобщающую способность модели. Этим шагом работа с текущим проектом полностью завершается.

Подготовка инфраструктуры и парсинг JSON-данных 4:52:23

Завершив блок по компьютерному зрению, лектор переходит к совершенно новому практическому разделу, посвященному автоматическому распознаванию сарказма в текстовых заголовках новостей. В качестве основы для этой задачи выбран популярный датасет с платформы Kaggle — "news headline data set for sarcasm detection". Каждая запись в исходном файле представляет собой JSON-объект, содержащий три ключевых поля: текст заголовка, ссылку на статью и бинарный флаг наличия сарказма (0 или 1). Автор демонстрирует примеры разметки, где шуточные заголовки (например, об изобретении учеными «часов Судного дня» против выпадения волос) ожидаемо имеют истинную метку сарказма.

Работа разворачивается в облачной среде Google Colab с подключением графического ускорителя T4 GPU. Процесс инициализации нового рабочего пространства включает следующие шаги:

После проверки доступности аппаратного движка CUDA спикер загружает файл с помощью функции pd.read_json(), обязательно выставляя параметр lines=True для корректной обработки потокового формата. Первичная предобработка включает обязательную очистку данных: удаление пустых значений с помощью dropna(inplace=True) и исключение дубликатов через drop_duplicates(inplace=True). В результате формируется чистый массив из 26 708 строк. Информационный шум в виде ссылок на первоисточники удаляется методом .drop() по оси axis=1. В качестве финального аккорда очищенный текстовый корпус разделяется через функцию train_test_split() в стандартной пропорции: 70% выделяется на обучение, а оставшиеся 30% поровну делятся между валидационным и тестовым множествами.

🤖 Текстовая классификация с BERT 5:06:07

Для решения задач текстовой классификации, таких как определение сарказма в заголовках новостей, современные разработчики часто обращаются к архитектуре Transformer. В частности, используется модель bert-base-uncased, предобученная компанией Google. Эта модель является мощным инструментом для широкого спектра задач, включая классификацию текста и маскированное языковое моделирование.

Важным преимуществом использования bert-base-uncased является то, что она распространяется с открытым исходным кодом через платформу Hugging Face. Вес модели составляет около 500 МБ, что делает её относительно компактной и удобной для интеграции в проекты.

Подготовка данных и токенизация 5:08:02

Прежде чем данные попадут в модель BERT, их необходимо соответствующим образом подготовить. Поскольку модель «понимает» не слова, а числа, выполняется процесс токенизации. Каждому слову в словаре модели соответствует уникальный числовой идентификатор (токен).

Для корректной работы с набором данных в PyTorch создается специальный класс Dataset, который автоматизирует процесс преобразования текста в тензоры:

Ранее в разговоре они касались общих принципов работы с объектами данных в PyTorch и создания загрузчиков (DataLoaders).

Архитектура модели классификатора 5:19:36

Для построения классификатора на базе BERT создается пользовательский класс модели, который наследуется от nn.Module. В этой архитектуре базовая модель BERT выступает в качестве «извлекателя признаков» (feature extractor).

Выходной вектор BERT имеет размерность 768. Чтобы адаптировать его под задачу бинарной классификации, добавляются дополнительные слои:

Тонкая настройка (Fine-tuning) 5:24:33

Одной из ключевых стратегий при работе с предобученными моделями является заморозка параметров основной сети. Устанавливая param.requires_grad = False для всех слоев BERT, мы фиксируем «знания», полученные моделью при обучении на огромных корпусах текстов.

В процессе обучения обновляются веса только добавленных нами линейных слоев. Это значительно ускоряет процесс сходимости и позволяет добиться высокой точности даже на относительно небольших специализированных наборах данных, так как модель фокусируется исключительно на адаптации предобученных признаков под конкретную задачу.

🚀 Обучение модели, отслеживание метрик и визуализация результатов 5:26:53

Процесс обучения модели требует настройки оптимизатора с определенным коэффициентом обучения (learning rate), установленным в данном случае на $1 \times 10^{-4}$. Основная работа по обучению строится вокруг циклов по эпохам (epochs), где на каждой итерации модель получает данные, вычисляет предсказания и обновляет свои веса. Поскольку модель BERT на выходе предоставляет два ключевых элемента — идентификаторы ввода (input IDs) и маску внимания (attention mask), их необходимо правильно обрабатывать, включая использование метода squeeze для корректного учета размера пакета (batch size).

Важным аспектом обучения является накопление метрик для последующего анализа: для каждого пакета данных вычисляется функция потерь (loss) и точность (accuracy). В процессе обратного распространения ошибки (backward) оптимизатор обновляет параметры модели, после чего выполняется optimizer.zero_grad для сброса градиентов перед следующим шагом.

Валидация и контроль переобучения 5:32:04

Этап валидации проводится в режиме torch.no_grad(), что отключает вычисление градиентов, так как на этом шаге модель не должна обучаться. Код валидации во многом повторяет структуру обучающего цикла: данные проходят через модель, вычисляются потери и точность, но без использования оптимизатора и функции обратного распространения.

Чтобы визуализировать прогресс и отследить возможное переобучение (overfitting), все значения потерь и точности для тренировочного и валидационного наборов данных сохраняются в списки. Эти данные впоследствии нормализуются и форматируются для отображения в консоли, где на каждой эпохе выводятся текущие показатели эффективности модели. Как было отмечено ранее в разговоре, работа с BERT в данном проекте демонстрирует эффективность использования предобученных моделей для классификации.

Тестирование и визуализация графиков 5:40:46

После завершения всех эпох обучения проводится итоговое тестирование на отдельном наборе данных, чтобы оценить обобщающую способность модели. Процедура тестирования практически идентична валидации, включая использование torch.no_grad() для экономии ресурсов. По итогам запуска модель достигла показателя точности на тестовых данных в 84,6%, что является хорошим результатом, который при необходимости можно улучшить путем настройки гиперпараметров.

Для финальной визуализации используется библиотека Matplotlib, создающая графики потерь и точности. С помощью функции plt.subplots формируется сетка графиков:

Визуальный анализ этих графиков позволяет увидеть, как менялось качество работы модели на протяжении 10 эпох, помогая сделать выводы о наличии или отсутствии переобучения.

💬 Цитаты

«Если вы хотите иметь дело с PyTorch, ваши данные должны быть тензорами.»

Инструктор 26:13

«Convolution increases the number of features but does not change the size.»

«Спектрограмма — это представление аудиофайла, не просто набор чисел, а нечто вроде изображения.»

«PyTorch doesn't automatically detect the GPU so you have to tell it.»

«Total trainable parameters are 138 million parameters.»

«pytoch is like a big ecosystem so all the objects understand each other perfectly fine»

👥 Спикеры
🔗 Упомянутые сайты и проекты
📖 Термины
Тензор
Многомерный массив, основной объект данных в PyTorch.
Спектрограмма
Визуальное представление аудиосигнала, используемое для анализа нейросетями.
Fine-tuning (Тонкая настройка)
Процесс дообучения предобученной модели на специфическом наборе данных.
Технологии и IT PyTorch Машинное обучение GoogLeNet BERT Нейронные сети