[AI 入门] 手写数字识别-获取数据集

2022年后,AI 以极其迅猛的速度发展,无论是从科技还是商业角度,大模型(LLM) 都具备无可估量的价值。但是,随着模型越发庞大,其推理所消耗的资源也越来越大,传统的由服务器资源推理成本是越来越高,但是,如今用户的设备也是越来越好了,为减轻服务器压力和开销,边缘计算 的价值越来越高,场景越来越重要。笔者作为一个页面仔小白曾思考:除了 wasm 这套技术可以加持端侧 ai 能力,那我浏览器亲儿子:JavaScript 难道就不能做做同样是脚本语言 Python 的事情?

答案是肯定的,在神经网络领域,JS 社区中已经有了 TensorFlow.js , transformer.js 的框架支持,最近笔者发现了另一个库 : JS-PyTorch

由于笔者之前用 PyTorch 做过一些模型研究,看它的语法非常亲切😶‍🌫️,比 TensorFlow 好多了。

看这个库未来还会支持 GPU?,那就决定用它试试水,看看它能不能跑通 CNN 的 hello world - 识别手写数字

数据集准备

  1. 下载地址 yann.lecun.com/exdb/mnist/ 其中,train-images 为训练集图片,train-labels 为训练集标签,t10k-images 为测试集图片,t10k-labels 为测试集标签,训练集和测试集没有交集。
  2. 解压后的数据为二进制数据,本文主要介绍对下载后的数据进行处理并验证数据

数据处理

解压后的二进制数据需要处理,本文在实战刚过程中就没有处理🫥,浪费了so much time 。 一般来说,在不压缩数据的情况下 图像被存储成二进制后,文件大小的计算公式如下: <math xmlns="http://www.w3.org/1998/Math/MathML"> l e n g t h = w i d t h i m a g e ∗ h e i g h t i m a g e ∗ c h a n n e l i m a g e ∗ n u m length = width_{image} * height_{image} * channel_{image} * num </math>length=widthimage∗heightimage∗channelimage∗num 即大小等于 图像的宽高 * 图像的通道数 * 图像的个数。 拿训练的图像数据为例: 它的大小是 47040016 字节,但是,根据公式大小应为 28* 28* 1* 60000 = 47040000 ,多了 16 个字节(128位)。(一度怀疑自我ing🤺) ,翻阅了几篇用 Python 跑识别数字的文章 ,其中一篇是这么做的: 其实在官网的下方提及了这几个变量的含义。(看文档的重要性) 之后我们便可以把原始的图像数据拿到了

js 复制代码
  const images = [];
  let index = 16;
  
  //* bufferSize: 47040016
  
  while (index < buffer.byteLength) {
    const array = new Float32Array(28 * 28 * 1);
    for (let i = 0; i < 28 * 28 * 1; i++) {
    
      //* 图片的原始值为 (0,255) 一个8位无符号整型 就能放的下
      
      array[i] = buffer.readUInt8(index++);

    }

    images.push(new Picture(array));

  }

label 数据也是这样处理,不过它的头除了魔数外,只有长度了,打印下 训练集 label 的数据:

label 数据很清晰,但对于图像数据来说,目前只是一个 (0, 255) 范围的数字数组,不能直观的感受图像是否是正确的,因此下一步工作是把数字数组,还原成一个图片来看数据是否加载成功。

还原图片

将数组还原成图像,是用 canvas 这个库,把数组中每一个点画在 canvas 上面再进行保存查看。在 RGB模型 中,R=G=B 为一种灰度值,灰度值越大,颜色越偏白,越小则越偏黑。如下图所示:

因此还原过程中,只需把 3 通道的值都设置为图像数组里的值即可,对于 PNG 这种格式,需要再加一个透明通道即可。还原图像代码如下:

js 复制代码
class Picture {

    image = null;

    constructor(image, width = 28, height = 28, depth =1){

        this.width = width;

        this.height = height;

        this.depth = depth;

        this.imgSize = width * height;

        this.image = image;

    }

    savePicture(path){

        const canvas = createCanvas(this.width, this.height);

        const context = canvas.getContext('2d');

        const imageData = context.createImageData(this.width, this.height);

        for(let i = 0; i< this.imgSize; i++){

            const value = this.image[i];

            imageData.data[(4 * i )] = value;     //0 4 8

            imageData.data[(4 * i ) + 1] = value; //1 5 9

            imageData.data[(4 * i ) + 2] = value; //2 6 10

            imageData.data[(4 * i ) + 3] = 255;   //3 7 11

        }

        context.putImageData(imageData, 0, 0);

        const out = fs.createWriteStream(path);

        const stream = canvas.createPNGStream();

        stream.pipe(out);

        out.on('finish', () => {console.log('还原图像成功')})

    }

}

在加载训练图像数据完成后,保存第一张图片: 运行结果: 和标签数据中的第一个数据一致! 加载数据完成,下一步,开始训练👋!

项目环境

版本
node v19.1.0
js-PyTorch 0.3.6
canvas 2.11.2

参考

深度学习实战入门------CNN实现MNIST手写数字识别:


hi, there👋

i am siroi

一个接触前端 2.5 年的新人,欢迎关注我的微信公众号:siroi的前端手册

让我们一起愉快的玩耍吧🤓

相关推荐
秋雨凉人心3 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
哥谭居民00015 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
踢足球的,程序猿5 小时前
Android native+html5的混合开发
javascript
前端没钱6 小时前
探索 ES6 基础:开启 JavaScript 新篇章
前端·javascript·es6
一条不想当淡水鱼的咸鱼7 小时前
taro中实现带有途径点的路径规划
javascript·react.js·taro
土豆炒马铃薯。7 小时前
【Vue】前端使用node.js对数据库直接进行CRUD操作
前端·javascript·vue.js·node.js·html5
温轻舟8 小时前
前端开发 -- 自动回复机器人【附完整源码】
前端·javascript·css·机器人·html·交互·温轻舟
赵大仁8 小时前
深入解析 Vue 3 的核心原理
前端·javascript·vue.js·react.js·ecmascript
张小虎在学习8 小时前
JS 数组创建、访问、常用方法
javascript
张小虎在学习8 小时前
JS 三种添加元素的方式、区别( write、createElement、innerHTML )
javascript