Русский
Русский
English
Статистика
Реклама

Mnist

Перевод Как распознать рукописный текст с помощью ИИ на микроконтроллерах

18.02.2021 18:15:13 | Автор: admin


Распознавание рукописных цифр с помощью TensorFlow и MNIST стало довольно распространённым введением в искусственный интеллект (ИИ) и ML. MNIST это база данных, которая содержит 70 000 примеров рукописных цифр. Она широко используется как источник изображений для обучения систем обработки изображений и программного обеспечения для машинного обучения.

Хотя учебные пособия по ML с использованием TensorFlow и MNIST стали привычными, до недавнего времени они обычно демонстрировались в полнофункциональных средах обработки с архитектурой x86 и графическими процессорами класса рабочих станций. Однако сегодня можно создать полнофункциональное приложение для распознавания рукописного ввода MNIST даже на 8-разрядном микроконтроллере. Чтобы продемонстрировать это, мы собираемся создать полнофункциональное приложение для распознавания рукописного ввода MNIST, используя TensorFlow Lite для получения результатов ИИ на маломощном микроконтроллере STMicroelectronics на базе процессора ARM Cortex M7.



В этой статье предполагается, что вы знакомы с C/C++ и машинным обучением, но не волнуйтесь, если это не так. Вы всё равно можете изучить предлагаемый пример и попробовать развернуть проект на собственном устройстве дома! Подобная реализация демонстрирует возможность создания надёжных приложений на основе машинного обучения даже на автономных устройствах с батарейным питанием в самых разных сценариях для Интернета вещей и портативных устройств.



Для создания этого проекта потребуется несколько компонентов:


Код этого проекта можно найти на GitHub.

Краткий обзор


Прежде чем начать, давайте рассмотрим действия, необходимые для запуска проекта ИИ с глубоким обучением на микроконтроллере с помощью TensorFlow:

  1. Обучить прогнозирующую модель на основе набора данных (рукописные цифры MNIST).
  2. Преобразовать модель в формат TensorFlow Lite.
  3. Создать встроенное приложение.
  4. Создать образцы данных.
  5. Развернуть и протестировать приложение.

Чтобы ускорить и упростить этот процесс, я создал записную книжку Jupyter в Google Colab, чтобы сделать первые два шага за вас из вашего браузера, не устанавливая и не настраивая Python на вашем компьютере. Она также может служить справочным материалом для других проектов, поскольку содержит весь код, необходимый для обучения и оценки модели MNIST с помощью TensorFlow, а также для преобразования модели в целях автономного использования в TensorFlow Lite для микроконтроллеров и создания версии кода массива Си модели для простой компиляции в любую программу на C++.

Чтобы перейти к встроенному приложению на шаге 3, сначала в меню записной книжки нажмите Runtime Run All (Время выполнения > Выполнить всё), чтобы создать файл model.h. Загрузите его из списка файлов на левой стороне. Также можно загрузить предварительно созданную модель из репозитория GitHub, чтобы включить её в проект.

Чтобы выполнить эти действия локально на своём компьютере, убедитесь, что используете платформу TensorFlow версии 2.0 или более поздней и дистрибутив Anaconda для установки и использования Python. Если вы используете упомянутую ранее записную книжку Jupyter, о которой говорилось выше, вам не придётся беспокоиться об установке TensorFlow 2.0, так как эта версия входит в состав этой записной книжки.

Обучение модели TensorFlow с использованием MNIST


Keras это высокоуровневая библиотека Python для нейронных сетей, часто используемая для создания прототипов ИИ-решений. Она интегрирована с TensorFlow, а также содержит встроенный набор данных MNIST из 60 000 изображений и 10 000 тестовых образцов, доступных прямо в TensorFlow.

Чтобы прогнозировать рукописные цифры, этот набор данных использовался для обучения относительно простой модели, в которой изображение 2828 принимается в качестве входной формы и выводятся до 10 категорий результатов с помощью функции активации Softmax с одним скрытым слоем между входным и выходным слоями. Этого было достаточно для достижения точности 96,6 %, но при желании можно добавить больше скрытых слоёв или тензоров.

За более глубоким обсуждением работы с набором данных MNIST в TensorFlow я рекомендую обратиться к некоторым (из многих) замечательным учебным пособиям по TensorFlow в Интернете, таким как Not another MNIST tutorial with TensorFlow, автор О'Рейли (O'Reilly). Вы также можете обратиться к примеру синусоидальной модели TensorFlow в этой записной книжке, чтобы ознакомиться с обучением и оценкой моделей TensorFlow и преобразованием модели в формат TensorFlow Lite для микроконтроллеров.



Преобразование модели в формат TensorFlow Lite


Созданная на первом шаге модель полезна и очень точна, но размер файла и использование памяти делают её недоступной для переноса или использования на встроенном устройстве. Именно здесь на помощь приходит TensorFlow Lite, так как данная среда выполнения оптимизирована для мобильных, встроенных устройств и устройств Интернета вещей и обеспечивает низкую задержку при очень небольших требованиях к размеру (всего несколько килобайт!). Это позволяет найти компромисс между точностью, скоростью и размером и выбрать модель в соответствии со своими потребностями.

В этом случае платформа TensorFlow Lite нужна, чтобы приложение занимало как можно меньше места во флеш-памяти и ОЗУ, оставаясь при этом быстрым, чтобы можно было немного понизить точность, не жертвуя слишком многим.

Чтобы ещё больше уменьшить размер, преобразователь TensorFlow Lite поддерживает дискретизацию модели, чтобы перейти в вычислениях от 32-разрядных значений с плавающей запятой к 8-разрядным целым числам, так как часто высокая точность значений с плавающей запятой не требуется. В результате также значительно уменьшается размер модели и повышается производительность.

