Source code for deeprank.learn.modelGenerator

import ast

import numpy as np


#################################
#
#   MODEL GENERATOR
#
#################################
[docs]class NetworkGenerator(object): def __init__( self, name='_tmp_model_', fname='_tmp_model_.py', conv_layers=None, fc_layers=None): """Automatic generation of NN files. This class allows for automatic generation of python file containing the definition of torch formatted neural network. Args: name (str, optional): name of the model in the python file fname (str, optional): name of the file containing the model conv_layers (list(layers)): list of convolutional layers fc_layers (list(layers)): list of fully connected layers Example: >>> conv_layers = [] >>> conv_layers.append(conv(output_size=4,kernel_size=2,post='relu')) >>> conv_layers.append(pool(kernel_size=2)) >>> conv_layers.append(conv(input_size=4,output_size=5,kernel_size=2,post='relu')) >>> conv_layers.append(pool(kernel_size=2)) >>> >>> fc_layers = [] >>> fc_layers.append(fc(output_size=84,post='relu')) >>> fc_layers.append(fc(input_size=84,output_size=1)) >>> >>> MG = NetworkGenerator(name='test',fname='model_test.py',conv_layers=conv_layers,fc_layers=fc_layers) >>> MG.print() >>> MG.write() """ # name of the model self.name = name # filename self.fname = fname # structure of the convolutional/fc layers self.conv_layers = conv_layers or [] self.fc_layers = fc_layers or [] # dimension of the final fc layer self.final_dim = 1 # possible number of randomly generated conv/fc layers self.num_conv_layers = range(1, 11) self.num_fc_layers = range(1, 5) # possible types of conv layers self.conv_types = ['conv', 'dropout', 'pool'] # conv parameters self.conv_params = {} self.conv_params['output_size'] = range(1, 10) self.conv_params['kernel_size'] = range(2, 5) # pool parameters self.pool_params = {} self.pool_params['kernel_size'] = range(2, 5) # params of the dropout layers self.dropout_params = {} self.dropout_params['percent'] = np.linspace(0.1, 0.9, 9) # params for the automatic generation of fc layers self.fc_params = {} self.fc_params['output_size'] = [2**i for i in range(4, 11)] # types of post processing # must be in torch.nn.functional self.post_types = [None, 'relu'] ####################################### # # Routines used to write dow the # file containing the model # #######################################
[docs] def write(self): """Write the model to file.""" f = open(self.fname, 'w') self._write_import(f) self._write_definition(f) self._write_init(f) self._write_conv_output(f) self._write_forward_feature(f) self._write_forward(f) f.close()
[docs] @staticmethod # import statement def _write_import(fhandle): modules = '''import torch from torch.autograd import Variable import torch.nn as nn import torch.nn.functional as F ''' fhandle.write(modules)
# comment and such
[docs] def _write_definition(self, fhandle): ndash = 70 fhandle.write('#' * ndash + '\n#\n') fhandle.write('# Model automatically generated by modelGenerator\n') fhandle.write('#\n' + '#' * ndash + '\n\n') fhandle.write('#' + '-' * ndash + '\n') fhandle.write('# Network Structure\n') fhandle.write('#' + '-' * ndash + '\n') for ilayer, layer in enumerate(self.conv_layers): fhandle.write('%s\n' % layer.__human_readable_str__(ilayer)) for ilayer, layer in enumerate(self.fc_layers): fhandle.write('%s\n' % layer.__human_readable_str__(ilayer)) fhandle.write('#' + '-' * ndash + '\n\n')
# initialization of the model # here all the layers are defined
[docs] def _write_init(self, fhandle): fhandle.write('class ' + self.name + '(nn.Module):\n') fhandle.write('\n') fhandle.write('\tdef __init__(self,input_shape):\n') fhandle.write('\t\tsuper(%s,self).__init__()\n' % (self.name)) fhandle.write('\n') # write the conv layer for ilayer, layer in enumerate(self.conv_layers): fhandle.write('\t\t%s\n' % layer.__def_str__(ilayer)) # the size determination between the conv and fc blocks fhandle.write('\n') fhandle.write('\t\tsize = self._get_conv_output(input_shape)\n') fhandle.write('\n') # write the fc layers for ilayer, layer in enumerate(self.fc_layers): fhandle.write('\t\t%s\n' % layer.__def_str__(ilayer)) fhandle.write('\n')
[docs] @staticmethod # get the size of the output at the end of the conv block def _write_conv_output(fhandle): func = ''' \tdef _get_conv_output(self,shape): \t\tinp = Variable(torch.rand(2,*shape)) \t\tout = self._forward_features(inp) \t\treturn out.data.view(2,-1).size(1) ''' fhandle.write(func)
# forward feature conatining all the conv layers # here all the conv layers are defined
[docs] def _write_forward_feature(self, fhandle): fhandle.write('\tdef _forward_features(self,x):\n') for ilayer, layer in enumerate(self.conv_layers): fhandle.write('\t\t%s\n' % layer.__use_str__(ilayer)) fhandle.write('\t\treturn x\n') fhandle.write('\n')
# total forward pass # here _forward_features is used # and all the fc layers are used
[docs] def _write_forward(self, fhandle): fhandle.write('\tdef forward(self,x):\n') fhandle.write('\t\tx = self._forward_features(x)\n') fhandle.write('\t\tx = x.view(x.size(0),-1)\n') for ilayer, layer in enumerate(self.fc_layers): fhandle.write('\t\t%s\n' % layer.__use_str__(ilayer)) fhandle.write('\t\treturn x\n') fhandle.write('\n')
# print the definition of the network to screen
[docs] def print(self): """Print the model to screen.""" ndash = 70 print('#' + '-' * ndash) print('# Network Structure') print('#' + '-' * ndash) for ilayer, layer in enumerate(self.conv_layers): print('%s' % layer.__human_readable_str__(ilayer)) for ilayer, layer in enumerate(self.fc_layers): print('%s' % layer.__human_readable_str__(ilayer)) print('#' + '-' * ndash + '\n')
######################################### # # get a new random model # #########################################
[docs] def get_new_random_model(self): """Get a new Random Model.""" # number of conv/fc layers nconv = np.random.choice(self.num_conv_layers) nfc = np.random.choice(self.num_fc_layers) # generate the conv layers self.conv_layers = [] for ilayer in range(nconv): self._init_conv_layer_random(ilayer) # generate the fc layers self.fc_layers = [] for ilayer in range(nfc): self._init_fc_layer_random(ilayer) # fix the final dimension self.fc_layers[-1].output_size = self.final_dim
# pick a layer type
[docs] def _init_conv_layer_random(self, ilayer): # determine wih type of layer we want # first layer is a conv # we can't have 2 pool in a row if ilayer == 0: name = self.conv_types[0] # if rpevious layer is pool, next can't be pool elif self.conv_layers[ilayer - 1].__name__ == 'pool': name = np.random.choice(self.conv_types[:-1]) # else it can be anything else: name = np.random.choice(self.conv_types) # init the parms of the layer # each layer type has its own params # the output/input size matching is done automatically if name == 'conv': params = {} params['name'] = name if ilayer == 0: params['input_size'] = -1 # fixed by input shape else: for isearch in range(ilayer - 1, -1, -1): if self.conv_layers[isearch].__name__ == 'conv': params['input_size'] = self.conv_layers[isearch].output_size break params['output_size'] = np.random.choice( self.conv_params['output_size']) params['kernel_size'] = np.random.choice( self.conv_params['kernel_size']) params['post'] = np.random.choice(self.post_types) if name == 'pool': params = {} params['name'] = name params['kernel_size'] = np.random.choice( self.pool_params['kernel_size']) params['post'] = np.random.choice(self.post_types) if name == 'dropout': params = {} params['name'] = name params['percent'] = np.random.choice( self.dropout_params['percent']) # create the current layer class instance # and initialize if with the __init_from_dict__() method current_layer = ast.list_eval(params['name'])() current_layer.__init_from_dict__(params) self.conv_layers.append(current_layer)
[docs] def _init_fc_layer_random(self, ilayer): # init the parms of the layer # each layer type has its own params # the output/input size matching is done automatically name = 'fc' # so far only fc layer here params = {} params['name'] = name if ilayer == 0: params['input_size'] = -1 # fixed by the conv layers else: params['input_size'] = self.fc_layers[ilayer - 1].output_size params['output_size'] = np.random.choice(self.fc_params['output_size']) params['post'] = np.random.choice(self.post_types) current_layer = ast.list_eval(params['name'])() current_layer.__init_from_dict__(params) self.fc_layers.append(current_layer)
################################# # CNN layer #################################
[docs]class conv(object): def __init__( self, input_size=-1, output_size=None, kernel_size=None, post=None): """Wrapper around the convolutional layer. Args: input_size (int, optional): input size (default, let the generator figure it out) output_size (int, optional): output size kernel_size (int, optional): kernel size post (str, optional): post process of the data Example: >>> conv_layers.append(conv(output_size=4,kernel_size=2,post='relu')) """ self.__name__ = 'conv' self.input_size = input_size self.output_size = output_size self.kernel_size = kernel_size self.post = post def __def_str__(self, ilayer): if ilayer == 0: return 'self.convlayer_%03d = nn.Conv3d(input_shape[0],%d,kernel_size=%d)' % ( ilayer, self.output_size, self.kernel_size) else: return 'self.convlayer_%03d = nn.Conv3d(%d,%d,kernel_size=%d)' % ( ilayer, self.input_size, self.output_size, self.kernel_size) def __use_str__(self, ilayer): if self.post is None: return 'x = self.convlayer_%03d(x)' % ilayer elif isinstance(self.post, str): return 'x = F.%s(self.convlayer_%03d(x))' % (self.post, ilayer) else: print('Error with post processing of conv layer %d' % ilayer) return 'x = self.convlayer_%03d(x)' % ilayer def __get_params__(self): params = {} params['name'] = self.__name__ params['input_size'] = self.input_size params['output_size'] = self.output_size params['kernel_size'] = self.kernel_size params['post'] = self.post return params def __init_from_dict__(self, params): self.input_size = params['input_size'] self.output_size = params['output_size'] self.kernel_size = params['kernel_size'] self.post = params['post'] def __human_readable_str__(self, ilayer): return '#conv layer % 3d: conv | input % 2d output % 2d kernel % 2d post %s' % ( ilayer, self.input_size, self.output_size, self.kernel_size, self.post)
################################# # POOL layer #################################
[docs]class pool(object): def __init__(self, kernel_size=None, post=None): """Wrapper around the pool layer. Args: kernel_size (int, optional): kernel size post (str, optional): post process of the data Example: >>> conv_layers.append(pool(kernel_size=2)) """ self.__name__ = 'pool' self.kernel_size = kernel_size self.post = post def __def_str__(self, ilayer): return 'self.convlayer_%03d = nn.MaxPool3d((%d,%d,%d))' % ( ilayer, self.kernel_size, self.kernel_size, self.kernel_size) def __use_str__(self, ilayer): if self.post is None: return 'x = self.convlayer_%03d(x)' % ilayer elif isinstance(self.post, str): return 'x = F.%s(self.convlayer_%03d(x))' % (self.post, ilayer) else: print('Error with post processing of conv layer %d' % ilayer) return 'x = self.convlayer_%03d(x)' % ilayer def __get_params__(self): params = {} params['name'] = self.__name__ params['kernel_size'] = self.kernel_size params['post'] = self.post return params def __init_from_dict__(self, params): self.kernel_size = params['kernel_size'] self.post = params['post'] def __human_readable_str__(self, ilayer): return '#conv layer % 3d: pool | kernel % 2d post %s' % ( ilayer, self.kernel_size, self.post)
################################# # dropout layer #################################
[docs]class dropout(object): def __init__(self, percent=0.5): """Wrapper around the dropout layer layer. Args: percent (float): percent of dropout Example: >>> fc_layers.append(dropout(precent=0.25)) """ self.__name__ = 'dropout' self.percent = percent def __def_str__(self, ilayer): return 'self.convlayer_%03d = nn.Dropout3d(%0.1f)' % ( ilayer, self.percent) @staticmethod def __use_str__(ilayer): return 'x = self.convlayer_%03d(x)' % ilayer def __get_params__(self): params = {} params['name'] = self.__name__ params['percent'] = self.percent return params def __init_from_dict__(self, params): self.percent = params['percent'] def __human_readable_str__(self, ilayer): return '#conv layer % 3d: drop | percent %0.1f' % ( ilayer, self.percent)
################################# # fully connected layer #################################
[docs]class fc(object): def __init__(self, input_size=-1, output_size=None, post=None): """Wrapper around the fully conneceted layer. Args: input_size (int, optional): input size (default, let the generator figure it out) output_size (int, optional): output size post (str, optional): post process of the data Example: >>> fc_layers.append(fc(output_size=84,post='relu')) """ self.__name__ = 'fc' self.input_size = input_size self.output_size = output_size self.post = post def __def_str__(self, ilayer): if ilayer == 0: return 'self.fclayer_%03d = nn.Linear(size,%d)' % ( ilayer, self.output_size) else: return 'self.fclayer_%03d = nn.Linear(%d,%d)' % ( ilayer, self.input_size, self.output_size) def __use_str__(self, ilayer): if self.post is None: return 'x = self.fclayer_%03d(x)' % ilayer elif isinstance(self.post, str): return 'x = F.%s(self.fclayer_%03d(x))' % (self.post, ilayer) else: print('Error with post processing of conv layer %d' % ilayer) return 'x = self.fclayer_%03d(x)' % ilayer def __get_params__(self): params = {} params['name'] = self.__name__ params['input_size'] = self.input_size params['output_size'] = self.output_size params['post'] = self.post return params def __init_from_dict__(self, params): self.input_size = params['input_size'] self.output_size = params['output_size'] self.post = params['post'] def __human_readable_str__(self, ilayer): return '#fc layer % 3d: fc | input % 2d output % 2d post %s' % ( ilayer, self.input_size, self.output_size, self.post)
if __name__ == '__main__': conv_layers = [] conv_layers.append(conv(output_size=4, kernel_size=2, post='relu')) conv_layers.append(pool(kernel_size=2)) conv_layers.append( conv( input_size=4, output_size=5, kernel_size=2, post='relu')) conv_layers.append(pool(kernel_size=2)) fc_layers = [] fc_layers.append(fc(output_size=84, post='relu')) fc_layers.append(fc(input_size=84, output_size=1)) MG = NetworkGenerator( name='test', fname='model_test.py', conv_layers=conv_layers, fc_layers=fc_layers) MG.print() MG.write() # MGR = NetworkGenerator(name='cnnrand',fname='modelrandom.py') # MGR.get_new_random_model() # MGR.print() # MGR.write()