Skip to content

Commit

Permalink
Layer and connection removing
Browse files Browse the repository at this point in the history
  • Loading branch information
Qwinpin committed Jul 24, 2019
1 parent fc8f6c9 commit 4ad932f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 25 deletions.
20 changes: 6 additions & 14 deletions neuvol/individs/individ_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class IndividBase:
# TODO: add support for different data types
# TODO: add support for different task types

def __init__(self, stage, options, task_type='classification', parents=None, freeze=None):
def __init__(self, stage, options, finisher, task_type='classification', parents=None, freeze=None):
"""Create individ randomly or with its parents
Attributes:
Expand All @@ -46,10 +46,10 @@ def __init__(self, stage, options, task_type='classification', parents=None, fre
self.options = options
self._history = []
self._name = FAKE.name().replace(' ', '_') + '_' + str(stage)
self._architecture = []
self._data_processing = None
self._training_parameters = None
self.shape_structure = None
self._architecture = None
self._finisher = finisher
self._data_processing = None or self.options.get('data_processing', None)
self._training_parameters = None or self.options.get('training_parameters', None)
self._layers_number = 0
self._result = -1.0

Expand All @@ -59,12 +59,6 @@ def __init__(self, stage, options, task_type='classification', parents=None, fre
self._random_init()
self._history.append(EVENT('Init', stage))
else:
# okay, we need some hack to avoid memory leak
self._parents[0]._parents = None
self._parents[1]._parents = None

self._task_type = parents[0].task_type
self._data_processing_type = parents[0].data_type
self._history.append(EVENT('Birth', self._stage))

def __str__(self):
Expand All @@ -80,7 +74,7 @@ def _random_init_architecture(self):
Init structure of the individ
"""
input_layer = Layer('input', options=self.options)
architecture = Structure(input_layer)
architecture = Structure(input_layer, self._finisher)

return architecture

Expand Down Expand Up @@ -215,7 +209,6 @@ def _serialize(self):
serial['training_parameters'] = self._training_parameters
serial['layers_number'] = self._layers_number
serial['result'] = self._result
serial['shape_structure'] = self.shape_structure

return serial

Expand Down Expand Up @@ -251,7 +244,6 @@ def load(cls, serial):
individ._training_parameters = serial['training_parameters']
individ._layers_number = serial['layers_number']
individ._result = serial['result']
individ.shape_structure = serial['shape_structure']

return individ

Expand Down
89 changes: 78 additions & 11 deletions neuvol/individs/structure/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
import numpy as np