Мне не удалось получить дискретизированную модель для правильного и согласованного использования функции Softmax. На моём устройстве STM32F7 Discovery возникает ошибка не удалось вызвать. Преобразователь TensorFlow Lite постоянно развивается, и некоторые конструкции моделей ещё не поддерживаются. Например, этот инструмент преобразует некоторые веса в значения типа int8 вместо uint8, а тип int8 не поддерживается. По крайней мере пока.

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

У встроенных микроконтроллеров в полевых условиях часто ограничено пространство для хранения данных. На стенде для внешнего хранения данных всегда можно использовать карту памяти большего размера. Однако, чтобы смоделировать среду без доступа к внешнему хранилищу для файлов .tflite, можно экспортировать модель как код, чтобы она содержалась в самом приложении.

Я добавил скрипт Python в конец своей записной книжки, чтобы обработать эту часть и превратить её в файл model.h. При желании в Linux с помощью команды оболочки xxd -i созданный tflite-файл также можно преобразовать в массив Си. Загрузите этот файл из меню слева и приготовьтесь добавить его в проект встроенного приложения на следующем шаге.

import binasciidef convert_to_c_array(bytes) -> str:  hexstr = binascii.hexlify(bytes).decode("UTF-8")  hexstr = hexstr.upper()  array = ["0x" + hexstr[i:i + 2] for i in range(0, len(hexstr), 2)]  array = [array[i:i+10] for i in range(0, len(array), 10)]  return ",\n  ".join([", ".join(e) for e in array])tflite_binary = open("model.tflite", 'rb').read()ascii_bytes = convert_to_c_array(tflite_binary)c_file = "const unsigned char tf_model[] = {\n  " + ascii_bytes +   "\n};\nunsigned int tf_model_len = " + str(len(tflite_binary)) + ";"# print(c_file)open("model.h", "w").write(c_file)

Создание встроенного приложения


Теперь мы готовы взять нашу обученную модель MNIST и реализовать её на реальном маломощном микроконтроллере. Ваши конкретные действия могут зависеть от используемого набора инструментов, но с моими интегрированной средой разработки PlatformIO и устройством STM32F746G Discovery мною были предприняты следующие действия.

Сначала создан новый проект приложения с настройками для соответствующего устройства на базе ARM Cortex-M и подготовлены основные функции setup и loop. Я выбрал структуру Stm32Cube, чтобы выводить результаты на экран. Если вы используете Stm32Cube, вы можете загрузить файлы stm32_app.h и stm32_app.c из репозитория и создать файл main.cpp с функциями setup и loop, например, как здесь:

#include "stm32_app.h"void setup() {}void loop() {}



Добавьте или загрузите библиотеку TensorFlow Lite Micro. Я предварительно настроил библиотеку для интегрированной среды разработки PlateformIO, чтобы вы могли загрузить папку tfmicro отсюда в папку lib проекта и добавить её в качестве зависимости библиотеки в файл platformio.ini:

[env:disco_f746ng]platform = ststm32board = disco_f746ngframework = stm32cubelib_deps = tfmicro

В верхней части своего кода укажите заголовки библиотек TensorFlowLite, например, как здесь:

#include "stm32_app.h"#include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h"#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"#include "tensorflow/lite/experimental/micro/micro_interpreter.h"#include "tensorflow/lite/schema/schema_generated.h"#include "tensorflow/lite/version.h"void setup() {}void loop() {}

Включите преобразованный ранее файл model.h в этот проект в папку Include и добавьте его под заголовками TensorFlow. Затем сохраните результат и выполните сборку, чтобы убедиться, что всё в порядке, ошибок нет.

#include "model.h"



Определите для TensorFlow следующие глобальные переменные, которые будут использоваться в вашем коде:

// Globalsconst tflite::Model* model = nullptr;tflite::MicroInterpreter* interpreter = nullptr;tflite::ErrorReporter* reporter = nullptr;TfLiteTensor* input = nullptr;TfLiteTensor* output = nullptr;constexpr int kTensorArenaSize = 5000; // Just pick a big enough numberuint8_t tensor_arena[ kTensorArenaSize ] = { 0 };float* input_buffer = nullptr;

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

void setup() {  // Load Model  static tflite::MicroErrorReporter error_reporter;  reporter = &error_reporter;  reporter->Report( "Let's use AI to recognize some numbers!" );  model = tflite::GetModel( tf_model );  if( model->version() != TFLITE_SCHEMA_VERSION ) {reporter->Report(   "Model is schema version: %d\nSupported schema version is: %d",   model->version(), TFLITE_SCHEMA_VERSION );return;  }   // Set up our TF runner  static tflite::ops::micro::AllOpsResolver resolver;  static tflite::MicroInterpreter static_interpreter(  model, resolver, tensor_arena, kTensorArenaSize, reporter );  interpreter = &static_interpreter;   // Allocate memory from the tensor_arena for the model's tensors.  TfLiteStatus allocate_status = interpreter->AllocateTensors();  if( allocate_status != kTfLiteOk ) {reporter->Report( "AllocateTensors() failed" );return;  }  // Obtain pointers to the model's input and output tensors.  input = interpreter->input(0);  output = interpreter->output(0);  // Save the input buffer to put our MNIST images into  input_buffer = input->data.f;}

Подготовьте TensorFlow к выполнению на устройстве ARM Cortex-M при каждом вызове функции loop с короткой задержкой (одна секунда) между обновлениями, например, как здесь:

void loop() {  // Run our model  TfLiteStatus invoke_status = interpreter->Invoke();  if( invoke_status != kTfLiteOk ) {reporter->Report( "Invoke failed" );return;  }   float* result = output->data.f;  char resultText[ 256 ];  sprintf( resultText, "It looks like the number: %d", std::distance( result, std::max_element( result, result + 10 ) ) );  draw_text( resultText, 0xFF0000FF );  // Wait 1-sec til before running again  delay( 1000 );}

Приложение готово к работе. Оно просто ждёт, когда мы скормим ему несколько забавных тестовых изображений MNIST для обработки!

Создание образца данных MNIST для встраивания


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

Чтобы добавить эти изображения в программу независимо от внешнего хранилища, мы можем заранее преобразовать 100 изображений MNIST из формата JPEG в чёрно-белые изображения, сохранённые в виде массивов, так же как и наша модель TensorFlow. Для этого я использовал веб-инструмент с открытым исходным кодом под названием image2cpp, который выполняет большую часть этой работы за нас в одном пакете. Если вы хотите сгенерировать их самостоятельно, проанализируйте пиксели и закодируйте по восемь в каждый байт и запишите их в формате массива Си, как показано ниже.

ПРИМЕЧАНИЕ. Веб-инструмент генерирует код для интегрированной среды разработки Arduino, поэтому в коде найдите и удалите все экземпляры PROGMEM, а затем компилируйте код в среде PlatformIO.

Например, это тестовое изображение рукописного нуля должно быть преобразовано в следующий массив:



// 'mnist_0_1', 28x28pxconst unsigned char mnist_1 [] PROGMEM = {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00,  0x00, 0x3f, 0xe0, 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x7e, 0x30, 0x00, 0x00, 0xfc, 0x38, 0x00,  0x00, 0xf0, 0x1c, 0x00, 0x00, 0xe0, 0x1c, 0x00, 0x00, 0xc0, 0x1e, 0x00, 0x00, 0xc0, 0x1c, 0x00,  0x01, 0xc0, 0x3c, 0x00, 0x01, 0xc0, 0xf8, 0x00, 0x01, 0xc1, 0xf8, 0x00, 0x01, 0xcf, 0xf0, 0x00,  0x00, 0xff, 0xf0, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

Сохраните сгенерированные изображения в новый файл mnist.h в проекте или, чтобы сэкономить время и пропустить этот шаг, можно просто загрузить мою версию изGitHub.

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

const unsigned char* test_images[] = {  mnist_1, mnist_2, mnist_3, mnist_4, mnist_5,   mnist_6, mnist_7, mnist_8, mnist_9, mnist_10,  mnist_11, mnist_12, mnist_13, mnist_14, mnist_15,   mnist_16, mnist_17, mnist_18, mnist_19, mnist_20,  mnist_21, mnist_22, mnist_23, mnist_24, mnist_25,   mnist_26, mnist_27, mnist_28, mnist_29, mnist_30,  mnist_31, mnist_32, mnist_33, mnist_34, mnist_35,   mnist_36, mnist_37, mnist_38, mnist_39, mnist_40,  mnist_41, mnist_42, mnist_43, mnist_44, mnist_45,   mnist_46, mnist_47, mnist_48, mnist_49, mnist_50,  mnist_51, mnist_52, mnist_53, mnist_54, mnist_55,   mnist_56, mnist_57, mnist_58, mnist_59, mnist_60,  mnist_61, mnist_62, mnist_63, mnist_64, mnist_65,   mnist_66, mnist_67, mnist_68, mnist_69, mnist_70,  mnist_71, mnist_72, mnist_73, mnist_74, mnist_75,   mnist_76, mnist_77, mnist_78, mnist_79, mnist_80,  mnist_81, mnist_82, mnist_83, mnist_84, mnist_85,   mnist_86, mnist_87, mnist_88, mnist_89, mnist_90,  mnist_91, mnist_92, mnist_93, mnist_94, mnist_95,   mnist_96, mnist_97, mnist_98, mnist_99, mnist_100,};

Не забудьте включить в верхнюю часть кода заголовок нового изображения:

#include "mnist.h"

Тестирование изображений MNIST


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

void bitmap_to_float_array( float* dest, const unsigned char* bitmap ) { // Populate input_vec with the monochrome 1bpp bitmap  int pixel = 0;  for( int y = 0; y < 28; y++ ) {for( int x = 0; x < 28; x++ ) {  int B = x / 8; // the Byte # of the row  int b = x % 8; // the Bit # of the Byte  dest[ pixel ] = ( bitmap[ y * 4 + B ] >> ( 7 - b ) ) & 0x1 ? 1.0f : 0.0f;  pixel++;}  }}void draw_input_buffer() {  clear_display();  for( int y = 0; y < 28; y++ ) {for( int x = 0; x < 28; x++ ) {  draw_pixel( x + 16, y + 3, input_buffer[ y * 28 + x ] > 0 ? 0xFFFFFFFF : 0xFF000000 );}  }}

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

void loop() {  // Pick a random test image for input  const int num_test_images = ( sizeof( test_images ) / sizeof( *test_images ) );  bitmap_to_float_array( input_buffer,  test_images[ rand() % num_test_images ] );  draw_input_buffer();   // Run our model  ...}

Если всё в порядке, ваш проект будет скомпонован и развёрнут, и вы увидите, как ваш микроконтроллер распознаёт все рукописные цифры и выдаёт отличные результаты! Верите?




Что дальше?


Теперь, когда вы узнали о возможностях маломощных микроконтроллеров ARM Cortex-M, позволяющих использовать возможности глубокого обучения с помощью TensorFlow, вы готовы сделать гораздо больше! От обнаружения животных и предметов различных типов до обучения устройства понимать речь или отвечать на вопросы вы со своим устройством можете открыть новые горизонты, которые ранее считались возможными только при использовании мощных компьютеров и устройств.

На GitHub доступны несколько потрясающих примеров TensorFlow Lite для микроконтроллеров, разработанных командой TensorFlow. Ознакомьтесь с этимирекомендациями, чтобы убедиться, что вы максимально эффективно используете свой проект ИИ, работающий на устройстве Arm Cortex-M. А если хотите прокачать себя в Machine Learning, Data Science или поднять уровень уже имеющихся знаний приходите учиться, будет сложно, но интересно.



image
Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодом HABR:

Подробнее..

Recovery mode интуиция Искусственного Интеллекта миф или реальность?

01.04.2021 10:10:31 | Автор: admin
Одно из самых известных, наиболее интересное и совсем не изученное свойство человеческого разума с давних пор привлекавшее исследователей это интуиция.

Со времен древности философы и математики пытались хоть как как то понять и определить смысл этого могучего нашего свойства.
Еще Платон разделял и выделял нелогическое познание,
Декарт, например, утверждал: Под интуицией я разумею не веру в шаткое свидетельство чувств и не обманчивое суждение беспорядочного воображения, но понятие ясного и внимательного ума, настолько простое и отчётливое, что оно не оставляет никакого сомнения в том, что мы мыслим, или, что одно и то же, прочное понятие ясного и внимательного ума, порождаемое лишь естественным светом разума и благодаря своей простоте более достоверное, чем сама дедукция.
Гегель указывал на непосредственное знание,
и даже Фейербах упомянул о чувственности познания!

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


Итак начнем!

для изучения и препарирования возьмем уже привычный, вдоль и поперек изученный, насквозь понятный пример классика ML, handwritten images MNIST.
И возьмем в дело также всем известный, многократно проверенный и изученный пример сети с сайта keras
https://keras.io/examples/vision/mnist_convnet/

или с Github
https://github.com/keras-team/keras-io/blob/master/examples/vision/mnist_convnet.py

"""Title: Simple MNIST convnetAuthor: [fchollet](http://personeltest.ru/aways/twitter.com/fchollet)Date created: 2015/06/19Last modified: 2020/04/21Description: A simple convnet that achieves ~99% test accuracy on MNIST."""import numpy as npfrom tensorflow import kerasfrom tensorflow.keras import layers# Model / data parametersnum_classes = 10input_shape = (28, 28, 1)# the data, split between train and test sets(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()# Scale images to the [0, 1] rangex_train = x_train.astype("float32") / 255x_test = x_test.astype("float32") / 255# Make sure images have shape (28, 28, 1)x_train = np.expand_dims(x_train, -1)x_test = np.expand_dims(x_test, -1)print("x_train shape:", x_train.shape)print(x_train.shape[0], "train samples")print(x_test.shape[0], "test samples")# convert class vectors to binary class matricesy_train = keras.utils.to_categorical(y_train, num_classes)y_test = keras.utils.to_categorical(y_test, num_classes)model = keras.Sequential(    [        keras.Input(shape=input_shape),        layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),        layers.MaxPooling2D(pool_size=(2, 2)),        layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),        layers.MaxPooling2D(pool_size=(2, 2)),        layers.Flatten(),        layers.Dropout(0.5),        layers.Dense(num_classes, activation="softmax"),    ])model.summary()batch_size = 512epochs = 15model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=2)


изменим только всего лишь один параметр batch_size = 512. Благо карта позволяет.
Проверим результат
score = model.evaluate(x_test, y_test, verbose=0)print("Test loss:", score[0])print("Test accuracy:", score[1])Test loss: 0.028873076662421227Test accuracy: 0.9898999929428101


так себе результат, бывает и лучше.

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

Выберем, конечно же интуитивно, некоторое множество из тестовых картинок и проверим, как на этих картинках работает наша сеть
pred = model.predict(x_test)idx = np.argwhere(    np.array(        [            pred[x, np.argsort(pred, axis=1)[x, -1]]            - pred[x, np.argsort(pred, axis=1)[x, -2]]            for x in range(pred.shape[0])        ]    )    > 0.999).reshape(-1)print(idx.shape)score = model.evaluate(x_test[idx], y_test[idx], verbose=0)print("Test loss:", score[0])print("Test accuracy:", score[1])


Оказывается мы выбрали только те картинки из тестового множества где сеть дает 100% результат!
Test loss: 0.00011477641965029761Test accuracy: 1.0


Обратите внимание, мы учили сеть на TRAIN, а точки интуитивно выбрали из TEST.
Никакого подвоха и сеть не могла подсмотреть.

Но на выбранном множестве картинок результат с accuracy: 1.0

Не буду сейчас обсуждать практическую или теоретическую часть описанного выше явления или, если угодно, свойства сети,
Но факт есть и ничем и никак не обоснованное решение позволяет выбрать точно распознаваемые картинки и сосредоточиться на распознавании оставшихся.
Их кстати меньше половины.
Можно конечно внести изменения и если снизить требования к интуиции ну хоть до 0.99 то мы пропустим только одну или две картинки, но выберем около 9500.
Оставшиеся 500 можно изучить глазамии и сделать тонкую настройку сети и подобающую аугментацию.

Уверен, что правильное применение интуиции AI в таком тонком и сложном вопросе как машинное обучение и искусственный интеллект, несомненно добавит точности вашим сетям.
Подробнее..

Из песочницы Как задача из классического сбора данных, перешла в решение простенькой задачи MNIST. Или как я спарсил сайт ЦИК

02.10.2020 14:18:50 | Автор: admin
В один из будничных дней, под вечер, от моего начальника прилетела интересная задачка. Прилетает ссылка с текстом: хочу отсюда получить все, но есть нюанс. Через 2 часа расскажешь, какие есть мысли по решению задачи. Время 16:00.

Как раз об этом нюансе и будет эта статья.

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

image

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

Проанализировав структуру сайта, было выяснено, что количество ссылок достигает порядка 30 тысяч.

Мне ничего не оставалось делать, как поискать на просторах интернета способы распознавания капчи. Нашел один сервис

+ Капчу распознают 100%, так же, как человек
Среднее время распознавания 9 сек, что очень долго, так как у нас порядка 30 тысяч различных ссылок, по которым нам надо перейти и распознать капчу.

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

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

На часах уже было 17:00, и я начал искать предобученные модели по распознаванию чисел. После проверки их на данной капче точность меня не удовлетворила ну что ж, пора собирать картинки и обучать свою нейросетку.

Для начала нужно собрать обучающую выборку.

Открываю вебдрайвер Хрома и скриню 1000 капчей себе в папку.

from selenium import webdriveri = 1000driver = webdriver.Chrome('/Users/aleksejkudrasov/Downloads/chromedriver')while i>0:    driver.get('http://www.vybory.izbirkom.ru/region/izbirkom?action=show&vrn=4274007421995&region=27&prver=0&pronetvd=0')    time.sleep(0.5)    with open(str(i)+'.png', 'wb') as file:        file.write(driver.find_element_by_xpath('//*[@id="captchaImg"]').screenshot_as_png)    i = i - 1


Так как у нас всего два цвета преобразовал наши капчи в чб:

from operator import itemgetter, attrgetterfrom PIL import Imageimport globlist_img = glob.glob('path/*.png')for img in list_img:    im = Image.open(img)    im = im.convert("P")    im2 = Image.new("P",im.size,255)    im = im.convert("P")    temp = {}# Бежим по картинке и переводим её в чб    for x in range(im.size[1]):        for y in range(im.size[0]):            pix = im.getpixel((y,x))            temp[pix] = pix            if pix != 0:                 im2.putpixel((y,x),0)    im2.save(img)

20761


Теперь нам надо нарезать наши капчи на цифры и преобразовать в единый размер 10*10.
Сначала мы разрезаем капчу на цифры, затем, так как капча смещается по оси OY, нам нужно обрезать все лишнее и повернуть картинку на 90.

def crop(im2):    inletter = False    foundletter = False    start = 0    end = 0    count = 0    letters = []    for y in range(im2.size[0]):         for x in range(im2.size[1]):             pix = im2.getpixel((y,x))            if pix != 255:                inletter = True#ищем первый черный пиксель цифры по оси OX        if foundletter == False and inletter == True:             foundletter = True            start = y#ищем последний черный пиксель цифры по оси OX         if foundletter == True and inletter == False:             foundletter = False            end = y            letters.append((start,end))        inletter = False    for letter in letters:#разрезаем картинку на цифры        im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] )) #поворачиваем на 90        im3 = im3.transpose(Image.ROTATE_90)         letters1 = []#Повторяем операцию выше        for y in range(im3.size[0]): # slice across            for x in range(im3.size[1]): # slice down                pix = im3.getpixel((y,x))                if pix != 255:                    inletter = True            if foundletter == False and inletter == True:                foundletter = True                start = y            if foundletter == True and inletter == False:                foundletter = False                end = y                letters1.append((start,end))            inletter=False        for letter in letters1:#обрезаем белые куски            im4 = im3.crop(( letter[0] , 0, letter[1],im3.size[1] )) #разворачиваем картинку в исходное положение         im4 = im4.transpose(Image.ROTATE_270)         resized_img = im4.resize((10, 10), Image.ANTIALIAS)        resized_img.save(img)

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

Объявляем простенькую модель, которая на вход принимает развернутую матрицу нашей картинки.

Для этого создаем входной слой из 100 нейронов, так как размер картинки 10*10. В качестве выходного слоя 10 нейронов каждый из которых соответствует цифре от 0 до 9.

from tensorflow.keras import Sequentialfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Activation, BatchNormalization, AveragePooling2Dfrom tensorflow.keras.optimizers import SGD, RMSprop, Adamdef mnist_make_model(10, 10):    # Neural network model    model = Sequential()    model.add(Dense(100, activation='relu', input_shape=(10*10)))    model.add(Dense(10, activation='softmax'))    model.compile(loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy'])    return model

Разбиваем наши данные на обучающую и тестовую выборку:

list_folder = ['0','1','2','3','4','5','6','7','8','9']X_Digit = []y_digit = []for folder in list_folder:    for name in glob.glob('path'+folder+'/*.png'):        im2 = Image.open(name)        X_Digit.append(np.array(im2))        y_digit.append(folder)

Разбиваем на обучающую и тестовую выборку:

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X_Digit, y_digit, test_size=0.15, random_state=42)train_data = X_train.reshape(X_train.shape[0], 10*10) #Преобразуем матрицу векторов размерностью 100test_data = X_test.reshape(X_test.shape[0], 10*10) #Преобразуем матрицу векторов размерностью 100#преобразуем номер класса в вектор размерностью 10num_classes = 10train_labels_cat = keras.utils.to_categorical(y_train, num_classes)test_labels_cat = keras.utils.to_categorical(y_test, num_classes)

Обучаем модель.

Эмпирическим путем подбираем параметры количество эпох и размер бэтча:

model = mnist_make_model(10,10)model.fit(train_data, train_labels_cat, epochs=20, batch_size=32, verbose=1, validation_data=(test_data, test_labels_cat))


Сохраняем веса:

model.save_weights("model.h5")

Точность на 11 эпохе получилась отличная: accuracy = 1.0000. Довольный, в 19:00 иду домой отдыхать, завтра еще нужно будет написать парсер для сбора информации с сайта ЦИКа.

Утро следующего дня.

Дело осталось за малым, осталось обойти все страницы на сайте ЦИКа и забрать данные:

Загружаем веса обученной модели:

model = mnist_make_model(10,10)model.load_weights('model.h5')

Пишем функцию для сохранения капчи:

def get_captcha(driver):    with open('snt.png', 'wb') as file:        file.write(driver.find_element_by_xpath('//*[@id="captchaImg"]').screenshot_as_png)    im2 = Image.open('path/snt.png')    return im2

Пишем функцию для предсказания капчи:

def crop(im):    list_cap = []    im = im.convert("P")    im2 = Image.new("P",im.size,255)    im = im.convert("P")    temp = {}    for x in range(im.size[1]):        for y in range(im.size[0]):            pix = im.getpixel((y,x))            temp[pix] = pix            if pix != 0:                im2.putpixel((y,x),0)        inletter = False    foundletter=False    start = 0    end = 0    count = 0    letters = []    for y in range(im2.size[0]):         for x in range(im2.size[1]):             pix = im2.getpixel((y,x))            if pix != 255:                inletter = True        if foundletter == False and inletter == True:            foundletter = True            start = y        if foundletter == True and inletter == False:            foundletter = False            end = y            letters.append((start,end))        inletter=False    for letter in letters:        im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))        im3 = im3.transpose(Image.ROTATE_90)        letters1 = []        for y in range(im3.size[0]):            for x in range(im3.size[1]):                pix = im3.getpixel((y,x))                if pix != 255:                    inletter = True            if foundletter == False and inletter == True:                foundletter = True                start = y            if foundletter == True and inletter == False:                foundletter = False                end = y                letters1.append((start,end))            inletter=False        for letter in letters1:            im4 = im3.crop(( letter[0] , 0, letter[1],im3.size[1] ))        im4 = im4.transpose(Image.ROTATE_270)        resized_img = im4.resize((10, 10), Image.ANTIALIAS)        img_arr = np.array(resized_img)/255        img_arr = img_arr.reshape((1, 10*10))        list_cap.append(model.predict_classes([img_arr])[0])    return ''.join([str(elem) for elem in list_cap])

Добавляем функцию, которая скачивает таблицу:

def get_table(driver):    html = driver.page_source #Получаем код страницы     soup = BeautifulSoup(html, 'html.parser') #Оборачиваем в "красивый суп"    table_result = [] #Объявляем лист в котором будет лежать финальная таблица    tbody = soup.find_all('tbody') #Ищем таблицу на странице    list_tr = tbody[1].find_all('tr') #Собираем все строки таблицы    ful_name = list_tr[0].text #Записываем название выборов    for table in list_tr[3].find_all('table'): #Бежим по всем таблицам        if len(table.find_all('tr'))>5: #Проверяем размер таблицы            for tr in table.find_all('tr'): #Собираем все строки таблицы                snt_tr = []#Объявляем временную строку                for td in tr.find_all('td'):                    snt_tr.append(td.text.strip())#Собираем все стоблцы в строку                table_result.append(snt_tr)#Формируем таблицу    return (ful_name, pd.DataFrame(table_result, columns = ['index', 'name','count']))

Собираем все линки за 13 сентября:

df_table = []driver.get('http://www.vybory.izbirkom.ru')driver.find_element_by_xpath('/html/body/table[2]/tbody/tr[2]/td/center/table/tbody/tr[2]/td/div/table/tbody/tr[3]/td[3]').click()html = driver.page_sourcesoup = BeautifulSoup(html, 'html.parser')list_a = soup.find_all('table')[1].find_all('a')for a in list_a:    name = a.text    link = a['href']    df_table.append([name,link])df_table = pd.DataFrame(df_table, columns = ['name','link'])

К 13:00 я дописываю код с обходом всех страниц:

result_df = []for index, line in df_table.iterrows():#Бежим по строкам таблицы с ссылками    driver.get(line['link'])#Загружаем ссылку    time.sleep(0.6)    try:#Разгадываем капчу если она вылетает        captcha = crop(get_captcha(driver))        driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)        driver.find_element_by_xpath('//*[@id="send"]').click()        time.sleep(0.6)        true_cap(driver)    except NoSuchElementException:#Отлавливаем ошибку если капче не появилась        pass    html = driver.page_source    soup = BeautifulSoup(html, 'html.parser')    if soup.find('select') is None:#Проверяем есть ли выпадающий список на странице        time.sleep(0.6)        html = driver.page_source        soup = BeautifulSoup(html, 'html.parser')                  for i in range(len(soup.find_all('tr'))):#Ищем ссылку на результат выборов            if '\nРЕЗУЛЬТАТ ВБОРОВ\n' == soup.find_all('tr')[i].text:#Ищем фразу, следующая за этой фразой наша ссылка на таблицу с выборами                rez_link = soup.find_all('tr')[i+1].find('a')['href']        driver.get(rez_link)        time.sleep(0.6)        try:            captcha = crop(get_captcha(driver))            driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)            driver.find_element_by_xpath('//*[@id="send"]').click()            time.sleep(0.6)            true_cap(driver)        except NoSuchElementException:            pass        ful_name , table = get_table(driver)#Получаем таблицу        head_name = line['name']        child_name = ''        result_df.append([line['name'],line['link'],rez_link,head_name,child_name,ful_name,table])    else:#Если выпадающий список присутствует, обходим все ссылки        options = soup.find('select').find_all('option')        for option in options:            if option.text == '---':#Пропускаем первую строку из выпадающего списка                continue            else:                link = option['value']                head_name = option.text                driver.get(link)                try:                    time.sleep(0.6)                    captcha = crop(get_captcha(driver))                    driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)                    driver.find_element_by_xpath('//*[@id="send"]').click()                    time.sleep(0.6)                    true_cap(driver)                except NoSuchElementException:                    pass                html2 = driver.page_source                second_soup = BeautifulSoup(html2, 'html.parser')                for i in range(len(second_soup.find_all('tr'))):                    if '\nРЕЗУЛЬТАТ ВБОРОВ\n' == second_soup.find_all('tr')[i].text:                        rez_link = second_soup.find_all('tr')[i+1].find('a')['href']                driver.get(rez_link)                try:                    time.sleep(0.6)                    captcha = crop(get_captcha(driver))                    driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)                    driver.find_element_by_xpath('//*[@id="send"]').click()                    time.sleep(0.6)                    true_cap(driver)                except NoSuchElementException:                    pass                ful_name , table = get_table(driver)                child_name = ''                result_df.append([line['name'],line['link'],rez_link,head_name,child_name,ful_name,table])                if second_soup.find('select') is None:                    continue                else:                    options_2 = second_soup.find('select').find_all('option')                    for option_2 in options_2:                        if option_2.text == '---':                            continue                        else:                            link_2 = option_2['value']                            child_name = option_2.text                            driver.get(link_2)                            try:                                time.sleep(0.6)                                captcha = crop(get_captcha(driver))                                driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)                                driver.find_element_by_xpath('//*[@id="send"]').click()                                time.sleep(0.6)                                true_cap(driver)                            except NoSuchElementException:                                pass                            html3 = driver.page_source                            thrid_soup = BeautifulSoup(html3, 'html.parser')                            for i in range(len(thrid_soup.find_all('tr'))):                                if '\nРЕЗУЛЬТАТ ВБОРОВ\n' == thrid_soup.find_all('tr')[i].text:                                    rez_link = thrid_soup.find_all('tr')[i+1].find('a')['href']                            driver.get(rez_link)                            try:                                time.sleep(0.6)                                captcha = crop(get_captcha(driver))                                driver.find_element_by_xpath('//*[@id="captcha"]').send_keys(captcha)                                driver.find_element_by_xpath('//*[@id="send"]').click()                                time.sleep(0.6)                                true_cap(driver)                            except NoSuchElementException:                                pass                            ful_name , table = get_table(driver)                            result_df.append([line['name'],line['link'],rez_link,head_name,child_name,ful_name,table])

А после приходит твит, который изменил мою жизнь

2020-09-29-12-11-31
Подробнее..

Как прикрутить нейросеть к сайту по быстрому

05.03.2021 16:08:13 | Автор: admin

В данном материале предлагается, приложив небольшие усилия, соединить python 3.7+flask+tensorflow 2.0+keras+небольшие вкрапления js и вывести на web-страницу определенный интерактив. Пользователь, рисуя на холсте, будет отправлять на распознавание цифры, а ранее обученная модель, использующая архитектуру CNN, будет распознавать полученный рисунок и выводить результат. Модель обучена на известном наборе рукописных цифр MNIST, поэтому и распознавать будет только цифры от 0 до 9 включительно. В качестве системы, на которой все это будет крутиться, используется windows 7.


Небольшое вступление

.
Чем печальны книги по машинному обучению, так, пожалуй, тем, что код устаревает почти с выходом самой книги. И хорошо, если автор издания поддерживает свое дитя, сопровождая и обновляя код, но, зачастую все ограничивается тем, что пишут вот вам requirements.txt, ставьте устаревшие пакеты, и все заработает.

Так вышло и в этот раз. Читая Hands-On Python Deep Learning for the Web авторства Anubhav Singh, Sayak Paul, сначала все шло хорошо. Однако, после первой главы праздник закончился. Самое неприятное было то, что заявленные требования в requirements в целом соблюдались.

Масло в огонь подлили и сами разработчики пакетов tensorflow и keras. Один пакет работает только с определенным другим и, либо даунгрейд одного из них либо бубен шамана.
Но и это еще не все. Оказывается, что некоторые пакеты еще и зависимы от архитектуры используемого железа!

Так, за неимением алтернативы железа, устанавливался tensorflow 2.0 на платформу с Celeron j1900 и, как оказалось, там нет инструкции AVX2:


И вариант через pip install tensorflow не работал.
Но не все так грустно при наличии желания и интернета!

Вариант с tensorflow 2.0 удалось реализовать через wheel github.com/fo40225/tensorflow-windows-wheel/tree/master/2.0.0/py37/CPU/sse2 и установку x86: vc_redist.x86.exe, x64: vc_redist.x64.exe (http://personeltest.ru/aways/support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).

Keras был установлен с минимальной версией, с которой он стал совместим с tensorflow Keras==2.3.0.

Поэтому
pip install tensorflow-2.0.0-cp37-cp37m-win_amd64.whl
и
pip install keras==2.3.0


Основное приложение.


Рассмотрим код основной программы.
flask_app.py
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()app = Flask(__name__)@app.route('/')def index():    return render_template("index.html")import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))@app.route('/predict/', methods=['GET', 'POST'])def predict():    global model, graph        imgData = request.get_data()    convertImage(imgData)    #print(imgData)       #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('output.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])if __name__ == "__main__":    # run the app locally on the given port    app.run(host='0.0.0.0', port=80)# optional if we want to run in debugging mode    app.run(debug=True)



