Содержание
Краткая памятка по созданию сверточной нейронной сети на Python
- Установите необходимые библиотеки: PyTorch или TensorFlow.
- Загрузите и подготовьте датасет (например, MNIST или CIFAR-10).
- Нормализуйте данные (приведите пиксели к диапазону [0,1] или [-1,1]).
- Создайте класс модели, унаследовав его от nn.Module (для PyTorch).
- Определите последовательность слоев: Conv2d, ReLU, MaxPool2d.
- Добавьте полносвязные слои в конце для классификации.
- Выберите функцию потерь (например, CrossEntropyLoss).
- Выберите оптимизатор (Adam или SGD).
- Запустите цикл обучения: forward pass, подсчет loss, backward pass, шаг оптимизатора.
- Оцените модель на тестовых данных и визуализируйте результаты.
Особенности CNN
Полностью соединенная нейросеть с несколькими слоями может много, но чтобы показать действительно выдающиеся результаты в задачах классификации, необходимо идти глубже. Другими словами, требуется использовать намного больше слоев в сети. Однако, добавление многих новых слоев влечет проблемы. Во-первых, сталкиваемся с проблемой забывания градиента, хотя это и можно решить при помощи чувствительной функции активации — семейства ReLU функций. Другая проблема с глубокими полностью соединенными сетями — количество весов для тренировки быстро растет. Это означает, что процесс тренировки замедляется или становится практически невыполнимым, а модель может переобучаться. Тем не менее, решение есть.
Сверточные нейронные сети пытаются решить вторую проблему, используя корреляции между смежными входами в картинках или временных рядах. Например, на картинке с котиком и собачкой пиксели, близкие к глазам котика, более вероятно будут коррелировать с расположенными рядом пикселями на его носу, чем с пикселями носа собаки на другой стороне картинки. Это означает, что нет необходимости соединять каждый узел с каждым в следующем слое. Такой прием уменьшает количество весовых параметров для тренировки модели. CNN также имеют и другие особенности, улучшающие процесс тренировки, которые будут рассмотрены в других главах.
Принцип работы CNN
Свертка — фактически главное, что необходимо понять о сверточных нейронных сетях. Этот замысловатый математический термин нужен для движущегося окна или фильтра по исследуемому изображению. Перемещающееся окно применяется к определенному участку узлов, как показано ниже. Где примененный фильтр — (0.5 * значение в узле):
На диаграмме показаны только два выходных значения, каждое из которых отображает входной квадрат размера 2×2. Вес отображения для каждого входного квадрата, как ранее упоминалось, равен 0.5 для всех четырех входов (inputs). Поэтому выход может быть посчитан так:
В сверточной части нейронной сети можно представить, что такой движущийся 2 х 2 фильтр скользит по всем доступным узлам или пикселям входного изображения. Такая операция может быть проиллюстрирована с использованием стандартных диаграмм узлов нейронных сетей:
Первое положение связей движущегося фильтра показано синей линией, второе — зеленой. Веса для каждых таких соединений равны 0.5.
Вот несколько вещей в сверточном шаге, которые ускоряют процесс тренировки, сокращая количество параметров, весов:
- Редкие связи — не каждый узел в первом (входном) слое соединен с каждым узлом во втором слое. Этим отличается архитектура CNN от полностью связанной нейронной сети, где каждый узел соединен со всем другими в следующем слое.
- Постоянные параметры фильтра. Другими словами, при движении фильтра по изображению одинаковые веса применяются для каждого 2 х 2 набора узлов. Каждый фильтр может быть обучен для выполнения специфичных трансформаций входного пространства. Следовательно, каждый фильтр имеет определенный набор весов, которые применяются для каждой операции свертки. Этот процесс уменьшает количество параметров. Нельзя говорить, что любой вес постоянен внутри отдельного фильтра. В примере выше веса были [0.5, 0.5, 0.5, 0.5], но ничего не мешало им быть и [0.25, 0.1, 0.8, 0.001]. Выбор конкретных значений зависит от обучения каждого фильтра.
Эти два свойства сверточных нейронный сетей существено уменьшают количество параметров для тренировки, по сравнению с полносвязными сетями.
Следующий шаг в структуре CNN — прохождение выхода операции свертки через нелинейную активационную функцию. Речь идет о некотором подвиде ReLU, который обеспечивает известное нелинейное поведение этой нейронной сети.
Процесс, использующийся в сверточном блоке, называется признаковым отображением (feature mapping). Название основано на идее, что каждый сверточный фильтр может быть обучен для поиска различных признаков в изображении, которые затем могут быть использованы в классификации. Перед разговором о следующем свойстве CNN, называемом объединением (pooling), рассмотрим идею признакового отображения и каналов.
Основы
На диаграмме можно наблюдать действие max pooling. Для первого окна голубого цвета max pooling выдает значение 3.0, которое является максимальным значением узла в 2х2 окне. Таким же образом зеленое окно выводит максимальное значение, равно 5.0, а для красного окна максимальное значение — 7.0. Здесь всё просто и понятно.
Шаги и даунсемплинг
На диаграмме сверху можно заметить, что пулинговое окно каждый раз перемещается на 2 места. Можем говорить, что шаг равен 2. На диаграмме показаны шаги только вдоль оси x, но для задачи предотвращения перекрытия окна, шаг должен быть также равен 2 и в направлении y. Другими словами, шаг обозначается как [2,2]. Следует упомянуть, если во время пулинга шаг больше 1, тогда размер выхода будет уменьшен. Как можно видеть на диаграмме, входной объект размера 5×5 уменьшается до 3х3 на выходе. И это хорошо — такое явление называется даунсемплингом и уменьшает количество обучаемых параметров в модели.
Padding
Важно отметить также, что в пулинговой диаграмме есть дополнительный столбец и строка, добавленные к входу размера 5х5, делающие эффективный размер пулингового пространства равным 6х6. Это делается для того, чтобы пулинговое окно размером 2х2 корректно работало с шагом [2,2]. Такой прием называется padding. Padding-узлы зачастую фиктивные, так как значения на них равны 0, и операция max pooling их не видит. Этот факт нужно будет учитывать при создании нашей сверточной сети на PyTorch.
Теперь мы разобрались в механизме работы пулинга в CNN, выяснили его полезность в осуществлении даунсемплинга. Рассмотрим еще его некоторые функции и ответим на вопрос, почему max pooling используется так часто.
Отображение признаков и мультиканальность
Поскольку веса отдельных фильтров остаются постоянными, будучи примененными на входных узлах, они могут обучаться выбирать определенные признаки из входных данных. В случае изображений, архитектура способна учиться различать общие геометрические объекты — линии, грани и другие формы исследуемого объекта. Вот откуда взялось определение признакового отображения. Из-за этого любой сверточный слой нуждается в множестве фильтров, которые тренируются детектировать различные признаки. Следовательно, необходимо дополнить предыдущую диаграмму движущегося фильтра следующим образом:
Теперь в правой части рисунка можно видеть несколько сложенных (stacked) выходов операции свертки. Их несколько, потому что существует несколько обучаемых фильтров, каждый из которых производит собственный 2D выход (в случае 2D изображения). Такое множество фильтров часто в глубоком обучении часто называют каналами. Каждый канал должен обучаться для выделения на изображении определенного ключевого признака. Выход сверточного слоя для черно-белого изображения, как в датасете MNIST, имеет 3 измерения — 2D для каждого из каналов и еще одно для их числа.
Если входной объект мультиканальный, то в случае цветного RGB изображения (один канал для каждого цвета) выход будет четырехмерным. К счастью, любая библиотека глубокого обучения, включая PyTorch, легко справляется с отображением. Наконец, не стоит забывать, что операция свертки проходит через активационную функцию в каждом узле.
Следующая важная часть сверточных нейронных сетей — концепция, называемая пулингом.
Объединение (Pooling)
Основными преимуществами для пулинга в сверточной нейронной сети являются:
- Уменьшение количества параметров в вашей модели благодаря процессу даунсемплинга (down-sampling).
- Детектирование признаков становится более правильным при изменении ориентации или размера объекта.
Использование пулинга в сверточных нейронных сетях
В дополнении к функции даунсемплинга пулинг используется в CNN, чтобы детектировать определенные признаки, инвариантные к изменениям размера или ориентации. Другой способ представить действие пулинга — он обобщает низкоуровневую, сложно структурированную информацию. Представим случай, когда у нас есть сверточный фильтр, который во время тренировки обучается распознавать знак «9» в различных положениях на входном изображении. Чтобы сверточная сеть научилась корректно классифицировать появление «9» на картинке, требуется каким-то образом активировать сеть каждый раз, когда эта цифра появляется на изображении независимо от размера и ориентации (кроме случая, когда «9» напоминает «9»). Пулинг может помочь в такой задаче выбора высокоуровневых, обобщенных признаков. Этот процесс иллюстрирован ниже:
Следовательно, пулинг выступает в качестве механизма обобщения низкоуровневых данных и позволяет нейросети переходить от данных с высоким разрешением до информации с более низким разрешением. Другими словами, пулинг в паре со сверточными фильтрами делают возможным детектирование объекта на изображении.
Общий взгляд
Ниже представлено изображение из Википедии, которое показывает структуру полностью разработанной сверточной нейронной сети:
Если смотреть на рисунок сверху слева направо, сначала мы видим изображение робота. Далее серия сверточных фильтров сканирует входное изображение для отображения признаков. Из выходов этих фильтров операции пулинга выбирают подвыборку. После этого идет следующий набор сверток и пулинга на выходе из первого набора операций пулинга и свертки. Наконец, на выходе нейросети находится прикрепленный полносвязный слой, смысл которого нуждается в объяснении.
Полносвязный слой
Ранее обсуждалось, сверточная нейронная сеть берет данные высокого разрешения и эффективно трансформирует их в представления объектов. Полносвязный слой может рассматриваться как стандартный классификатор над богатым информацией выходом нейросети для интерпретации и финального предсказания результата классификации. Для прикрепления полносвязного слоя к сети измерения выхода CNN необходимо «расплющить».
Рассматривая предыдущую диаграмму, на выходе имеем несколько каналов x x y тензоров/матриц. Эти каналы необходимо привести к одному (N x 1) тензору. Возьмем пример: имеем 100 каналов с 2х2 матрицами, отображающими выход последней пулинг операции в сети. В PyTorch можно легко осуществить преобразование в 2х2х100 = 400 строк, как будет показано ниже.
Теперь, когда основы сверточных нейронных сетей заложены, настало время реализовать CNN с помощью PyTorch.
Реализация CNN на PyTorch
Любой достойный фреймворк глубокого обучения может с легкостью справиться с операциями сверточной нейросети. PyTorch является таким фреймворком. В данном разделе будет показано, как создавать CNN с помощью PyTorch шаг за шагом. В идеале вы должны обладать некоторым представлением о PyTorch, но это не обязательно. Мы хотим разработать нейронную сеть для классификации символов в датасете MNIST. Полный код к этому туториалу находится в этом репозитории на GitHub.
В самом начале на вход подаются черно-белые представления символов размером 28х28 пикселей каждое. Первый слой состоит из 32 каналов сверточных фильтров размера 5х5 + активационная функция ReLU, затем идет 2х2 max pooling с даунсемплингом с шагом 2 (этот слой выводит данные размером 14х14). На следующий слой подается выход с первого слоя размера 14х14, который сканируется снова 5х5 сверточными фильтрами с 64 каналов, затем следует 2х2 max pooling с даунсемплингом для генерирования выхода размером 7х7.
- операция выравнивания, которая создает 7х7х64=3164 узлов
- средний слой из 1000 полносвязных улов
- операция softmax над крайними 10 узлами для генерирования вероятностей классов.
Загрузка датасета
В PyTorch уже интегрирован датасет MNIST, который мы можем использовать при помощи функции DataLoader. В этом подразделе выясним, как установить загрузчик данных для датасета MNIST. Но для начала нужно определить предварительные переменные:
Прежде всего, устанавливаем тренировочные гиперпараметры. Далее идет спецификация пути папки для хранения датасета MNIST (PyTorch автоматически загрузит датасет в эту папку) и локации для тренировочных параметров модели после того, как обучение будет завершено.
Далее задаем преобразование для применения к MNIST и переменные для данных:
Первое, на что необходимо обратить внимание в коде сверху, это функция (). Функция находится в пакете torchvision. Она позволяет разработчику делать различные манипуляции с указанным датасетом. Численные трансформации могут быть соединены вместе в список при использовании функции Compose(). В этом случае сначала устанавливается преобразование, которое конвертирует входной датасет в PyTorch тензор. PyTorch тензор — особый тип данных, используемый в библиотеке для всех различных операций с данными и весами внутри нейросети. По сути, такой тензор — простая многомерная матрица. Но в любом случае, PyTorch требует преобразования датасета в тензор таким образом, что его можно использовать для тренировки и тестирования сети.
Следующий аргумент в списке Compose() — нормализация. Нейронная сеть обучается лучше, когда входные данные нормализованы так, что их значения находятся в диапазоне от -1 до 1 или от 0 до 1. Чтобы это сделать с помощью нормализации PyTorch, необходимо указать среднее и стандартное отклонение MNIST датасета, которые в этом случае равны 0.1307 и 0.3081 соответственно. Отметим, среднее значение и стандартное отклонение должны быть установлены для каждого входного канала. У MNIST есть только один канал, но уже для датасета CIFAR c 3 каналами (по одному на каждый цвет из RGB спектра) надо указывать среднее и стандартное отклонение для каждого.
Далее необходимо создать объекты train_dataset и test_dataset, которые будут последовательно проходить через загрузчик данных. Чтобы создать такие датасеты из данных MNIST, требуется задать несколько аргументов. Первый — путь до папки, где хранится файл с данными для тренировки и тестирования. Логический аргумент train показывает, какой файл из или стоит брать в качестве тренировочного сета. Следующий аргумент — transform, в котором мы указываем ранее созданный объект trans, который осуществляет преобразования. Наконец, аргумент загрузки просит функцию датасета MNIST загрузить при необходимости данные из онлайн источника.
Теперь когда тренировочный и тестовый датасеты созданы, настало время загрузить их в загрузчик данных:
Объект загрузчик данных в PyTorch обеспечивает несколько полезных функций при использовании тренировочных данных:
- Возможность легко перемешивать данные.
- Возможность группировать данные в партии.
- Более эффективное использование данных с помощью параллельной загрузки, используя многопроцессорную обработку.
Как можно видеть, требуется задать три простых аргумента. Первый — данные, которые вы хотите загрузить; второй — желаемый размер партии; третий — перемешивать ли случайным образом датасет. Загрузчик может использоваться в качестве итератора. Поэтому для извлечения данных мы можем просто воспользоваться стандартным итератором Python, например, перечислением. Такая возможность будет позже продемонстрирована на практике в туториале.
Создание модели
Первый шаг — создание нескольких объектов последовательного слоя с помощью функции class _init_. Сначала создаем слой 1 (self.layer1) через создание объекта. Этот метод позволяет создавать упорядоченные слои в сети и является удобным способом создания последовательности свертка + ReLu + пулинг. Как можно видеть, первый элемент в определении этой последовательности — метод Conv2d, который создает набор сверточных фильтров. В этом методе первый аргумент — количество входных каналов; в нашем случае изображения MNIST черно-белые и количество каналов равно 1. Второй аргумент в Conv2d — количество выходных каналов; как показано на диаграмме архитектуры модели, первый слой сверточных фильтров состоит из 32 каналов, поэтому значение второго аргумента равно 32.
Аргумент kernel_size отвечает за размер сверточного фильтра, в нашем случае мы хотим фильтр размеров 5х5, поэтому аргумент равен 5. Если бы мы хотели фильтры с разными размерными формами по оси х и y, необходимо было указать параметры в виде python tuple (размер по х, размер по y). Наконец, мы хотим установить padding, что делается несколько сложнее. Размер измерения на выходе от операции сверточной фильтрации или пулинга может быть посчитан с помощью уравнения:
Где W(in) — ширина выхода, F — размер фильтра, P — паддинг, S — шаг. Такая же формула применима для расчета высоты. В нашем случае изображение и фильтрация симметричны, поэтому формула применима и к ширине и к высоте. Если хотим оставить входные и выходные измерения без изменений с размером фильтра 5 и шагом 1, то исходя из верхней формулы паддинг нужно задать равным 2. Таким образом, аргумент для паддинга в Conv2d равен 2.
Следующий аргумент в последовательности — простая ReLU функция активации. Последний элемент в последовательном определении для self.layer1 — операция max pooling. Первый аргумент — размер объединения, который равен 2х2; следовательно, аргумент равен 2. Во втором аргументе мы хотим взять подвыборку из данных через уменьшение эффективного размера изображения с факторов 2. Чтобы это сделать используя формулу выше, устанавливаем шаг равным 2 и паддинг равным 0. Следовательно, шаговый аргумент равен 2. Паддинг аргумент по умолчанию равен 0, если не указано другое значение, что сделано в коде сверху. Из этих вычислений теперь понятно, что выход слоя self.layer1 имеет 32 канала и “изображения” размером 14х14.
Второй слой, self.layer2, определяется таким же образом, как и первый. Единственное отличие здесь — вход в функцию Conv2d теперь 32 канальный, а выход — 64 канальный. Следуя такой же логике и учитывая пулинг и даунсемплинг, выход из self.layer2 представляет из себя 64 канала изображения размера 7х7.
Далее определяем отсеивающий слой для предотвращения переобучения модели. В конце создаем два полносвязных слоя. Первый слой имеет размер 7х7х64 узла и соединяется со вторым слоем с 1000 узлами. Чтобы создать полносвязный слой в PyTorch, используем метод. Первый аргумент метода — число узлов в данном слое, второй аргумент — число узлов в следующем слое.
Определение слоев создано при помощи _init_. Следующий шаг — определить потоки данных через слои при прямом прохождении через сеть:
Здесь важно назвать функцию “forward”, так как она будет использовать базовую функцию прямого распространения в и позволит всему функционалу работать корректно. Как можно видеть, функция принимает на вход аргумент х, представляющий собой данные, которые должны проходить через модель (например, партия данных). Направляем эти данные в первый слой (self.layer1) и возвращаем выходные данные как out. Эти выходные данные поступают на следующий слой и так далее. Отметим, после self.layer2 применяем функцию преобразования формы к out, которая разглаживает размеры данных с 7х7х64 до 3164х1. После двух полносвязных слоев применяется dropout-слой и из функции возвращается финальное предсказание.
Мы определили, что такое сверточная нейронная сеть, и как она работает. Настало время обучить нашу модель.
Обучение модели
Перед тренировкой модели мы должны сначала создать экземпляр (instance) нашего класса ConvNet(), определить функцию потерь и оптимизатор:
Экземпляр класса ConvNet() создается под названием model. Далее определяем операцию над потерями, которая будет использоваться для подсчета потерь. В нашем случае используем доступную в PyTorch функцию CrossEntropyLoss(). Вы уже могли заметить, что мы еще не задали активационную функцию SoftMax для последнего слоя, выполняющего классификацию. Это сделано потому, что функция CrossEntropyLoss() объединяет и SoftMax и кросс-энтропийную функцию потерь в единую функцию. Далее определяем оптимизатор Adam. Первым аргументом функции являются параметры, которые мы хотим обучить оптимизатором. Это легко сделать с помощью класса, из которого вы получается ConvNet. Всё что нужно сделать — снабдить функцию аргументом () и PyTorch будет отслеживать все параметры нашей модели, которые необходимо обучить. Последним определяется скорость обучения.
Самыми важными частями для начала являются два цикла. Первый цикл проходит по количеству эпох, в нём итерации проходят по train_loader используя перечисление. Во внутреннем цикле сначала считаются выходы прямого прохождения изображений (которые представляют собой партии нормализованных MNIST изображений из train_loader). Отметим, нам не требуется вызывать (images), так как знает, что нужно вызывать forward при выполнении model(images).
Следующий шаг — отправление выходов модели и настоящих меток в функцию CrossEntropyLoss, определенную как criterion. Потери добавляются в список, который будет использован позже для отслеживания прогресса обучения. Затем — выполнение обратного распространения ошибки и оптимизированного шага тренировки. Сначала градиенты должны быть обнулены, что легко делается вызовом zero_grad() на оптимизаторе. Далее вызываем функцию.backward() на переменной loss для выполнения обратного распространения. Теперь, когда градиенты посчитаны при обратном распространении, просто вызываем () для выполнения шага обучения оптимизатора Adam. PyTorch делает процесс обучения модели очень легким и интуитивным.
Следующий набор строк отвечает за отслеживание точности на тренировочном сете. Предсказания модели могут быть определены с помощью функции (), которая возвращает индекс максимального значения в тензоре. Первый аргумент этой функции — тензор, который мы хотим исследовать; второй — ось, по которой определяется максимум. Выходящий из модели тензор должен иметь размер (batch_size,10). Чтобы определить предсказание модели, необходимо для каждого примера в партии найти максимальные значения из 10 выходных узлов. Каждый из этих узлов будет соответствовать одному рукописному символу (например, выход 2 равен символу «2» и так далее). Выходной узел с наибольшим значением и будет предсказанием модели. Следовательно, надо задать второй аргумент в функции () равным 1, что указывает функции максимума проверить ось выходного узла (axis = 0 соответствует размерности batch_size).
На этом шаге возвращается список целочисленных предсказаний модели, а следующая строка сравнивает эти предсказания с настоящими метками (predicted == labels) и суммирует правильные предсказания. Отметим, что на этом шаге выход функции sum() всё еще тензор, поэтому для оценки его значений требуется вызвать.item(). Делим количество правильных предсказаний на размер партии batch_size (эквивалентно (0)) для подсчета точности. Наконец, во время тренировки после каждых 100 итераций внутреннего цикла выводим прогресс.
Epoch [1/6], Step [100/600], Loss: 0.2183, Accuracy: 95.00% Epoch [1/6], Step [200/600], Loss: 0.1637, Accuracy: 95.00% Epoch [1/6], Step [300/600], Loss: 0.0848, Accuracy: 98.00% Epoch [1/6], Step [400/600], Loss: 0.1241, Accuracy: 97.00% Epoch [1/6], Step [500/600], Loss: 0.2433, Accuracy: 95.00% Epoch [1/6], Step [600/600], Loss: 0.0473, Accuracy: 98.00% Epoch [2/6], Step [100/600], Loss: 0.1195, Accuracy: 97.00%
Тестирование
В первой строке устанавливаем режим оценки используя (). Это удобная функция запрещает любые исключения или слои нормализации партии в модели, которые будут мешать объективной оценке. Конструкция torch.no_grad() выключает функцию автоградиента в модели, так как она не нужна при тестировании/оценке модели и замедляет вычисления. Остальная часть кода совпадает с вычислением точности во время тренировки, за исключением того, что в этом случае итерации проходят по test_loader.
Наконец, результат выводится в консоль, а модель сохраняется при помощи функции ().
В последней части кода в этом репозитории на GitHub дополнительно построен график отслеживания точности и потерь, используя библиотеку для рисования Bokeh. Финальный результат выглядит так:
Можно видеть, сеть достаточно быстро достигает высокого уровня точности на тренировочном сете. На тестовом наборе после 6 эпох модель показывает точность 99%, что очень хорошо. Этот результат определенно лучше точности базовой полносвязной нейронной сети.
Как итог: в туториале были показаны все преимущества и структура сверточной нейронной сети, механизм её работы. Вы также научились реализовывать CNN в библиотеке глубокого обучения PyTorch, которая имеет большое будущее.
- Как создать чат-бота с нуля на Python: подробная инструкция
- Как создать собственную нейронную сеть с нуля на языке Python
- Как создать собственный датасет из картинок Google
Базовые нейронные сети на Python для новичков
Нейронная сеть — это не обязательно сложная структура с миллионами параметров. Начать можно с создания элементарного персептрона, способного решать линейно-разделимые задачи, например, имитации логических операторов.
import numpy as np class Perceptron: def __init__(self, learning_rate=0.1, n_iterations=100): self.learning_rate = learning_rate self.n_iterations = n_iterations = None = None def fit(self, X, y): n_samples, n_features = = (n_features) = 0 for _ in range(self.n_iterations): for idx, x_i in enumerate(X): linear_output = (x_i,) + y_predicted = 1 if linear_output >= 0 else 0 update = self.learning_rate * (y[idx] – y_predicted) += update * x_i += update def predict(self, X): linear_output = (X,) + return (linear_output >= 0, 1, 0) X = ([[0, 0], [0, 1], [1, 0], [1, 1]]) y = ([0, 0, 0, 1]) perceptron = Perceptron() (X, y) predictions = (X) print(«Входные данные:») print(X) print(«Предсказания:») print(predictions) print(«Ожидаемый результат:») print(y)
Этот код создает и обучает простейшую нейронную сеть — персептрон, который учится распознавать логическую операцию AND. Несмотря на свою простоту, такие модели являются фундаментом для понимания принципов работы более сложных нейронных сетей.
Но давайте перейдем к более практичному примеру — построению модели для прогнозирования цен на недвижимость:
import numpy as np from import fetch_california_housing from sklearn.model_selection import train_test_split from import StandardScaler housing = fetch_california_housing() X, y =, X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = (X_test) class SimpleNeuralNetwork: def __init__(self, input_size, hidden_size, output_size=1): self.W1 = (input_size, hidden_size) * 0.01 self.b1 = ((1, hidden_size)) self.W2 = (hidden_size, output_size) * 0.01 self.b2 = ((1, output_size)) def forward(self, X): self.z1 = (X, self.W1) + self.b1 self.a1 = (0, self.z1) self.z2 = (self.a1, self.W2) + self.b2 return self.z2 def backward(self, X, y, y_pred, learning_rate=0.001): m = dz2 = (y_pred – (-1, 1)) / m dW2 = (self.a1.T, dz2) db2 = (dz2, axis=0, keepdims=True) da1 = (dz2, self.W2.T) dz1 = da1 * (self.z1 > 0) dW1 = (X.T, dz1) db1 = (dz1, axis=0, keepdims=True) self.W1 -= learning_rate * dW1 self.b1 -= learning_rate * db1 self.W2 -= learning_rate * dW2 self.b2 -= learning_rate * db2 def train(self, X, y, epochs=1000, learning_rate=0.001): for i in range(epochs): y_pred = (X) (X, y, y_pred, learning_rate) if i % 100 == 0: loss = ((y_pred – (-1, 1))) print(f»Эпоха {i}, Потери: {loss:.4f}») def predict(self, X): return (X) nn = SimpleNeuralNetwork(input_size=X_train.shape[1], hidden_size=64) (X_train, y_train, epochs=500, learning_rate=0.01) predictions = (X_test) mse = ((predictions – y_test.reshape(-1, 1))) print(f»Среднеквадратическая ошибка на тестовых данных: {mse:.4f}»)
В этом примере мы создали нейронную сеть с одним скрытым слоем, которая обучается прогнозировать цены на недвижимость. Несмотря на относительную простоту, подобная модель способна улавливать нелинейные зависимости в данных.
Таблица №1
| Тип нейронной сети | Применение | Сложность реализации | Время обучения |
|---|---|---|---|
| Персептрон | Логические операции, линейная классификация | Низкая | Секунды |
| Полносвязная NN | Регрессия, простая классификация | Средняя | Минуты |
| Сверточная NN | Распознавание образов, изображения | Высокая | Часы |
| Рекуррентная NN | Последовательные данные, текст | Высокая | Часы |
Однажды мой студент, бухгалтер по профессии без опыта программирования, сомневался, что сможет создать нейронную сеть. Мы начали с простейшего персептрона для задачи бинарной классификации. Мария (так звали студентку) реализовала его на чистом Python, строчка за строчкой. «Это похоже на математический расчет в Excel, только немного сложнее,» — призналась она после успешного запуска модели.
Через месяц Мария уже экспериментировала с многослойными сетями для прогнозирования спроса на товары компании. Ключевым моментом стало не изучение сложной теории, а практические эксперименты с готовым кодом, который она постепенно модифицировала под свои задачи. Эта история убедила меня: начинать нужно с запуска рабочего кода, а не с изучения математики нейронных сетей.
Простой классификатор изображений с TensorFlow и Keras
Классификация изображений — одно из самых впечатляющих применений нейронных сетей. С помощью TensorFlow и Keras можно создать модель для распознавания изображений буквально в несколько строк кода.
Начнем с классического примера — распознавания рукописных цифр из набора данных MNIST:
import tensorflow as tf from tensorflow import keras import as plt import numpy as np (X_train, y_train), (X_test, y_test) =.load_data() X_train = X_train / 255.0 X_test = X_test / 255.0 model = ([((28, 28, 1), input_shape=(28, 28)),.Conv2D(32, kernel_size=(3, 3), activation=’relu’),.MaxPooling2D(pool_size=(2, 2)),.Conv2D(64, kernel_size=(3, 3), activation=’relu’),.MaxPooling2D(pool_size=(2, 2)), (), (128, activation=’relu’), (0.5), (10, activation=’softmax’)]) (optimizer=’adam’, loss=’sparse_categorical_crossentropy’, metrics=[‘accuracy’]) history = (X_train, y_train, epochs=5, validation_data=(X_test, y_test), batch_size=128) test_loss, test_acc = (X_test, y_test) print(f»Точность на тестовых данных: {test_acc:.4f}») predictions = (X_test[:10]) predicted_labels = (predictions, axis=1) (figsize=(12, 4)) for i in range(10): (1, 10, i+1) (X_test[i], cmap=’gray’) (f»Pred: {predicted_labels[i]}\nTrue: {y_test[i]}») (‘off’) plt.tight_layout() ()
Этот код создает сверточную нейронную сеть (CNN), которая способна распознавать рукописные цифры с точностью более 98%. Важно отметить, что с помощью всего около 30 строк кода мы получили модель, сравнимую по эффективности с тем, что несколько десятилетий назад требовало месяцев исследований.
Теперь рассмотрим более сложный пример — классификацию цветных изображений с использованием предобученной модели:
import tensorflow as tf from tensorflow import keras import numpy as np import as plt (X_train, y_train), (X_test, y_test) =.cifar10.load_data() class_names = [‘самолет’, ‘автомобиль’, ‘птица’, ‘кот’, ‘олень’, ‘собака’, ‘лягушка’, ‘лошадь’, ‘корабль’, ‘грузовик’] X_train = X_train.astype(‘float32’) / 255.0 X_test = X_test.astype(‘float32′) / 255.0 y_train =.to_categorical(y_train, 10) y_test =.to_categorical(y_test, 10) base_model =.MobileNetV2(input_shape=(160, 160, 3), include_top=False, weights=’imagenet’) base_model.trainable = False model = ([(160, 160), base_model,.GlobalAveragePooling2D(), (128, activation=’relu’), (0.5), (10, activation=’softmax’)]) (optimizer=(learning_rate=0.0001), loss=’categorical_crossentropy’, metrics=[‘accuracy’]) history = (X_train, y_train, batch_size=32, epochs=3, validation_data=(X_test, y_test)) test_loss, test_acc = (X_test, y_test) print(f»Точность на тестовых данных: {test_acc:.4f}»)
Здесь мы использовали технику переноса обучения (transfer learning), применяя предобученную на миллионах изображений модель MobileNetV2 для классификации изображений из набора CIFAR-10. Такой подход позволяет получить высокую точность даже при небольшом количестве собственных данных и ограниченных вычислительных ресурсах.
Несколько рекомендаций для улучшения моделей классификации изображений:
- Аугментация данных — добавление случайных искажений к изображениям для расширения обучающего набора
- Регуляризация — использование слоев Dropout и L2-регуляризации для предотвращения переобучения
- Применение BatchNormalization — стабилизирует и ускоряет обучение
- Использование предобученных моделей — значительно сокращает время обучения и улучшает точность
- Мониторинг метрик — отслеживание не только точности, но и других показателей, таких как precision, recall и F1-score
Обработка текста: создание чат-бота на PyTorch
Обработка естественного языка (NLP) — еще одна область, где нейронные сети произвели революцию. PyTorch предоставляет гибкие инструменты для разработки моделей обработки текста, включая чат-ботов.
Рассмотрим пример создания простого чат-бота с использованием рекуррентной нейронной сети (RNN):
import torch import as nn import as optim import numpy as np import random import json import nltk from import LancasterStemmer (‘punkt’) stemmer = LancasterStemmer() with open(») as file: data = (file) «»» { «intents»: [{ «tag»: «greeting», «patterns»: [«Привет», «Здравствуйте», «Добрый день», «Как дела»], «responses»: [«Привет! Чем могу помочь?», «Здравствуйте!», «Рад вас видеть!»] }, { «tag»: «goodbye», «patterns»: [«Пока», «До свидания», «Увидимся»], «responses»: [«До свидания!», «Хорошего дня!», «Буду рад помочь снова!»] }, { «tag»: «thanks», «patterns»: [«Спасибо», «Благодарю», «Это помогло»], «responses»: [«Всегда пожалуйста!», «Рад был помочь!», «Обращайтесь!»] }] } «»» words = [] tags = [] patterns_and_tags = [] for intent in data[‘intents’]: tag = intent[‘tag’] (tag) for pattern in intent[‘patterns’]: w = nltk.word_tokenize(()) (w) patterns_and_tags.append((w, tag)) words = [(()) for w in words if w not in «?!.,»] words = sorted(list(set(words))) tags = sorted(list(set(tags))) training = [] output = [] output_empty = * len(tags) for doc in patterns_and_tags: bag = [] pattern_words = doc[0] pattern_words = [(()) for word in pattern_words] for w in words: (1) if w in pattern_words else (0) output_row = list(output_empty) output_row[(doc[1])] = 1 (bag) (output_row) X_train = (training) y_train = (output) class ChatbotModel(): def __init__(self, input_size, hidden_size, output_size): super(ChatbotModel, self).__init__() self.layer1 = (input_size, hidden_size) self.layer2 = (hidden_size, hidden_size) self.layer3 = (hidden_size, output_size) = () = (dim=1) def forward(self, x): x = self.layer1(x) x = (x) x = self.layer2(x) x = (x) x = self.layer3(x) return (x) input_size = len(X_train[0]) hidden_size = 8 output_size = len(tags) model = ChatbotModel(input_size, hidden_size, output_size) criterion = () optimizer = ((), lr=0.001) epochs = 1000 for epoch in range(epochs): optimizer.zero_grad() outputs = model(X_train) loss = criterion(outputs, y_train) () () if (epoch + 1) % 100 == 0: print(f»Эпоха {epoch+1}/{epochs}, Потери: {():.4f}») def bag_of_words(sentence, words): sentence_words = nltk.word_tokenize(()) sentence_words = [(()) for word in sentence_words] bag = * len(words) for sw in sentence_words: for i, word in enumerate(words): if word == sw: bag[i] = 1 return ([bag]) def predict_response(message): bow = bag_of_words(message, words) with torch.no_grad(): output = model(bow) _, predicted = (output, dim=1) tag = tags[()] for intent in data[‘intents’]: if intent[‘tag’] == tag: responses = intent[‘responses’] return (responses) return «Извините, я не понимаю.» while True: message = input(«Вы: «) if () == «выход»: break response = predict_response(message) print(f»Бот: {response}»)
Этот код создает простую модель чат-бота, которая может распознавать намерения пользователя и выдавать соответствующие ответы. Обратите внимание, что для работы этого примера необходим файл с шаблонами вопросов и ответов.
В 2026 году я консультировал компанию, которая хотела внедрить чат-бот поддержки для своих клиентов. Они получали тысячи однотипных вопросов, на которые приходилось отвечать живым операторам. Компания рассматривала предложения от разработчиков стоимостью от 500 000 рублей с длительными сроками разработки.
Я предложил альтернативу — создать прототип на PyTorch за неделю. Мы взяли исторические диалоги из системы поддержки, структурировали их по тематикам и создали датасет для обучения, похожий на представленный выше. Результат превзошел ожидания: бот корректно распознавал около 70% запросов с первого дня работы.
За месяц мы довели точность до 85%, что позволило сократить нагрузку на операторов на 40%. Самое удивительное, что клиенты даже не замечали, что общаются с ботом, а не с человеком. Этот случай наглядно показал, как относительно простые решения на основе нейронных сетей могут давать значимый бизнес-эффект без огромных инвестиций.
Для создания более продвинутого чат-бота можно использовать архитектуру Transformer, которая является основой современных языковых моделей:
import torch import as nn import as optim from import Dataset, DataLoader import math import numpy as np class ChatDataset(Dataset): def __init__(self, X, y): self.n_samples = len(X) self.x_data = X self.y_data = y def __getitem__(self, index): return self.x_data[index], self.y_data[index] def __len__(self): return self.n_samples class AttentionLayer(): def __init__(self, d_model, n_heads): super(AttentionLayer, self).__init__() self.d_model = d_model self.n_heads = n_heads self.head_dim = d_model // n_heads = (d_model, d_model) = (d_model, d_model) = (d_model, d_model) = (d_model, d_model) def forward(self, query, key, value, mask=None): batch_size = Q = (query).view(batch_size, -1, self.n_heads, self.head_dim).transpose(1, 2) K = (key).view(batch_size, -1, self.n_heads, self.head_dim).transpose(1, 2) V = (value).view(batch_size, -1, self.n_heads, self.head_dim).transpose(1, 2) scores = (Q, (-2, -1)) / (self.head_dim) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) attention = (scores, dim=-1) out = (attention, V) out = (1, 2).contiguous().view(batch_size, -1, self.d_model) out = (out) return out class TransformerBlock(): def __init__(self, d_model, n_heads, dropout=0.1): super(TransformerBlock, self).__init__() = AttentionLayer(d_model, n_heads) self.norm1 = (d_model) self.norm2 = (d_model) self.feed_forward = ((d_model, d_model * 4), (), (d_model * 4, d_model)) = (dropout) def forward(self, x, mask=None): attention_out = (x, x, x, mask) x = self.norm1(x + (attention_out)) ff_out = self.feed_forward(x) x = self.norm2(x + (ff_out)) return x class TransformerChatbot(): def __init__(self, input_size, output_size, d_model=64, n_heads=4, n_layers=2, dropout=0.1): super(TransformerChatbot, self).__init__() = (input_size, d_model) self.transformer_blocks = ([TransformerBlock(d_model, n_heads, dropout) for _ in range(n_layers)]) self.fc_out = (d_model, output_size) = (dropout) def forward(self, x): x = (x) x = (1) for transformer in self.transformer_blocks: x = transformer(x) x = (1) x = self.fc_out(x) return (x, dim=1)
Этот фрагмент кода демонстрирует реализацию модели на основе архитектуры Transformer для более сложных задач обработки языка. Хотя полная реализация чат-бота требует дополнительных компонентов, этот код предоставляет основу для создания современной модели обработки естественного языка.
Таблица №2
| Тип модели | Преимущества | Недостатки | Типичные применения |
|---|---|---|---|
| Простая нейронная сеть | Легко реализовать, быстрое обучение | Ограниченная способность понимать контекст | Классификация запросов по шаблонам |
| RNN/LSTM | Понимает последовательности, запоминает контекст | Медленное обучение, проблема исчезающего градиента | Обработка текста среднего размера, генерация ответов |
| Transformer | Отлично работает с контекстом, параллельные вычисления | Требует много данных, вычислительно интенсивен | Современные чат-боты, перевод, генерация текста |
| Pre-trained LLMs | Огромная база знаний, понимание естественного языка | Очень ресурсоемкие, сложны в доработке | Передовые диалоговые системы, ответы на сложные вопросы |
Генеративные сети: код для синтеза изображений
Генеративно-состязательные сети (GAN) произвели революцию в области синтеза контента, позволяя создавать реалистичные изображения, которые не существуют в реальности. Рассмотрим пример реализации базовой GAN для создания изображений рукописных цифр:
import torch import as nn import as optim from import DataLoader from torchvision import datasets, transforms import as plt import numpy as np batch_size = 64 latent_dim = 100 image_shape = (1, 28, 28) device = («cuda» if.is_available() else «cpu») transform = ([(), ([0\.5], [0\.5])]) mnist_dataset = (root=’./data’, train=True, download=True, transform=transform) dataloader = DataLoader(mnist_dataset, batch_size=batch_size, shuffle=True) class Generator(): def __init__(self): super(Generator, self).__init__() = ((latent_dim, 128), (0.2, inplace=True), (128, 256), nn.BatchNorm1d(256), (0.2, inplace=True), (256, 512), nn.BatchNorm1d(512), (0.2, inplace=True), (512, 1024), nn.BatchNorm1d(1024), (0.2, inplace=True), (1024, int((image_shape))), ()) def forward(self, z): img = (z) img = ((0), *image_shape) return img class Discriminator(): def __init__(self): super(Discriminator, self).__init__() = ((int((image_shape)), 512), (0.2, inplace=True), (512, 256), (0.2, inplace=True), (256, 1), ()) def forward(self, img): img_flat = ((0), -1) validity = (img_flat) return validity generator = Generator().to(device) discriminator = Discriminator().to(device) optimizer_G = ((), lr=0.0002, betas=(0.5, 0.999)) optimizer_D = ((), lr=0.0002, betas=(0.5, 0.999)) adversarial_loss = () num_epochs = 100 sample_interval = 10 for epoch in range(num_epochs): for i, (real_imgs, _) in enumerate(dataloader): real_labels = (real_imgs.size(0), 1).to(device) fake_labels = (real_imgs.size(0), 1).to(device) real_imgs = real_imgs.to(device) optimizer_G.zero_grad() z = (real_imgs.size(0), latent_dim).to(device) gen_imgs = generator(z) g_loss = adversarial_loss(discriminator(gen_imgs), real_labels) g_loss.backward() optimizer_G.step() optimizer_D.zero_grad() real_loss = adversarial_loss(discriminator(real_imgs), real_labels) fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake_labels) d_loss = (real_loss + fake_loss) / 2 d_loss.backward() optimizer_D.step() if i % 100 == 0: print(f»[Эпоха {epoch}/{num_epochs}] [Батч {i}/{len(dataloader)}] » f»[D loss: {d_loss.item():.4f}] [G loss: {g_loss.item():.4f}]») if epoch % sample_interval == 0: with torch.no_grad(): z = (25, latent_dim).to(device) gen_imgs = generator(z).cpu() fig, axs = (5, 5, figsize=(10, 10)) for i in range(5): for j in range(5): axs[i, j].imshow(gen_imgs[i*5 + j].view(28, 28), cmap=’gray’) axs[i, j].axis(‘off’) (f»gan_images/epoch_{epoch}.png») ()
Эта реализация GAN демонстрирует, как можно создать генеративную модель для синтеза изображений рукописных цифр. Обучение такой модели на стандартном наборе данных MNIST позволяет создавать новые, не существующие ранее изображения цифр.
import torch import as nn import as F from import DataLoader from torchvision import datasets, transforms import numpy as np class AdaIN(): def __init__(self, style_dim, channels): super(AdaIN, self).__init__() = channels self.style_mapping = (style_dim, channels * 2) self.style_mapping.[:channels] = 1 self.style_mapping.[channels:] = 0 def forward(self, x, style): style = self.style_mapping(style).unsqueeze(2).unsqueeze(3) gamma, beta = (2, dim=1) normalized = F.instance_norm(x) out = gamma * normalized + beta return out class GeneratorBlock(): def __init__(self, in_channels, out_channels, style_dim): super(GeneratorBlock, self).__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1) self.adain1 = AdaIN(style_dim, out_channels) self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1) self.adain2 = AdaIN(style_dim, out_channels) def forward(self, x, style): out = self.conv1(x) out = self.adain1(out, style) out = F.leaky_relu(out, 0.2) out = self.conv2(out) out = self.adain2(out, style) out = F.leaky_relu(out, 0.2) return out class StyleGANGenerator(): def __init__(self, style_dim=512, num_channels=3, img_size=64): super(StyleGANGenerator, self).__init__() self.style_dim = style_dim self.init_size = img_size // 8 self.init_channel = 512 = ((style_dim, style_dim), (0.2), (style_dim, style_dim), (0.2), (style_dim, style_dim), (0.2), (style_dim, style_dim), (0.2)) = ((1, self.init_channel, self.init_size, self.init_size)) = ([GeneratorBlock(self.init_channel, 512, style_dim), GeneratorBlock(512, 256, style_dim), GeneratorBlock(256, 128, style_dim), GeneratorBlock(128, 64, style_dim)]) = (scale_factor=2, mode=’bilinear’, align_corners=False) self.to_rgb = nn.Conv2d(64, num_channels, 1) def forward(self, noise): style = (noise) out = (, 1, 1, 1) for block in: out = (out) out = block(out, style) out = self.to_rgb(out) return (out) class StyleGANDiscriminator(): def __init__(self, num_channels=3, img_size=64): super(StyleGANDiscriminator, self).__init__() = (nn.Conv2d(num_channels, 64, 4, stride=2, padding=1), (0.2, inplace=True), nn.Conv2d(64, 128, 4, stride=2, padding=1), nn.BatchNorm2d(128), (0.2, inplace=True), nn.Conv2d(128, 256, 4, stride=2, padding=1), nn.BatchNorm2d(256), (0.2, inplace=True), nn.Conv2d(256, 512, 4, stride=2, padding=1), nn.BatchNorm2d(512), (0.2, inplace=True), nn.Conv2d(512, 1, 4, padding=0)) def forward(self, img): validity = (img) return ((0), -1)
StyleGAN — это продвинутая архитектура GAN, которая позволяет генерировать высококачественные изображения с контролем стилистических параметров. Приведенный выше код демонстрирует основные компоненты этой архитектуры, хотя полная реализация значительно сложнее.
- DCGAN (Deep Convolutional GAN) — использует свёрточные слои вместо полносвязных, что улучшает качество генерируемых изображений
- CycleGAN — позволяет переносить стиль между изображениями без необходимости иметь парные образцы
- Pix2Pix — модель для генерации изображений на основе контурных рисунков или схем
- BigGAN — масштабная архитектура, способная генерировать изображения высокого разрешения
- Diffusion Models — новейший класс генеративных моделей, показывающий великолепные результаты в генерации изображений
Готовые проекты нейросетей с GitHub для практики
GitHub — это настоящая сокровищница готовых проектов по нейронным сетям, которые можно изучать и использовать для практики. Вот список полезных репозиториев с работающим кодом:
Для начинающих особенно полезны репозитории с пошаговыми руководствами:
Если вы хотите практиковаться с продвинутыми моделями, не имея мощного оборудования, рассмотрите возможность использования Google Colab. Многие репозитории адаптированы для запуска в этой среде:
!git clone!cd models/research/object_detection && pip install -q. from object_detection.utils import visualization_utils as vis_util from object_detection.builders import model_builder
Для более структурированного подхода к практике рекомендую выполнить следующие проекты в порядке возрастания сложности:
- Классификация MNIST — создание модели для распознавания рукописных цифр
- Классификация CIFAR-10 —
- Создание нейронной сети на Python: пошаговое руководство для новичков
- Нейронные сети на Python: эффективные методы обучения моделей
- 10 способов трансформировать бизнес с помощью искусственного интеллекта
- 5 шагов внедрения машинного обучения: от теории к бизнес-результату
- 10 лучших бесплатных нейросетей: доступные ИИ-инструменты 2026
- Цифровая трансформация бизнеса через технологии: этапы и кейсы
- Машинное обучение с подкреплением: принципы, алгоритмы, применение
- Топ-10 нейросетей для бизнеса и разработки: возможности и выбор
- Character AI: пошаговое руководство по созданию виртуальных личностей
- GPT-чаты: революция бизнес-процессов и аналитики для компаний
Часто задаваемые вопросы о создании сверточной нейронной сети на Python
Вопрос: Какая библиотека лучше всего подходит для создания CNN на Python?
Ответ: Наиболее популярны PyTorch и TensorFlow (с Keras). PyTorch часто хвалят за интуитивность и динамический вычислительный граф, а TensorFlow — за масштабируемость и развертывание.
Вопрос: Нужно ли иметь мощный компьютер для обучения CNN?
Ответ: Для простых моделей и маленьких датасетов (например, MNIST) достаточно CPU. Для сложных задач желателен GPU (NVIDIA с CUDA) или использование облачных сервисов (Google Colab).
Вопрос: Что такое сверточный слой и зачем он нужен?
Ответ: Сверточный слой применяет набор фильтров (ядер) к входному изображению, выделяя ключевые признаки, такие как края, текстуры и формы. Это основа CNN.
Вопрос: Какой размер ядра свертки выбрать для начала?
Ответ: Чаще всего используют ядра размером 3×3 или 5×5. Размер 3×3 является стандартным де-факто, так как позволяет эффективно извлекать признаки при небольшом количестве параметров.
Вопрос: Что такое пулинг (Pooling) и зачем он применяется?
Ответ: Пулинг — это операция субдискретизации, которая уменьшает пространственный размер карт признаков, снижая вычислительную нагрузку и количество параметров, а также борясь с переобучением.
Вопрос: Какой тип пулинга используется чаще всего?
Ответ: Max Pooling (максимальный пулинг). Он выбирает максимальное значение из окна, сохраняя наиболее яркие признаки. Average Pooling (средний пулинг) используется реже.
Вопрос: Что такое полносвязный слой в CNN?
Ответ: Полносвязный слой (Fully Connected Layer) обычно располагается в конце сети. Он принимает «сплющенные» признаки от сверточных слоев и выполняет окончательную классификацию или регрессию.
Вопрос: Какой датасет использовать для первой CNN?
Ответ: Лучше всего начать с MNIST (рукописные цифры) или CIFAR-10 (цветные изображения 10 классов). Они небольшие, хорошо изучены и легко загружаются через torchvision или keras.datasets.
Вопрос: Сколько эпох нужно для обучения простой CNN?
Ответ: Для MNIST достаточно 5-10 эпох. Для CIFAR-10 может потребоваться 20-50 эпох. Следите за кривыми обучения, чтобы не допустить переобучения.
Вопрос: Как улучшить точность модели, если она плохо обучается?
Ответ: Попробуйте добавить больше сверточных слоев, изменить количество фильтров, использовать Dropout для регуляризации, нормализацию (BatchNorm) или аугментацию данных.




















