Подтвердить что ты не робот

Увеличение скорости реализации чистой сверхочной сети Numpy/Scipy

Фон

Я обучил сверточную нейронную сеть, которую хотел бы, чтобы другие могли использовать, не требуя жестких для установки библиотек, таких как 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, что немного быстрее.

4b9b3361

Ответ 1

Оглядываясь вокруг, кажется, что функция scipy convolve2d неоптимизирована и довольно неэффективна. Существует открытый вопрос об этом с января 2014 года (https://github.com/scipy/scipy/issues/3184), и этот вопрос кажется связанным Улучшение производительности Numpy.

Я бы предложил попробовать решение, отправленное Theran, и посмотреть, дает ли это лучшую производительность первый.

Ответ 2

Ускоряющая свертка

Построение предложения mplf Я нашел, что можно удалить оба цикла for и вызов convolve2d:

d = x[:,:-1,:-1].swapaxes(0,1)
c = x[:,:-1,1:].swapaxes(0,1)
b = x[:,1:,:-1].swapaxes(0,1)
a = x[:,1:,1:].swapaxes(0,1)
x = W[:,:,0,0].dot(a) + W[:,:,0,1].dot(b) + W[:,:,1,0].dot(c) + W[:,:,1,1].dot(d) + biases.reshape(-1,1,1)

Это в 10 раз быстрее, чем исходный код.

Ускорение максимального пула

С помощью этого нового кода максимальная стадия пула теперь занимает 50% времени. Это также можно ускорить, используя:

def max_pool(x):
    """Return maximum in groups of 2x2 for a N,h,w image"""
    N,h,w = x.shape
    x = x.reshape(N,h/2,2,w/2,2).swapaxes(2,3).reshape(N,h/2,w/2,4)
    return np.amax(x,axis=3)

Это ускоряет шаг max_pool в 10 раз, поэтому в целом программа снова удваивает скорость.