基于TensorFlow.js的JavaScript机器学习


Credits: aijs.rocksjavascript

虽然python或r编程语言有一个相对容易的学习曲线,可是Web开发人员更喜欢在他们温馨的javascript区域内作事情。目前来看,node.js已经开始向每一个领域应用javascript,在这一大趋势下咱们须要理解并使用JS进行机器学习。因为可用的软件包数量众多,python变得流行起来,可是JS社区也紧随其后。这篇文章会帮助初学者学习如何构建一个简单的分类器。html

扩展:
2019年11个javascript机器学习库
很棒的机器学习库,能够在你的下一个应用程序中添加一些人工智能!
Big.bitsrc.io前端

建立java

咱们能够建立一个使用tensorflow.js在浏览器中训练模型的网页。考虑到房屋的“avgareanumberofrows”,模型能够学习去预测房屋的“价格”。node

为此咱们要作的是:python

加载数据并为培训作好准备。git

定义模型的体系结构。程序员

训练模型并在训练时监控其性能。github

经过作出一些预测来评估通过训练的模型。算法

第一步:让咱们从基础开始

建立一个HTML页面并包含JavaScript。将如下代码复制到名为index.html的HTML文件中。

<!DOCTYPE html>
<html>
<head>
  <title>TensorFlow.js Tutorial</title>
  <!-- Import TensorFlow.js -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
  <!-- Import tfjs-vis -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis@1.0.2/dist/tfjs-vis.umd.min.js"></script>
  <!-- Import the main script file -->
  <script src="script.js"></script>
</head>
<body>
</body>
</html>

为代码建立javascript文件

在与上面的HTML文件相同的文件夹中,建立一个名为script.js的文件,并将如下代码放入其中。

console.log('Hello TensorFlow');

测试

既然已经建立了HTML和JavaScript文件,那么就测试一下它们。在浏览器中打开index.html文件并打开devtools控制台。

若是一切正常,那么应该在devtools控制台中建立并可用两个全局变量:

  • tf是对tensorflow.js库的引用
  • tfvis是对tfjs vis库的引用

如今你应该能够看到一条消息,上面写着“Hello TensorFlow”。若是是这样,你就能够继续下一步了。

须要这样的输出

注意:可使用Bit来共享可重用的JS代码

Bit(GitHub上的Bit)是跨项目和应用程序共享可重用JavaScript代码的最快和最可扩展的方式。能够试一试,它是免费的:

组件发现与协做·Bit

Bit是开发人员共享组件和协做,共同构建使人惊叹的软件的地方。发现共享的组件…
Bit.dev

例如:Ramda用做共享组件

Ramda by Ramda·Bit

一个用于JavaScript程序员的实用函数库。-256个javascript组件。例如:等号,乘…
Bit.dev

第2步:加载数据,格式化数据并可视化输入数据

咱们将加载“house”数据集,能够在这里找到。它包含了特定房子的许多不一样特征。对于本教程,咱们只须要有关房间平均面积和每套房子价格的数据。

将如下代码添加到script.js文件中。

async function getData() {
  Const houseDataReq=await
fetch('https://raw.githubusercontent.com/meetnandu05/ml1/master/house.json');  
  const houseData = await houseDataReq.json();  
  const cleaned = houseData.map(house => ({
    price: house.Price,
    rooms: house.AvgAreaNumberofRooms,
  }))
  .filter(house => (house.price != null && house.rooms != null));
  
  return cleaned;
}

这能够删除没有定义价格或房间数量的任何条目。咱们能够将这些数据绘制成散点图,看看它是什么样子的。

将如下代码添加到script.js文件的底部。

async function run() {
  // Load and plot the original input data that we are going to train on.
  const data = await getData();
  const values = data.map(d => ({
    x: d.rooms,
    y: d.price,
  }));
  tfvis.render.scatterplot(
    {name: 'No.of rooms v Price'},
    {values}, 
    {
      xLabel: 'No. of rooms',
      yLabel: 'Price',
      height: 300
    }
  );
  // More code will be added below
}
document.addEventListener('DOMContentLoaded', run);

刷新页面时,你能够在页面左侧看到一个面板,上面有数据的散点图,以下图。