Подгрузили пакеты:
from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,io

Как выяснилось imread, imresize устарели еще со времен scipy==1.0. Непонятно, как у автора все работало, учитывая, что книга относительно нова (2019). С современной scipy==1.6.1 книжный вариант кода не работал.

Загружаем с диска, компилируем модель нейросети:
json_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()

Здесь произведена замена на tf.compat.v1.get_default_graph() в виду несовместимости.

Далее часть, относящаяся к серверу на flask. Прорисовка шаблона страницы:
@app.route('/')def index():    return render_template("index.html")


Часть, преобразующая картинку в числовой массив:
import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))


Основная функция предсказания:
def predict():    global model, graph        imgData = request.get_data()    convertImage(imgData)    #print(imgData)       #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('output.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])

Закоментированы строки, которые были заменены на рабочие, а также оставлены выводы отдельных строк для наглядности.

Как все работает.


После запуска командой python flask_app.py запускается локальный flask-сервер, который выводит index.html с вкраплением js.
Пользователь рисует на холсте цифру, нажимает predict. Картинка улетает на сервер, где сохраняется и преобразуется в цифровой массив. Далее в бой вступает CNN, распознающая цифру и возвращающая ответ в виде цифры.
Сеть не всегда дает верный ответ, т.к. обучалась всего на 10 эпохах. Это можно наблюдать, если нарисовать спорную цифру, которая может трактоваться по-разному.
*Можно покрутить слайдер, увеличивая или уменьшая толщину начертания цифры для целей распознавания.

