Автор оригинала: 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.
Особая благодаря Ари Зильнику.