散点图

一般,在处理数据时,最好找到方法来查看数据,并在必要时对其进行清理。可视化数据可让咱们了解模型是否能够学习数据的任何结构。

从上面的图中能够看出,房间数量与价格之间存在正相关关系,即随着房间数量的增长,房屋价格广泛上涨。

第三步:创建待培训的模型

这一步咱们将编写代码来构建机器学习模型。模型主要基于此代码进行架构,因此这是一个比较重要的步骤。机器学习模型接受输入,而后产生输出。对于tensorflow.js,咱们必须构建神经网络

将如下函数添加到script.js文件中以定义模型。

function createModel() {
  // Create a sequential model
  const model = tf.sequential(); 
  
  // Add a single hidden layer
  model.add(tf.layers.dense({inputShape: [1], units: 1, useBias: true}));
  
  // Add an output layer
  model.add(tf.layers.dense({units: 1, useBias: true}));
  return model;
}

这是咱们能够在tensorflow.js中定义的最简单的模型之一,咱们来试下简单分解每一行。

实例化模型

const model = tf.sequential();

这将实例化一个tf.model对象。这个模型是连续的,由于它的输入直接流向它的输出。其余类型的模型能够有分支,甚至能够有多个输入和输出,但在许多状况下,你的模型是连续的。

添加层

model.add(tf.layers.dense({inputShape: [1], units: 1, useBias: true}));

这为咱们的网络添加了一个隐藏层。由于这是网络的第一层,因此咱们须要定义咱们的输入形状。输入形状是[1],由于咱们有1这个数字做为输入(给定房间的房间数)。

单位(连接)设置权重矩阵在层中的大小。在这里将其设置为1,咱们能够说每一个数据输入特性都有一个权重。

model.add(tf.layers.dense({units: 1}));

上面的代码建立了咱们的输出层。咱们将单位设置为1,由于咱们要输出1这个数字。

建立实例

将如下代码添加到前面定义的运行函数中。

// Create the model
const model = createModel();  
tfvis.show.modelSummary({name: 'Model Summary'}, model);

这样能够建立实例模型,而且在网页上有显示层的摘要。

步骤4:为建立准备数据

为了得到TensorFlow.js的性能优点,使培训机器学习模型实用化,咱们须要将数据转换为Tensors。

将如下代码添加到script.js文件中。

function convertToTensor(data) {
  
  return tf.tidy(() => {
    // Step 1. Shuffle the data    
    tf.util.shuffle(data);
    // Step 2. Convert data to Tensor
    const inputs = data.map(d => d.rooms)
    const labels = data.map(d => d.price);
    const inputTensor = tf.tensor2d(inputs, [inputs.length, 1]);
    const labelTensor = tf.tensor2d(labels, [labels.length, 1]);
    //Step 3. Normalize the data to the range 0 - 1 using min-max scaling
    const inputMax = inputTensor.max();
    const inputMin = inputTensor.min();  
    const labelMax = labelTensor.max();
    const labelMin = labelTensor.min();
    const normalizedInputs = inputTensor.sub(inputMin).div(inputMax.sub(inputMin));
    const normalizedLabels = labelTensor.sub(labelMin).div(labelMax.sub(labelMin));
    return {
      inputs: normalizedInputs,
      labels: normalizedLabels,
      // Return the min/max bounds so we can use them later.
      inputMax,
      inputMin,
      labelMax,
      labelMin,
    }
  });  
}

接下来,咱们能够分析一下将会出现什么状况。

随机播放数据

// Step 1. Shuffle the data    
tf.util.shuffle(data);

在训练模型的过程当中,数据集被分红更小的集合,每一个集合称为一个批。而后将这些批次送入模型运行。整理数据很重要,由于模型不该该一次又一次地获得相同的数据。若是模型一次又一次地获得相同的数据,那么模型将没法概括数据,并为运行期间收到的输入提供指定的输出。洗牌将有助于在每一个批次中拥有各类数据。

转换为Tensor

// Step 2. Convert data to Tensor
const inputs = data.map(d => d.rooms)
const labels = data.map(d => d.price);
const inputTensor = tf.tensor2d(inputs, [inputs.length, 1]);
const labelTensor = tf.tensor2d(labels, [labels.length, 1]);

