【问题标题】:Which convolution algorithm Keras uses?Keras 使用哪种卷积算法?
【发布时间】:2019-11-20 08:18:33
【问题描述】:

我在 Python 中为 CNN 编写了一个通用卷积函数。
事实证明,这个功能所花费的时间几乎是 Keras Conv2D 所花费的 5 倍。
所以我很好奇是否有人知道为什么会有速度差异

(对于我的卷积函数,MNIST 数据集的 1 个 epoch 花费了将近 10-15 分钟。而 Keras 几乎在 3-4 分钟内完成)


这是我的 Conv 课程:


class Convolutional2D(Layer):
    def __init__(self, kernel_size, feature_maps):
        self.kernel_size = kernel_size
        self.feature_maps = feature_maps
        self.b = np.zeros((feature_maps))#np.random.rand(feature_maps)

    def connect(self, to_layer):
        if len(to_layer.layer_shape) == 2:
            kernel_shape = [self.feature_maps, self.kernel_size, self.kernel_size]
            self.layer_shape = [self.feature_maps] + list(np.array(to_layer.layer_shape)-self.kernel_size+1)
        else:
            kernel_shape = [self.feature_maps, to_layer.layer_shape[0], self.kernel_size, self.kernel_size]
            self.layer_shape = [self.feature_maps] + list(np.array(to_layer.layer_shape[1:])-self.kernel_size+1)
        self.kernel = np.random.random(kernel_shape)
        super().init_adam_params(self.kernel, self.b)

    def convolve(self, x, k, mode='forward'):
        if mode == 'forward':
            ksize = k.shape[-1]
            if len(x.shape) == 3:
                out = np.zeros((x.shape[0], k.shape[0], x.shape[1]-k.shape[1]+1, x.shape[2]-k.shape[2]+1))
            else:
                out = np.zeros((x.shape[0], k.shape[0], x.shape[2]-k.shape[2]+1, x.shape[3]-k.shape[3]+1))
            for i in range(out.shape[2]):
                for j in range(out.shape[3]):
                    if len(x.shape) == 3:
                        window = x[:,i:i+ksize,j:j+ksize]
                        m = np.reshape(window, (window.shape[0], 1, window.shape[1], window.shape[2]))*k
                        m = np.sum(m, axis=(2,3))
                    else:
                        window = x[:,:,i:i+ksize,j:j+ksize]
                        m = np.reshape(window, (window.shape[0], 1, window.shape[1], window.shape[2], window.shape[3]))*k
                        m = np.sum(m, axis=(2,3,4))
                    out[:,:,i,j] = m
            return out

        elif mode == 'backward_i':
            if len(k.shape) == 3:
                out = np.zeros((x.shape[0], x.shape[2]+k.shape[1]-1, x.shape[3]+k.shape[2]-1))
                x = np.pad(x, ((0, 0), (0, 0), (k.shape[1]-1, k.shape[1]-1), (k.shape[2]-1, k.shape[2]-1)))
            else:
                out = np.zeros((x.shape[0], k.shape[1], x.shape[2]+k.shape[2]-1, x.shape[3]+k.shape[3]-1))
                x = np.pad(x, ((0, 0), (0, 0), (k.shape[2]-1, k.shape[2]-1), (k.shape[3]-1, k.shape[3]-1)))
                fk = np.transpose(k, axes=(1,0,2,3))
                x = np.reshape(x, (x.shape[0], 1, x.shape[1], x.shape[2], x.shape[3]))
            ksize = k.shape[-1]
            for i in range(out.shape[-2]):
                for j in range(out.shape[-1]):
                    if len(k.shape) == 3:
                        window = x[:,:,i:i+ksize,j:j+ksize]
                        m = window*k
                        m = np.sum(m, axis=(1,2,3))
                        out[:,i,j] = m
                    else:
                        window = x[:,:,:,i:i+ksize,j:j+ksize]
                        m = window*fk
                        m = np.sum(m, axis=(2,3,4))
                        out[:,:,i,j] = m
            return out

        elif mode == 'backward_k':
            if len(x.shape) == 3:
                out = np.zeros((k.shape[1], x.shape[1]-k.shape[2]+1, x.shape[2]-k.shape[3]+1))
            else:
                out = np.zeros((k.shape[1], x.shape[1], x.shape[2]-k.shape[2]+1, x.shape[3]-k.shape[3]+1))
                x = np.transpose(x, axes=(1,0,2,3))
                x = np.reshape(x, (x.shape[0], x.shape[1], x.shape[2], x.shape[3]))
            ksize = k.shape[-1]
            k = np.transpose(k, axes=(1,0,2,3))
            if len(x.shape) != 3:
                fk = np.reshape(k, (k.shape[0], 1, k.shape[1], k.shape[2], k.shape[3]))
            for i in range(out.shape[-2]):
                for j in range(out.shape[-1]):
                    if len(x.shape) == 3:
                        window = x[:,i:i+ksize,j:j+ksize]
                        m = window*k
                        m = np.sum(m, axis=(1,2,3))
                        out[:,i,j] = m
                    else:
                        window = x[:,:,i:i+ksize,j:j+ksize]
                        m = window*fk
                        m = np.sum(m, axis=(2,3,4))
                        out[:,:,i,j] = m
            return out


    def forward(self, x):
        return self.convolve(x, self.kernel)        

    def backward(self, x, loss_grad, params):
        if len(self.kernel.shape) == 3:
            flipped_kernel = np.flip(self.kernel, axis=(1,2))
            flipped_loss_grad = np.flip(loss_grad, axis=(1,2))
        else:
            flipped_kernel = np.flip(self.kernel, axis=(2,3))
            flipped_loss_grad = np.flip(loss_grad, axis=(2,3))
        i_grad = self.convolve(loss_grad, flipped_kernel, mode='backward_i')
        k_grad = self.convolve(x, flipped_loss_grad, mode='backward_k')

        self.vw = params['beta1']*self.vw + (1-params['beta1'])*k_grad
        self.sw = params['beta2']*self.sw + (1-params['beta2'])*(k_grad**2)

        self.kernel += params['lr']*self.vw/np.sqrt(self.sw+params['eps'])

        return i_grad


    def get_save_data(self):
        return {'type':'Convolutional2D', 'shape':np.array(self.layer_shape).tolist(), 'data':[self.kernel_size, self.feature_maps, self.kernel.tolist()]}

    def load_saved_data(data):
        obj = Convolutional2D(data['data'][0], data['data'][1])
        obj.layer_shape = data['shape']
        obj.kernel = np.array(data['data'][2])
        obj.init_adam_params(obj.kernel, obj.b)
        return obj

