deeplearn.js API用例(二)softmax的手动实现(解决Softmax backprop is not yet implemented问题)

【写在最前】

玩了一周的python,没忍住又跑回来了。
这次是从tensorflow官网例子中得到的灵感,解决deeplearn.js中的如下问题:

Softmax backprop is not yet implemented

Softmax backprop is not yet implemented

Softmax backprop is not yet implemented

也就是之前写的《源码解析》中的动力源泉。

至于灵感,是tensorflow官方给的demo中用数学计算手动实现了交叉熵。所以既然deeplearn.js暂时没有实现softmax的bp,我可以用数学计算代替它,并且实际测试后证明是可行的。

github工程地址:https://github.com/knimet/deeplearn.js-softmax-can-backprop

【干货】

【明确目标】

在Graph内手动实现softmax。
当前deeplearn.js文档中对Graph的说明如下:

【目标分析】

1.分解softmax公式

对向量X=[x1,x2,...,xn],softmax(X)的结果为与其维度相同的向量。该向量的每个元素分子部分为e^xi,其中xi为对应位置向量X的值;分母部分相同,为sum(e^xi)。
由此,明确计算softmax(X)的基本思路,伪代码如下:
//基本思路
powX = pow(Math.E , X); 
sum_powX = sum(powX);
sfX = divide(powX,sum_powX)

2.尝试实现

首先,Graph由Tensor构建,由Node记录/表现网络中的行为,无法操作具体的数值。在查阅Graph提供的方法后,不存在能实现e^xi的类似于power的方法,甚至math中的pow方法也只支持整形次方的计算,如e^2,e^0.1是无法计算的。
综上,基本思路失败。

3.改进

基本思路失败的关键是无法计算e的次方,所以解决问题需要找到能够提供e的次方的方法。查阅文档,铛铛铛铛~
sigmoid登场~
对向量X=[x1,x2,...,xn],sigmoid(X)的结果为与其维度相同的向量。向量的每个元素为1/(1+e^(-x))。
综上,改进思路如下sigmoid提供了e^(-x),是很好的中间件。以此为基础可以尝试计算softmax。

4.改进思路尝试实现

改进思路的伪代码如下:
sigX = sigmoid(X)
sig1 = 1/sigX
sig2 = sig1-1
sum_sig = sum(sig2)
sfX = sig2 / sum_sig
这是我第一次写出的伪代码,有坑有坑有坑!
查阅文档可以发现,这次的思路可以实现,Graph分别提供了sigmoid、divide、subtract、reduceSum来满足我们的需求。

5.NDArrayMath下的测试

接下来要测试改进思路的实际测试了,代码如下:
var target = dl.Array1D.new([1,2,3,4,5])

var sf1 = math.softmax(target)
sf1.getValues()


var _tsg = math.sigmoid(_t1)
_tsg.getValues()
var _t1 = math.divide(dl.Array1D.new([1]) , _tsg)
_t1.getValues()
var _t2 = math.subtract(_t1,dl.Array1D.new([1]))
_t2.getValues()
var sf2 = math.divide(_t2,math.sum(_t2))
sf2.getValues()
运行结果如下:

两个结果不一样不一样不一样啊坑啊坑啊坑啊
哎为啥结果是正相反的?

6.填坑

检查改进思路发现,sigmoid在计算的过程中,每个元素为1/(1+e^(-x))。e的次方为-x而不是我们需要的x。因此,正确的填了坑的伪代码如下
mX = X * (-1)
sigX = sigmoid(mX)
sig1 = 1/sigX
sig2 = sig1-1
sum_sig = sum(sig2)
sfX = sig2 / sum_sig
给X的每个元素xi乘上-1,在计算得到sigmoid后就是我们需要的e^x了。

7.填坑后的测试

代码如下:
var target = dl.Array1D.new([1,2,3,4,5])

var sf1 = math.softmax(target)
sf1.getValues()

var _t1 = math.multiply(dl.Scalar.new(-1),target)
_t1.getValues()
var _tsg = math.sigmoid(_t1)
_tsg.getValues()
var _t2 = math.divide(dl.Array1D.new([1]) , _tsg)
_t2.getValues()
var _t3 = math.subtract(_t2,dl.Array1D.new([1]))
_t3.getValues()
var sf2 = math.divide(_t3,math.sum(_t3))
sf2.getValues()
测试结果如下:

完美完美完美!

8.Graph样例测试

代码如下:
var dl = require('deeplearn');
var math = new dl.NDArrayMathCPU();
const session = new dl.Session(g, math);
var target = [dl.Array1D.new([1,2,3,4,5])];

var g = new dl.Graph();

var X = g.placeholder('X',[5]);
var sf1 = g.softmax(X);

var _t1 = g.multiply(X,g.constant(-1));
var _tsg = g.sigmoid(_t1);
var _t2 = g.divide(g.constant(1),_tsg);
var _t3 = g.subtract(_t2,g.constant(1));
var sf2 = g.divide(_t3,g.reduceSum(_t3));


const shuffledInputProviderBuilder = new dl.InCPUMemoryShuffledInputProviderBuilder([target]);
    
const inputX = shuffledInputProviderBuilder.getInputProviders()[0];
    


const feedEntries = [{
            tensor: X,
            data: inputX
        }
    ];


console.log(session.eval(sf1,feedEntries ).getValues());
console.log(session.eval(sf2,feedEntries ).getValues());

运行结果如下:

完美完美完美!

9.mnist数据集测试

下次见~





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值