前言
如今各种前端技术迭代很快,我们使用的前端页面框架有VUE、React等,我们可以使用three.js处理3D的业务,我们也能使用ReactNative来写移动端项目,甚至可以使用node取代java来实现全栈开发,这归功于js庞大的使用人数。
但目前AI领域,前端似乎并为涉足太多。现在AI神经网络发展成熟,各种项目落地产生巨大价值,对于前端开发我们需要接触并了解它,它的门槛并不是很高,可以说前端完全能够理解并且开发出类似的神经网络。那么前端怎么涉足AI呢,我们知道python是处理数据常用的语言,那么js能处理吗,答案是当然,它就是 tensorflow.js。
这个系列希望做为前端开发也能轻松明白人工智能的原理,并且能够使用js实现一些简单的和经典的神经网络,并且提供所有代码。
tensorflow.js
tensorflow是世界最火的人工智能框架由Google研发,目前它对应的前端库叫tensorflow.js其官网的链接地址tensorflow.google.cn/js/models 里面有各种教程和API供查阅
人工智能神经网络的原理
对于原理,我们先说一个故事,比如我去买房子,中介跟你说他有很多房源,但是各个房子价格不一,中介小姐姐会说有的房子价格高是因为是“面积大”、并且“位置好”所以贵,有的小区房型面积太小、位置又差所以价格相对便宜,这个时候我们就明白了。哦,原来房子的价格和面积还有位置是有联系的,我们就可以认为“面积”、“位置好坏”是房价的一个特征,神经网络干的事情就是利用特征去预测我们的“房价“。
如果我们极端点只考虑面积是决定性的特征并且认为它是线性的,那么我们可以假设
房价y = w * x,w是未知的参数我们称为权重,x为面积。
现在我在上海某个小区有300万元,400万元,600万元3套房,对应的面积为70平、90平、130平,那么我该怎么预测这里的房价呢,我可以在我们的公式 y = w * x中,初始化一个w比如“6”,好了这样我们可以把我们旧的数据70平输入进去,那么得到420万,这比300万多了120万!显然我们预测大了,或许我们把w减小点就能更准确了,我们适当的把w减小到5,我们 得到的结果是350万,差了50万但好像更准确了,那么我再减小点这个过程不断重复,我们会发现渐渐的我的w能使得这个误差越来越小,然后我们再用其他数据再来同样进行这样的训练,直到最后我们误差很小能够做出较为准确的预测时我们就说训练完成了。
我们预测的房价 – 实际的房价 = 误差影响我改变w权重,直到误差越来越小,这就是目前神经网络最核心的思想,可以说目前无论工业界还是学术界所有的网络它的原理根本上来说,都可以简单归纳为 误差-> 改变w,直到误差越来越小这么一种模式。
总结上面过程,我们可以提炼出一个很简单的流程:
graph TD
W权重 --> 结果 --> 误差 --> W权重
而AI框架做的事情,就是自动帮我们实现根据误差来更新W权重。
代码实现
好了,不多说,咱们开始用tensorflow.js实现我们上面的场景吧。神经网络项目的工程步骤,万变不离其宗都包含了4个步骤:
1. 获得数据集
首先我们先引入tensorflow.js库
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
// 我们房价的数据目前数据有点少,可以再加两个,分别是800万,150平的房子和200万,60平的房子
const xData = [60, 70, 90, 130, 150]
const yData = [200, 300, 400, 600, 800]
// 把基础数据转化为tensor对象方便输入到框架中
const xs = tf.tensor2d(xData, [xData.length,1])
const ys = tf.tensor2d(yData, [yData.length,1])
2. 定义一个神经网络
// 定义神经网络结构 可以类比这里定义的是上面的房价计算公式
model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [1], units: 1}),
tf.layers.batchNormalization()
]
})
// 定义损失函数,这里采用的是均方误差MSE,可以类比这里是上面的误差计算
model.compile({
loss: 'meanSquaredError',
optimizer: 'sgd'
})
3. 根据数据集训练神经网络
// 开始训练网络,可以类比开始执行上述算误差过程,epochs说我们把这个过程重复执行300次
// 在callbacks中我们打印每轮计算的误差
result = model.fit(
xs,
ys,
{
epochs: 300,
callbacks: {
onEpochEnd: async (epoch, logs) => {
console.log(logs, "???")
trainLogs.push({
mse: Math.sqrt(logs.loss),
});
tfvis.show.history(lossContainer, trainLogs, ["mse"]);
}
}
}
)
}
4. 预测数据
// 我们在页面绑定一个输入事件,然后将值输入到训练好的网络中进行预测
function formpredict(v,r) {
const result = model.predict(tf.tensor2d([+v],[1,1]))
const value = result.dataSync()[0].toFixed(2)
document.getElementById('res').innerHTML = `${value} 万`
}
这样我们整个网络就搭建完成了,是不是简单到爆炸!!!接下来就可以来检验啦
训练可视化
这里我们也使用tensorflow自带的数据展示包tfjs-vis,它可以帮我们直接生成图标,使用也很简单,具体api可以查js.tensorflow.org/api_vis/1.5…
可以看出我们的数据的确符合线性分布,经过300轮训练,我们发现我们的损失下降了!并且最后的误差在10(单位:万)以内,训练好后我们就能在下面输入框输入我们的面积来预测房价啦!
总结
这样我们的网络就算搭建完成,这一期我们讲的主要是神经网络中的线性回归,并且使用了单层感知机来训练,可能会有不太明白的地方,下一期会详细介绍它的进阶版BP网络,希望大家能轻松理解神经网络,并且能搭建属于自己的神经网络。提供以下完整html代码,直接copy就能跑了。
下一章:BP网络传送门
<html>
<head>
<title>Tensor Flow</title>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis"></script>
</head>
<body>
<div id="acc-cont"></div>
<div style="text-align: center;margin-bottom: 20px;">
<span>样本数据分布</span>
</div>
<div id="loss-cont"></div>
<form name='iForm' onSubmit='formpredict(this.val.value,document.getElementById("res")); return false;')>
输入数字: <input name='val'><input type=submit>
<div>
结果: <span id="res"></span>
</div>
</form>
</body>
<script>
const nr_epochs = 300 // 训练轮数
const trainLogs = [];
const lossContainer = document.getElementById("loss-cont");
const accContainer = document.getElementById("acc-cont");
const createBias = () => 3 * (Math.random() < 0.5 ? -1 : 1) // 创造正负3的误差
let model, result
const xData = [60, 70, 90, 130, 150]
const yData = [200, 300, 400, 600, 800]
const seriesData = xData.map((x, index) => ({
x,
y: yData[index]
}))
const data = { values: [seriesData] }
const surface = document.getElementById("acc-cont")
tfvis.render.scatterplot(surface, data);
function initTF() {
// 1. 建立数据集
const xs = tf.tensor2d(xData, [xData.length,1])
const ys = tf.tensor2d(yData, [yData.length,1])
// 2. 定义模型
model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [1], units: 1}),
tf.layers.batchNormalization()
]
})
model.compile({
loss: 'meanSquaredError',
optimizer: 'sgd'
})
// 3.训练模型
result = model.fit(
xs,
ys,
{
epochs: nr_epochs,
callbacks: {
onEpochEnd: async (epoch, logs) => {
console.log(logs, "???")
trainLogs.push({
mse: Math.sqrt(logs.loss),
});
tfvis.show.history(lossContainer, trainLogs, ["mse"]);
}
}
}
)
}
// 4. 预测数据
function formpredict(v,r) {
const result = model.predict(tf.tensor2d([+v],[1,1]))
const value = result.dataSync()[0].toFixed(2)
document.getElementById('res').innerHTML = `${value} 万`
}
initTF()
</script>
</html>