我正在尝试用python开发一个小的卷积神经网络框架 . 卷积节点的代码已经(慢慢地)工作,我想加快它的速度 . 热点是卷积滤波器在图像上移动的循环 . 我选择使用cython来加速这些循环 .
显而易见的小注释,所有局部变量的cdef和删除boundscheck,使我的运行时间几乎没有减少10% . 这对我来说很奇怪,基于我在网上看到的内容,cython应该已经能够发挥它的魔力 .
不幸的是,代码在一个类中,并且在很大程度上依赖于该类的属性 . 我决定将它转换为cdef类 . 这意味着必须使用cdef声明所有类属性 . 显然cython不支持numpy数组,所以我将所有numpy数组声明为 double[:,:,...]
到目前为止,代码工作正常,所有单元测试都通过 . 现在编译到.pyd(我在Windows下工作)仍然有效 . 但是运行代码会创建一个Typeerror:
TypeError:只能将length-1数组转换为Python标量
这是一些代码 . 这是我的卷积节点的整个前向方法,它可能太多而且不易读取 . 你可能只需要最后一行 . 那是错误发生的:
@cython.boundscheck(False)
@cython.nonecheck(False)
def forward(self):
# im2col: x -> in_cols
# padding
cdef np.ndarray[DTYPE_t, ndim=4] x_padded = np.zeros((self.batch_size, self.in_colors, self.in_width + self.padding*2, self.in_height + self.padding*2))
if self.padding>0:
x_padded[:, :, self.padding:self.in_width+self.padding, self.padding:self.in_height+self.padding] = self.x
else:
x_padded[:]=self.x
# allocating new field
cdef np.ndarray[DTYPE_t, ndim=4] rec_fields = np.empty((self.filter_size**2* self.in_colors, self.batch_size, self.out_width, self.out_height))
# copying receptive fields
cdef int w,h
for w, h in np.ndindex((self.out_width, self.out_height)):
rec_fields[:, :, w, h] = x_padded[:, :, w*self.stride:w*self.stride + self.filter_size, h*self.stride:h*self.stride + self.filter_size] \
.reshape((self.batch_size, self.filter_size**2* self.in_colors)) \
.T
self.in_cols = rec_fields.reshape((self.filter_size**2 * self.in_colors, self.batch_size * self.out_width * self.out_height))
# linear node: in_cols -> out_cols
cdef np.ndarray[DTYPE_t, ndim=2] out_cols=np.dot(self.W,self.in_cols)+self.b
# col2im: out_cols -> out_image -> y
cdef np.ndarray[DTYPE_t, ndim=4] out_image = out_cols.reshape((self.out_colors, self.batch_size, self.out_width, self.out_height))
self.y[:] = out_image.transpose(1, 0, 2, 3)
最后一次调用transpose在异常中标记 . 我无法解释这一点 . 转置后,记忆视图的行为会有所不同吗?
更新:
我确信尺寸定义正确 . 如果存在尺寸不匹配,则会产生不同的运行时错误 . 现在无法检查,但它有点像“得到4-dim,预计2-Dim” . 我必须说,我对cython的类型系统印象非常深刻 . python异常中的这种运行时类型信息非常有用 . 可悲的是,它没有解释为什么上面的转置失败了 .
更新:
这些数组有一些复杂性:它们不能被覆盖,只能用作引用 .
有点难以解释:神经网络的核心是一个循环,它连续地在网络中的所有节点上调用方法forward() .
for node in self.nodes:
node.forward()
在此方法中,节点查看其输入数据,进行一些计算并写入其输出 . 它依赖于输入已包含正确数据的事实 .
为了设置我的网络,我按正确的顺序存储节点 . 我手动连接它们 .
node2.x=node1.y
现在,如果我写
self.y[:]= data
在node1的forward方法中,node2自动拥有正确的输入 . 这需要仔细编程:必须以正确的顺序调用forward方法,并且绝不能覆盖输出,只写入 .
替代方案是一个巨大的结构,我存储每个节点的输出并传递这些数据 . 这将产生大量的样板代码并搞乱向前和向后传递 .
更新:
前进的最后几行现在看起来像这样:
cdef np.ndarray[DTYPE_t, ndim=4] out_image = out_cols.reshape((self.out_colors, self.batch_size, self.out_width, self.out_height))
cdef double[:,:,:,:] temp
temp=out_image.transpose(1,0,2,3)
self.y[...] = temp
对temp的赋值失败,并带有相同的TypeError消息 .
1 回答
将
some_array
复制到self.y
,必须已经初始化为正确的大小 . 它似乎只有some_array
已经是一个记忆视图(这对我来说没有多大意义,但似乎是这样) .(
self.y[:] = some_array
仅适用于1D阵列)如果你只是想制作一个numpy数组,你只想做
有可能这对你的目的是好的!
如果你're particularly keen on making a copy (possibly if you'已经将C指针指向
self.y
或类似的东西)那么你必须强迫some_array
成为一个内存视图 . 你会做类似的事情我同意看到的错误信息无益!
Edit: 以下是适用于我的最小完整示例(Cython 0.24,Python 3.5.1,Linux - 我可以't easily test on Anaconda) At this stage I'我不清楚你的代码有什么不同 .
和test_script.py显示它的工作原理: