基于tensorflow.js的人工智能开发入门

什么是Tensorflow.js?

TensorFlow.js是一个开源的基于硬件加速的JavaScript库,用于训练和部署机器学习模型。谷歌推出的第一个基于TensorFlow的前端深度学习框架TensorFlow.js 是一个开源的用于开发机器学习项目的 WebGL-accelerated JavaScript 库。TensorFlow.js可以提供高性能的、易于使用的机器学习构建模块,允许在浏览器上训练模型,或以推断模式运行预训练的模型。TensorFlow.js 不仅可以提供低级的机器学习构建模块,还可以提供高级的类似 Keras 的 API 来构建神经网络。

个人站点:https://whl1207.github.io/,记录一些学习笔记,主要写点基于anylogic的仿真和web开发方面的知识点。有兴趣的同学可以探讨一下!

Tensorflow.js的优点

  • 1、不用安装驱动器和软件,通过链接即可分享程序。
  • 2、网页应用交互性更强。
  • 3、有访问GPS,Camera,Microphone,Accelerator,Gyroscope等传感器的标准api(主要是指手机端)。
  • 4、安全性,因为数据都是保存在客户端的。

TensorFlow.js的应用方式

  • 1、在浏览器中开发ML。使用简单直观的API从头构建模型,然后使用低级别的JavaScript线性代数库或高层API进行训练。

  • 2、运行现有模型。使用TensorFlow.js模型转换器在浏览器中运行预训练好的TensorFlow模型。

  • 3、重新训练现有模型。使用连接到浏览器的传感器数据或其他客户端数据重新训练ML模型。

一、基本概念

张量(Tensor)和变量(Variable)是TensorFlow.js中数据的主要表现形式,两者不同之处在于张量是不可变的,而变量是可变的。

(一)张量(Tensors)

张量=容器,张量是现代机器学习的基础。它的核心是一个数据容器,多数情况下,它包含数字,有时候它也包含字符串,但这种情况比较少。因此把它想象成一个数字的水桶。

张量是由一组数值形成一个或多个维度的数组。 张量实例具有定义数组形状的形状属性。

Tensorflow.js中数据的主要表现形式就是tensor(张量):由 一组数值形成一维或多维数组。一个Tensor实例有一个shape属性来定义这一组数值如何组成张量,而最主要的Tensor实例的构造函数就是 tf.tensor 函数,如下所示:

// 2x3 Tensor
const shape = [2, 3]; // 2 行, 3 列
const a = tf.tensor([1.0, 2.0, 3.0, 10.0, 20.0, 30.0], shape);
a.print(); // 打印张量值
// 输出:    [[1 , 2 , 3 ],
//          [10, 20, 30]]

// shape也可以用下面的方式实现:
const b = tf.tensor([[1.0, 2.0, 3.0], [10.0, 20.0, 30.0]]);
b.print();
// 输出:    [[1 , 2 , 3 ],
//          [10, 20, 30]]

但是,为了构造低秩张量,我们推荐使用下面的函数来增强代码的可读性:tf.scalar(零维), tf.tensor1d(一维), tf.tensor2d(二维), tf.tensor3d(三维)、tf.tensor4d(四维)以及 tf.ones(值全是1)或者tf.zeros(值全是0) ,如下所示:

const a = tf.scalar(3.14);
a.print(); // 输出零维张量

const b = tf.tensor2d([[2, 3, 4], [5, 6, 7]]);
b.print(); // 输出二维张量

const c = tf.zeros([2, 3]);
c.print(); // 输出2行3列的值全是0的张量

const d = tf.ones([3, 5]);
d.print(); // 输出3行5列的值全是1的张量

在TensorFlow.js中,张量是不变的; 一旦创建你就不能改变它们的值。 但是,您可以对它们执行操作来生成新的张量。

(二)变量(Variable)

变量(Variables)是通过张量进行初始化得到的。不像Tensor的值不可变,变量的值是可变的。你可以使用变量的assign方法分配一个新的tensor到这个变量上,这是变量就会改变:

const initialValues = tf.zeros([5]);
const biases = tf.variable(initialValues); // 初始化biases
biases.print(); // 输出: [0, 0, 0, 0, 0]

const updatedValues = tf.tensor1d([0, 1, 0, 1, 0]);
biases.assign(updatedValues); // 更新 biases的值
biases.print(); // 输出: [0, 1, 0, 1, 0]

如上所示,首先使用tf.zeros得到一个张量,然后利用这个张量初始化得到一个变量,接着我们就可以打印这个变量,并且通Object.prototype.toString.call(biases)方法可以判断变量也是一个对象,接着,我们再生成一个张量,然后变量调用assign方法传入这个张量,就可以得到一个新的变量了。

由此我们可以得出一个结论:变量由张量生成,且张量不可变而变量可变。

二、Tensorflow.js 模型

在Tensorflow.js中,从概念上来说,一个模型就是一个给定一些输入将会产生特定的输出的函数。简单来说,一个模型就是一个函数,只是它完成了特定的任务。

在TensorFlow.js中有两种方式来创建模型,一种是通过操作(ops)来直接完成模型本身所做的工作,另外一种就是通过高级API tf.model来创建一个模型,显然第二种是更容易的。

我们先看第一种创建模型的方法:

function predict(input) {
  // y = a * x ^ 2 + b * x + c
  // More on tf.tidy in the next section
  return tf.tidy(() => {
    const x = tf.scalar(input);

    const ax2 = a.mul(x.square());
    const bx = b.mul(x);
    const y = ax2.add(bx).add(c);

    return y;
  });
}

const a = tf.scalar(2);
const b = tf.scalar(4);
const c = tf.scalar(8);

const result = predict(2);
result.print();

如上所示,我们定义的predict函数就是一个模型,对于给定的输入,我们就可以得到预测的输出。注意:所有的数字都需要经过tf.scalar()张量处理。

而第二种创建模型的方法就是用 TensorFlow.js 中的 tf.model 方法(这里的model并不是真正可以调用的方法,而是一个总称,比如实际上可以调用的是tf.sequential模型),这在深度学习中是非常流行的概念。 下面的代码就创建了 tf.sequential 模型:

const model = tf.sequential();
model.add(
  tf.layers.simpleRNN({
    units: 20,
    recurrentInitializer: 'GlorotNormal',
    inputShape: [80, 4]
  })
);

const optimizer = tf.train.sgd(LEARNING_RATE);
model.compile({optimizer, loss: 'categoricalCrossentropy'});
model.fit({x: data, y: labels)});

三、Tensorflow.js 内存管理

因为TensorFlow.js使用了GPU来加速数学运算,因此当tensorflow处理张量和变量时就有必要来管理GPU内存。在TensorFlow.js中,我们可以通过dispose 和 tf.tidy这两种方法来管理内存。

(一)dispose

您可以在张量或变量上调用dispose来清除它并释放其GPU内存:

const x = tf.tensor2d([[0.0, 2.0], [4.0, 6.0]]);
const x_squared = x.square();

x.dispose();
x_squared.dispose();

(二)tf.tidy

进行大量的张量操作时使用dispose可能会很麻烦。 TensorFlow.js提供了另一个函数tf.tidy,它对JavaScript中的常规范围起到类似的作用,不同的是它针对GPU支持的张量。

tf.tidy执行一个函数并清除所有创建的中间张量,释放它们的GPU内存。 它不清除内部函数的返回值。

const average = tf.tidy(() => {
  const y = tf.tensor1d([1.0, 2.0, 3.0, 4.0]);
  const z = tf.ones([4]);

  return y.sub(z).square().mean();
});

average.print();

使用tf.tidy将有助于防止应用程序中的内存泄漏。它也可以用来更谨慎地控制内存何时回收。

两个重要的注意事项:

  • 1、传递给tf.tidy的函数应该是同步的,并且不会返回Promise。我们建议在tf.tidy内不要有更新UI或在发出远程请求的代码。

  • 2、tf.tidy不会清理变量。变量通常持续到机器学习模型的整个生命周期,因此TensorFlow.js不会清理它们,即使它们是在tidy中创建的。不过,您可以手动调用dispose处理它们。

四、实例

(一)使用tensorflow进行线性回归

下面是一个使用tensorflow.js进行线性回归的案例,完整代码如下:

