在论文Girshick, R Fast-RCNN (ICCV 2015)的“3.1截断SVD以加快检测速度”部分中,作者建议使用SVD技巧来减少完全连接层的大小和计算时间。

给定训练有素的模型(deploy.prototxtweights.caffemodel),我如何使用此技巧用截断的图层替换完全连接的图层?

最佳答案

一些线性代数背景
奇异值分解(SVD)是将任何矩阵W分解为三个矩阵:

W = U S V*

其中UV是正交正规矩阵,而S是对角线,其对角线上的元素的数量递减。
SVD有趣的特性之一是,它可以使用较低的秩矩阵轻松近似W:假设您截断S,使其仅具有k前导元素(而不是对角线上的所有元素),则
W_app = U S_trunc V*

k的秩W近似值。

使用SVD近似完全连接的图层
假设我们有一个模型deploy_full.prototxt和一个完全连接的层
# ... some layers here
layer {
  name: "fc_orig"
  type: "InnerProduct"
  bottom: "in"
  top: "out"
  inner_product_param {
    num_output: 1000
    # more params...
  }
  # some more...
}
# more layers...

此外,我们还有trained_weights_full.caffemodel-deploy_full.prototxt模型的训练参数。
  • deploy_full.protoxt复制到deploy_svd.protoxt并在您选择的编辑器中将其打开。用以下两层替换完全连接的层:
    layer {
      name: "fc_svd_U"
      type: "InnerProduct"
      bottom: "in" # same input
      top: "svd_interim"
      inner_product_param {
        num_output: 20  # approximate with k = 20 rank matrix
        bias_term: false
        # more params...
      }
      # some more...
    }
    # NO activation layer here!
    layer {
      name: "fc_svd_V"
      type: "InnerProduct"
      bottom: "svd_interim"
      top: "out"   # same output
      inner_product_param {
        num_output: 1000  # original number of outputs
        # more params...
      }
      # some more...
    }
    
  • 在python中,有点net surgery:
    import caffe
    import numpy as np
    
    orig_net = caffe.Net('deploy_full.prototxt', 'trained_weights_full.caffemodel', caffe.TEST)
    svd_net = caffe.Net('deploy_svd.prototxt', 'trained_weights_full.caffemodel', caffe.TEST)
    # get the original weight matrix
    W = np.array( orig_net.params['fc_orig'][0].data )
    # SVD decomposition
    k = 20 # same as num_ouput of fc_svd_U
    U, s, V = np.linalg.svd(W)
    S = np.zeros((U.shape[0], k), dtype='f4')
    S[:k,:k] = s[:k]  # taking only leading k singular values
    # assign weight to svd net
    svd_net.params['fc_svd_U'][0].data[...] = np.dot(U,S)
    svd_net.params['fc_svd_V'][0].data[...] = V[:k,:]
    svd_net.params['fc_svd_V'][1].data[...] = orig_net.params['fc_orig'][1].data # same bias
    # save the new weights
    svd_net.save('trained_weights_svd.caffemodel')
    

  • 现在我们有了deploy_svd.prototxttrained_weights_svd.caffemodel,它们的乘积和权重都远小于原始网络。

    关于machine-learning - 如何使用截断的SVD减少完全连接的(`“InnerProduct”`)层,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40480827/

    10-12 19:39