这里咱们制做了两个数组,一个用于输入示例(房间条目数),另外一个用于实际输出值(在机器学习中称为标签,在咱们的例子中是每一个房子的价格)。而后咱们将每一个数组数据转换为一个二维张量。

规范化数据

//Step 3. Normalize the data to the range 0 - 1 using min-max scaling
const inputMax = inputTensor.max();
const inputMin = inputTensor.min();  
const labelMax = labelTensor.max();
const labelMin = labelTensor.min();
const normalizedInputs = inputTensor.sub(inputMin).div(inputMax.sub(inputMin));
const normalizedLabels = labelTensor.sub(labelMin).div(labelMax.sub(labelMin));

接下来,咱们规范化数据。在这里,咱们使用最小-最大比例将数据规范化为数值范围0-1。规范化很重要,由于您将使用tensorflow.js构建的许多机器学习模型的内部设计都是为了使用不太大的数字。规范化数据以包括0到1或-1到1的公共范围。

返回数据和规范化界限

return {
  inputs: normalizedInputs,
  labels: normalizedLabels,
  // Return the min/max bounds so we can use them later.
  inputMax,
  inputMin,
  labelMax,
  labelMin,
}

咱们能够在运行期间保留用于标准化的值,这样咱们就能够取消标准化输出,使其恢复到原始规模,咱们就能够用一样的方式规范化将来的输入数据。

步骤5:运行模型

经过建立模型实例、将数据表示为张量,咱们能够准备开始运行模型。

将如下函数复制到script.js文件中。

async function trainModel(model, inputs, labels) {
  // Prepare the model for training.  
  model.compile({
    optimizer: tf.train.adam(),
    loss: tf.losses.meanSquaredError,
    metrics: ['mse'],
  });
  
  const batchSize = 28;
  const epochs = 50;
  
  return await model.fit(inputs, labels, {
    batchSize,
    epochs,
    shuffle: true,
    callbacks: tfvis.show.fitCallbacks(
      { name: 'Training Performance' },
      ['loss', 'mse'], 
      { height: 200, callbacks: ['onEpochEnd'] }
    )
  });
}

咱们把它分解一下。

准备运行

// Prepare the model for training.  
model.compile({
  optimizer: tf.train.adam(),
  loss: tf.losses.meanSquaredError,
  metrics: ['mse'],
});

咱们必须在训练前“编译”模型。要作到这一点,咱们必须明确一些很是重要的事情:

优化器:这是一个算法,它能够控制模型的更新,就像上面看到的例子同样。TensorFlow.js中有许多可用的优化器。这里咱们选择了Adam优化器,由于它在实践中很是有效,不须要进行额外配置。

损失函数:这是一个函数,它用于检测模型所显示的每一个批(数据子集)方面完成的状况如何。在这里,咱们可使用meansquaredrror将模型所作的预测与真实值进行比较。

度量:这是咱们要在每一个区块结束时用来计算的度量数组。咱们能够用它计算整个训练集的准确度,这样咱们就能够检查本身的运行结果了。这里咱们使用mse,它是meansquaredrror的简写。这是咱们用于损失函数的相同函数,也是回归任务中经常使用的函数。

const batchSize = 28;
const epochs = 50;

接下来,咱们选择一个批量大小和一些时间段:

batchSize指的是模型在每次运行迭代时将看到的数据子集的大小。常见的批量大小一般在32-512之间。对于全部问题来讲,并无一个真正理想的批量大小,描述各类批量大小的精确方式这一知识点本教程没有相关讲解,对这些有兴趣能够经过别的渠道进行了解学习。

epochs指的是模型将查看你提供的整个数据集的次数。在这里,咱们经过数据集进行50次迭代。

启动列车环路

return model.fit(inputs, labels, {
  batchSize,
  epochs,
  callbacks: tfvis.show.fitCallbacks(
    { name: 'Training Performance' },
    ['loss', 'mse'], 
    { 
      height: 200, 
      callbacks: ['onEpochEnd']
    }
  )
});

