Обзор предметной области искусственного интеллекта
Искусственный интеллект (ИИ) – обширная область информатики, которая направлена на создание систем, выполняющих сложные задачи, для решения которых требуется человеческий интеллект. Этими задачами являются:
Распознавание речи и изображений;
Принятие решений;
Прогнозирование;
Обучение на основе данных.
Современные системы ИИ используют алгоритмы машинного обучения и нейронные сети.
Одной из самых распространенных демонстраций возможностей ИИ является решение задач по распознаванию образов (например, рукописных символов). Для этого применяются специализированные нейронные сети. Они обеспечивают высокую точность и надежность распознавания.
Цель работы и постановка задачи
Целью текущей курсовой работы является разработка и реализация решения для распознавания рукописных цифр на основе нейронной сети. Задача решается с применением языка программирования Python и современных библиотек машинного обучения.
Необходимо обучить нейросеть на наборе данных MNIST и оценить ее эффективность, решив в ходе выполнения работы следующие задачи:
Постановка задачи: формулировка, параметризация, требования к исходным данным и результату работы алгоритма;
Выбор модели нейросети. Архитектурное моделирование, проектирование, математическое описание;
Подготовка исходных данных;
Разработка;
Обучение, верификация, контрольный пример работы модели.
Аналитический раздел
Анализ исходных данных
Сбор и предварительная обработка данных
В рамках исследования будет использоваться классический набор данных MNIST, в котором собраны изображения рукописных цифр от 0 до 9. В нем каждый элемент набора – это изображение размером 28 × 28 пикселей в градациях серого.
Обучающая выборка набора содержит 60 000 изображений (рисунок 1), а тестовая выборка – 10 000 изображений.
Рисунок 1 – Пример изображений из набора MNIST
Для загрузки данных воспользуемся кодом ниже.
from tensorflow.keras.datasets import mnist
# Загрузка данных
(x_train, y_train), (x_test, y_test) = mnist.load_data()
Разбиение данных на обучающую и тестовую выборки
Поскольку набор данных MNIST уже разделен на обучающую и тестовую выборки, необходимости выполнять разделение нет.
Однако, мы можем выделить валидационную выборку из обучающей выборки, которая нужна для подбора гиперпараметров разрабатываемой нейросети (число слоев, скорость обучения, количество нейронов в слое и т.п.) и оценки качества на «невидимых» данных до выполнения финального теста модели.
Код для выделения валидационной выборки:
from sklearn.model_selection import train_test_split
# Дополнительная разбивка: 10% от train → validation
x_train, x_val, y_train, y_val = train_test_split(
x_train, y_train,
test_size=0.1,
random_state=42,
stratify=y_train)
Для того, чтобы во всех выборках распределение цифр (классов) было приблизительно одинаковым (параметр stratify=y_train). Это особенно важно при неравномерных классах.
Также мы фиксируем параметр random_state=42, чтобы при повторении экспериментов получать детерминированные результаты.
На рисунке 2 представлены гистограммы количества примеров каждого класса в выборках.
Рисунок 2 – Количество примеров в выборках
Анализ характеристик исходных данных
Как было указано ранее, элементы выборки представляют собой изображения 28 × 28 пикселей в градациях серого. Метки (т.е. цифры 0-9) заданы целыми числами. Это поможет выяснить код, приведенный ниже.
# Проверяем форму массивов и тип данных
print("x_train shape:", x_train.shape)
print("y_train shape:", y_train.shape)
print("x_test shape:", x_test.shape)
print("y_test shape:", y_test.shape)
print("x_train dtype:", x_train.dtype)
print("y_train dtype:", y_train.dtype)
Проверим наличие пропусков в данных:
import numpy as np
print("Пропуски в x_train:", np.isnan(x_train).sum())
print("Пропуски в y_train:", np.isnan(y_train).sum())
Для того, чтобы картинка стала понятным, структурированным массивом численных признаков, подаваемый на вход нейросети, проанализируем распределение пиксельных значений:
# Срезаем первые 1000 изображений для ускорения вычислений
sample = x_train[:1000].reshape(-1)
print("Мин. значение пикселя:", sample.min()) # 0
print("Макс. значение пикселя:", sample.max()) # 255
print("Среднее до нормализации:", sample.mean()) # ≈33.5
print("Std до нормализации:", sample.std()) # ≈78.6
Перед проведением нормализации (следующий шаг подготовки данных) диапазон пикселей – [0, 255], а среднее значение ≈ 33,5 и стандартное отклонение ≈ 78,6. Это позволяет оценить эффективность проведения нормализации путем простого деления на 255.
Полученное значение среднего говорит нам, что большинство пикселей элементов набора – темные (фон) и только небольшая часть (чернила) – светлые (более яркие). В таком случае, можем нормализовать данные делением на 255 для получения «близко» распределенных данных и ускорения сходимости обучения. Высокое стандартное отклонение (≈ 78,6) показывает, что яркие пиксели сильно отличаются от темных. Для нас это значит, что изображения высоко контрастные и будет несложно определить написанную цифру.
Нормализуем данные, приведя пиксели к диапазону [0, 1]:
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
Выбор алгоритмов и моделей машинного обучения
Обзор основных алгоритмов
При решении задач распознавания изображений применяются различные варианты машинного обучения, но наиболее эффективными считаются нейросети. Они могут быть следующих типов (основных):
Полносвязные нейронные сети (FCNN). Состоят из последовательности слоев, где каждый нейрон текущего слоя соединен со всеми нейронами следующего слоя. Линейное преобразование входных данных происходит в каждом нейроне. Применяется нелинейная функция активации (ReLU, сигмоида).
В таких нейросетях изображение представляется вектором признаков. В нашем случае это будет вектор из 784 элементов (28 × 28 пикселей).
Такие сети просты в реализации и имеют понятную архитектуру.
Недостатками полносвязных сетей считается высокая размерность весов (каждый входной признак требует отдельный вес и это приводит к очень большому числу параметров, и, как следствие, переобучению). Эти сети плохо масштабируются поскольку увеличение размера изображения резко увеличит число параметров сети. Также, свертывание изображения в вектор уничтожает информацию о расположении пикселей относительно друг друга.
Таким образом, полносвязные сети могут применяться для очень простых и маленьких изображений, но при работе с реальными изображениями они сильно уступают специализированным архитектурам.
Сверточные нейронные сети (CNN). Разработаны специально для обработки двумерных данных (изображений). В них используются сверточные и подвыборочные слои, которые эффективно извлекают локальные признаки.
Здесь каждый нейрон связан с небольшой областью предыдущего слоя (рецептивное поле). Это дает возможность учитывать локальные особенности изображения (текстуры, края, углы).
Используются общие веса, называемые сверточными фильтрами. Один фильтр используется для разных частей изображения. Это сильно снижает число параметров по сравнению с FCNN.
Первые слои сети выявляют простые признаки (линии, границы), последующие слои – сложные структуры (части объектов, формы). Т.е. реализуется иерархия признаков.
Происходит снижение размерности признаков в слоях подвыборки, что повышает эффективность вычислений и снижает переобучение сети.
Стандартно в CNN применяются следующие компоненты:
Сверточные слои (Conv2D). Применяют набор фильтров к изображению;
Функции активации. Обычно ReLU;
Слои подвыборки (MaxPooling2D). Уменьшают размер карты признаков;
Полносвязные слои (Dense). Для классификации (на последних этапах);
Sftmax-функция на выходе. Для многоклассовой классификации.
Преимуществами CNN является сохранение пространственных зависимостей между пикселями, меньшее количество параметров, высокая точность в задачах компьютерного зрения (CV), способность к обобщению на новых данных.
Фактически CNN – стандарт для задач распознавания изображений благодаря своей высокой эффективности, способности к извлечению сложных признаков и низкому риску переобучения.
На рисунке 3 продемонстрирована структура FCNN и CNN.
Рисунок 3 – Структура FCNN и CNN
Обоснование выбора модели
В таблице 1 приведено сравнение описанных нейросетей.
Таблица 1
Сравнение FCNN и CNN
Исходя из таблицы 1 выбрана сверточная нейронная сеть (CNN), т.к. она лучше обрабатывает изображения благодаря выделению пространственных признаков.
Проектирование и реализация программного решения
Описание архитектуры программного решения
Реализуем архитектуру (рисунок 4) с помощью фреймворка TensorFlow/Keras. Она будет включать следующие элементы:
Входной слой. Форма входа (28, 28, 1) - 28×28 пикселей, 1 канал (градации серого). Изображения предварительно нормализованы и приведены к нужной размерности;
Первый сверточный слой – 32 фильтра размерностью (3, 3), ReLU. Извлекает базовые признаки изображения за счет применения 32 различных сверточных фильтров;
Первый слой подвыборки с размерностью окна (2, 2). Снижает размерность карт признаков выбором максимального значения из каждой области 2 × 2 для уменьшения вычислительной нагрузки и обеспечения чувствительности модели к небольшим сдвигам изображения;
Второй сверточный слой;
Уплощение признаков (Flatten). Преобразует выходной тензор из двумерной формы в одномерный вектор для подачи в полносвязный слой;
Полносвязный слой (Dense) – 128 нейронов, ReLU. Объединяет признаки и выявляет сложные зависимости между ними для последующей классификации;
Выходной слой (Dense) – 10 нейронов (по 1 на каждуй цифру от 0 до 9), Softmax. Генерирует вероятностное распределение по классам.
Рисунок 4 – Архитектура сети
Реализация выбранных алгоритмов и моделей в выбранном программном окружении
Реализация архитектуры в коде:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
Здесь импортируются модули:
Sequential – реализует базовый способ создания нейронной сети, где слои идут последовательно друг за другом;
Conv2D – сверточный слой для выделения локальных признаков изображения;
MaxPooling2D – слой подвыборки, уменьшает размерность признаков;
Flatten – слой преобразования многомерного массива признаков в одномерный вектор;
Dense – полносвязный слой.
После построения модели компилируем ее. При этом используется оптимизатор Adam (эффективный алгоритм оптимизации), функция потерь для задачи многоклассовой классификации (для оценки расхождения между предсказанными вероятностями и истинными метками), для отслеживания указана метрика accuracy, по которой будет оцениваться качество обучения модели (показывает долю правильных предсказаний).
Эксперименты и результаты
Описание проведенных экспериментов
Эксперименты проводились на инфраструктуре Google Colab, который предоставляет вычислительные мощности для разработки моделей машинного обучения.
Общие параметры модели при запуске экспериментов:
batch_size = 128
epochs = 15
optimizer = 'adam' # learning_rate по умолчанию 0.001
loss = 'sparse_categorical_crossentropy'
metrics = ['accuracy']
validation_split = 0.1 # 10% обучающей выборки на валидацию
shuffle = True
random_state= 42
Колбэки:
EarlyStopping(
monitor='val_loss', # метрика, за которой следим
patience=3, # число эпох без улучшения, после которого останавливаемся
restore_best_weights=True # после остановки вернуть веса из лучшей эпохи
)
ModelCheckpoint(
'best_model.h5', # файл для сохранения
monitor='val_accuracy', # метрика, за которой следим
save_best_only=True # сохранять только если метрика улучшилась
)
Колбэки в Keras – специальные объекты, вызываемые во время обучения. Позволяют подключаться к процессу обучения на разных этапах и помогают управлять обучением и сохранять лучшие версии модели.
Они позволяют экономить время и ресурсы – обучение не будет длиться все 50 эпох, например, а остановится, когда обучение станет бесполезным. Это, также, позволяет избежать переобучения.
Благодаря параметрам restore_best_weights=True и save_best_only=True мы получаем веса модели, при которых было достигнуто оптимальное качество модели.
Проведены следующие эксперименты:
Получение эталонного качества на базовой CNN без регуляции и аугументации. Код:
history_base = model.fit(
x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_split=validation_split,
shuffle=shuffle,
callbacks=[early_stopping, model_checkpoint]
)
Добавление Dropout. Проверяем, улучшает ли регуляризация (Dropout) обобщающую способность.
Модифицируем модель:
from tensorflow.keras.layers import Dropout
model_dropout = Sequential([
Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)),
MaxPooling2D((2,2)),
Flatten(),
Dense(128, activation='relu'),
Dropout(0.5), # новый слой
Dense(10, activation='softmax')
])
model_dropout.compile(optimizer=optimizer, loss=loss, metrics=metrics)
Код:
history_do = model_dropout.fit(
x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_split=validation_split,
callbacks=[early_stopping, model_checkpoint]
)
Аугументация данных. Оцениваем влияние на качество обучения случайных трансформаций изображений.
Изменения в модели:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
rotation_range=10, # поворот ±10°
width_shift_range=0.1, # сдвиг по ширине ±10%
height_shift_range=0.1,# сдвиг по высоте ±10%
zoom_range=0.1 # масштабирование ±10%
)
datagen.fit(x_train)
Код:
history_aug = model.fit(
datagen.flow(x_train, y_train, batch_size=batch_size),
epochs=epochs,
validation_data=(x_test, y_test),
callbacks=[early_stopping, model_checkpoint]
)
Анализ и интерпретация полученных результатов
В таблице 2 представлен разбор поведения модели в каждом эксперименте на этапах обучения и на тестовой выборке.
Таблица 2
Разбор поведения модели в экспериментах
Из таблицы 2 видно, что у базовой CNN очень быстрая сходимость, которая уже ко 2-й эпохе достигла ≈97,8% train-accuracy, а к 4-й и вовсе ≈99,0%. Эта модель быстро переобучается, т.к. train-accuracy поднялась до ≈99,7% а val-accuracy достигает ≈98,4% и val-loss растет к 5-7 эпохам, из-за чего срабатывает остановка по колбэку EarlyStopping.
Хотя эта модель имеет вполне хорошее качество, однако небольшой разрыв между train и val говорит о переобучении.
Модель CNN + Dropout показывает более плавную сходимость – первые две эпохи train-accuracy ≈80-96%, а потом наблюдается устойчивый рост. Регуляризация помогла снизить переобучение – разрыв вместо ≈1,3% у базовой CNN стал ≈0,3%. Виден прирост качества модели благодаря регуляризации – ≈98,69%.
И, наконец, модель CNN + Augmentation показала медленную сходимость – лишь к 6-7 эпохе train-accuracy достигает ≈97-98%. Здесь, благодаря аугментации val_loss val_accuracy более ровные. Модель показала лучший результат test_accuracy ≈ 98.85%.
Оценка и сравнение методов
Гистограмма сравнения точности моделей приведена на рисунке 5.
Рисунок 5 – Сравнение точности моделей
Из анализа результатов экспериментов и представленной гистограммы видим, что применение аугментации дает наилучшую точность модели нейросети для распознавания изображений.