提高纯 Numpy/Scipy 卷积神经网络实现的速度
Increasing speed of a pure Numpy/Scipy convolutional neural network implementation
背景
我已经训练了一个卷积神经网络,我希望其他人能够使用它而无需费力地安装 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 秒。
更新
使用 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,稍微快一点。
环顾四周,似乎 scipy convolve2d 函数未优化且效率低下。从 2014 年 1 月开始有一个未解决的问题 (https://github.com/scipy/scipy/issues/3184) and this question seems to be related Improving Numpy Performance.
我建议先尝试 the solution posted by Theran 看看这是否会产生更好的性能。
加速卷积
根据 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 倍,因此整个程序的速度再次翻倍。
背景
我已经训练了一个卷积神经网络,我希望其他人能够使用它而无需费力地安装 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 秒。
更新
使用 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,稍微快一点。
环顾四周,似乎 scipy convolve2d 函数未优化且效率低下。从 2014 年 1 月开始有一个未解决的问题 (https://github.com/scipy/scipy/issues/3184) and this question seems to be related Improving Numpy Performance.
我建议先尝试 the solution posted by Theran 看看这是否会产生更好的性能。
加速卷积
根据 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 倍,因此整个程序的速度再次翻倍。