Второй вариант программы через API,curl

.
Поользователь загружает на сервер свое изображение с цифрой для распознавания и нажимает отправить:


Заменим index.js на следующий
index.js:
$("form").submit(function(evt){evt.preventDefault();var formData = new FormData($(this)[0]);$.ajax({url: '/predict/',type: 'POST',data: formData,async: false,cache: false,contentType: false,enctype: 'multipart/form-data',processData: false,success: function (response) {$('#result').empty().append(response);}});return false;});



Шаблон страницы также изменится:
index.html
<!DOCTYPE html><html lang="en"><head><title>MNIST CNN</title></head><body><h1>MNIST Handwritten Digits Prediction</h1><form><input type="file" name="img"></input><input type="submit"></input></form><hr><h3>Prediction: <span id="result"></span></h3><scriptsrc='https://code.jquery.com/jquery-3.6.0.min.js'></script><script src="{{ url_for('static',filename='index.js') }}"></script></body></html>



Немного изменится и основная программа:
flask_app2.py
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()app = Flask(__name__)@app.route('/')def index():    return render_template("index.html")import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))@app.route('/predict/', methods=['POST'])def predict():    global model, graph        imgData = request.get_data()    try:        stringToImage(imgData)    except:        f = request.files['img']        f.save('image.png')           #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('image.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])if __name__ == "__main__":    # run the app locally on the given port    app.run(host='0.0.0.0', port=80)# optional if we want to run in debugging mode    app.run(debug=True)



