Cython:转置内存视图
Cython: Transpose a memoryview
问题的一些背景:
我正在尝试优化自定义神经网络代码。
它严重依赖循环,我决定使用cython来加速计算。
我遵循了通常的在线提示:使用适当的 cdef 声明所有局部变量并关闭 boundscheck 和 nonecheck。这勉强给了我 10% 的性能。
好吧,我的代码依赖于很多 class 成员。因此,我决定将整个 class 转换为 cdef class。原来 cython 不允许 numpy ndarrays 作为 class 成员的类型。相反,必须使用内存视图。
不幸的是,这两种类型似乎非常不兼容。
我已经 运行 解决了这个问题:
总结一下:您可以将 np.ndarray 存储在内存视图中。您可以 t运行spose 并将返回的数组存储在内存视图中。但如果该 memview 是 class 成员则不是。然后你必须创建一个中间 memview,将结果存储在其中并将中间 memview 分配给 class 成员。
这是代码(非常感谢 DavidW)
def 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).
现在我遇到了一个新问题。
上面的代码来自所谓的"forward-pass"。还有一个相应的向后传递,它向后进行所有计算(对于分析梯度)。
这意味着对于反向传递,我必须运行定位内存视图并将其存储在一个 numpy 数组中:
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = self.d_y.transpose(1, 0, 2,3)
d_y 必须是 class 成员,因此它必须是内存视图。 Memoryviews 不允许 t运行sposing。他们有一个 .T 方法,但这对我没有帮助。
真题:
- 如何将 numpy 数组正确存储为 cdef class 的 class 成员?
- 如果答案是:"as a memoryview",我如何运行设置内存视图?
我认为最佳答案是"you store the numpy as an untyped python object"
cdef class C:
cdef object array
def example_function(self):
# if you want to use the fast Cython array indexing in a function
# you can do:
cdef np.ndarray[np.float64_t,ndim=4] self_array = self.array
# or
cdef np.float64_t[:,:,:,:] self_array2 = self.array
# note that neither of these are copies - they're references
# to exactly the same array and so if you modify one it'll modify
# self.array too
def function2(self):
return self.array.transpose(1,0,2,3) # works fine!
这样做的小成本是在 example_function
开始时进行了一些类型检查,以检查它实际上是一个具有正确 dtype 的 4D numpy 数组。只要您在函数中做了相当多的工作,那应该无关紧要。
作为替代方案(如果您决定将它们存储为内存视图),您可以 use np.asarray
to convert it back to a numpy array without making a copy(即它们共享数据)。
例如
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = np.asarray(self.d_y).transpose(1, 0, 2,3)
问题的一些背景:
我正在尝试优化自定义神经网络代码。 它严重依赖循环,我决定使用cython来加速计算。
我遵循了通常的在线提示:使用适当的 cdef 声明所有局部变量并关闭 boundscheck 和 nonecheck。这勉强给了我 10% 的性能。
好吧,我的代码依赖于很多 class 成员。因此,我决定将整个 class 转换为 cdef class。原来 cython 不允许 numpy ndarrays 作为 class 成员的类型。相反,必须使用内存视图。 不幸的是,这两种类型似乎非常不兼容。
我已经 运行 解决了这个问题:
总结一下:您可以将 np.ndarray 存储在内存视图中。您可以 t运行spose 并将返回的数组存储在内存视图中。但如果该 memview 是 class 成员则不是。然后你必须创建一个中间 memview,将结果存储在其中并将中间 memview 分配给 class 成员。
这是代码(非常感谢 DavidW)
def 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).
现在我遇到了一个新问题。 上面的代码来自所谓的"forward-pass"。还有一个相应的向后传递,它向后进行所有计算(对于分析梯度)。
这意味着对于反向传递,我必须运行定位内存视图并将其存储在一个 numpy 数组中:
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = self.d_y.transpose(1, 0, 2,3)
d_y 必须是 class 成员,因此它必须是内存视图。 Memoryviews 不允许 t运行sposing。他们有一个 .T 方法,但这对我没有帮助。
真题:
- 如何将 numpy 数组正确存储为 cdef class 的 class 成员?
- 如果答案是:"as a memoryview",我如何运行设置内存视图?
我认为最佳答案是"you store the numpy as an untyped python object"
cdef class C:
cdef object array
def example_function(self):
# if you want to use the fast Cython array indexing in a function
# you can do:
cdef np.ndarray[np.float64_t,ndim=4] self_array = self.array
# or
cdef np.float64_t[:,:,:,:] self_array2 = self.array
# note that neither of these are copies - they're references
# to exactly the same array and so if you modify one it'll modify
# self.array too
def function2(self):
return self.array.transpose(1,0,2,3) # works fine!
这样做的小成本是在 example_function
开始时进行了一些类型检查,以检查它实际上是一个具有正确 dtype 的 4D numpy 数组。只要您在函数中做了相当多的工作,那应该无关紧要。
作为替代方案(如果您决定将它们存储为内存视图),您可以 use np.asarray
to convert it back to a numpy array without making a copy(即它们共享数据)。
例如
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = np.asarray(self.d_y).transpose(1, 0, 2,3)