# TODO: rewrite all structure
class Structure:
def __init__(self, root):
def __init__(self, root, finisher):
"""
Class of individ architecture
Growing steps is applied to the origin matrix
Expand All @@ -28,6 +27,7 @@ def __init__(self, root):
and are used for growing changes.
"""
self._matrix = None # matrix of layers connections
self._finisher = finisher
# matrix and layers indexes should be updated after each mutation and changes of the network
# to avoid excess computations updated version stored in _matrix_updated
self._matrix_mutated = None
Expand Down Expand Up @@ -131,6 +131,32 @@ def _inject_layer(self, matrix, layers_index_reverse, branchs_end, branchs_count

return matrix, layers_index_reverse, branchs_end, branchs_counter

def _remove_layer(self, matrix, layers_index_reverse, branchs_end, branchs_counter, layer_index):
before_layer_indexes = np.where(matrix[:, layer_index] == 1)[0]
after_layer_indexes = np.where(matrix[layer_index, :] == 1)[0]

if not before_layer_indexes:
# restricted operation - network head could not be removed
return matrix, layers_index_reverse, branchs_end, branchs_counter

if after_layer_indexes:
matrix[before_layer_indexes, after_layer_indexes] = 1
matrix[before_layer_indexes, layer_index] = 0
matrix[layer_index, after_layer_indexes] = 0

else:
matrix[before_layer_indexes, layer_index] = 0

branchs_end_reverse = {value: key for key, value in branchs_end.items()}
branch_to_remove = branchs_end_reverse.get(layer_index, None)
if branch_to_remove is not None:
branchs_counter = [i for i in branchs_counter if i != branch_to_remove]
del branchs_end[branch_to_remove]

del layers_index_reverse[layer_index]

return matrix, layers_index_reverse, branchs_end, branchs_counter

def _add_connection(self, matrix, before_layer_index, after_layer_index):
"""
Add connection between two layer. Does not add new layer
Expand All @@ -147,6 +173,11 @@ def _add_connection(self, matrix, before_layer_index, after_layer_index):

return matrix

def _remove_connection(self, matrix, before_layer_index, after_layer_index):
matrix[before_layer_index, after_layer_index] = 0

return matrix

def _merge_branchs(self, matrix, layers_index_reverse, branchs_end, branchs_counter, layer, branchs):
"""
Concat a set of branchs to one single layer
Expand Down Expand Up @@ -352,7 +383,30 @@ def _cyclic_check(self, matrix):

return self._cyclic_check_dict(tree)

def mutations_applier(self):
def finisher_applier(self, matrix, layers_index_reverse, branchs_end, branchs_counter):
matrix_copy = np.array(self._matrix) or matrix
layers_index_reverse_copy = dict(self._layers_index_reverse) or layers_index_reverse
branchs_end_copy = dict(self.branchs_end) or branchs_end
branchs_counter_copy = list(self.branchs_counter) or branchs_counter

# current number of branches
branchs_number = len(branchs_counter_copy)

if branchs_number > 1:
matrix_copy_tmp, layers_index_reverse_copy_tmp, branchs_end_copy_tmp, branchs_counter_copy_tmp, last_branch_index = self._merge_branchs(
matrix_copy, layers_index_reverse_copy,
branchs_end_copy, branchs_counter_copy,
self._finisher, branchs_counter_copy)

else:
matrix_copy_tmp, layers_index_reverse_copy_tmp, branchs_end_copy_tmp = self._add_layer(
matrix_copy, layers_index_reverse_copy,
branchs_end_copy, self._finisher,
branchs_counter_copy[0])

return matrix_copy_tmp, layers_index_reverse_copy_tmp, branchs_end_copy_tmp, branchs_counter_copy_tmp

def mutations_applier(self, matrix, layers_index_reverse, branchs_end, branchs_counter):
"""
Apply all mutations, which does not create cycle
Expand All @@ -364,10 +418,10 @@ def mutations_applier(self):
"""
# create copy of properties
# mutations can lead to a cycle and should be performed with additional checks
matrix_copy = np.array(self._matrix)
layers_index_reverse_copy = dict(self._layers_index_reverse)
branchs_end_copy = dict(self.branchs_end)
branchs_counter_copy = list(self.branchs_counter)
matrix_copy = np.array(self._matrix) or matrix
layers_index_reverse_copy = dict(self._layers_index_reverse) or layers_index_reverse
branchs_end_copy = dict(self.branchs_end) or branchs_end
branchs_counter_copy = list(self.branchs_counter) or branchs_counter

for mutation in self.mutations_pool:
if mutation.config.get('state', None) == 'broken':
Expand All @@ -393,10 +447,21 @@ def mutations_applier(self):
branchs_counter_copy_tmp = None

elif mutation.mutation_type == 'remove_layer':
continue
layer_index = mutation.layer

matrix_copy_tmp, layers_index_reverse_copy_tmp, branchs_end_copy_tmp, branchs_counter_copy_tmp = self._remove_layer(
matrix_copy, layers_index_reverse_copy,
branchs_end_copy, branchs_counter_copy,
layer_index)

elif mutation.mutation_type == 'remove_connection':
continue
before_layer_index = mutation.config['before_layer_index']
after_layer_index = mutation.config['after_layer_index']

matrix_copy_tmp = self._remove_connection(matrix_copy, before_layer_index, after_layer_index)
layers_index_reverse_copy_tmp = None
branchs_end_copy_tmp = None
branchs_counter_copy_tmp = None

# its should be False
if not self._cyclic_check(matrix_copy_tmp):
Expand All @@ -418,6 +483,8 @@ def _update_mutated(self):
Update architecture using new mutations
"""
matrix, layers_index_reverse, branchs_end, branchs_counter = self.mutations_applier()
matrix, layers_index_reverse, branchs_end, branchs_counter = self.finisher_applier(
matrix, layers_index_reverse, branchs_end, branchs_counter)

self._matrix_updated = True
self._matrix_mutated = matrix
Expand Down Expand Up @@ -446,7 +513,7 @@ def layers_index_reverse(self):


class StructureText(Structure):
def __init__(self, root, embedding):
def __init__(self, root, embedding, finisher):
"""
Initialize the architecture of the individual with textual data
Can used in case of pure text as the input
Expand Down Expand Up @@ -479,7 +546,7 @@ def __init__(self, root, embedding):


class StructureImage(Structure):
def __init__(self, root):
def __init__(self, root, finisher):
super().__init__(root)

self._matrix = np.zeros((1, 1))
Expand Down
5 changes: 5 additions & 0 deletions neuvol/mutation/base_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ class MutationInjectorRemoveLayer(MutationInjector):
def __init__(self, mutation_type, matrix, layers_types, config=None, layer=None):
super().__init__(mutation_type, matrix, layers_types, config=config, layer=layer)

def _choose_parameters(self, matrix, layers_types):
layer_indexes = layers_types.keys()

layer_to_remove = np.random.choice(layer_indexes, size=1)


class MutationInjectorRemoveConnection(MutationInjector):
def __init__(self, mutation_type, matrix, layers_types, config=None, layer=None):
Expand Down

0 comments on commit 4ad932f

Please sign in to comment.