在 clojure 中,通过内核对向量进行卷积的有效方法是什么?
我想出了这个:
(def kernel [0 1 1 2 3 3 0 0 0 0 0 0])
(def data [1 5 7 4 8 3 9 5 6 3 2 1 1 7 4 9 3 2 1 8 6 4])
(defn capped+ [a b c] (let [s (+ a b)] (if (> s c) c s)))
(defn *+ [a b]
(if (> (count a) (count b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) a))))
(defn slide*i [d k]
(let [ki (into [] (reverse k)) kl (count k) dl (count d)]
(map-indexed
(fn [idx item]
(/ (*+ ki (subvec d idx (capped+ idx kl dl)))
(reduce + ki)))
d)))
(def s (slide*i data kernel))
这不是最优雅的代码,但它工作得很好。 我实际上想用它来平滑一些非常尖的!数据。
欢迎任何使之更美观、更高效或更准确的建议(我个人并不关心尾巴不准确,因为就我而言,我从不使用它)。
I came up with this:
(def kernel [0 1 1 2 3 3 0 0 0 0 0 0])
(def data [1 5 7 4 8 3 9 5 6 3 2 1 1 7 4 9 3 2 1 8 6 4])
(defn capped+ [a b c] (let [s (+ a b)] (if (> s c) c s)))
(defn *+ [a b]
(if (> (count a) (count b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) a))))
(defn slide*i [d k]
(let [ki (into [] (reverse k)) kl (count k) dl (count d)]
(map-indexed
(fn [idx item]
(/ (*+ ki (subvec d idx (capped+ idx kl dl)))
(reduce + ki)))
d)))
(def s (slide*i data kernel))
It's not the most elegant code, but it works fine.
I actually want to use it to smooth some very spiky! data.
Any suggestions to make this more beautiful or more efficient or more accurate (personally I don't care about the tail being inaccurate because in my case I never use it) are welcomed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您当然可以显着提高此操作的性能。好消息是,您不需要为此投入 Java:如果正确优化,Clojure 会非常快,并且在大多数情况下可以产生与纯 Java 相同的速度。
为了在 Clojure 中实现数字代码的最大性能,您将需要使用:
以下内容应该是正确的,并且可能会为您提供与 Java 大致相同的性能:
我没有修剪输出数组或进行任何限制,因此您可能需要稍微修改此解决方案才能准确获得您想要的输出,但希望你能明白......
一些非常粗略的基准测试:
即每个内核/数据对组合大约 30 纳秒 - 我预计这几乎达到了缓存内存访问的界限。
You can certainly improve the performance of this operation significantly. The good news is that you don't need to drop into Java for this: Clojure is extremely fast if you optimise it correctly and in most instances can produce the same speed as pure Java.
For maximum performance of numerical code in Clojure you will want to use:
The following should be along the right lines and will probably get you roughly equivalent performance to Java:
I've not trimmed the output array or done any bounding so you'll probably need to hack this solution a bit to get exactly the output you want, but hopefully you get the idea.....
Some very rough benchmarking:
i.e. that's about 30ns per kernel / data pair combination - I expect that's pretty much hitting the bounds of cached memory access.
使用
partition
,结果是:但是使用
partition-all
,您将得到您的解决方案的结果:With
partition
, the results are:But with
partition-all
, you'll get exactly what your solution resulted in:执行此操作的有效方法是创建执行卷积的 java 类并从 clojure 调用它,如果可能的话向其传递一个 java 数组。如果考虑效率的话,Clojure 实现也应该在 java 数组上运行。
The efficient way of doing this is to create java class that does convolution and call it from clojure, passing it a java array if possible. Clojure implementation should operate on java arrays as well if efficiency is a concern.