我在这里彻底搞砸了.我正在使用循环重复形式,我正在使用doall,但我仍然得到大循环的堆栈溢出.我的Clojure版本是1.5.1.
背景:我正在训练神经网络来模仿XOR.该功能xor是前馈功能,接受权重和输入并返回结果; 该函数b-xor是反向传播函数,它根据最后一次调用的结果返回更新的权重xor.
以下循环运行得很好,运行速度非常快,并返回结果,并根据返回的结果,完美地训练权重:
(loop [res 1 ; <- initial value doesn't matter
weights xorw ; <- initial pseudo-random weights
k 0] ; <- count
(if (= k 1000000)
res
(let [n (rand-int 4)
r (doall (xor weights (first (nth xorset n))))]
(recur (doall r)
(doall (b-xor weights r (second (nth xorset n))))
(inc k)))))
Run Code Online (Sandbox Code Playgroud)
但是,当然,这只能给我最后一次运行的结果.显然我想知道为了获得这个结果而训练了多少重量!以下循环,只返回值更改,溢出:
(loop [res 1
weights xorw
k 0]
(if (= k 1000000)
weights ; <- new return value
(let [n (rand-int 4)
r (doall (xor weights (first (nth xorset n))))]
(recur (doall r)
(doall (b-xor weights r (second (nth xorset n))))
(inc k)))))
Run Code Online (Sandbox Code Playgroud)
这对我来说没有意义.weights每个调用中使用的全部内容xor.那么为什么我可以在weights内部使用但不将它打印到REPL?
正如你所看到的,我已经陷入doall各种各样的地方,比我认为我需要的更多.XOR是一个玩具例子,所以weights和xorset都非常小.我认为溢出不是因为执行xor和b-xor,而是在REPL尝试打印时weights,出于以下两个原因:
(1)这个循环可以达到1500而不会溢出堆栈.
(2)循环运行的时间与循环的长度一致; 也就是说,如果我循环到5000,它运行半秒然后打印堆栈溢出; 如果我循环到1000000,它运行十秒然后打印堆栈溢出 - 再次,只有我打印weights而不是res在结尾.
(3)编辑:另外,如果我只是将循环包装进去(def w ... ),那么就没有堆栈溢出.但是,试图查看结果变量的确如此.
user=> (clojure.stacktrace/e)
java.lang.StackOverflowError: null
at clojure.core$seq.invoke (core.clj:133)
clojure.core$map$fn__4211.invoke (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
clojure.core$seq.invoke (core.clj:133)
clojure.core$map$fn__4211.invoke (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
nil
Run Code Online (Sandbox Code Playgroud)
懒惰序列在哪里?
如果你有更好的方法来做这个建议(这只是我的动态REPL代码),那就太棒了,但是我真的在寻找关于这种情况下发生了什么的解释.
编辑2:肯定(?)REPL的问题.
这很奇怪.weights是一个包含六个列表的列表,其中四个是空的.到现在为止还挺好.但是尝试将其中一个空列表打印到屏幕会导致堆栈溢出,但这只是第一次.第二次打印而不会丢失任何错误.打印非空列表不会产生堆栈溢出.现在我可以继续我的项目了,但是......到底发生了什么?有任何想法吗?(请原谅以下丑陋,但我认为这可能有帮助)
user=> (def ww (loop etc. etc. ))
#'user/ww
user=> (def x (first ww))
#'user/x
user=> x
StackOverflowError clojure.lang.RT.seq (RT.java:484)
user=> x
()
user=> (def x (nth ww 3))
#'user/x
user=> x
(8.47089879874061 -8.742792338501289 -4.661609290853221)
user=> (def ww (loop etc. etc. ))
#'user/ww
user=> ww
StackOverflowError clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError clojure.core/seq (core.clj:133)
user=> ww
(() () () (8.471553034351501 -8.741870954507117 -4.661171802683782) () (-8.861958958234174 8.828933147027938 18.43649480263751 -4.532462509591159))
Run Code Online (Sandbox Code Playgroud)
如果调用doall包含更多延迟序列的序列,doall则不会递归迭代子序列.在这种特殊情况下,b-xor包含的空列表的返回值是从以前的空列表中懒惰地定义的,这些空列表是从之前的空列表中延迟定义的,依此类推.我所要做的就是doall在map生成空列表(in b-xor)的位置添加一个,然后问题就消失了.这个循环(删除了所有doall)从不溢出:
(loop [res 1
weights xorw
k 0]
(if (= k 1000000)
weights
(let [n (rand-int 4)
r (xor weights (first (nth xorset n)))]
(recur r
(b-xor weights r (second (nth xorset n)))
(inc k)))))
Run Code Online (Sandbox Code Playgroud)
好的.所以我有一个答案.我希望这对其他一些可怜的灵魂有所帮助,他们认为他已经解决了他的懒惰排序问题doall.
这仍然让我有一个关于REPL的问题,但它可能会在一个不同的问题下进行,所以它不会有这个问题的所有包袱.您可以在上面的问题中看到空列表已正确评估.为什么第一次打印它们会抛出异常?我将尝试一下这个,如果我无法弄明白......新问题!