deeplearn.js科研之路(二)

来源:互联网 发布:网站数据监控 编辑:程序博客网 时间:2024/05/12 13:44

【写在最前】

周末过去了,好好休息后要好好科研~

代码上周五就写好了,本想着周末结合源码看一下。。。后来发现还是不要把事情安排到周末比较好

其实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());

【结果】

代价在迭代过程中逐步减小,并且输出值符合预期。


【其他】

根据如上代码可以尝试不同结构的全连接网络,训练次数和训练结果相差特别大!包括不同的优化器,对性能的影响也很大!另外就是...我女朋友用python写的,优化器选择相同的情况下只迭代了2000多次就得到很好的结果了,我这个为啥这么多次抓狂
而且(二)里面这个效果不如我(一)里面手写的那个难过

【预期】

接下来要继续手推公式看论文了,另外这周要把去年接触的kaggle的比赛重做一次提交看看结果~