Cython memoryview转置:Typeerror

时间:2016-05-11 22:07:10

标签: python numpy neural-network cython conv-neural-network

我正在尝试使用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 个答案:

答案 0 :(得分:1)

add_filter( 'woocommerce_variable_sale_price_html', 'wc_wc20_variation_price_format', 10, 2 );
 add_filter( 'woocommerce_variable_price_html', 'wc_wc20_variation_price_format', 10, 2 );
 function wc_wc20_variation_price_format( $price, $product ) {
 // Main Price
 $prices = array( $product->get_variation_price( 'min', true ), $product->get_variation_price( 'max', true ) );
 $price = $prices[0] !== $prices[1] ? sprintf( __( 'Price: %1$s', 'woocommerce' ), wc_price( $prices[0] ) ) : wc_price( $prices[0] );
 // Sale Price
 $prices = array( $product->get_variation_regular_price( 'min', true ), $product->get_variation_regular_price( 'max', true ) );
 sort( $prices );
 $saleprice = $prices[0] !== $prices[1] ? sprintf( __( 'Previous Price: %1$s', 'woocommerce' ), wc_price( $prices[0] ) ) : wc_price( $prices[0] );
 if ( $price !== $saleprice ) {
   $price = '<ins>' . $price . '</ins> <del>' . $saleprice . '</del>';
 }
   return $price;
 }

self.y[...] = some_array # or equivalently self.y[:,:,:,:] = some_array 复制到some_array,必须已将其初始化为正确的大小。如果self.y已经是一个记忆视图(这对我来说没有多大意义,但似乎就是这种情况),它似乎也有效。

some_array仅适用于1D数组)

如果你只想让make self.y[:] = some_array“看看”你想做的numpy数组

self.y

这很可能是为了你的目的!

如果您特别热衷于制作副本(可能是您已经使用C指针指向self.y = some_array # in your case: # self.y = out_image.transpose(1, 0, 2, 3) 或类似的东西),那么您必须强制self.y成为内存视图。你会做像

这样的事情
some_array

我同意所看到的错误消息无益!

编辑:以下是适用于我的最小完整示例(Cython 0.24,Python 3.5.1,Linux - 我无法在Anaconda上轻松测试)在此阶段我不清楚你的代码有什么不同。

cdef double[:,:,:,:] temporary_view_of_transpose

# temporary_view_of_transpose now "looks at" the memory allocated by transpose
# no square brackets!
temporary_view_of_transpose = out_image.transpose(1, 0, 2, 3)

# data is copied from temporary_view_of_transpose to self.y
self.y[...] = temporary_view_of_transpose # (remembering that self.y must be the correct shape before this assignment).

和test_script.py显示它有效:

# memview.pyx
cimport numpy as np
import numpy as np

cdef class MemviewClass:
    cdef double[:,:,:,:] y

    def __init__(self):
        self.y = np.zeros((2,3,4,5))

    def do_something(self):
        cdef np.ndarray[np.float64_t,ndim=4] out_image = np.ones((3,2,4,5))
        cdef double[:,:,:,:] temp
        temp = out_image.transpose(1,0,2,3)
        self.y[...] = temp

    def print_y(self):
        # just to check it gets changed
        print(np.asarray(self.y))