├── LICENSE ├── README.md ├── cbd ├── __init__.py ├── factorization_j.py ├── gaussian.py └── rank_a.py ├── main.py └── models └── resnet-18.prototxt /LICENSE: -------------------------------------------------------------------------------- 1 | T License 2 | 3 | Copyright (c) 2018 Zheng Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Composite Binary Decomposition Networks 2 | In this repository, we released code for Composite Binary Decomposition Networks (CBDNet). 3 | 4 | ### Contents 5 | 1. [Installation](#installation) 6 | 2. [Usage](#channel-pruning) 7 | 3. [Experiment Result](#experiment-results) 8 | 4. [Reference](#reference) 9 | 10 | ### Installation 11 | 1. Clone the repository of Caffe and compile it 12 | ```Shell 13 | git clone https://github.com/BVLC/caffe.git 14 | cd caffe 15 | # modify Makefile.config to the path of the library on your machine, please make sure the python interface is supported 16 | make -j8 17 | make pycaffe 18 | ``` 19 | 2. Clone this repository 20 | ```Shell 21 | https://github.com/wangzheng17/CBDNet.git 22 | ``` 23 | 24 | ### Usage 25 | 1. Download the original model files (.prototxt and .caffemodel) and move them to the directory of `models` 26 | 27 | 2. Command Line Usage 28 | To decompose a network, use the following command 29 | ```Shell 30 | python main.py 31 | arguments for CBDNet: 32 | -bottleneck bottleneck ratio value (Numbers like 0.2,0.3,0.4,0.5,...) 33 | -j the number of overall channel except the sign channel(overall-fix channel, post-variable channel. Numbers like 5,6,7,8,...) 34 | -model MODEL caffe prototxt file path 35 | -weight WEIGHT caffemodel file path 36 | 37 | 38 | ``` 39 | 40 | For example, suppose the Resnet-18 network is in folder `models/` and named as `resnet-18.prototxt` and `resnet-18.caffemodel`, you can decompose all layers with same number of channels: 41 | ```Shell 42 | python main.py -model models/resnet-18.prototxt -weight models/resnet-18.caffemodel -bottleneck 0.5 -j 6 43 | ``` 44 | 45 | Note: please use same prefix name for prototxt and weights file, a floder will be created in "\models\". `btn_xx_a_list.npy` will be stored for reusing, where `xx` is the bottleneck ratio. Results are saved under `j_model` sub-directory for same number of non-compressed channel setting and same number of overall channel setting. The parameter 'j' in the result indicates overall channels include the sign. 46 | Using model name compatiable with that in `line 33-43, rank_a.py`. These lines claim exclusive layers that don't require computing for specific model. 47 | 48 | ### Reference 49 | 50 | This work is based on our work *Composite Binary Decomposition Networks (AAAI2019)*[[Paper]](https://arxiv.org/pdf/1811.06668.pdf). If you think this is helpful for your research, please consider append following bibtex config in your latex file. 51 | 52 | ```Latex 53 | ``` 54 | -------------------------------------------------------------------------------- /cbd/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzheng17/CBDNet/5f47ff2b52199943ae5c7e35f7b30a49b7b986d7/cbd/__init__.py -------------------------------------------------------------------------------- /cbd/factorization_j.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from cbd.gaussian import gauss_elimi, gauss_rank, bindot, exchange_zero_rows 4 | import caffe 5 | def dist(matA, matB): 6 | newmatA = np.mod((matA - matB), 2) 7 | tmp = np.count_nonzero(newmatA) 8 | return tmp 9 | 10 | #model = "SSD" 11 | #model = "MobileNetSSD" 12 | #model = "resnet-152" 13 | #model = "DenseNet_121" 14 | #model = "resnet-18" 15 | #model = "VGG_16" 16 | 17 | def fj(prototxt, source, model, source_path, j, bottleneck, a): 18 | if model == "SSD": 19 | ext_layer = ['bn', 'scale', 'mbox', 'norm'] 20 | if model == "MobileNetSSD": 21 | ext_layer = ['bn', 'scale', 'mbox'] 22 | if model == "resnet-152": 23 | ext_layer = ['bn', 'scale', 'fc1000'] 24 | if model == "resnet-18": 25 | ext_layer = ['bn', 'scale'] 26 | elif model == "VGG_16": 27 | ext_layer = [] 28 | elif model == "DenseNet_121": 29 | ext_layer = ['bn', 'scale'] 30 | prototxt = os.path.join(source_path, model+".prototxt") 31 | source = os.path.join(source_path, model+".caffemodel") 32 | qtarget = os.path.join(source_path, "j_model/", model + "_j_" + str(j+1) + "_btn_" + str(bottleneck) + ".caffemodel") 33 | caffe.set_mode_cpu() 34 | net = caffe.Net(prototxt, source, caffe.TEST) 35 | layers = net.params.keys() 36 | n = 0 37 | all_d = 0 38 | all_d2 = 0 39 | bpl = [] 40 | num_bin_param = 0. 41 | num_param = 0. 42 | over_size = 0. 43 | J = j 44 | a_s_arr = [] 45 | for idx, layer in enumerate(layers): 46 | print (layer) 47 | if all([not ext_layer[e] in layer for e in range(len(ext_layer))]): 48 | w = net.params[layer][0].data 49 | wMax = np.max(np.abs(w)) 50 | r = w/wMax # normalize 51 | sign = np.sign(r) 52 | r = np.absolute(r) 53 | q = r.copy() 54 | if (model=='VGG_16' or model=='resnet-18') and 'fc' in layer: 55 | height = w.shape[0] 56 | width = w.shape[1] 57 | else: 58 | height = w.shape[0] * w.shape[2] 59 | width = w.shape[1] * w.shape[3] 60 | num_param += height*width 61 | a_s = np.floor(np.log2(np.max(r/a[n]))) 62 | print (a_s) 63 | a_s_arr.append(a_s) 64 | a_i = a_s-J+1 65 | D = 2**(a_i-1)/(1-2**(a_i-1)) * a[n] 66 | ### code improve, better approximate a_s with D 67 | #a_s = np.floor(np.log2(np.max((r+D)/(a[n]+D)))) 68 | #a_i = a_s-J+1 69 | ### 70 | print (n, w.shape, np.max(np.max((r+D)/(a[n]+D))), a[n]) 71 | num_bin_param_lay = 0. 72 | a_s = int(a_s) 73 | for i in range(a_s, a_s-J, -1): 74 | r_idx = r > 2**(i)*(a[n]+D)-D 75 | print ("==========") 76 | if i>=0: 77 | # factorization 78 | r_tmp_idx = r_idx.reshape([height,width]).copy() 79 | up, invert, trans = gauss_elimi(r_tmp_idx.copy()) 80 | r_sum = np.sum(up,axis = 1) 81 | nonzero_row = np.where(r_sum != 0)[0] 82 | zero_row = np.where(r_sum == 0)[0] 83 | rr = len(nonzero_row) 84 | num_bin_param_lay += rr*(height+width) 85 | print ("up", up.shape, min(up.shape)*bottleneck, " -> ", rr) 86 | print ("n, i, btn, o_size, c_size") 87 | print (n, i, rr, height*width, rr*(height+width)) 88 | """ 89 | ## following codes verifies A = BC 90 | # A=BC 91 | exchange_zero_rows(up, invert) # make first rr rows all non-zero 92 | C = up[:rr, :] 93 | tmpI = np.zeros([up.shape[0], rr]) 94 | for ri in range(rr): 95 | tmpI[ri,ri]=1 96 | B = bindot(invert, tmpI) 97 | r_new_idx = bindot(B, C) 98 | if trans: 99 | r_new_idx = r_new_idx.T 100 | print 'dist ', dist(r_idx, r_new_idx.reshape(r_idx.shape)) 101 | """ 102 | r = r - (r_idx)*((a[n]+D)*2**i) 103 | if num_bin_param_lay > height*width: # do not compress 104 | over_size += 1 105 | num_bin_param += height*width*(J+1) # non-compressed value and sign 106 | delta = 2**(-J-1)/(1-2**(-J-1)) # 107 | q = np.round(q/delta)*delta 108 | print ("oversize: ", num_bin_param_lay, height*width) 109 | 110 | else: # compress 111 | num_bin_param += height*width*(abs(a_i)+1) # non-compressed value and sign 112 | num_bin_param += num_bin_param_lay # compressed part 113 | print ("test_w", np.max(r), np.min(r), D) 114 | q = q - r 115 | q = sign * q 116 | q = q*wMax 117 | np.copyto(net.params[layer][0].data,q) 118 | n += 1 119 | net.save(qtarget) 120 | print (a) 121 | print ('btn', bottleneck, 'a_i', a_i, 'J', J+1) 122 | print ("Originial size:", num_param*32/8/1024/1024, "MB") 123 | print ("Compressed size:", num_bin_param/8/1024/1024, "MB") 124 | print ("bit-rate", num_bin_param/num_param) 125 | print ("Oversize", over_size, "/", n) 126 | with open(os.path.join(source_path, 'j_model/', model+'_result_btn_'+str(bottleneck)+'_j_'+str(J+1)+'.txt'), 'w') as f: 127 | f.write("Originial size: "+str(num_param*32/8/1024/1024)+"MB"+'\n') 128 | f.write("Compressed size:"+str(num_bin_param/8/1024/1024)+"MB"+'\n') 129 | f.write("bit-rate: "+str(num_bin_param/num_param)+'\n') 130 | f.write("Oversize: "+str(over_size)+"/"+str(n)+'\n') 131 | f.write("Model path: "+qtarget+'\n') 132 | f.write("Compressed Channel: "+str(a_s_arr)+'\n') 133 | -------------------------------------------------------------------------------- /cbd/gaussian.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def dist(matA, matB): 4 | newmatA = np.mod((matA + matB), 2) 5 | tmp = np.sum(newmatA) 6 | return tmp 7 | 8 | def pivot_matrix(M, lm, j): 9 | m = max(M.shape[0], M.shape[1]) 10 | n = min(M.shape[0], M.shape[1]) 11 | idm = np.identity(m) 12 | row = max(range(j, m), key=lambda i: M[i][j]) 13 | exchange = False 14 | if j != row: 15 | exchange = True 16 | tmp = M[row].copy() 17 | M[row] = M[j] 18 | M[j] = tmp 19 | tmp = lm[:,row].copy() 20 | lm[:, row] = lm[:, j] 21 | lm[:, j] = tmp 22 | return exchange 23 | 24 | def exchange_zero_rows(M, lm): 25 | r_sum = np.sum(M, axis=1) 26 | zero_row = np.where(r_sum==0)[0] 27 | non_zero_row = np.where(r_sum!=0)[0] 28 | non_zero_row = np.flip(non_zero_row, axis=0) 29 | for r in non_zero_row: 30 | if len(zero_row)>0 and r > zero_row[0]: 31 | j = zero_row[0] 32 | tmp = M[r].copy() 33 | M[r] = M[j] 34 | M[j] = tmp 35 | tmp = lm[:,r].copy() 36 | lm[:, r] = lm[:, j] 37 | lm[:, j] = tmp 38 | zero_row=np.delete(zero_row,0) 39 | 40 | def row_trans_matrix(h, i_idx, j): 41 | idm = np.identity(h) 42 | invert = np.identity(h) 43 | for i in i_idx: 44 | idm[i, j] = -1 45 | invert[i, j] = 1 46 | return idm, invert 47 | 48 | def bindot(m1,m2): 49 | return np.mod(np.dot(m1, m2), 2) 50 | 51 | def gauss_elimi(M): 52 | M = M.astype(np.int8) 53 | h = M.shape[0] 54 | w = M.shape[1] 55 | transpose = False 56 | if h < w: 57 | M = M.T 58 | transpose = True 59 | h = M.shape[0] 60 | w = M.shape[1] 61 | m = M.copy() 62 | lm = np.identity(h) 63 | mark = 0 64 | for j in range(w): 65 | exchange = pivot_matrix(M, lm,j) 66 | if exchange: 67 | pass 68 | row_idx = np.where(M[:,j]==1)[0] 69 | if row_idx.shape[0] > 1 and j in row_idx: 70 | p_idx = np.where(row_idx==j)[0][0] 71 | row_idx = row_idx[p_idx+1:] 72 | M[row_idx,:] = np.mod(M[row_idx,:]-M[j,:], 2) 73 | for ri in row_idx: 74 | lm[:,j] = np.mod(lm[:,ri]+lm[:,j], 2) 75 | return M, lm, transpose 76 | 77 | def gauss_rank(M): 78 | up, inv, trans = gauss_elimi(M) 79 | r_sum = np.sum(up, axis=1) 80 | return np.sum(r_sum!=0) 81 | 82 | -------------------------------------------------------------------------------- /cbd/rank_a.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | #import matplotlib.pyplot as plt 4 | from numpy.linalg import matrix_rank 5 | from cbd.gaussian import gauss_rank 6 | import caffe 7 | 8 | def binrank(aa): 9 | aa_height = aa.shape[0] 10 | aa_width = aa.shape[1] 11 | mark = np.zeros(aa_height) 12 | 13 | for i in range(0,aa_width): 14 | indexj = np.where(aa[:,i] == 1) 15 | if (indexj[0].shape[0] != 0):# if some elements equals 1 16 | j = indexj[0][0] 17 | mark[j] = 1 18 | for x in range(0,aa_width): 19 | if ((aa[j,x] == 1) and (i != x)): 20 | aa[:,x] = np.mod(aa[:,x] + aa[:,i],2) 21 | return np.sum(mark) 22 | 23 | #model = "SSD" 24 | #model = "MobileNetSSD" 25 | #model = "resnet-152" 26 | #model = "DenseNet_121" 27 | #model = "resnet-18" 28 | #model = "VGG_16" 29 | #bottleneck = 0.5 30 | 31 | def compute_a(prototxt, source, model, source_path, bottleneck): 32 | if model == "SSD": 33 | ext_layer = ['bn', 'scale', 'mbox', 'norm'] 34 | if model == "MobileNetSSD": 35 | ext_layer = ['bn', 'scale', 'mbox'] 36 | if model == "resnet-152": 37 | ext_layer = ['bn', 'scale', 'fc1000'] 38 | if model == "resnet-18": 39 | ext_layer = ['bn', 'scale'] 40 | elif model == "VGG_16": 41 | ext_layer = [] 42 | elif model == "DenseNet_121": 43 | ext_layer = ['bn', 'scale'] 44 | else: 45 | raise Exception('Please use model name compatiable with that in line 33-43, rank_a.py') 46 | prototxt = os.path.join(source_path, model+".prototxt") 47 | source = os.path.join(source_path, model+".caffemodel") 48 | 49 | #caffe.set_mode_cpu() 50 | net = caffe.Net(prototxt, source, caffe.TEST) 51 | layers = net.params.keys() 52 | 53 | detail = False 54 | a = [] 55 | n = 0 56 | for idx, layer in enumerate(layers): 57 | if all([not ext_layer[e] in layer for e in range(len(ext_layer))]): 58 | 59 | w = net.params[layer][0].data 60 | if model=='MobileNetSSD' and w.shape[-1] == 1 and w.shape[-2] == 1: 61 | continue 62 | print (w.shape, layer) 63 | n+=1 64 | #continue 65 | wMax = np.max(np.abs(w)) 66 | r = w/wMax # normalize 67 | if (model=='VGG_16' or model=='resnet-18') and 'fc' in layer: 68 | height = w.shape[0] 69 | width = w.shape[1] 70 | else: 71 | height = w.shape[0] * w.shape[2] 72 | width = w.shape[1] * w.shape[3] 73 | 74 | bound = 0 75 | maxlength = 0 76 | minlength = 0 77 | if height < width: 78 | maxlength = width 79 | minlength = height 80 | else: 81 | maxlength = height 82 | minlength = width 83 | bound = int(minlength * bottleneck) 84 | if bound == 0: 85 | raise 86 | print (bound) 87 | w_shape = height*width 88 | w_one = np.reshape(r, height * width) 89 | w_copy = np.abs(np.reshape(r,[height, width])) 90 | w_one = np.absolute(w_one) 91 | w_sort = np.sort(w_one) 92 | w_sort = w_sort[::-1] 93 | r_tmp = np.reshape(np.abs(r), (height, width)) 94 | #binary search 95 | min = 0 96 | max = int((minlength*maxlength) * bottleneck) 97 | #max = int((bound-1)*(bound-1)) 98 | print (max) 99 | r_tmp_idx = r_tmp > w_sort[max] 100 | rank = gauss_rank(r_tmp_idx) 101 | if rank > bound: 102 | while True: 103 | center = int((min + max)/2) 104 | r_tmp_idx = r_tmp > w_sort[center] 105 | rank = gauss_rank(r_tmp_idx) 106 | print (min, max, center, rank) 107 | if center != 0 and rank == 0: 108 | min += 1 109 | continue 110 | if max <= min: 111 | print ('min>=max, rank, bound, value') 112 | print (min, max, rank, bound, w_sort[center]) 113 | a.append(w_sort[center]) 114 | break 115 | if rank > bound: 116 | max = center - 1 117 | elif rank < bound: 118 | min = center + 1 119 | elif rank == bound: 120 | #my_rank = gauss_rank(w_copy>w_sort[center]) 121 | #print ("my_rank", my_rank, w_sort[center]) 122 | print ('-----id, rank, value-----') 123 | print (center, rank, w_sort[center]) 124 | a.append(w_sort[center]) 125 | if detail: 126 | print ('===============>') 127 | for i in range(0, 20): 128 | r_tmp_idx = r_tmp > w_sort[center+i] 129 | rank = gauss_rank(r_tmp_idx) 130 | print ('id, rank, value',center+i, rank, w_sort[center+i]) 131 | break 132 | else: 133 | print ('good matrix') 134 | print (rank, w_sort[max]) 135 | a.append(w_sort[max]) 136 | a_tmp = np.asarray(a) 137 | with open(os.path.join(source_path, 'btn_'+str(bottleneck)+'_a_list.npy'), 'wb') as f: 138 | np.save(f, a_tmp) 139 | print ('btn', bottleneck) 140 | return a_tmp 141 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import sys 4 | import numpy as np 5 | from cbd.rank_a import compute_a 6 | from cbd.factorization_j import fj 7 | 8 | 9 | def get_a_list(pt, weight, model_name, model_dir): 10 | a = np.array([]) 11 | btn = CBD_param['bottleneck_ratio'] 12 | a_file_name = os.path.join(model_dir,'btn_'+str(btn)+'_a_list.npy') 13 | if os.path.isfile(a_file_name): 14 | print ("a_list exists when bottleneck is"+str(btn)) 15 | with open(a_file_name, 'rb') as f: 16 | a = np.load(f) 17 | else: 18 | print ("Computing a...") 19 | a = compute_a(prototxt=pt, source=weight, model=model_name, source_path=model_dir, bottleneck=btn) 20 | return a 21 | 22 | def factorization(a_list, pt, weight, model_name, model_dir): 23 | j = CBD_param['j'] 24 | btn = CBD_param['bottleneck_ratio'] 25 | fj(pt, weight, model_name, model_dir, j, btn, a_list) 26 | return 27 | 28 | def cb_decomp(pt, weight, CBD_param): 29 | model_name = pt.split('/')[-1].split('.')[0] 30 | model_name1 = weight.split('/')[-1].split('.')[0] 31 | assert model_name == model_name1, "Please name prototxt and weight file by the same way." 32 | model_path='models/' 33 | model_dir = model_path+model_name 34 | j_dir = os.path.join(model_dir, 'j_model') 35 | for d in [model_dir, j_dir]: 36 | if not os.path.exists(d): 37 | os.makedirs(d) 38 | a_list = get_a_list(pt=pt, weight=weight, model_name=model_name, model_dir=model_dir) 39 | result = factorization(a_list=a_list, pt=pt, weight=weight, model_name=model_name, model_dir=model_dir) 40 | 41 | def parse_args(): 42 | parser = argparse.ArgumentParser("decouple CNN") 43 | # args for CBDNet 44 | parser.add_argument('-bottleneck', dest='btn', help='bottleneck ratio', default=None, type=float) 45 | parser.add_argument('-j', dest='j', help='total number of non-compressed binary bits except the sign channel', default=None, type=int) 46 | parser.add_argument('-model', dest='model', help='caffe prototxt file path', default=None, type=str) 47 | parser.add_argument('-weight', dest='weight', help='caffemodel file path', default=None, type=str) 48 | 49 | args = parser.parse_args() 50 | 51 | return args 52 | 53 | if __name__ == '__main__': 54 | caffe_path = '/home/path-to-caffe/' 55 | sys.path.insert(0, caffe_path+"python") 56 | 57 | args = parse_args() 58 | CBD_param = edict() 59 | CBD_param['bottleneck_ratio'] = args.btn 60 | CBD_param['j'] = args.j 61 | cb_decomp(pt=args.model,weight=args.weight,CBD_param=CBD_param) 62 | -------------------------------------------------------------------------------- /models/resnet-18.prototxt: -------------------------------------------------------------------------------- 1 | name: "ResNet-18" 2 | layer { 3 | name: "data" 4 | type: "Data" 5 | top: "data" 6 | top: "label" 7 | include { 8 | phase: TEST 9 | } 10 | transform_param { 11 | mirror: false 12 | crop_size: 224 13 | mean_value: 103.94 14 | mean_value: 116.78 15 | mean_value: 123.68 16 | } 17 | data_param { 18 | source: "/data/disk1/jli59/wangzheng/CBDNet/ilsvrc12_val_lmdb" 19 | backend: LMDB 20 | batch_size: 50 21 | } 22 | } 23 | 24 | layer { 25 | bottom: "data" 26 | top: "conv1" 27 | name: "conv1" 28 | type: "Convolution" 29 | convolution_param { 30 | num_output: 64 31 | kernel_size: 7 32 | pad: 3 33 | stride: 2 34 | weight_filler { 35 | type: "msra" 36 | } 37 | bias_term: false 38 | 39 | } 40 | } 41 | 42 | layer { 43 | bottom: "conv1" 44 | top: "conv1" 45 | name: "bn_conv1" 46 | type: "BatchNorm" 47 | batch_norm_param { 48 | use_global_stats: false 49 | } 50 | } 51 | 52 | layer { 53 | bottom: "conv1" 54 | top: "conv1" 55 | name: "scale_conv1" 56 | type: "Scale" 57 | scale_param { 58 | bias_term: true 59 | } 60 | } 61 | 62 | layer { 63 | bottom: "conv1" 64 | top: "conv1" 65 | name: "conv1_relu" 66 | type: "ReLU" 67 | } 68 | 69 | layer { 70 | bottom: "conv1" 71 | top: "pool1" 72 | name: "pool1" 73 | type: "Pooling" 74 | pooling_param { 75 | kernel_size: 3 76 | stride: 2 77 | pool: MAX 78 | } 79 | } 80 | 81 | layer { 82 | bottom: "pool1" 83 | top: "res2a_branch1" 84 | name: "res2a_branch1" 85 | type: "Convolution" 86 | convolution_param { 87 | num_output: 64 88 | kernel_size: 1 89 | pad: 0 90 | stride: 1 91 | weight_filler { 92 | type: "msra" 93 | } 94 | bias_term: false 95 | 96 | } 97 | } 98 | 99 | layer { 100 | bottom: "res2a_branch1" 101 | top: "res2a_branch1" 102 | name: "bn2a_branch1" 103 | type: "BatchNorm" 104 | batch_norm_param { 105 | use_global_stats: false 106 | } 107 | } 108 | 109 | layer { 110 | bottom: "res2a_branch1" 111 | top: "res2a_branch1" 112 | name: "scale2a_branch1" 113 | type: "Scale" 114 | scale_param { 115 | bias_term: true 116 | } 117 | } 118 | 119 | layer { 120 | bottom: "pool1" 121 | top: "res2a_branch2a" 122 | name: "res2a_branch2a" 123 | type: "Convolution" 124 | convolution_param { 125 | num_output: 64 126 | kernel_size: 3 127 | pad: 1 128 | stride: 1 129 | weight_filler { 130 | type: "msra" 131 | } 132 | bias_term: false 133 | 134 | } 135 | } 136 | 137 | layer { 138 | bottom: "res2a_branch2a" 139 | top: "res2a_branch2a" 140 | name: "bn2a_branch2a" 141 | type: "BatchNorm" 142 | batch_norm_param { 143 | use_global_stats: false 144 | } 145 | } 146 | 147 | layer { 148 | bottom: "res2a_branch2a" 149 | top: "res2a_branch2a" 150 | name: "scale2a_branch2a" 151 | type: "Scale" 152 | scale_param { 153 | bias_term: true 154 | } 155 | } 156 | 157 | layer { 158 | bottom: "res2a_branch2a" 159 | top: "res2a_branch2a" 160 | name: "res2a_branch2a_relu" 161 | type: "ReLU" 162 | } 163 | 164 | layer { 165 | bottom: "res2a_branch2a" 166 | top: "res2a_branch2b" 167 | name: "res2a_branch2b" 168 | type: "Convolution" 169 | convolution_param { 170 | num_output: 64 171 | kernel_size: 3 172 | pad: 1 173 | stride: 1 174 | weight_filler { 175 | type: "msra" 176 | } 177 | bias_term: false 178 | 179 | } 180 | } 181 | 182 | layer { 183 | bottom: "res2a_branch2b" 184 | top: "res2a_branch2b" 185 | name: "bn2a_branch2b" 186 | type: "BatchNorm" 187 | batch_norm_param { 188 | use_global_stats: false 189 | } 190 | } 191 | 192 | layer { 193 | bottom: "res2a_branch2b" 194 | top: "res2a_branch2b" 195 | name: "scale2a_branch2b" 196 | type: "Scale" 197 | scale_param { 198 | bias_term: true 199 | } 200 | } 201 | 202 | layer { 203 | bottom: "res2a_branch1" 204 | bottom: "res2a_branch2b" 205 | top: "res2a" 206 | name: "res2a" 207 | type: "Eltwise" 208 | eltwise_param { 209 | operation: SUM 210 | } 211 | } 212 | 213 | layer { 214 | bottom: "res2a" 215 | top: "res2a" 216 | name: "res2a_relu" 217 | type: "ReLU" 218 | } 219 | 220 | layer { 221 | bottom: "res2a" 222 | top: "res2b_branch2a" 223 | name: "res2b_branch2a" 224 | type: "Convolution" 225 | convolution_param { 226 | num_output: 64 227 | kernel_size: 3 228 | pad: 1 229 | stride: 1 230 | weight_filler { 231 | type: "msra" 232 | } 233 | bias_term: false 234 | 235 | } 236 | } 237 | 238 | layer { 239 | bottom: "res2b_branch2a" 240 | top: "res2b_branch2a" 241 | name: "bn2b_branch2a" 242 | type: "BatchNorm" 243 | batch_norm_param { 244 | use_global_stats: false 245 | } 246 | } 247 | 248 | layer { 249 | bottom: "res2b_branch2a" 250 | top: "res2b_branch2a" 251 | name: "scale2b_branch2a" 252 | type: "Scale" 253 | scale_param { 254 | bias_term: true 255 | } 256 | } 257 | 258 | layer { 259 | bottom: "res2b_branch2a" 260 | top: "res2b_branch2a" 261 | name: "res2b_branch2a_relu" 262 | type: "ReLU" 263 | } 264 | 265 | layer { 266 | bottom: "res2b_branch2a" 267 | top: "res2b_branch2b" 268 | name: "res2b_branch2b" 269 | type: "Convolution" 270 | convolution_param { 271 | num_output: 64 272 | kernel_size: 3 273 | pad: 1 274 | stride: 1 275 | weight_filler { 276 | type: "msra" 277 | } 278 | bias_term: false 279 | 280 | } 281 | } 282 | 283 | layer { 284 | bottom: "res2b_branch2b" 285 | top: "res2b_branch2b" 286 | name: "bn2b_branch2b" 287 | type: "BatchNorm" 288 | batch_norm_param { 289 | use_global_stats: false 290 | } 291 | } 292 | 293 | layer { 294 | bottom: "res2b_branch2b" 295 | top: "res2b_branch2b" 296 | name: "scale2b_branch2b" 297 | type: "Scale" 298 | scale_param { 299 | bias_term: true 300 | } 301 | } 302 | 303 | layer { 304 | bottom: "res2a" 305 | bottom: "res2b_branch2b" 306 | top: "res2b" 307 | name: "res2b" 308 | type: "Eltwise" 309 | eltwise_param { 310 | operation: SUM 311 | } 312 | } 313 | 314 | layer { 315 | bottom: "res2b" 316 | top: "res2b" 317 | name: "res2b_relu" 318 | type: "ReLU" 319 | } 320 | 321 | layer { 322 | bottom: "res2b" 323 | top: "res3a_branch1" 324 | name: "res3a_branch1" 325 | type: "Convolution" 326 | convolution_param { 327 | num_output: 128 328 | kernel_size: 1 329 | pad: 0 330 | stride: 2 331 | weight_filler { 332 | type: "msra" 333 | } 334 | bias_term: false 335 | 336 | } 337 | } 338 | 339 | layer { 340 | bottom: "res3a_branch1" 341 | top: "res3a_branch1" 342 | name: "bn3a_branch1" 343 | type: "BatchNorm" 344 | batch_norm_param { 345 | use_global_stats: false 346 | } 347 | } 348 | 349 | layer { 350 | bottom: "res3a_branch1" 351 | top: "res3a_branch1" 352 | name: "scale3a_branch1" 353 | type: "Scale" 354 | scale_param { 355 | bias_term: true 356 | } 357 | } 358 | 359 | layer { 360 | bottom: "res2b" 361 | top: "res3a_branch2a" 362 | name: "res3a_branch2a" 363 | type: "Convolution" 364 | convolution_param { 365 | num_output: 128 366 | kernel_size: 3 367 | pad: 1 368 | stride: 2 369 | weight_filler { 370 | type: "msra" 371 | } 372 | bias_term: false 373 | 374 | } 375 | } 376 | 377 | layer { 378 | bottom: "res3a_branch2a" 379 | top: "res3a_branch2a" 380 | name: "bn3a_branch2a" 381 | type: "BatchNorm" 382 | batch_norm_param { 383 | use_global_stats: false 384 | } 385 | } 386 | 387 | layer { 388 | bottom: "res3a_branch2a" 389 | top: "res3a_branch2a" 390 | name: "scale3a_branch2a" 391 | type: "Scale" 392 | scale_param { 393 | bias_term: true 394 | } 395 | } 396 | 397 | layer { 398 | bottom: "res3a_branch2a" 399 | top: "res3a_branch2a" 400 | name: "res3a_branch2a_relu" 401 | type: "ReLU" 402 | } 403 | 404 | layer { 405 | bottom: "res3a_branch2a" 406 | top: "res3a_branch2b" 407 | name: "res3a_branch2b" 408 | type: "Convolution" 409 | convolution_param { 410 | num_output: 128 411 | kernel_size: 3 412 | pad: 1 413 | stride: 1 414 | weight_filler { 415 | type: "msra" 416 | } 417 | bias_term: false 418 | 419 | } 420 | } 421 | 422 | layer { 423 | bottom: "res3a_branch2b" 424 | top: "res3a_branch2b" 425 | name: "bn3a_branch2b" 426 | type: "BatchNorm" 427 | batch_norm_param { 428 | use_global_stats: false 429 | } 430 | } 431 | 432 | layer { 433 | bottom: "res3a_branch2b" 434 | top: "res3a_branch2b" 435 | name: "scale3a_branch2b" 436 | type: "Scale" 437 | scale_param { 438 | bias_term: true 439 | } 440 | } 441 | 442 | layer { 443 | bottom: "res3a_branch1" 444 | bottom: "res3a_branch2b" 445 | top: "res3a" 446 | name: "res3a" 447 | type: "Eltwise" 448 | eltwise_param { 449 | operation: SUM 450 | } 451 | } 452 | 453 | layer { 454 | bottom: "res3a" 455 | top: "res3a" 456 | name: "res3a_relu" 457 | type: "ReLU" 458 | } 459 | 460 | layer { 461 | bottom: "res3a" 462 | top: "res3b_branch2a" 463 | name: "res3b_branch2a" 464 | type: "Convolution" 465 | convolution_param { 466 | num_output: 128 467 | kernel_size: 3 468 | pad: 1 469 | stride: 1 470 | weight_filler { 471 | type: "msra" 472 | } 473 | bias_term: false 474 | 475 | } 476 | } 477 | 478 | layer { 479 | bottom: "res3b_branch2a" 480 | top: "res3b_branch2a" 481 | name: "bn3b_branch2a" 482 | type: "BatchNorm" 483 | batch_norm_param { 484 | use_global_stats: false 485 | } 486 | } 487 | 488 | layer { 489 | bottom: "res3b_branch2a" 490 | top: "res3b_branch2a" 491 | name: "scale3b_branch2a" 492 | type: "Scale" 493 | scale_param { 494 | bias_term: true 495 | } 496 | } 497 | 498 | layer { 499 | bottom: "res3b_branch2a" 500 | top: "res3b_branch2a" 501 | name: "res3b_branch2a_relu" 502 | type: "ReLU" 503 | } 504 | 505 | layer { 506 | bottom: "res3b_branch2a" 507 | top: "res3b_branch2b" 508 | name: "res3b_branch2b" 509 | type: "Convolution" 510 | convolution_param { 511 | num_output: 128 512 | kernel_size: 3 513 | pad: 1 514 | stride: 1 515 | weight_filler { 516 | type: "msra" 517 | } 518 | bias_term: false 519 | 520 | } 521 | } 522 | 523 | layer { 524 | bottom: "res3b_branch2b" 525 | top: "res3b_branch2b" 526 | name: "bn3b_branch2b" 527 | type: "BatchNorm" 528 | batch_norm_param { 529 | use_global_stats: false 530 | } 531 | } 532 | 533 | layer { 534 | bottom: "res3b_branch2b" 535 | top: "res3b_branch2b" 536 | name: "scale3b_branch2b" 537 | type: "Scale" 538 | scale_param { 539 | bias_term: true 540 | } 541 | } 542 | 543 | layer { 544 | bottom: "res3a" 545 | bottom: "res3b_branch2b" 546 | top: "res3b" 547 | name: "res3b" 548 | type: "Eltwise" 549 | eltwise_param { 550 | operation: SUM 551 | } 552 | } 553 | 554 | layer { 555 | bottom: "res3b" 556 | top: "res3b" 557 | name: "res3b_relu" 558 | type: "ReLU" 559 | } 560 | 561 | layer { 562 | bottom: "res3b" 563 | top: "res4a_branch1" 564 | name: "res4a_branch1" 565 | type: "Convolution" 566 | convolution_param { 567 | num_output: 256 568 | kernel_size: 1 569 | pad: 0 570 | stride: 2 571 | weight_filler { 572 | type: "msra" 573 | } 574 | bias_term: false 575 | 576 | } 577 | } 578 | 579 | layer { 580 | bottom: "res4a_branch1" 581 | top: "res4a_branch1" 582 | name: "bn4a_branch1" 583 | type: "BatchNorm" 584 | batch_norm_param { 585 | use_global_stats: false 586 | } 587 | } 588 | 589 | layer { 590 | bottom: "res4a_branch1" 591 | top: "res4a_branch1" 592 | name: "scale4a_branch1" 593 | type: "Scale" 594 | scale_param { 595 | bias_term: true 596 | } 597 | } 598 | 599 | layer { 600 | bottom: "res3b" 601 | top: "res4a_branch2a" 602 | name: "res4a_branch2a" 603 | type: "Convolution" 604 | convolution_param { 605 | num_output: 256 606 | kernel_size: 3 607 | pad: 1 608 | stride: 2 609 | weight_filler { 610 | type: "msra" 611 | } 612 | bias_term: false 613 | 614 | } 615 | } 616 | 617 | layer { 618 | bottom: "res4a_branch2a" 619 | top: "res4a_branch2a" 620 | name: "bn4a_branch2a" 621 | type: "BatchNorm" 622 | batch_norm_param { 623 | use_global_stats: false 624 | } 625 | } 626 | 627 | layer { 628 | bottom: "res4a_branch2a" 629 | top: "res4a_branch2a" 630 | name: "scale4a_branch2a" 631 | type: "Scale" 632 | scale_param { 633 | bias_term: true 634 | } 635 | } 636 | 637 | layer { 638 | bottom: "res4a_branch2a" 639 | top: "res4a_branch2a" 640 | name: "res4a_branch2a_relu" 641 | type: "ReLU" 642 | } 643 | 644 | layer { 645 | bottom: "res4a_branch2a" 646 | top: "res4a_branch2b" 647 | name: "res4a_branch2b" 648 | type: "Convolution" 649 | convolution_param { 650 | num_output: 256 651 | kernel_size: 3 652 | pad: 1 653 | stride: 1 654 | weight_filler { 655 | type: "msra" 656 | } 657 | bias_term: false 658 | 659 | } 660 | } 661 | 662 | layer { 663 | bottom: "res4a_branch2b" 664 | top: "res4a_branch2b" 665 | name: "bn4a_branch2b" 666 | type: "BatchNorm" 667 | batch_norm_param { 668 | use_global_stats: false 669 | } 670 | } 671 | 672 | layer { 673 | bottom: "res4a_branch2b" 674 | top: "res4a_branch2b" 675 | name: "scale4a_branch2b" 676 | type: "Scale" 677 | scale_param { 678 | bias_term: true 679 | } 680 | } 681 | 682 | layer { 683 | bottom: "res4a_branch1" 684 | bottom: "res4a_branch2b" 685 | top: "res4a" 686 | name: "res4a" 687 | type: "Eltwise" 688 | eltwise_param { 689 | operation: SUM 690 | } 691 | } 692 | 693 | layer { 694 | bottom: "res4a" 695 | top: "res4a" 696 | name: "res4a_relu" 697 | type: "ReLU" 698 | } 699 | 700 | layer { 701 | bottom: "res4a" 702 | top: "res4b_branch2a" 703 | name: "res4b_branch2a" 704 | type: "Convolution" 705 | convolution_param { 706 | num_output: 256 707 | kernel_size: 3 708 | pad: 1 709 | stride: 1 710 | weight_filler { 711 | type: "msra" 712 | } 713 | bias_term: false 714 | 715 | } 716 | } 717 | 718 | layer { 719 | bottom: "res4b_branch2a" 720 | top: "res4b_branch2a" 721 | name: "bn4b_branch2a" 722 | type: "BatchNorm" 723 | batch_norm_param { 724 | use_global_stats: false 725 | } 726 | } 727 | 728 | layer { 729 | bottom: "res4b_branch2a" 730 | top: "res4b_branch2a" 731 | name: "scale4b_branch2a" 732 | type: "Scale" 733 | scale_param { 734 | bias_term: true 735 | } 736 | } 737 | 738 | layer { 739 | bottom: "res4b_branch2a" 740 | top: "res4b_branch2a" 741 | name: "res4b_branch2a_relu" 742 | type: "ReLU" 743 | } 744 | 745 | layer { 746 | bottom: "res4b_branch2a" 747 | top: "res4b_branch2b" 748 | name: "res4b_branch2b" 749 | type: "Convolution" 750 | convolution_param { 751 | num_output: 256 752 | kernel_size: 3 753 | pad: 1 754 | stride: 1 755 | weight_filler { 756 | type: "msra" 757 | } 758 | bias_term: false 759 | 760 | } 761 | } 762 | 763 | layer { 764 | bottom: "res4b_branch2b" 765 | top: "res4b_branch2b" 766 | name: "bn4b_branch2b" 767 | type: "BatchNorm" 768 | batch_norm_param { 769 | use_global_stats: false 770 | } 771 | } 772 | 773 | layer { 774 | bottom: "res4b_branch2b" 775 | top: "res4b_branch2b" 776 | name: "scale4b_branch2b" 777 | type: "Scale" 778 | scale_param { 779 | bias_term: true 780 | } 781 | } 782 | 783 | layer { 784 | bottom: "res4a" 785 | bottom: "res4b_branch2b" 786 | top: "res4b" 787 | name: "res4b" 788 | type: "Eltwise" 789 | eltwise_param { 790 | operation: SUM 791 | } 792 | } 793 | 794 | layer { 795 | bottom: "res4b" 796 | top: "res4b" 797 | name: "res4b_relu" 798 | type: "ReLU" 799 | } 800 | 801 | layer { 802 | bottom: "res4b" 803 | top: "res5a_branch1" 804 | name: "res5a_branch1" 805 | type: "Convolution" 806 | convolution_param { 807 | num_output: 512 808 | kernel_size: 1 809 | pad: 0 810 | stride: 2 811 | weight_filler { 812 | type: "msra" 813 | } 814 | bias_term: false 815 | 816 | } 817 | } 818 | 819 | layer { 820 | bottom: "res5a_branch1" 821 | top: "res5a_branch1" 822 | name: "bn5a_branch1" 823 | type: "BatchNorm" 824 | batch_norm_param { 825 | use_global_stats: false 826 | } 827 | } 828 | 829 | layer { 830 | bottom: "res5a_branch1" 831 | top: "res5a_branch1" 832 | name: "scale5a_branch1" 833 | type: "Scale" 834 | scale_param { 835 | bias_term: true 836 | } 837 | } 838 | 839 | layer { 840 | bottom: "res4b" 841 | top: "res5a_branch2a" 842 | name: "res5a_branch2a" 843 | type: "Convolution" 844 | convolution_param { 845 | num_output: 512 846 | kernel_size: 3 847 | pad: 1 848 | stride: 2 849 | weight_filler { 850 | type: "msra" 851 | } 852 | bias_term: false 853 | 854 | } 855 | } 856 | 857 | layer { 858 | bottom: "res5a_branch2a" 859 | top: "res5a_branch2a" 860 | name: "bn5a_branch2a" 861 | type: "BatchNorm" 862 | batch_norm_param { 863 | use_global_stats: false 864 | } 865 | } 866 | 867 | layer { 868 | bottom: "res5a_branch2a" 869 | top: "res5a_branch2a" 870 | name: "scale5a_branch2a" 871 | type: "Scale" 872 | scale_param { 873 | bias_term: true 874 | } 875 | } 876 | 877 | layer { 878 | bottom: "res5a_branch2a" 879 | top: "res5a_branch2a" 880 | name: "res5a_branch2a_relu" 881 | type: "ReLU" 882 | } 883 | 884 | layer { 885 | bottom: "res5a_branch2a" 886 | top: "res5a_branch2b" 887 | name: "res5a_branch2b" 888 | type: "Convolution" 889 | convolution_param { 890 | num_output: 512 891 | kernel_size: 3 892 | pad: 1 893 | stride: 1 894 | weight_filler { 895 | type: "msra" 896 | } 897 | bias_term: false 898 | 899 | } 900 | } 901 | 902 | layer { 903 | bottom: "res5a_branch2b" 904 | top: "res5a_branch2b" 905 | name: "bn5a_branch2b" 906 | type: "BatchNorm" 907 | batch_norm_param { 908 | use_global_stats: false 909 | } 910 | } 911 | 912 | layer { 913 | bottom: "res5a_branch2b" 914 | top: "res5a_branch2b" 915 | name: "scale5a_branch2b" 916 | type: "Scale" 917 | scale_param { 918 | bias_term: true 919 | } 920 | } 921 | 922 | layer { 923 | bottom: "res5a_branch1" 924 | bottom: "res5a_branch2b" 925 | top: "res5a" 926 | name: "res5a" 927 | type: "Eltwise" 928 | eltwise_param { 929 | operation: SUM 930 | } 931 | } 932 | 933 | layer { 934 | bottom: "res5a" 935 | top: "res5a" 936 | name: "res5a_relu" 937 | type: "ReLU" 938 | } 939 | 940 | layer { 941 | bottom: "res5a" 942 | top: "res5b_branch2a" 943 | name: "res5b_branch2a" 944 | type: "Convolution" 945 | convolution_param { 946 | num_output: 512 947 | kernel_size: 3 948 | pad: 1 949 | stride: 1 950 | weight_filler { 951 | type: "msra" 952 | } 953 | bias_term: false 954 | 955 | } 956 | } 957 | 958 | layer { 959 | bottom: "res5b_branch2a" 960 | top: "res5b_branch2a" 961 | name: "bn5b_branch2a" 962 | type: "BatchNorm" 963 | batch_norm_param { 964 | use_global_stats: false 965 | } 966 | } 967 | 968 | layer { 969 | bottom: "res5b_branch2a" 970 | top: "res5b_branch2a" 971 | name: "scale5b_branch2a" 972 | type: "Scale" 973 | scale_param { 974 | bias_term: true 975 | } 976 | } 977 | 978 | layer { 979 | bottom: "res5b_branch2a" 980 | top: "res5b_branch2a" 981 | name: "res5b_branch2a_relu" 982 | type: "ReLU" 983 | } 984 | 985 | layer { 986 | bottom: "res5b_branch2a" 987 | top: "res5b_branch2b" 988 | name: "res5b_branch2b" 989 | type: "Convolution" 990 | convolution_param { 991 | num_output: 512 992 | kernel_size: 3 993 | pad: 1 994 | stride: 1 995 | weight_filler { 996 | type: "msra" 997 | } 998 | bias_term: false 999 | 1000 | } 1001 | } 1002 | 1003 | layer { 1004 | bottom: "res5b_branch2b" 1005 | top: "res5b_branch2b" 1006 | name: "bn5b_branch2b" 1007 | type: "BatchNorm" 1008 | batch_norm_param { 1009 | use_global_stats: false 1010 | } 1011 | } 1012 | 1013 | layer { 1014 | bottom: "res5b_branch2b" 1015 | top: "res5b_branch2b" 1016 | name: "scale5b_branch2b" 1017 | type: "Scale" 1018 | scale_param { 1019 | bias_term: true 1020 | } 1021 | } 1022 | 1023 | layer { 1024 | bottom: "res5a" 1025 | bottom: "res5b_branch2b" 1026 | top: "res5b" 1027 | name: "res5b" 1028 | type: "Eltwise" 1029 | eltwise_param { 1030 | operation: SUM 1031 | } 1032 | } 1033 | 1034 | layer { 1035 | bottom: "res5b" 1036 | top: "res5b" 1037 | name: "res5b_relu" 1038 | type: "ReLU" 1039 | } 1040 | 1041 | layer { 1042 | bottom: "res5b" 1043 | top: "pool5" 1044 | name: "pool5" 1045 | type: "Pooling" 1046 | pooling_param { 1047 | kernel_size: 7 1048 | stride: 1 1049 | pool: AVE 1050 | } 1051 | } 1052 | 1053 | layer { 1054 | bottom: "pool5" 1055 | top: "fc1000" 1056 | name: "fc1000" 1057 | type: "InnerProduct" 1058 | param { 1059 | lr_mult: 1 1060 | decay_mult: 1 1061 | } 1062 | param { 1063 | lr_mult: 2 1064 | decay_mult: 1 1065 | } 1066 | inner_product_param { 1067 | num_output: 1000 1068 | weight_filler { 1069 | type: "xavier" 1070 | } 1071 | bias_filler { 1072 | type: "constant" 1073 | value: 0 1074 | } 1075 | } 1076 | } 1077 | layer { 1078 | bottom: "fc1000" 1079 | bottom: "label" 1080 | top: "acc/top-1" 1081 | name: "acc/top-1" 1082 | type: "Accuracy" 1083 | include { 1084 | phase: TEST 1085 | } 1086 | } 1087 | 1088 | layer { 1089 | bottom: "fc1000" 1090 | bottom: "label" 1091 | top: "acc/top-5" 1092 | name: "acc/top-5" 1093 | type: "Accuracy" 1094 | include { 1095 | phase: TEST 1096 | } 1097 | accuracy_param { 1098 | top_k: 5 1099 | } 1100 | } 1101 | 1102 | --------------------------------------------------------------------------------