【写在最前】
转发请注明原地址~
要读博了,得做研究了,不能成天想着JavaScript接私活了,就让我用另一种方式来爱你吧~
deeplearn.js本身并没有什么特别的东西,基本就是Numpy和Tensorflow两套API。
从基础开始接触深度学习,这几天一直在手写推导公式,暂时喘口气,把最早接触deeplearn.js的这个全连接拟合异或的实验记录下来。
以前没有接触过tensorflow,好在官方文档给的很清晰,好在有个玩得转tensorflow的女朋友。这次的实验应该是非常非常简单,甚至简陋,甚至甚至丑陋了,只用了一些矩阵运算。后面会慢慢地按tensorflow的架构来搭网络做实验搞研究,一些不重要的内容也会记录在blog里。
本篇文章不包含任何全连接网络的基础知识,只有deeplearn.js实现全连接网络拟合异或的实现,基础知识网上大把大把。也不包含任何对API的说明,官网上有API文档的。如果有疑问可以发消息给我~
本次实验少一点点东西...就是隐藏层到输出层的bias。
还处于学习阶段,还差得远呢
github展示页(可以进去直接体验直接控制台写代码玩哦):https://knimet.github.io/my-research-with-deeplearn.js/xor.html
github工程地址:https://github.com/knimet/my-research-with-deeplearn.js
【任务】全连接网络拟合XOR。
【思路】
实验设计的全连接网络,输入层3节点,1个包含3节点的隐藏层,输出层1节点。
【实现】
首先按官网指导引入文件
开启严格模式,搭建基础环境。
var dl = deeplearn;
var math = new dl.NDArrayMathCPU();
定义实验数据。输入的第三个数据恒为1,为bias准备的。
const data = [dl.Array2D.new([3,1],[0,0,1]),dl.Array2D.new([3,1],[0,1,1]),dl.Array2D.new([3,1],[1,0,1]),dl.Array2D.new([3,1],[1,1,1])];
const label = dl.Array1D.new([0,1,1,0]);
激活函数使用sigmoid函数,基础环境中math.sigmoid()已定义。
定义学习速率const n = dl.Scalar.new(0.1);
定义权重矩阵,也就是要训练的部分。wih是输入层到隐藏层的3*3权重矩阵,who是隐藏层到输出层的1*3权重矩阵。
var wih = dl.Array2D.rand([3,3],Math.random);
var who = dl.Array2D.rand([1,3],Math.random);
定义初始的输出,用于启动训练。我定义的初始输出为[0,0,0,0],这样可以让循环继续下去。
var output = dl.Array1D.new([0,0,0,0]);
定义loss函数,误差平方和。
function loss(output){ //计算loss,认为误差平方和小于0.01为合格
let result = math.subtract(label,output);
return math.dotProduct(result,result).getValues();
}
定义c记录迭代次数。
var c = 0;
开始训练。
while( loss(output) > 0.01 ){//误差平方和小于0.01为合格
c++;
if(c % 100 ==0){ //每100轮打印一次loss
document.write("第"+c+"轮的loss值为"+loss(output)+"
");
}
data.forEach(function(value,index,array){ //对每个样本进行训练
//前向计算
var outih = math.sigmoid(math.matMul(wih,value));//3*1,3*3的权重矩阵和3*1的输入矩阵相乘,得到隐藏层值
//console.log("隐藏层计算结果为"+outih.getValues());
var out = math.sigmoid(math.matMul(who,outih));//1*1,1*3的权重矩阵和3*1的隐藏层值相乘,得到输出结果
//console.log("输出为"+out.getValues());
//反向传播调整权重
var y = out.getValues(); //得到输出层的值
var t = label.getValues()[index]; //得到label值,也就是目标值
if(y != t){
//console.log("输出结果和label不同,开始BP");
//计算误差
var deltay = y * (1-y) * (t-y); //输出层误差
let deltaihp1 = math.subtract(dl.Scalar.new(1),outih);
let deltaihp2 = math.multiply(outih , deltaihp1);
let deltaihp3 = math.multiply(dl.Scalar.new(deltay),deltaihp2);
let deltaihp4 = math.multiply(deltaihp3 , math.transpose(who)); //隐藏层误差
//console.log("开始调整权重");
var wihp1 = math.matMul(deltaihp4,math.transpose(value)); //这一步写了草稿才确定写法是正确的
var wihp2 = math.multiply(n,wihp1);
wih = math.add(wih,wihp2);//更新输入到隐藏层的权重矩阵
who = math.add(who,math.multiply(dl.Scalar.new(deltay),math.transpose(outih)));//更新隐藏层到输出层的权重矩阵
//前向计算,更新本轮输出
var outih = math.sigmoid(math.matMul(wih,value));//3*1
var out = math.sigmoid(math.matMul(who,outih));//1*1
output.set(out.getValues(),index);
}
})
}
训练结束后,打印训练后的结果到屏幕上。
document.write("计算结果为
");
data.forEach(function(value,index,array){ //对每个样本
var outih = math.sigmoid(math.matMul(wih,value));//3*1
var out = math.sigmoid(math.matMul(who,outih));//1*1
var v = value.getValues();
document.write(v[0]+"异或"+v[1]+"="+out.getValues()+"
");
})
【体验】
权重初值,隐藏层节点数对训练次数影响非常非常大~文章开始留下的github展示页,多刷新几次就能体验到权重初值对训练次数的影响。
【THE END】