def __init__(self, layer_type, name=None, **kwargs): if not is_valid_layer_name(layer_type): raise NetworkValidationError( "Invalid layer_type: '{}'".format(layer_type)) if not (name is None or is_valid_layer_name(name)): raise NetworkValidationError( "Invalid name for layer: '{}'".format(name)) super(LayerDetails, self).__init__(name or layer_type) self.layer_type = layer_type """The type this layer should have when later being instantiated.""" self.incoming = [] """A list of all incoming connections, including input/output names. Each entry of the list has the form: (incoming_layer, output_name, input_name) and the type: tuple[LayerDetails, str, str] """ self.outgoing = [] """A list of all outgoing connections, including input/output names. Each entry of the list has the form: (output_name, input_name, outgoing_layer) and the type: tuple[str, str, LayerDetails] """ self.layer_kwargs = kwargs """Dictionary of additional parameters for this layer""" self._traversing = False
def create(cls, layer_type, name=None, **kwargs): if isinstance(layer_type, six.string_types): layer_type_name = layer_type else: layer_type_name = layer_type.__name__ if not layer_type_name.endswith('LayerImpl'): raise NetworkValidationError( "{} should end with 'LayerImpl'".format(layer_type_name)) layer_type_name = layer_type_name[:-9] details = LayerDetails(layer_type_name, name=name, **kwargs) return ConstructionWrapper(details)
def validate_architecture(architecture): # schema for name, layer in architecture.items(): if not isinstance(name, string_types): raise NetworkValidationError('Non-string name {}'.format(name)) if '@type' not in layer: raise NetworkValidationError( 'Missing @type for "{}"'.format(name)) if not isinstance(layer['@type'], string_types): raise NetworkValidationError('Invalid @type for "{}": {}'.format( name, type(layer['@type']))) if '@outgoing_connections' in layer and not isinstance( layer['@outgoing_connections'], (list, tuple, dict)): raise NetworkValidationError( 'Invalid @outgoing_connections for "{}"'.format(name)) # layer naming for name in architecture: if not is_valid_layer_name(name): raise NetworkValidationError( "Invalid layer name: '{}'".format(name)) # all outgoing connections are present connections = collect_all_connections(architecture) end_layers = {c.end_layer for c in connections} undefined_end_layers = end_layers.difference(architecture) if undefined_end_layers: raise NetworkValidationError( 'Could not find end layer(s) "{}"'.format(undefined_end_layers)) # has exactly one Input and its called Input if "Input" not in architecture or \ architecture['Input']['@type'] != 'Input': raise NetworkValidationError( 'Needs exactly one Input that is called "Input"') # no connections to Input if 'Input' in end_layers: raise NetworkValidationError( 'Input can not have incoming connections!') # TODO: check if connected # TODO: check for cycles return True
def permute_rows(self): """ Given a list of sources and a connection table, find a permutation of the sources, such that they can be connected to the sinks via a single buffer. """ # systematically try all permutations until one satisfies the condition for perm in itertools.permutations(self.nesting): self.perm = list(flatten(perm)) ct = np.atleast_2d(self.connection_table[self.perm]) if Hub.can_be_connected_with_single_buffer(ct): self.connection_table = ct self.flat_sources = [self.flat_sources[i] for i in self.perm] return raise NetworkValidationError("Failed to lay out buffers. " "Please change connectivity.")
def get_key_to_references_mapping(keys, references): """ Create a mapping that maps keys to their matching references. The 'default' reference matches only if no other reference does. The 'fallback' reference is ignored. Args: keys (iterable[str]): List of keys that the references are referring to. references (iterable[str]): List of references. Can contain star wildcards. Returns: dict[str, set[str]]: Dictionary mapping keys to sets of matching references. """ key_to_reference = {key: set() for key in keys} for ref in references: if ref in {'default', 'fallback'}: continue expr = get_regex_for_reference(ref) matching_keys = [key for key in key_to_reference if expr.match(key)] if not matching_keys: raise NetworkValidationError( "{} does not match any keys. Possible keys are: {}".format( ref, sorted(key_to_reference.keys()))) for key in matching_keys: key_to_reference[key].add(ref) if 'default' in references: for key, refs in key_to_reference.items(): if not refs: refs.add('default') return key_to_reference
def initialize(self, default_or_init_dict=None, seed=None, **kwargs): """Initialize the weights of the network. Initialization can be specified in three equivalent ways: 1. just a default initializer: >>> net.initialize(Gaussian()) Note that this is equivalent to: >>> net.initialize(default=Gaussian()) 2. by passing a dictionary: >>> net.initialize({'RegularLayer': Uniform(), ... 'LstmLayer': Gaussian()}) 3. by using keyword arguments: >>> net.initialize(RegularLayer=Uniform(), ... LstmLayer=Uniform()) All following explanations will be with regards to the dictionary style of initialization, because it is the most general one. Note: It is not recommended to combine 2. and 3. but if they are, then keyword arguments take precedence. Each initialization consists of a layer-pattern and that maps to an initializer or a weight-pattern dictionary. Layer patterns can take the following forms: 1. ``{'layer_name': INIT_OR_SUBDICT}`` Matches all the weights of the layer named layer_name 2. ``{'layer_*': INIT_OR_SUBDICT}`` Matches all layers with a name that starts with ``layer_`` The wild-card ``*`` can appear at arbitrary positions and even multiple times in one path. There are two special layer patterns: 3. ``{'default': INIT}`` Matches all weights that are not matched by any other path-pattern 4. ``{'fallback': INIT}`` Set a fallback initializer for every weight. It will only be evaluated for the weights for which the regular initializer failed with an InitializationError. `This is useful for initializers that require a certain shape of weights and will not work otherwise. The fallback will then be used for all cases when that initializer failed.` The weight-pattern sub-dictionary follows the same form as the layer- pattern: 1) ``{'layer_pattern': {'a': INIT_A, 'b': INIT_B}}`` 2) ``{'layer_pattern': {'a*': INIT}`` 3) ``{'layer_pattern': {'default': INIT}`` 4) ``{'layer_pattern': {'fallback': INIT}`` An initializer can either be a scalar, something that converts to a numpy array of the correct shape or an :class:`Initializer` object. So for example: >>> net.initialize(default=0, ... RnnLayer={'b': [1, 2, 3, 4, 5]}, ... ForwardLayer=bs.Gaussian()) Note: Each view must match exactly one initialization and up to one fallback to be unambiguous. Otherwise the initialization will fail. You can specify a seed to make the initialization reproducible: >>> net.initialize({'default': bs.Gaussian()}, seed=1234) """ init_refs = _update_references_with_dict(default_or_init_dict, kwargs) self.initializers = get_description(init_refs) all_parameters = { k: v.parameters for k, v in self.buffer.items() if isinstance(v, BufferView) and 'parameters' in v } _replace_lists_with_array_initializers(init_refs) initializers, fallback = resolve_references(all_parameters, init_refs) init_rnd = self.rnd.create_random_state(seed) for layer_name, views in sorted(all_parameters.items()): if views is None: continue for view_name, view in sorted(views.items()): init = initializers[layer_name][view_name] fb = fallback[layer_name][view_name] if len(init) > 1: raise NetworkValidationError( "Multiple initializers for {}.{}: {}".format( layer_name, view_name, init)) if len(init) == 0: raise NetworkValidationError( "No initializer for {}.{}".format( layer_name, view_name)) if len(fb) > 1: raise NetworkValidationError( "Multiple fallbacks for {}.{}: {}".format( layer_name, view_name, fb)) fb = fb.pop() if len(fb) else None self.handler.set_from_numpy( view, evaluate_initializer(init.pop(), view.shape, fb, seed=init_rnd.generate_seed()))