提高纯 Numpy/Scipy 卷积神经网络实现的速度

Posted

技术标签:

【中文标题】提高纯 Numpy/Scipy 卷积神经网络实现的速度【英文标题】:Increasing speed of a pure Numpy/Scipy convolutional neural network implementation 【发布时间】:2015-11-05 12:30:57 【问题描述】:

背景

我已经训练了一个卷积神经网络,我希望其他人能够使用它而无需安装 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(前一层的特征数)。所以总的来说这条线很慢,因为它会导致对 convolve2d 例程的 32*16=512 次调用。

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.075 秒,稍微快一点。

【问题讨论】:

+1 提出了一个有趣的问题,但是我看到平均 0.037 秒超过 20 次运行。您的目标基准速度是多少? 我有 3 个类似大小的网络,我想在实时摄像机输入上运行。它目前确实有效(大约 2 fps),但我想把它推高 - 理想情况下超过 10 fps。也许您的计算机速度更快? 我在第一次运行时得到 0.024,然后在 i5 4200M 笔记本电脑上得到 0.018-0.020,在 windows 10 下使用 python 3.4,后台运行的东西足以让我的 CPU 保持在 20% .你有什么硬件/操作系统? Windows 7 上相当旧的笔记本电脑,如果可能的话,我对相对收益比绝对速度更感兴趣 你能给我一些关于(i>>1)&1::2, i&1::2部分的提示吗?我可以看到最终效果是以 2 的步幅对 2x2 网格进行采样,但并没有完全得到按位部分。如何以s 的步幅泛化到最大nxn 【参考方案1】:

环顾四周,似乎 scipy convolve2d 函数未优化且效率低下。从 2014 年 1 月开始有一个未解决的问题 (https://github.com/scipy/scipy/issues/3184),这个问题似乎与 Improving Numpy Performance 相关。

我建议先尝试Theran 发布的the solution,看看这是否会产生更好的性能。

【讨论】:

谢谢,我早上试试 我认为这种方法仍然需要进行大约 512*4 的矩阵运算 - 你认为有办法将这些运算减少到更少的较大矩阵运算,而我希望它会更快吗? specialconvolve 表达式都是线性运算,因此包含num_in 维度应该很容易推广。如果需要,请在另一个问题中讨论。 但是specialconvolve 是为特定内核设计的,你如何将它用于其他大小和值的内核?【参考方案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 倍,因此总体而言,程序的速度再次翻倍。

【讨论】:

我正要提出一个等效的概括:np.sum(a*W[f,:,0,0][...,None,None]+b*W[f,:,0,1][...,None,None]+c*W[f,:,1,0][...,None,None]+d*W[f,:,1,1][...,None,None], axis=0) max_pool 的等效公式如下,只需要一个 reshape 而不需要 swapaxesreturn x.reshape(N, h / 2, 2, w / 2, 2).max(axis=(2, 4))

以上是关于提高纯 Numpy/Scipy 卷积神经网络实现的速度的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 python + NumPy / SciPy 计算滚动/移动平均值?

如何在STM32上部署卷积神经网络(纯C语言搭建)

numpy/scipy 中的平方差总和 (SSD)

浅谈卷积神经网络及matlab实现

windows上安装numpy,scipy

仅依赖于 NumPy/SciPy 的二次规划 (QP) 求解器?