如何在Tensorflow.js中处理MNIST图像数据

作者: 机器之心 2018-06-26 15:40:49
有人开玩笑说有 80% 的数据科学家在清理数据,剩下的 20% 在抱怨清理数据……在数据科学工作中,清理数据所占比例比外人想象的要多得多。一般而言,训练模型通常只占机器学习或数据科学家工作的一小部分(少于 10%)。

——Kaggle CEO Antony Goldbloom

对任何一个机器学习问题而言,数据处理都是很重要的一步。本文将采用 Tensorflow.js(0.11.1)的 MNIST 样例

(https://github.com/tensorflow/tfjs-examples/blob/master/mnist/data.js),逐行运行数据处理的代码。

如何在Tensorflow.js中处理MNIST图像数据

MNIST 样例

  1. 18 import * as tf from '@tensorflow/tfjs'; 
  2. 19 
  3. 20 const IMAGE_SIZE = 784
  4. 21 const NUM_CLASSES = 10
  5. 22 const NUM_DATASET_ELEMENTS = 65000
  6. 23 
  7. 24 const NUM_TRAIN_ELEMENTS = 55000
  8. 25 const NUM_TEST_ELEMENTS = NUM_DATASET_ELEMENTS - NUM_TRAIN_ELEMENTS; 
  9. 26 
  10. 27 const MNIST_IMAGES_SPRITE_PATH = 
  11. 28 'https://storage.googleapis.com/learnjs-data/model-builder/mnist_images.png'; 
  12. 29 const MNIST_LABELS_PATH = 
  13. 30 'https://storage.googleapis.com/learnjs-data/model-builder/mnist_labels_uint8';` 

首先,导入 TensorFlow(确保你在转译代码)并建立一些常量,包括:

  • IMAGE_SIZE:图像尺寸(28*28=784)
  • NUM_CLASSES:标签类别的数量(这个数字可以是 0~9,所以这里有 10 类)
  • NUM_DATASET_ELEMENTS:图像总数量(65000)
  • NUM_TRAIN_ELEMENTS:训练集中图像的数量(55000)
  • NUM_TEST_ELEMENTS:测试集中图像的数量(10000,亦称余数)
  • MNIST_IMAGES_SPRITE_PATH&MNIST_LABELS_PATH:图像和标签的路径

将这些图像级联为一个巨大的图像,如下图所示:

MNISTData

接下来,从第 38 行开始是 MnistData,该类别使用以下函数:

  • load:负责异步加载图像和标注数据;
  • nextTrainBatch:加载下一个训练批;
  • nextTestBatch:加载下一个测试批;
  • nextBatch:返回下一个批的通用函数,该函数的使用取决于是在训练集还是测试集。

本文属于入门文章,因此只采用 load 函数。

load

  1. async load() { 
  2.  // Make a request for the MNIST sprited image. 
  3.  const img = new Image(); 
  4.  const canvas = document.createElement('canvas'); 
  5.  const ctx = canvas.getContext('2d'); 

异步函数(async)是 Javascript 中相对较新的语言功能,因此你需要一个转译器。

Image 对象是表示内存中图像的本地 DOM 函数,在图像加载时提供可访问图像属性的回调。canvas 是 DOM 的另一个元素,该元素可以提供访问像素数组的简单方式,还可以通过上下文对其进行处理。

因为这两个都是 DOM 元素,所以如果用 Node.js(或 Web Worker)则无需访问这些元素。有关其他可替代的方法,请参见下文。

imgRequest

  1. const imgRequest = new Promise((resolve, reject) => { 
  2.  img.crossOrigin = ''
  3.  img.onload = () => { 
  4.  imgimg.width = img.naturalWidth; 
  5.  imgimg.height = img.naturalHeight; 

该代码初始化了一个 new promise,图像加载成功后该 promise 结束。该示例没有明确处理误差状态。

crossOrigin 是一个允许跨域加载图像并可以在与 DOM 交互时解决 CORS(跨源资源共享,cross-origin resource sharing)问题的图像属性。naturalWidth 和 naturalHeight 指加载图像的原始维度,在计算时可以强制校正图像尺寸。

  1. const datasetBytesBuffer = 
  2.  new ArrayBuffer(NUMDATASETELEMENTS * IMAGESIZE * 4); 
  3. 57 
  4. 58 const chunkSize = 5000
  5. 59 canvas.width = img.width; 
  6. 60 canvas.height = chunkSize

该代码初始化了一个新的 buffer,包含每一张图的每一个像素。它将图像总数和每张图像的尺寸和通道数量相乘。

我认为 chunkSize 的用处在于防止 UI 一次将太多数据加载到内存中,但并不能 100% 确定。

  1. 62 for (let i = 0; i < NUMDATASETELEMENTS / chunkSize; i++) { 
  2. 63 const datasetBytesView = new Float32Array( 
  3. 64 datasetBytesBuffer, i * IMAGESIZE * chunkSize * 4, 
  4.  IMAGESIZE * chunkSize); 
  5. 66 ctx.drawImage( 
  6. 67 img, 0, i * chunkSize, img.width, chunkSize, 0, 0, img.width, 
  7. 68 chunkSize); 
  8. 69 
  9. 70 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); 

该代码遍历了每一张 sprite 图像,并为该迭代初始化了一个新的 TypedArray。接下来,上下文图像获取了一个绘制出来的图像块。最终,使用上下文的 getImageData 函数将绘制出来的图像转换为图像数据,返回的是一个表示底层像素数据的对象。

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

我们遍历了这些像素并除以 255(像素的可能***值),以将值限制在 0 到 1 之间。只有红色的通道是必要的,因为它是灰度图像。

  1. 78 this.datasetImages = new Float32Array(datasetBytesBuffer); 
  2. 79 
  3. 80 resolve(); 
  4. 81 }; 
  5. 82 img.src = MNISTIMAGESSPRITEPATH
  6. ); 

这一行创建了 buffer,将其映射到保存了我们像素数据的新 TypedArray 中,然后结束了该 promise。事实上***一行(设置 src 属性)才真正启动函数并加载图像。

起初困扰我的一件事是 TypedArray 的行为与其底层数据 buffer 相关。你可能注意到了,在循环中设置了 datasetBytesView,但它永远都不会返回。

datasetBytesView 引用了缓冲区的 datasetBytesBuffer(初始化使用)。当代码更新像素数据时,它会间接编辑缓冲区的值,然后将其转换为 78 行的 new Float32Array。

获取 DOM 外的图像数据

如果你在 DOM 中,使用 DOM 即可,浏览器(通过 canvas)负责确定图像的格式以及将缓冲区数据转换为像素。但是如果你在 DOM 外工作的话(也就是说用的是 Node.js 或 Web Worker),那就需要一种替代方法。

fetch 提供了一种称为 response.arrayBuffer 的机制,这种机制使你可以访问文件的底层缓冲。我们可以用这种方法在完全避免 DOM 的情况下手动读取字节。这里有一种编写上述代码的替代方法(这种方法需要 fetch,可以用 isomorphic-fetch 等方法在 Node 中进行多边填充):

  1. const imgRequest = fetch(MNISTIMAGESSPRITE_PATH).then(resp => resp.arrayBuffer()).then(buffer => { 
  2.  return new Promise(resolve => { 
  3.  const reader = new PNGReader(buffer); 
  4.  return reader.parse((err, png) => { 
  5.  const pixels = Float32Array.from(png.pixels).map(pixel => { 
  6.  return pixel / 255; 
  7.  }); 
  8.  this.datasetImages = pixels
  9.  resolve(); 
  10.  }); 
  11.  }); 
  12. }); 

这为特定图像返回了一个缓冲数组。在写这篇文章时,我***次试着解析传入的缓冲,但我不建议这样做。如果需要的话,我推荐使用 pngjs 进行 png 的解析。当处理其他格式的图像时,则需要自己写解析函数。

有待深入

理解数据操作是用 JavaScript 进行机器学习的重要部分。通过理解本文所述用例与需求,我们可以根据需求在仅使用几个关键函数的情况下对数据进行格式化。

TensorFlow.js 团队一直在改进 TensorFlow.js 的底层数据 API,这有助于更多地满足需求。这也意味着,随着 TensorFlow.js 的不断改进和发展,API 也会继续前进,跟上发展的步伐。

原文链接:

https://medium.freecodecamp.org/how-to-deal-with-mnist-image-data-in-tensorflow-js-169a2d6941dd

【本文是51CTO专栏机构“机器之心”的原创文章,微信公众号“机器之心( id: almosthuman2014)”】

戳这里,看该作者更多好文

Tensorflow.js MNIST 图像数据 数据科学
上一篇:智能一点发布智能交互推荐系统与售前导购机器人 下一篇:高考填志愿选学校,人工智能专业,一定要选这些学校!
评论
取消
暂无评论,快去成为第一个评论的人吧

更多资讯推荐

大盘点:8月Github上7个值得关注的数据科学项目

本文带你来看看GitHub上创建于2019年8月的7个数据科学项目。笔者所选项目的范围十分广泛,涉及从机器学习到强化学习的诸多领域。

读芯术 ·  2019-10-14 15:41:37
2019年较热门的5大深度学习课程

今天,我们将和大家盘点一下,当下较流行的深度学习资源/课程,可以帮助你们提升深度学习技能。

猿哥 ·  2019-09-26 05:16:24
十个基本的Python数据科学软件包

过去五年人们对数据科学的兴趣显著上升。尽管有许多编程语言适合数据科学和机器学习,但Python是最受欢迎的。

布加迪 ·  2019-09-25 09:20:33
非结构化数据在人工智能中的作用

非结构化数据对人工智能系统的发展至关重要。人工智能系统与用户的沟通越好,就越能自主学习,因此,它的效率也就越高。这一点很重要,因为如果一个人工智能系统只要求用户以结构化的格式进行交互,那么它的组件将受到极大的限制。为了让人工智能成功,它必须理解杂乱的信息。

Harris ·  2019-09-25 08:35:21
人工智能和大数据存在什么隐患?

从数据稀缺到现在有大量的数据,近年来,可用的数据量呈指数级增长,大数据变得无处不在。这是由于数据记录设备数量的巨大增长,以及这些设备之间通过物联网连接。似乎每个人都有收集、分析大数据的力量。

佚名 ·  2019-09-25 07:34:10
民主化与自动化:降低机器学习门槛的六大工具

以下介绍的六种机器学习工具为那些使用数字、电子表格和数据的人们打开了通向机器学习世界的大门,而且不需要他们成为编程和数据科学方面的专家。

CashCat ·  2019-09-06 09:00:33
学好机器学习和大数据必备的6本好书!

机器学习和数据科学是复杂而相互关联的概念。要想让自己在技术紧跟趋势,你必须消耗大量的时间进行知识的更新。日复一日的重复劳动不等于学习,这样仍有可能掉队。这里我们精选出六本好书,助你学好机器学习和大数据科学!

千锋大数据开发学院 ·  2019-03-14 13:06:41
如何将Scikit-learn Python库用于数据科学项目

灵活多样的 Python 库为数据分析和数据挖掘提供了强力的机器学习工具。Scikit-learn Python 库最初于 2007 年发布,通常用于解决各种方面的机器学习和数据科学问题。这个多种功能的库提供了整洁、一致、高效的 API 和全面的在线文档。

Dr.michael J.garbade ·  2018-10-15 09:10:09
Copyright©2005-2019 51CTO.COM 版权所有 未经许可 请勿转载