【写在最前】
周末过去了,好好休息后要好好科研~
代码上周五就写好了,本想着周末结合源码看一下。。。后来发现还是不要把事情安排到周末比较好
其实deeplearn.js的细节是很多的,很多!
感谢官网给的introduction~很清晰非常清晰
github展示页:https://knimet.github.io/my-research-with-deeplearn.js/tensorflow.html
github工程地址:https://github.com/knimet/my-research-with-deeplearn.js
【目标】
tensorflow架构下实现的全连接拟合异或
【实现】
首先还是按官网指导引入文件
<script src="https://unpkg.com/deeplearn"></script>
开启严格模式,搭建基础环境
var dl = deeplearn;
var math = new dl.NDArrayMathCPU();
建立图,可以理解为tensorflow的数据流图
var g= new dl.Graph();
定义placeholder。placeholder直译就可以了,占位量,占位用的。这个量就好比一个未知量x,当定义好另一个y值,y=x+2后,输入x给模型,x可以计算x+2得到y。
在此定义输入为1*2的矩阵,输出为1*1的矩阵。
var data = g.placeholder('data',[1,2]);
var label = g.placeholder('label',[1,1]);
定义variable。variable是模型在训练过程中逐步进行调整的值,也就是一系列的权重和偏置。
模型和本文的前篇(一)中的设计一致,一层隐藏层3个节点,因此需要2个权重矩阵和2份偏置。
var w1 = g.variable('w1',dl.Array2D.randNormal([2,3]));
var b1 = g.variable('b1',dl.Array2D.randNormal([1,3]));
var w2 = g.variable('w2',dl.Array2D.randNormal([3,1]));
var b2 = g.variable('b2',dl.Array2D.randNormal([1,1]));
接下来就是前面提到的,根据placeholder计算y的过程了。模型中需要计算的y有两个阶段,隐藏层的值和输出层的值。
其实就是两个方程~
var m1h1 = g.sigmoid( g.add( g.matmul( data , w1), b1 ) ); //隐藏层值计算
var m1o = g.sigmoid( g.add( g.matmul( m1h1 , w2), b2 ) ); //输出层值计算
定义代价。作为评判训练的标准,我们希望这个代价越小越好。这里选择均方误差作为代价。
var costTensor = g.meanSquaredCost(m1o,label);
定义学习速率和优化器。优化器就是的作用是在训练过程中试图让代价最小。
这里我尝试了SGDOptimizer和AdamOptimizer,后者效果好一些。
const learningRate = 0.001;
const optimizer = new dl.AdamOptimizer(learningRate,0.9,0.999);
定义session。训练在会话中进行。两个参数分别为当前的图,和当前的math环境。我们使用的是CPU环境,在最前面定义的基础环境。
const session = new dl.Session(g, math);
定义batchSize。每次模型都从输入数据源获取batchSize个数据。这里我们一共只有4个输入,定义batchSize为4,每次全部输入模型。
const batchSize = 4;
接下来需要定义的是给模型提供输入的对象。
首先是我们的数据。
const inputs = [
dl.Array2D.new([1,2],[1,1]),
dl.Array2D.new([1,2],[1,0]),
dl.Array2D.new([1,2],[0,1]),
dl.Array2D.new([1,2],[0,0])
];
const labels = [
dl.Array2D.new([1,1],[0]),
dl.Array2D.new([1,1],[1]),
dl.Array2D.new([1,1],[1]),
dl.Array2D.new([1,1],[0])
];
获取提供给模型的训练数据。上面定义的数据是原始数据,要输入给模型,需要重新构造。我们的模型运行在cpu上,因此将以上数据交给InCPUMemoryShuffledInputProviderBuilder,并获取输入给模型的数据,inputProvider和labelProvider。
const shuffledInputProviderBuilder = new dl.InCPUMemoryShuffledInputProviderBuilder([inputs, labels]);
const inputProvider = shuffledInputProviderBuilder.getInputProviders()[0];
const labelProvider = shuffledInputProviderBuilder.getInputProviders()[1];
定义输入实体。这个和tensorflow里用于输入的词典是一样的。这里所说的输入都是指给模型输入训练数据,而不是图的输入。数组内的对象分别为数据和标签,对象的tensor键对应的值为开始定义的placeholder,data键对应的值为刚才通过providerBuilder获取的provider。
const feedEntries = [{
tensor: data,
data: inputProvider
},
{
tensor: label,
data: labelProvider
}
];
定义迭代次数为20000次。
const NUM_BATCHES = 20001;
开始迭代,并且每5000次输出一次代价。
for (let i = 0; i < NUM_BATCHES; i++) {
var cost = session.train(
costTensor, feedEntries, batchSize, optimizer, dl.CostReduction.MEAN);
if(i % 5000 == 0){
console.log('last average cost (' + i + '): ' + cost.get());
}
}
循环结束后,可以通过session.eval或session.evalAll计算定义的测试数据的值。以下以0,0为例,输出应该接近0。
const testInput = math.track(dl.Array2D.new([1,2],[0,0]));
const testFeedEntries = [
{tensor: data, data: testInput}
];
const testOutput = session.eval(m1o, testFeedEntries);
console.log('---inference output---');
console.log('shape: ' + testOutput.shape);
console.log('value: ' + testOutput.getValues());
【结果】
【其他】
![抓狂](http://static.blog.csdn.net/xheditor/xheditor_emot/default/crazy.gif)
![难过](http://static.blog.csdn.net/xheditor/xheditor_emot/default/sad.gif)
【预期】