model.fit是咱们调用的启动循环的函数。它是一个异步函数,所以咱们返回它给咱们的特定值,以便调用者能够肯定运行结束时间。

为了监控运行进度,咱们将一些回调传递给model.fit。咱们使用tfvis.show.fitcallbacks生成函数,这些函数能够为前面指定的“损失”和“毫秒”度量绘制图表。

把它们放在一块儿

如今咱们必须调用从运行函数定义的函数。

将如下代码添加到运行函数的底部。

// Convert the data to a form we can use for training.
const tensorData = convertToTensor(data);
const {inputs, labels} = tensorData;
    
// Train the model  
await trainModel(model, inputs, labels);
console.log('Done Training');

刷新页面时,几秒钟后,你应该会看到图形正在更新。

这些是由咱们以前建立的回调建立的。它们在每一个时代结束时显示丢失(在最近的批处理上)和毫秒(在整个数据集上)。

当训练一个模型时,咱们但愿看到损失减小。在这种状况下,由于咱们的度量是一个偏差度量,因此咱们但愿看到它也降低。

第6步:作出预测

既然咱们的模型通过了训练,咱们想作一些预测。让咱们经过观察它预测的低到高数量房间的统一范围来评估模型。

将如下函数添加到script.js文件中

function testModel(model, inputData, normalizationData) {
  const {inputMax, inputMin, labelMin, labelMax} = normalizationData;  
  
  // Generate predictions for a uniform range of numbers between 0 and 1;
  // We un-normalize the data by doing the inverse of the min-max scaling 
  // that we did earlier.
  const [xs, preds] = tf.tidy(() => {
    
    const xs = tf.linspace(0, 1, 100);      
    const preds = model.predict(xs.reshape([100, 1]));      
    
    const unNormXs = xs
      .mul(inputMax.sub(inputMin))
      .add(inputMin);
    
    const unNormPreds = preds
      .mul(labelMax.sub(labelMin))
      .add(labelMin);
    
    // Un-normalize the data
    return [unNormXs.dataSync(), unNormPreds.dataSync()];
  });
  
 
  const predictedPoints = Array.from(xs).map((val, i) => {
    return {x: val, y: preds[i]}
  });
  
  const originalPoints = inputData.map(d => ({
    x: d.rooms, y: d.price,
  }));
  
  
  tfvis.render.scatterplot(
    {name: 'Model Predictions vs Original Data'}, 
    {values: [originalPoints, predictedPoints], series: ['original', 'predicted']}, 
    {
      xLabel: 'No. of rooms',
      yLabel: 'Price',
      height: 300
    }
  );
}

在上面的函数中须要注意的一些事情。

const xs = tf.linspace(0, 1, 100);      
const preds = model.predict(xs.reshape([100, 1]));

咱们生成100个新的“示例”以提供给模型。model.predict是咱们如何将这些示例输入到模型中的。注意,他们须要有一个相似的形状([num_的例子,num_的特色每一个_的例子])当咱们作培训时。

// Un-normalize the data
const unNormXs = xs
  .mul(inputMax.sub(inputMin))
  .add(inputMin);
    
const unNormPreds = preds
  .mul(labelMax.sub(labelMin))
  .add(labelMin);

为了将数据恢复到原始范围(而不是0–1),咱们使用规范化时计算的值,但只需反转操做。

return [unNormXs.dataSync(), unNormPreds.dataSync()];

.datasync()是一种方法,咱们可使用它来获取存储在张量中的值的typedarray。这容许咱们在常规的javascript中处理这些值。这是一般首选的.data()方法的同步版本。

最后,咱们使用tfjs-vis来绘制原始数据和模型中的预测。

将如下代码添加到运行函数中。

testModel(model, data, tensorData);

刷新页面,如今已经完成啦!

如今你已经学会使用tensorflow.js建立一个简单的机器学习模型了。这里是Github存储库供参考。

结论

我开始接触这些是由于机器学习的概念很是吸引我,还有就是我想看看有没有方法可让它在前端开发中实现,我很高兴发现tensorflow.js库能够帮助我实现个人目标。这只是前端开发中机器学习的开始,TensorFlow.js还能够完成不少工做。谢谢你的阅读!


原文连接 本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索