1'''Copyright (c) 2017-2018 Mozilla 2 3 Redistribution and use in source and binary forms, with or without 4 modification, are permitted provided that the following conditions 5 are met: 6 7 - Redistributions of source code must retain the above copyright 8 notice, this list of conditions and the following disclaimer. 9 10 - Redistributions in binary form must reproduce the above copyright 11 notice, this list of conditions and the following disclaimer in the 12 documentation and/or other materials provided with the distribution. 13 14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 18 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25''' 26 27""" helper functions for dumping some Keras layers to C files """ 28 29import numpy as np 30 31 32def printVector(f, vector, name, dtype='float', dotp=False, static=True): 33 """ prints vector as one-dimensional C array """ 34 if dotp: 35 vector = vector.reshape((vector.shape[0]//4, 4, vector.shape[1]//8, 8)) 36 vector = vector.transpose((2, 0, 3, 1)) 37 v = np.reshape(vector, (-1)) 38 if static: 39 f.write('static const {} {}[{}] = {{\n '.format(dtype, name, len(v))) 40 else: 41 f.write('const {} {}[{}] = {{\n '.format(dtype, name, len(v))) 42 for i in range(0, len(v)): 43 f.write('{}'.format(v[i])) 44 if (i!=len(v)-1): 45 f.write(',') 46 else: 47 break; 48 if (i%8==7): 49 f.write("\n ") 50 else: 51 f.write(" ") 52 f.write('\n};\n\n') 53 return vector 54 55def printSparseVector(f, A, name, have_diag=True): 56 N = A.shape[0] 57 M = A.shape[1] 58 W = np.zeros((0,), dtype='int') 59 W0 = np.zeros((0,)) 60 if have_diag: 61 diag = np.concatenate([np.diag(A[:,:N]), np.diag(A[:,N:2*N]), np.diag(A[:,2*N:])]) 62 A[:,:N] = A[:,:N] - np.diag(np.diag(A[:,:N])) 63 A[:,N:2*N] = A[:,N:2*N] - np.diag(np.diag(A[:,N:2*N])) 64 A[:,2*N:] = A[:,2*N:] - np.diag(np.diag(A[:,2*N:])) 65 printVector(f, diag, name + '_diag') 66 AQ = np.minimum(127, np.maximum(-128, np.round(A*128))).astype('int') 67 idx = np.zeros((0,), dtype='int') 68 for i in range(M//8): 69 pos = idx.shape[0] 70 idx = np.append(idx, -1) 71 nb_nonzero = 0 72 for j in range(N//4): 73 block = A[j*4:(j+1)*4, i*8:(i+1)*8] 74 qblock = AQ[j*4:(j+1)*4, i*8:(i+1)*8] 75 if np.sum(np.abs(block)) > 1e-10: 76 nb_nonzero = nb_nonzero + 1 77 idx = np.append(idx, j*4) 78 vblock = qblock.transpose((1,0)).reshape((-1,)) 79 W0 = np.concatenate([W0, block.reshape((-1,))]) 80 W = np.concatenate([W, vblock]) 81 idx[pos] = nb_nonzero 82 f.write('#ifdef DOT_PROD\n') 83 printVector(f, W, name, dtype='qweight') 84 f.write('#else /*DOT_PROD*/\n') 85 printVector(f, W0, name, dtype='qweight') 86 f.write('#endif /*DOT_PROD*/\n') 87 printVector(f, idx, name + '_idx', dtype='int') 88 return AQ 89 90def dump_sparse_gru(self, f, hf): 91 name = 'sparse_' + self.name 92 print("printing layer " + name + " of type sparse " + self.__class__.__name__) 93 weights = self.get_weights() 94 qweights = printSparseVector(f, weights[1], name + '_recurrent_weights') 95 printVector(f, weights[-1], name + '_bias') 96 subias = weights[-1].copy() 97 subias[1,:] = subias[1,:] - np.sum(qweights*(1./128),axis=0) 98 printVector(f, subias, name + '_subias') 99 if hasattr(self, 'activation'): 100 activation = self.activation.__name__.upper() 101 else: 102 activation = 'TANH' 103 if hasattr(self, 'reset_after') and not self.reset_after: 104 reset_after = 0 105 else: 106 reset_after = 1 107 neurons = weights[0].shape[1]//3 108 max_rnn_neurons = neurons 109 f.write('const SparseGRULayer {} = {{\n {}_bias,\n {}_subias,\n {}_recurrent_weights_diag,\n {}_recurrent_weights,\n {}_recurrent_weights_idx,\n {}, ACTIVATION_{}, {}\n}};\n\n' 110 .format(name, name, name, name, name, name, weights[0].shape[1]//3, activation, reset_after)) 111 hf.write('#define {}_OUT_SIZE {}\n'.format(name.upper(), weights[0].shape[1]//3)) 112 hf.write('#define {}_STATE_SIZE {}\n'.format(name.upper(), weights[0].shape[1]//3)) 113 hf.write('extern const SparseGRULayer {};\n\n'.format(name)); 114 return max_rnn_neurons 115 116def dump_gru_layer(self, f, hf, dotp=False, sparse=False): 117 name = self.name 118 print("printing layer " + name + " of type " + self.__class__.__name__) 119 weights = self.get_weights() 120 if sparse: 121 qweight = printSparseVector(f, weights[0], name + '_weights', have_diag=False) 122 else: 123 qweight = printVector(f, weights[0], name + '_weights') 124 125 if dotp: 126 f.write('#ifdef DOT_PROD\n') 127 qweight2 = np.clip(np.round(128.*weights[1]).astype('int'), -128, 127) 128 printVector(f, qweight2, name + '_recurrent_weights', dotp=True, dtype='qweight') 129 f.write('#else /*DOT_PROD*/\n') 130 else: 131 qweight2 = weights[1] 132 133 printVector(f, weights[1], name + '_recurrent_weights') 134 if dotp: 135 f.write('#endif /*DOT_PROD*/\n') 136 137 printVector(f, weights[-1], name + '_bias') 138 subias = weights[-1].copy() 139 subias[0,:] = subias[0,:] - np.sum(qweight*(1./128.),axis=0) 140 subias[1,:] = subias[1,:] - np.sum(qweight2*(1./128.),axis=0) 141 printVector(f, subias, name + '_subias') 142 if hasattr(self, 'activation'): 143 activation = self.activation.__name__.upper() 144 else: 145 activation = 'TANH' 146 if hasattr(self, 'reset_after') and not self.reset_after: 147 reset_after = 0 148 else: 149 reset_after = 1 150 neurons = weights[0].shape[1]//3 151 max_rnn_neurons = neurons 152 f.write('const GRULayer {} = {{\n {}_bias,\n {}_subias,\n {}_weights,\n {},\n {}_recurrent_weights,\n {}, {}, ACTIVATION_{}, {}\n}};\n\n' 153 .format(name, name, name, name, name + "_weights_idx" if sparse else "NULL", name, weights[0].shape[0], weights[0].shape[1]//3, activation, reset_after)) 154 hf.write('#define {}_OUT_SIZE {}\n'.format(name.upper(), weights[0].shape[1]//3)) 155 hf.write('#define {}_STATE_SIZE {}\n'.format(name.upper(), weights[0].shape[1]//3)) 156 hf.write('extern const GRULayer {};\n\n'.format(name)); 157 return max_rnn_neurons 158 159def dump_dense_layer_impl(name, weights, bias, activation, f, hf): 160 printVector(f, weights, name + '_weights') 161 printVector(f, bias, name + '_bias') 162 f.write('const DenseLayer {} = {{\n {}_bias,\n {}_weights,\n {}, {}, ACTIVATION_{}\n}};\n\n' 163 .format(name, name, name, weights.shape[0], weights.shape[1], activation)) 164 hf.write('#define {}_OUT_SIZE {}\n'.format(name.upper(), weights.shape[1])) 165 hf.write('extern const DenseLayer {};\n\n'.format(name)); 166 167def dump_dense_layer(self, f, hf): 168 name = self.name 169 print("printing layer " + name + " of type " + self.__class__.__name__) 170 weights = self.get_weights() 171 activation = self.activation.__name__.upper() 172 dump_dense_layer_impl(name, weights[0], weights[1], activation, f, hf) 173 return False 174 175def dump_conv1d_layer(self, f, hf): 176 name = self.name 177 print("printing layer " + name + " of type " + self.__class__.__name__) 178 weights = self.get_weights() 179 printVector(f, weights[0], name + '_weights') 180 printVector(f, weights[-1], name + '_bias') 181 activation = self.activation.__name__.upper() 182 max_conv_inputs = weights[0].shape[1]*weights[0].shape[0] 183 f.write('const Conv1DLayer {} = {{\n {}_bias,\n {}_weights,\n {}, {}, {}, ACTIVATION_{}\n}};\n\n' 184 .format(name, name, name, weights[0].shape[1], weights[0].shape[0], weights[0].shape[2], activation)) 185 hf.write('#define {}_OUT_SIZE {}\n'.format(name.upper(), weights[0].shape[2])) 186 hf.write('#define {}_STATE_SIZE ({}*{})\n'.format(name.upper(), weights[0].shape[1], (weights[0].shape[0]-1))) 187 hf.write('#define {}_DELAY {}\n'.format(name.upper(), (weights[0].shape[0]-1)//2)) 188 hf.write('extern const Conv1DLayer {};\n\n'.format(name)); 189 return max_conv_inputs 190