<!DOCTYPE html>
<html style="height: 100%">
   <head>
       <meta charset="utf-8">
    </head>
   <body style="height: 100%; margin: 0">
       <div id="container" style="height: 100%"></div><!-- 绘图区域 -->
       <script type="text/javascript" src="https://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script> <!-- 引入echart进行绘图 -->
       <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.2/dist/tf.min.js"></script><!-- 引入tersorflow.js -->
       <script type="text/javascript">

            function generateData(numPoints, coeff, sigma = 0.04) {//产生伪随机数
                return tf.tidy(() => {
                    const [k, b] = [tf.scalar(coeff.k),tf.scalar(coeff.b)];

                    const xs = tf.randomUniform([numPoints], -1, 1);//x坐标
                    const ys = k.mul(xs).add(b)//y坐标
                    .add(tf.randomNormal([numPoints], 0, sigma));//叠加噪声

                    return {xs, ys: ys};
                })
            }

            // Step 1. 要回归的变量
            const k = tf.variable(tf.scalar(Math.random()));
            const b = tf.variable(tf.scalar(Math.random()));
            // Step 2. 选取优化器、迭代次数等参数
            const numIterations = 75;
            const learningRate = 0.5;
            const optimizer = tf.train.sgd(learningRate);
            // Step 3. 预测函数,定义为线性函数y = k * x + b
            function predict(x) {// y = k * x + b
            return tf.tidy(() => {return k.mul(x).add(b);});
            }
            // Step 4. 计算方差,方差越小说明预测值越精确
            function loss(prediction, labels) {
            const error = prediction.sub(labels).square().mean();
            return error;
            }
            // Step 5. 训练函数
            async function train(xs, ys, numIterations) {
            for (let iter = 0; iter < numIterations; iter++) {
                //优化并使方差最小
                optimizer.minimize(() => {
                const pred = predict(xs);//根据输入数据预测输出值
                return loss(pred, ys);//计算预测值与训练数据间的方差
                });

                await tf.nextFrame();//
            }
            }
            //机器学习
            async function learnCoefficients() {//
            const trueCoefficients = {k: 0.6, b: 0.8};//真实值
            const trainingData = generateData(100, trueCoefficients);//用于模型训练的数据
            
            await train(trainingData.xs, trainingData.ys, numIterations);// 模型训练

            var xvals = await trainingData.xs.data();//训练数据的x坐标值
            var yvals = await trainingData.ys.data();//训练数据的y坐标值
            var sDatas = Array.from(yvals).map((y,i) => {return [xvals[i],yvals[i]]});//整理训练数据以便绘图
            
            console.log("k&b:",k.dataSync()[0],b.dataSync()[0]);//经过训练后的系数
            showResult(sDatas,k.dataSync()[0],b.dataSync()[0]);
            }

            //使用echart绘制结果
            function showResult(scatterData,k,b){
            var dom = document.getElementById("container");
            var myChart = echarts.init(dom);
            function realFun(x){return 0.6*x+0.8;}//理想曲线
            function factFun(x){return k*x+b;}//回归后的曲线
            var realData = [[-1,realFun(-1)],[1,realFun(1)]];
            var factData = [[-1,factFun(-1)],[1,factFun(1)]];

            var option = {
                title: {text: '线性回归',left: 'left'},
                tooltip: {trigger: 'axis',axisPointer: {type: 'cross'}},
                xAxis: {type: 'value',splitLine: {lineStyle: {type: 'dashed'}},},
                yAxis: {type: 'value',splitLine: {lineStyle: {type: 'dashed'}}},
                series: [{
                    name: '离散点',type: 'scatter',
                    label: {
                        emphasis: {
                            show: true,
                            position: 'left',
                            textStyle: {
                                color: 'blue',
                                fontSize: 16
                            }
                        }
                    },
                    data: scatterData
                }, 
                {name: '理想曲线',type: 'line',showSymbol: false,data: realData,},
                {name: '回归曲线',type: 'line',showSymbol: false,data: factData,},],
                legend: {data:['离散点','理想曲线','回归曲线']},//图例文字
            };

            myChart.setOption(option, true);
            }
        learnCoefficients();
        </script>
    </body>
</html>

个人主页:https://whl1207.github.io/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343