Распознавание рукописных цифр (MNIST Dataset) без использования нейронных сетей
Введение
Нейронная сеть очень быстро и точно (ошибка менее 0,1 % на данных MNIST Dataset) распознаёт рукописные цифры. Однако этому предшествует длительная настройка сети, т. е. подбор большого числа внутренних параметров, называемый обучением (pre-traing, deep learning). Кроме того, нейронная сеть узкоспециализирована. Обученная для распознавания цифр, она абсолютно бесполезна при работе с чем-либо иным — буквами, дорожными знаками и т. п.
Обычный интеллект лишен этих недостатков. Приведем простой пример. Выберем из игральной колоды четыре карты разной масти. Покажем их ребенку и попросим его разложить оставшиеся в колоде карты по мастям. Малыш легко справится, даже если он видит карты впервые и раньше ничем подобным не занимался.
Врожденная способность человека правильно оценивать степень сходства объектов разной формы позволяет ему узнавать их без какого-либо предварительного действия.
Смоделируем эту ситуацию и представим систему распознавания со встроенной функцией вычисления сходства (similarity) двух изображений:
similarity = get_similarity(path_image1, path_image2)
Если в такую систему ввести представителей (образцы) разных классов, подобно четырем картам разной масти, она сразу приобретет способность распознавать. Действительно, вычисляя сходство каждого входного объекта с этими представителями, она отнесет его к тому классу, представитель которого обнаруживает с этим объектом максимальное значение сходства. Действие такой системы неотличимо от поведения ребенка с картами.
Новый метод
В основе нового метода распознавания рукописных цифр лежит предложенная нами функция вычисления сходства. Ниже показан скриншот PyCharm с текстом программы сравнения двух mnist-изображений и выводом результата сравнения в окно Терминала.
строка 4:
здесь предполагается, что пакет mnist-separator из PyPI
уже установлен (pip install mnist-separator)
строка 6:
путь к базе данных mnist-изображений MNIST_DATASET_100,
о которой пойдет речь ниже (у вас этот путь будет иным)
строки 8 и 9:
полное имя файлов сравниваемых изображений 0_6.png и
1_7.png
строки 11 и 13:
вычисление степени сходства двух изображений и печать
результата
Смотрите нашу предыдущую статью “Распознавание геометрических фигур”, где обсуждаются принципы, лежащие в основе построения функции сходства.
MNIST_DATASET_100
Знаменитая база данных MNIST Dataset от Google состоит из двух неравных частей. Первая часть data_train, содержащая 60 000 рукописных изображений каждой цифры, используется для обучения нейронных сетей, а вторая, data_test (10 000 изображений на цифру), содержит данные для распознавания. Для наших целей эта база данных избыточна и мы будем использовать лишь ее малую часть MNIST_DATASET_100, в которой каждая из цифр представлена первыми 100 экземплярами исходной базы данных. Скачать MNIST_DATASET_100 можно здесь.
Программа mnist-separator
Давайте усложним задачу нашему ребенку и заменим игральные карты карточками с изображениями рукописных цифр. Вначале покажем ему всего два таких образца: карточки с нулем на одной их них и с единицей — на другой. Тем самым мы познакомим ребенка с двумя новыми понятиями — «ноль» и «единица». Затем вручим малышу колоду из 20 карточек с другими рукописными нулями и единицами (10 + 10) и попросим ее отсортировать: те карточки, которые кажутся ему похожими на образец нуля, предложим поместить слева, а другие, более похожие на образец единицы, — справа.
Ниже приведен скриншот программы, которая реализует данный сценарий. В окне Терминала показан результат ее выполнения и видно, что время вычислений составило 44 секунды.
строка 9:
инициализация рабочих директорий
строки 11 и 12:
"знакомство с образцами": изображения (образцы) нуля и единицы
копируются из MNIST_DATASET_100/data_train в две внутренние
директории программы $mnist/train_0 и $mnist/train_1
MNIST_DATASET_100/data_train/0/0_0.png -> $mnist/train_0/0_0.png
MNIST_DATASET_100/data_train/1/1_0.png -> $mnist/train_1/1_0.png
строки 14 и 15:
"загрузка данных для распознавания": копируются 10 экземпляров
нуля и 10 экземпляров единицы из MNIST_DATASET_100/data_test
в общую директорию $mnist/test:
MNIST_DATASET_100/data_test/0/0_0.png -> $mnist/test/0_0.png
MNIST_DATASET_100/data_test/0/0_1.png -> $mnist/test/0_1.png
MNIST_DATASET_100/data_test/0/0_2.png -> $mnist/test/0_2.png
MNIST_DATASET_100/data_test/0/0_3.png -> $mnist/test/0_3.png
MNIST_DATASET_100/data_test/0/0_4.png -> $mnist/test/0_4.png
MNIST_DATASET_100/data_test/0/0_5.png -> $mnist/test/0_5.png
MNIST_DATASET_100/data_test/0/0_6.png -> $mnist/test/0_6.png
MNIST_DATASET_100/data_test/0/0_7.png -> $mnist/test/0_7.png
MNIST_DATASET_100/data_test/0/0_8.png -> $mnist/test/0_8.png
MNIST_DATASET_100/data_test/0/0_9.png -> $mnist/test/0_9.png
MNIST_DATASET_100/data_test/1/1_0.png -> $mnist/test/1_0.png
MNIST_DATASET_100/data_test/1/1_1.png -> $mnist/test/1_1.png
MNIST_DATASET_100/data_test/1/1_2.png -> $mnist/test/1_2.png
MNIST_DATASET_100/data_test/1/1_3.png -> $mnist/test/1_3.png
MNIST_DATASET_100/data_test/1/1_4.png -> $mnist/test/1_4.png
MNIST_DATASET_100/data_test/1/1_5.png -> $mnist/test/1_5.png
MNIST_DATASET_100/data_test/1/1_6.png -> $mnist/test/1_6.png
MNIST_DATASET_100/data_test/1/1_7.png -> $mnist/test/1_7.png
MNIST_DATASET_100/data_test/1/1_8.png -> $mnist/test/1_8.png
MNIST_DATASET_100/data_test/1/1_9.png -> $mnist/test/1_9.png
строка 17:
"функция go()" - распознавание или сепарация рукописных цифр
Функция go() последовательно вычисляет степень сходства каждого из 20 изображений директории $mnist/test с образцами нуля$mnist/train_0/0_0.png и единицы $mnist/train_1/1_0.png. Если значение сходства текущего изображения с образцом нуля окажется больше, то имя текущего изображения сохраняется в файле list_result_0.txt. В противном случае — в list_result_1.txt.
Как видим, в полученные результаты закралась ошибка: изображение единицы 1_7.png было причислено к классу нулей. Убедитесь, что эта ошибка исчезнет, если мы несколько увеличим число образцов:
ms.load_samples(dir_mnist, 1, 1) -> ms.load_samples(dir_mnist, 1, 3)
Еще один пример
Мы начинали с первых цифр, с 0 и 1. Теперь поработаем с тремя последними — 7, 8, 9.
строки 11, 12 и 13:
"знакомство с образцами": загрузка 50 образцов семерки,
50 образцов восьмерки и 50 образцов девятки в три внутренние
директории программы $mnist/train_7, $mnist/train_8 и
$mnist/train_9
строки 15, 16 и 17:
"загрузка данных для распознавания":
копируются по 15 экземпляров семерки, восьмерки и девятки в
общую директорию $mnist/test
строка 19:
"функция go()" - распознавание или сепарация рукописных цифр
На скриншот результата мы вручную добавили красные метки, указывающие на ошибки. Время вычислений составило 2 часа 25 минут 13 секунд (iMac 2019, 3 GHz Intel Core i5, 8GB DDR4).
Мы надеемся, что теперь вы сможете самостоятельно планировать и проводить собственные вычислительные эксперименты с mnist-separator.
Заключение
Нейронные сети перестали быть единственным способом распознавания рукописных цифр. Появилась альтернатива, и эта альтернатива имеет ряд важных преимуществ:
- Отсутствие фазы подготовки к работе (не нужно обучение).
- Простота. По требованию к вычислительным ресурсам и объему исходного кода (TensorFlow, Keras, PyTorch) нейронные сети многократно уступают mnist-separator. Скачать исходный код.
- Универсальность. В статье “Замечательные AI-таблицы” мы показали, что помимо mnist-изображений новая технология способна распознавать также изображения другой природы (см. ниже).