Запускается все похоже python flask_app2.py

Вариант с curl (для windows)

.
Скачиваем curl

В командной строке windows отправляем команду:
curl -X POST -F img=@1.png http://localhost/predict/

где 1.png картинка с цифрой (или она же с путем к ней).
В ответ прилетит распознанная цифра.

Файлы для скачивания скачать.
Подробнее..

Перевод Принципиально новый метод позволяет тренировать ИИ практически без данных

26.10.2020 16:12:51 | Автор: admin

Мифический носорогоединорог. MS TECH / PIXABAY

Обучение менее чем с одной попытки помогает модели идентифицировать больше объектов, чем количество примеров, на которых она тренировалась.

Как правило, машинное обучение требует множества примеров. Чтобы ИИ-модель научилась распознавать лошадь, вам потребуется показать ей тысячи изображений лошадей. Поэтому технология настолько вычислительно затратна и сильно отличается от человеческого обучения. Ребенку зачастую нужно увидеть всего несколько примеров объекта, или даже один, чтобы научиться распознавать его на всю жизнь.

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


Ммм Не совсем! MS TECH / PIXABAY

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

Как работает обучение менее чем с одной попытки


Исследователи впервые продемонстрировали эту идею, экспериментируя с популярным набором данных для тренировки компьютерного зрения, известным как MNIST. MNIST содержит 60 000 изображений рукописных цифр от 0 до 9, и набор часто используют для проверки новых идей в этой области.

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


