Я пытаюсь разработать небольшую структуру сверточной нейронной сети с помощью Python. Код для сверточного узла уже работает (медленно), и я хотел бы его ускорить. Горячие точки - это петли, в которых сверточный фильтр перемещается по изображению. Я решил использовать cython, чтобы ускорить эти циклы.
Очевидные небольшие аннотации, cdef для всех локальных переменных и удаление boundscheck, почти на 10% сократили мое время выполнения. Мне это показалось странным, судя по тому, что я читал в Интернете, cython уже должен творить чудеса.
К сожалению, код находится внутри класса и сильно зависит от свойств этого класса. Я решил преобразовать его в класс cdef. Это означает, что все атрибуты класса должны быть объявлены с помощью cdef. По-видимому, cython не поддерживает массивы numpy, поэтому я объявил все массивы numpy как double[:,:,...]
Пока код работал нормально, все юнит-тесты прошли. Теперь компиляция в .pyd (я работаю под windows) все еще работает. Но запуск кода создает ошибку Typeerror:
TypeError: только массивы длины 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)
Этот последний вызов транспонирования отмечен как исключение. Я не могу этого объяснить. Виды памяти ведут себя по-другому при транспонировании?
ОБНОВИТЬ:
Уверен, что размеры определены правильно. Если есть несоответствие размеров, это вызывает другую ошибку времени выполнения. Не могу проверить прямо сейчас, но это было что-то вроде «получил 4-тусклое, ожидалось 2-тусклое». Должен сказать, что меня очень впечатлила система типов Cython. Такая информация о типе среды выполнения в исключении python весьма полезна. К сожалению, это не объясняет, почему вышеуказанное транспонирование не удается.
ОБНОВИТЬ:
Есть некоторая сложность с массивами: их нельзя перезаписывать, их можно использовать только как ссылки.
Сложно объяснить: в основе нейронной сети лежит цикл, который последовательно вызывает метод forward () на всех узлах сети.
for node in self.nodes:
node.forward()
В этом методе узел просматривает свои входные данные, выполняет некоторые вычисления и записывает в свой выход. Он полагается на тот факт, что ввод уже содержит правильные данные.
Для настройки моей сети я храню узлы в правильном порядке. И подключаю их вручную.
node2.x=node1.y
Теперь, если я напишу
self.y[:]= data
в прямом методе node1, node2 автоматически получает правильный ввод. Это требует тщательного программирования: методы пересылки должны вызываться в правильном порядке, а вывод никогда не должен перезаписываться, а только записываться.
Альтернативой была бы огромная структура, в которой я сохраняю вывод каждого узла и передаю эти данные. Это приведет к созданию большого количества шаблонного кода и испортит прямой и обратный проход.
ОБНОВИТЬ:
последние несколько строк вперед теперь выглядят так:
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.
cdef object y
). Таким образом, они могут быть любого типа. Вы можете немного потерять скорость, но вы можете частично ее вернуть, присвоив типизированной локальной переменной перед использованием. - person DavidW   schedule 12.05.2016self.y[:,:,:,:] = ...
- person DavidW   schedule 12.05.2016