Некоторые предпосылки для вопроса:
Я пытаюсь оптимизировать пользовательский код нейронной сети.
Он сильно зависит от циклов, и я решил использовать Cython для ускорения вычислений.
Я следовал обычным онлайн-советам: объявляйте все локальные переменные с соответствующими cdefs, отключайте boundscheck и nonecheck. Это едва дало мне 10% производительности.
Ну, мой код опирается на множество учеников. Поэтому я решил преобразовать весь класс в класс cdef. Оказывается, что Cython не допускает пустые ndarrays в качестве типов для членов класса. Вместо этого нужно использовать виды памяти.
К сожалению, эти два типа кажутся несовместимыми.
Я уже столкнулся с этой проблемой: Cython memoryview transpose: Typeerror
Подводя итог: вы можете сохранить np.ndarray в памяти. Вы можете транспонировать его и сохранить возвращенный массив в memview. Но не в том случае, если это memview является членом класса. Затем вы должны создать промежуточный memview, сохранить в нем результат и назначить промежуточный memview члену класса.
Вот код (большое спасибо 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).
Теперь у меня новая проблема.
Код выше взят из так называемого «прямого прохода». Существует также соответствующий обратный проход, который выполняет все вычисления в обратном направлении (для аналитических градиентов).
Это означает, что для обратного прохода я должен транспонировать представление памяти и сохранить его в массивном массиве:
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = self.d_y.transpose(1, 0, 2,3)
d_y должен быть членом класса, поэтому он должен быть представлением памяти. Взгляды на память не позволяют транспонировать. У них есть метод .T, но это мне не помогает.
Актуальный вопрос:
Я думаю, что лучший ответ: «Вы храните NumPy как нетипизированный объект Python»
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
чтобы проверить, что это на самом деле 4D-массив с правильным dtype. При условии, что вы выполняете приличную работу в функции, которая не должна иметь значения.
В качестве альтернативы (если вы решите, что вы действительно хотите сохранить их в виде памяти), вы можете использование np.asarray
преобразовать его обратно в массив Numpy, не делая копию (то есть они обмениваются данными).
например
cdef np.ndarray[DTYPE_t, ndim=4] d_out_image = np.asarray(self.d_y).transpose(1, 0, 2,3)
Других решений пока нет …