Примеры изображений из набора MNIST. WIKIMEDIA


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

Исследователи из университета Уотрелу хотели продолжить процесс дистилляции. Если возможно уменьшить 60 000 изображений до 10, почему бы не сжать их до пяти? Фокус, как они поняли, заключался в том, чтобы смешивать несколько цифр в одном изображении, а затем передавать их в модель ИИ с так называемыми гибридными, или мягкими, метками. (Представьте лошадь и носорога, которым придали черты единорога).

Подумайте о цифре 3, она похожа на цифру 8, но не на цифру 7, говорит Илья Сухолуцкий, аспирант Уотерлу и главный автор статьи. Мягкие метки пытаются запечатлеть эти общие черты. Поэтому вместо того, чтобы сказать машине: Это изображение цифра 3, мы говорим: Это изображение на 60% цифра 3, на 30% цифра 8 и на 10% цифра 0.

Ограничения нового метода обучения


После того как исследователи успешно использовали мягкие метки для достижения адаптации MNIST под метод обучения менее чем с одной попытки, то начали задаваться вопросом, насколько далеко может зайти идея. Существует ли ограничение на количество категорий, которые ИИ-модель может научиться определять на крошечном количестве примеров?

Удивительно, но ограничений, похоже, нет. С помощью тщательно разработанных мягких меток даже два примера теоретически могут кодировать любое количество категорий. Всего лишь двумя точками можно разделить тысячу классов, или 10 000 классов, или миллион классов, говорит Сухолуцкий.


