每当谈起机器学习、深度学习,大家自然就会想到 python c++ GPU tensorflow python 这些词。似乎距离前端人员比较遥远的事 ,今天我们就用 js 来实现一个简单分类的算法。
一切从简单开始,从一个分类问题开始吧。任务就是一个二分类的问题,通过既有的数据训练得到一个模型,然后用模型来对一个数据预测其属于哪一个类别,来了解是如何使用 javascript 进行机器学习。
const R = require('ramda');
randomPoints = R.range(0,5);
复制代码
这里引入了 ramda 库帮助生成随机数,实现起来很简单。
[ 0, 1, 2, 3, 4 ]
复制代码
var rand = (high,low)=> Math.random()*(high - low) + low
randomPoints = R.range(0,100).map(_=> rand(-1,1));
复制代码
生成在 -1 到 1 之间的随机数,这里使用 map 方法来实现返回在某个区间的随机数、
[ -0.9515869798311445,
-0.884506721474116,
0.7849131001955243,
-0.8271371680230728,
0.6015755023017424,
复制代码
上面随机数,然后我们用这些随机数生成随机点,x 和 y 都是 -1 到 1 之间随机数
randomPoints = R.range(0,100).map(_=> (
{
x:rand(-1,1),
y:rand(-1,1)
}));
复制代码
我们将这些随机点通过 svg 以绘制在宽和高同为 400 的区域来显示,让你更直观地观察机器学习的过程。
// const R = require('ramda');
var rand = (high,low)=> Math.random()*(high - low) + low
const X_MAX = 400;
const Y_MAX = 400;
randomPoints = R.range(0,100).map(_=> (
{
x:rand(0,X_MAX),
y:rand(0,Y_MAX)
}));
console.log(randomPoints)
var html=`
<svg width="${X_MAX}" height="${Y_MAX}">
${randomPoints.map(point=>
`<circle
cx="${point.x}"
cy="${point.y}"
r="5"
/>`
)}
<line x1="0" x2="${X_MAX}" y1="0" y2="${Y_MAX}" stroke="purple"/>
</svg>
`;
document.getElementById("app").innerHTML = html;
复制代码
并且将这些随机点显示出来,这些点散落在宽高都是 400 的个方形区域,然后绘制一条线,这条线就是我们随后拟合分割线,这条线将这些样本点分成了两个类别,位于线上部分是一个类别,位于线下面又是一个类别。
其实问题很简单,只要某一个点 y 坐标大于 x 坐标,或者 y 坐标小于 x 坐标属于另一个类别,如果某一个点 x 坐标大于 y 输入 1 否则 -1 也就是用 1 和 -1 表示两个类别
var team = point => point.x > point.y ? 1 : -1;
复制代码
接下来通过颜色来表示两个不同类别,类别为 -1 的点用蓝色来表示,而 1 则用红色点来表示。
var html=`
<svg width="${X_MAX}" height="${Y_MAX}">
${randomPoints.map(point=>
`<circle
cx="${point.x}"
cy="${point.y}"
r="5"
fill="${team(point)===-1?'blue':'red'}"
/>`
)}
<line x1="0" x2="${X_MAX}" y1="0" y2="${Y_MAX}" stroke="purple"/>
</svg>
`;
复制代码
team 函数通过简单逻辑(规则)对随机点进行分类,通过点的 x 和 y 值大小进行判断可以进行判断。
开始写模型
好接下来我们就开始构建 AI 来模拟机器学习过程。初始一个随机参数模型参数比较有意思是两个取值从 -1 到 1 的随机数,这里这样理解我们通过 x 和 y 之间比值来划分 ax + by
var randomWeights = ({
x:rand(-1,1),
y:rand(-1,1)
})
复制代码
这里 guess 函数会输出一个类别,也就是我们函数模型,这里有连个参数,或者叫做权重,权重负责根据 x 和 y 给出一个该点所属类别 1 * x + -1*y > 0 这个关系。
var guess =(weights,point) => {
const sum =
point.x * weights.x +
point.y * weights.y
const team = sum >= 0 ? 1 : -1
return team
}
testGuess = guess(randomWeights,{x:300,y:400})
复制代码
这里 weights 是权重,point 是输入,sum 为输入和权重乘积的和,这个神经元有两个输入 x 和 y(两个特征)然后输出为 1 或 -1
var html=`
<svg width="${X_MAX}" height="${Y_MAX}">
${randomPoints.map(point=>
`<circle
cx="${point.x}"
cy="${point.y}"
r="5"
fill="${guess(randomWeights,point)===-1?'blue':'red'}"
/>`
)}
<line x1="0" x2="${X_MAX}" y1="0" y2="${Y_MAX}" stroke="purple"/>
</svg>
`;
复制代码
以为 weight 是随机数,我们每次刷新会得到不同结果。
创建我们训练函数,训练接收 weights 和 point 输入,以及期望值 actualTeam 通过对比判断结果和期望值对比来反馈到训练,进行优化调整weight 获取正确计算模型
function train(weights,point, actualTeam){
//loss
//otimizer
}
复制代码
function train(weights,point, actualTeam){
//loss
const guessResult = guess(weights,point) //1
const error = actualTeam - guessResult;
return error
//otimizer
}
复制代码
var testTrain = () => {
const point = {x:200,y:400}
return train(randomWeights,point,team(point))
}
console.log(`result ${testTrain()}`)
复制代码
上面代码可以简单测试我们计算结果和实际期望值的差距。
function train(weights,point, actualTeam){
//loss
const guessResult = guess(weights,point) //1
const error = actualTeam - guessResult;
return {
x: weights.x + (point.x * error),
y: weights.y + (point.y * error)
}
//otimizer
}
复制代码
在 trainedWeights 方法中,通过给定点来训练出权重,我们通过返回训练 weights 做为下一次参数传入到 train 不断调整 weight。
var trainedWeights =()=> {
const p1 = {x:721, y:432}
const p2 = {x:211, y:122}
const p3 = {x:328, y:833}
const p4 = {x:900, y:400}
let trainedWeights;
trainedWeights = train(randomWeights,p1,team(p1))
trainedWeights = train(trainedWeights,p2,team(p2))
trainedWeights = train(trainedWeights,p3,team(p3))
trainedWeights = train(trainedWeights,p4,team(p4))
return trainedWeights;
}
复制代码
得到结果并不在我们weight(-1,1)取值范围内,
trainedWeights = 785.6063038318143, -801.4601438564098
复制代码
var html=`
<svg width="${X_MAX}" height="${Y_MAX}">
${randomPoints.map(point=>
`<circle
cx="${point.x}"
cy="${point.y}"
r="5"
fill="${guess(trainedWeights(),point)===-1?'blue':'red'}"
/>`
)}
<line x1="0" x2="${X_MAX}" y1="0" y2="${Y_MAX}" stroke="purple"/>
</svg>
`;
document.getElementById("app").innerHTML = html;
复制代码
将我们训练好的结果 trainedWeights() 代替随机权重返回到图,我们发现图中点分布接近我们期望结果,蓝色和红色点大致都分布在线两侧。