java 误差范围_现代化的 Java (三十三)—— 误差反向传播

神经网络的训练和误差反向传播

神经网络的训练,简单的说就是通过梯度下降,沿着代价函数的梯度向量反方向生成一个反向量,由此得到关于神经单元的新的权重值和截距,以使得神经网络的精度得以提高。

误差反向传播,是这一过程的快速算法。具体的实现我这里不过多展开,还是那句话,推荐有兴趣的读者去阅读《深度学习的数学》,这几章仅仅是对此书的一篇学习笔记。简单来说,我们需要先求得误差函数在每个 zeta 上的偏导数,这里称为 delta 值,通过 delta 值,我们可以简单快速的求得每个 weight 和 b 对 Cost 的导数值。再根据学习率 eta ,可以得到我们在当次训练中需要对 weight 或 b 做的修正。

所以,求解 alpha 的时候,我们是先得到输入层的值,再递归的逐层向后求出每一层的值。而求解 delta 的时候,我们先由 alpha 和 t 得到输出层的 delta ,再逐层向前求出所有隐藏层的值,输入层由于总是将输入值作为 alpha,不需要进行训练。

输出层每一个节点的 delta 都很容易得到:

(defnoutput-delta

"这里仅针对点火函数为 sigmoid 的输出层神经元给出 delta 值,接收的参数为 alpha值,zeta 和正值"

[a z t]

(*(-a t) (d-sigmoid z)))

如果得到了一整层 delta 后,上一层的每个节点的 delta 也很容易得到:

(defnresolve-delta

"利用误差反向传播法递推指定节点的误差 delta 值。参数为节点的 idx,计算结果,下一层节点和下一层的结果"

[idx node-result next-layer next-layer-results]

(let[weights (map#(->% :w (nthidx)) next-layer)

deltas (map:d next-layer-results)]

(*(reduce +0 (map *weights deltas))

(d-sigmoid (:z node-result)))))

但是其实利用输出层 delta 求解隐藏层 delta 的过程,我实现的意外的麻烦:

(defnupdate-hidden

"依据已得到输出层 delta 的结果集,更新指定网络的隐藏层结果,将求得的 delta 附在结果集中"

[result network]

(loop[layer-index (-(countresult) 2) result result]

(if(=layer-index 0)

result

(recur

(declayer-index)

(update

result

layer-index

(fn[layer]

(map-indexed

(fn[indexnode]

(assocnode

:d

(resolve-delta

indexnode

(nthnetwork (inclayer-index))

(nthresult (inclayer-index)))))

layer)))))))

这里,我猜用一个“平凡”的编程语言,比如java,可能写起来更简单,因为这里的计算逻辑都是依据索引定位的。

有了这些函数,我们就能根据结果集和正值集合,得到一个带有 delta 值的集合:

(defnupdate-delta

"根据结果集和正值生成整个网络(隐藏层和输出层)的误差,生成与结果集合并的结果"

[result t-vector network]

(let[last-index (->result countdec)]

(->result

(update last-index (fn[layer] (map(fn[nodet]

(assoc node:d (output-delta (:a node) (:z node) t)))

layer t-vector)))

(update-hidden network))))

顾名思义,update-delta 会生成一个新的集合,这里面包含神经网络的计算结果,并且在每一个节点都附带求得的 delta 。将它们合并在一个新的集合中,可以更方便的进行后续的计算:

在求得delta的前提下,可以通过一组函数,方便的求得各节点的权重和截距的偏导数:

(defnnode-differential

"单节点微分函数,根据当前节点的计算结果(delta)以及上一层节点的计算结果(alpha),计算权重和偏置量的偏微分结果"

[result prev-layer-results]

{:w (vec (map#(*(:d result) (:a %)) prev-layer-results))

:b (:d result)})

(defnlayer-differential

"层微分函数,根据当前层和上一层的计算结果,生成整层的微分结果。"

[layer-results prev-layer-results]

(vec (map#(node-differential % prev-layer-results) layer-results)))

(defndifferential

"根据 delta 结果集求得神经元网络的偏微分结果集"

[result]

(loop[layer-index 1 dataset [(firstresult)]]

(if(=(->result count) layer-index)

dataset

(let[next-layer (inclayer-index)]

(recur next-layer

(assocdataset

layer-index

(layer-differential

(nthresult layer-index)

(nthresult (declayer-index)))))))))

再根据偏导数的值和学习率 eta ,我们就可以对神经网络进行修正了:

(defnnode-fix

"跟据给定的 offset 数据集生成修正后的 node"

[nodeoffset]

(->node

(update :w #(vec (map -% (:w offset))))

(update :b -(:b offset))))

(defnlayer-fix

"根据给定的 offset 层生成修订后的 layer"

[layer offset]

(mapnode-fix layer offset))

这个训练过程可以封装进一个 train 函数:

(defntrain

"根据 delta 结果集对神经网络进行训练"

[network eta deltas]

(reduceconj

[(firstnetwork)]

(maplayer-fix

(restnetwork)

(rest(create-fix eta (mapdifferential deltas))))))

那么,给定了期望达到的误差范围,我们可以将训练过程自动化,让程序反复进行训练,直至 cost 得到的误差足够小:

(defntrain

[dataset t-set network eta d]

(loop[network network]

(let[delta-set (->dataset

(resolve-results network)

(resolve-deltas t-set network))

w (total-cost delta-set t-set)]

(printlnw)

(if(

network

(recur (n/train network eta (valsdelta-set)))))))

封装单次训练的 train 函数,在 liu.mars.ml.neural 中,而封装自动化训练过程的 train函数,我放在了 liu.mars.ml.repl 中,以便在 repl 环境中调用。

(defnew-network (learn dataset t-all network eta 0.1))

在下图中,我们可以形象的看出梯度下降的过程中误差逐步减小

现在,我们可以用新生成的神经网络,重新计算一组结果,看一下它的准确程度:

(defresults

(resolve-results dataset new-network))

(into(sorted-map)

(map#(vector(key%) (->> % val last(map:a) n/binary-pair))

results))

>>>

{1 [1 0],

2 [1 0],

3 [1 0],

4 [1 0],

5 [1 0],

6 [1 0],

7 [1 0],

...

53 [0 1],

54 [0 1],

55 [0 1],

56 [0 1],

57 [0 1],

58 [0 1],

59 [0 1],

60 [0 1],

61 [0 1],

62 [0 1],

63 [0 1],

64 [0 1]}

在我的这次实验中,新的神经网络已经能够达到足够好的效果。

在今年十月份的上海 QCon ,我会向大家演示同一个算法的 PostgreSQL 实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值