【问题讨论】:

  • 能分享一下这个功能吗?也许您可以阅读here,了解您的功能与 Keras 使用的功能有何不同。
  • 请分享您的代码。
  • @RishabhSahrawat 是的,我确实检查过了..它从 cpp 中的 CNTK 库中调用了一个卷积函数..所以我无法追踪到该函数..(github.com/microsoft/CNTK src 是好吧,但是通过 CPP 对我来说似乎很难)
  • 知道这是在 CPP 中,我也开始认为,这仅仅是它更快的原因,但仍然是速度太快了,再说一次.. 甚至 numpy 也必须有它的基础CPP对吗? :(

标签: keras convolution


【解决方案1】:

我不知道 keras 使用哪种算法,但有更好的方法来进行卷积。一种“简单”的方法是利用 numpy 数组在内存中的存储方式并使用补丁提取 (https://twitter.com/MishaLaskin/status/1478500251376009220?s=20) 来避免使用 for 循环(这在 Python 中非常慢)。另一种方法是使用 Winograd 算法或其他基于快速傅立叶变换的算法。然而,这种算法非常复杂,互联网上没有太多关于它们的信息。我目前也在从头开始实施 cnn,您可以通过以下方式查看我的进度:https://github.com/Pabloo22/Deep-Learning-from-Scratch

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 2020-11-15
    • 2019-08-09
    • 1970-01-01
    相关资源
    最近更新 更多