Фон
Я обучил сверточную нейронную сеть, которую хотел бы, чтобы другие могли использовать, не требуя жестких для установки библиотек, таких как Theano (которые я нашел тривиальным для установки в Linux, но очень тяжело в Windows).
Я написал реализацию с использованием Numpy/Scipy, которая почти достаточно быстро, но была бы еще лучше, если бы это было в два-три раза быстрее.
Что я пробовал
90% времени проводится в следующей строке:
conv_out = np.sum([scipy.signal.convolve2d(x[i],W[f][i],mode='valid') for i in range(num_in)], axis=0)
Эта строка вызывается 32 раза (один раз для каждой карты функций), а num_in - 16 (количество функций на предыдущем слое). Таким образом, общая длина этой линии медленная, так как она приводит к 32 * 16 = 512 вызовам процедуры convolve2d.
x [i] - только 25 * 25, а W [f] [i] равно 2 * 2.
Вопрос
Есть ли лучший способ выразить этот тип сверточного слоя в Numpy/Scipy, который будет выполняться быстрее?
(Я использую только этот код для применения узнаваемой сети, поэтому у меня не так много изображений, которые нужно делать параллельно).
код
Полный код для проведения эксперимента по синхронизации:
import numpy as np
import scipy.signal
from time import time
def max_pool(x):
"""Return maximum in groups of 2x2 for a N,h,w image"""
N,h,w = x.shape
return np.amax([x[:,(i>>1)&1::2,i&1::2] for i in range(4)],axis=0)
def conv_layer(params,x):
"""Applies a convolutional layer (W,b) followed by 2*2 pool followed by RelU on x"""
W,biases = params
num_in = W.shape[1]
A = []
for f,bias in enumerate(biases):
conv_out = np.sum([scipy.signal.convolve2d(x[i],W[f][i],mode='valid') for i in range(num_in)], axis=0)
A.append(conv_out + bias)
x = np.array(A)
x = max_pool(x)
return np.maximum(x,0)
W = np.random.randn(32,16,2,2).astype(np.float32)
b = np.random.randn(32).astype(np.float32)
I = np.random.randn(16,25,25).astype(np.float32)
t0 = time()
O = conv_layer((W,b),I)
print time()-t0
В данный момент это значение составляет 0,084 секунды.
Update
Использование предложения mplf:
d = x[:,:-1,:-1]
c = x[:,:-1,1:]
b = x[:,1:,:-1]
a = x[:,1:,1:]
for f,bias in enumerate(biases):
conv_out = np.sum([a[i]*W[f,i,0,0]+b[i]*W[f,i,0,1]+c[i]*W[f,i,1,0]+d[i]*W[f,i,1,1] for i in range(num_in)], axis=0)
Я получаю 0.075s, что немного быстрее.