Разбивка яблок (зеленых и красных точек) и апельсинов (оранжевых точек) по весу и цвету. Адаптировано из презентации Джейсона Мейса Машинное обучение 101

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

Чтобы понять, как работает метод kNN, возьмем как пример задачу классификации фруктов. Чтобы обучить kNN-модель понимать разницу между яблоками и апельсинами, сначала нужно выбрать функции, которые вы хотите использовать для представления каждого фрукта. Если выбираете цвет и вес, то для каждого яблока и апельсина вы вводите одну точку данных с цветом фрукта в качестве значения x и весом в качестве значения y. Затем алгоритм kNN отображает все точки данных на двухмерной диаграмме и проводит границу посередине между яблоками и апельсинами. Теперь график аккуратно разделен на два класса, и алгоритм может решить, представляют ли новые точки данных яблоки или апельсины в зависимости от того, на какой стороне линии находится точка.

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


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

Различные диаграммы показывают границы, построенные с помощью алгоритма kNN. На каждой диаграмме все больше и больше граничных линий, закодированных в крохотных наборах данных.

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

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

Это больше всего интересует Тунчжоу Вана, аспиранта Массачусетского технологического института. Он руководил предшествующим исследованием дистилляции данных. Статья опирается на действительно новую и важную цель: обучение мощных моделей на основе небольших наборов данных, говорит он о вкладе Сухолуцкого.

Райан Хурана, исследователь из Монреальского института этики искусственного интеллекта, разделяет это мнение: Что еще важнее, обучение менее чем с одной попытки радикально снизит требования к данным для построения функционирующей модели. Это может сделать ИИ более доступным для компаний и отраслей, которым до сих пор мешали требования к данным в этой области. Это также может улучшить конфиденциальность данных, поскольку для обучения полезных моделей нужно будет получать меньше информации от людей.

Сухолуцкий подчеркивает, что исследование находится на ранней стадии. Тем не менее, оно уже будоражит воображение. Каждый раз, когда автор начинает представлять свою статью коллегам-исследователям, их первая реакция утверждать, что эта идея находится за гранью возможного. Когда они внезапно понимают, что ошибаются, то открывается целый новый мир.


Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru