5

Girshick, RFast-RCNN (ICCV 2015)の論文「3.1短縮型SVDの高速検出」では、SVDトリックを使用してサイズと計算時間を短縮することを提案しています完全に接続されたレイヤーの訓練を受けモデル(deploy.prototxtweights.caffemodel)を考えるとトランケートされたSVDを使用して完全に接続された( "" InnerProduct "")レイヤーを減らす方法

、どのように私は切り捨てものと完全に接続された層を置き換えるために、このトリックを使うことができますか?

答えて

7

一部線形代数背景
特異値分解(SVD)は3つの行列に任意の行列Wの分解である:UVは正規直交行列であり、Sである場合

W = U S V* 

対角線上に要素が減少する対角線。 SVDの興味深い性質の一つは、それが低ランク行列でW簡単に近似することができることである:あなたが(代わりに、対角線上のすべての要素)だけそのk有数の要素を持っているSを切り捨てるものと、その後

W_app = W S_trunc V* 

ランクk近似Wです。完全に接続された層
に近づけるためにSVDを使用し

は、私たちが完全に接続された層で

# ... 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... 

をモデルdeploy_full.prototxtがあるとさらに、我々はtrained_weights_full.caffemodel持っている - deploy_full.prototxtモデルの訓練を受けたパラメータを。 deploy_svd.protoxt

  1. コピーdeploy_full.protoxt、お好みのエディタで開きます。 Pythonで少しnet surgery

    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... 
    } 
    
  2. は、これら2つの層を有する完全に接続された層を交換し

    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') 
    

今、私たちはこれまでに、元のネットを近似trained_weights_svd.caffemodeldeploy_svd.prototxtを持っています乗算、重み付けが少なくなります。

+2

素晴らしいソリューションです! – Dale

+1

驚くべき解決策:) –

+1

@Dale私の解決策ではありません - それはRoss Girshick'sです。 – Shai

1

実際、Ross Girshickのpy-faster-rcnn repoには、SVDステップの実装が含まれています。compress_net.py

ところで、通常、精度を回復する(またはより洗練された方法で圧縮するために、圧縮モデルを微調整する必要があります(例えば、Accelerating Very Deep Convolutional Networks for Classification and Detection、Zhangらを参照)。

また、私にとってscipy.linalg.svdはnumpyのsvdよりも速く働いていました。

+0

これを見逃しやすい、参考に感謝します。 –

関連する問題