Рубрики
Без рубрики

Как бороться с данными изображения Mnist в Tensorflow.js

Кевину Скотту, как иметь дело с данными Mnist Image в Tensorflow.jsthere – шутка, что 80 процентов науки о данных убирают данные, а 20 процентов жалуются на очистку данных … Уборка данных является гораздо более высокой долей науки о данных, чем посторонний ожидать.

Автор оригинала: FreeCodeCamp Community Member.

Кевин Скотт

Манипулирующие данные – это решающий этап для любой проблемы обучения машины. Эта статья займет Мнист Пример для Tensorflow.js (0.11.1) И пройдите по коду, который обрабатывает строку загрузки данных.

Мнист Пример

18 import * as tf from '@tensorflow/tfjs';1920 const IMAGE_SIZE = 784;21 const NUM_CLASSES = 10;22 const NUM_DATASET_ELEMENTS = 65000;2324 const NUM_TRAIN_ELEMENTS = 55000;25 const NUM_TEST_ELEMENTS = NUM_DATASET_ELEMENTS - NUM_TRAIN_ELEMENTS;2627 const MNIST_IMAGES_SPRITE_PATH =28     'https://storage.googleapis.com/learnjs-data/model-builder/mnist_images.png';29 const MNIST_LABELS_PATH =30     'https://storage.googleapis.com/learnjs-data/model-builder/mnist_labels_uint8';`

Во-первых, код импорта Tensorflow (Убедитесь, что вы транспилите свой код!) и устанавливает некоторые константы, в том числе:

  • Image_size – размер изображения (ширина и высота)
  • Num_classes – Количество категорий этикеток (число может быть 0-9, так что есть 10 классов)
  • Num_dataset_elements – Количество изображений Всего (65 000)
  • Num_train_elements – Количество учебных изображений (55 000)
  • Num_test_elements – Количество тестовых изображений (10000, ака остальная часть)
  • Mnist_images_sprite_path & Mnist_labels_path – Пути к изображениям и этикеткам

Изображения объединяются в одно огромное изображение, которое выглядит как:

Мнистдата

Далее, начиная с линии 38, это Mnistdata , класс, который подвергает следующие функции:

  • нагрузка – Ответственный за асинхронно загрузку изображений и данных маркировки
  • NextTrainBatch – загрузить следующую тренировочную партию
  • NextTestBatch – Загрузите следующую тестовую партию
  • NextBatch – общая функция для возврата следующей партии, в зависимости от того, находится ли она в наборе тренировок или тестового набора

Для целей начала начать, эта статья пройдет только через нагрузка функция.

нагрузка

44 async load() {45   // Make a request for the MNIST sprited image.46   const img = new Image();47   const canvas = document.createElement('canvas');48   const ctx = canvas.getContext('2d');

async Это относительно новая языковая функция в JavaScript Для чего вам понадобится транспортир.

Изображение Объект – это собственная функция DOM, которая представляет изображение в памяти. Он предоставляет обратные вызовы, когда изображение загружено вместе с доступом к атрибутам изображения. холст Это еще один элемент DOM, который обеспечивает легкий доступ к массивам пикселей и обработке в соответствии с контекст Отказ

Поскольку оба из них являются элементами DOM, если вы работаете в Node.js (или веб-работнику), у вас не будет доступа к этим элементам. Для альтернативного подхода см. Ниже Отказ

имен

49 const imgRequest = new Promise((resolve, reject) => {50   img.crossOrigin = '';51   img.onload = () => {52     img.width = img.naturalWidth;53     img.height = img.naturalHeight;

Код инициализирует новое обещание, которое будет разрешено, как только изображение будет успешно загружено. Этот пример явно не обрабатывает состояние ошибки.

Crossorigin это IMG Атрибут, который обеспечивает загрузку изображений между доменами и приобретает проблемы (совместное использование ресурсов перекрестного происхождения) при взаимодействии с DOM. Naturalwidth и EaturalHeight Обратитесь к исходным размерам загруженного изображения и служите для обеспечения обеспечения того, чтобы размер изображения правильный при выполнении расчетов.

55     const datasetBytesBuffer =56     new ArrayBuffer(NUM_DATASET_ELEMENTS * IMAGE_SIZE * 4);5758     const chunkSize = 5000;59     canvas.width = img.width;60     canvas.height = chunkSize;

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

Я верю что Chunksize Используется для предотвращения загрузки UI слишком много данных в память сразу, хотя я уверен, что я не на 100%.

62     for (let i = 0; i < NUM_DATASET_ELEMENTS / chunkSize; i++) {63       const datasetBytesView = new Float32Array(64         datasetBytesBuffer, i * IMAGE_SIZE * chunkSize * 4,65         IMAGE_SIZE * chunkSize);66       ctx.drawImage(67         img, 0, i * chunkSize, img.width, chunkSize, 0, 0, img.width,68         chunkSize);6970       const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

Этот код контуров через каждое изображение в спрайт и инициализирует новый TypedArray для этой итерации. Затем изображение контекста получает кусок рисунков изображения. Наконец, это нарисованное изображение превращается в данные изображения, используя контекст Getimagedata Функция, которая возвращает объект, представляющий основные данные пикселей.

72       for (let j = 0; j < imageData.data.length / 4; j++) {73         // All channels hold an equal value since the image is grayscale, so74         // just read the red channel.75         datasetBytesView[j] = imageData.data[j * 4] / 255;76       }77     }

Мы проходим через пиксели, и разделите на 255 (максимально возможное значение пикселя), чтобы зажимать значения от 0 до 1. Нужно только красный канал, поскольку это изображение в серогое.

78     this.datasetImages = new Float32Array(datasetBytesBuffer);7980     resolve();81   };82   img.src = MNIST_IMAGES_SPRITE_PATH;83 });

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

Одна вещь, которая смущает меня сначала, было поведение TypedArray в отношении его базового буфера данных. Вы можете заметить, что datasetbytesview устанавливается внутри цикла, но никогда не возвращается.

Под капотом datasetbytesview ссылается на буфер datasetbytesbuffer (с которым он инициализируется). Когда код обновляет данные пикселей, он косвенно редактирует значения самого буфера, что, в свою очередь, перезапускается в новом Float32Array на линии 78.

Получение данных изображения вне дома

Если вы находитесь в доме, вы должны использовать DOM. Браузер (через Canvas ) заботится о выявлении формата изображений и переводят данные буферов в пиксели. Но если вы работаете за пределами DOM (скажем, в Node.js, или веб-работник), вам понадобится альтернативный подход.

извлекать Обеспечивает механизм, Ответ. Ларбайбафер , что дает вам доступ к базовому буфере файла. Мы можем использовать это, чтобы прочитать байты вручную, полностью избегая дома. Вот альтернативный подход к написанию вышеуказанного кода (этот код требует Fetch , которые можно многофильзировать в узле с чем-то вроде Isomorphic-Feetch ):

const imgRequest = fetch(MNIST_IMAGES_SPRITE_PATH).then(resp => resp.arrayBuffer()).then(buffer => {  return new Promise(resolve => {    const reader = new PNGReader(buffer);    return reader.parse((err, png) => {      const pixels = Float32Array.from(png.pixels).map(pixel => {        return pixel / 255;      });      this.datasetImages = pixels;      resolve();    });  });});

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

Просто царапать поверхность

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

Команда Tensorflow.js постоянно меняет базовые данные API в Tensorflow.js. Это может помочь приспособить больше наших потребностей в качестве API. Это также означает, что стоит оставаться в курсе Разработки для API Как Tensorflow.js продолжает расти и быть улучшенным.

Первоначально опубликовано в thekevinscott.com.

Особая благодаря Ари Зильнику.