10 Commits
TF20 ... TF113

19 changed files with 954 additions and 367 deletions

View File

@ -2,13 +2,17 @@ import os
import time import time
import dill import dill
from tqdm import tqdm from tqdm import tqdm
import copy from copy import copy
from tensorflow.python.keras import backend as K from tensorflow.python.keras import backend as K
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
class IllegalArgumentError(ValueError):
pass
class Experiment(ABC): class Experiment(ABC):
@staticmethod @staticmethod
@ -20,15 +24,24 @@ class Experiment(ABC):
def reset_model(): def reset_model():
K.clear_session() K.clear_session()
def __init__(self, name=None, ident=None): def __init__(self, name=None, ident=None, **kwargs):
self.experiment_id = f'{ident or ""}_{time.time()}' self.experiment_id = f'{ident or ""}_{time.time()}'
self.experiment_name = name or 'unnamed_experiment' self.experiment_name = name or 'unnamed_experiment'
self.next_iteration = 0 self.iteration = 0
self.log_messages = list() self.log_messages = list()
self.historical_particles = dict() self.historical_particles = dict()
self.params = dict(exp_iterations=100, application_steps=100, prints=True, trains_per_application=100)
self.with_params(**kwargs)
def __copy__(self, *args, **kwargs):
params = self.params
params.update(name=self.experiment_name)
params.update(**kwargs)
self_copy = self.__class__(*args, **params)
return self_copy
def __enter__(self): def __enter__(self):
self.dir = os.path.join('experiments', f'exp-{self.experiment_name}-{self.experiment_id}-{self.next_iteration}') self.dir = os.path.join('experiments', f'exp-{self.experiment_name}-{self.experiment_id}-{self.iteration}')
os.makedirs(self.dir) os.makedirs(self.dir)
print(f'** created {self.dir} **') print(f'** created {self.dir} **')
return self return self
@ -36,7 +49,14 @@ class Experiment(ABC):
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
self.save(experiment=self.without_particles()) self.save(experiment=self.without_particles())
self.save_log() self.save_log()
self.next_iteration += 1 # Clean Exit
self.reset_all()
# self.iteration += 1 Taken From here!
def with_params(self, **kwargs):
# Make them your own
self.params.update(kwargs)
return self
def log(self, message, **kwargs): def log(self, message, **kwargs):
self.log_messages.append(message) self.log_messages.append(message)
@ -47,16 +67,15 @@ class Experiment(ABC):
for log_message in self.log_messages: for log_message in self.log_messages:
print(str(log_message), file=log_file) print(str(log_message), file=log_file)
def __copy__(self):
self_copy = self.__class__(name=self.experiment_name,)
self_copy.__dict__ = {attr: self.__dict__[attr] for attr in self.__dict__ if
attr not in ['particles', 'historical_particles']}
return self_copy
def without_particles(self): def without_particles(self):
self_copy = copy.copy(self) self_copy = copy(self)
# self_copy.particles = [particle.states for particle in self.particles] # Check if attribute exists
self_copy.historical_particles = {key: val.states for key, val in self.historical_particles.items()} if hasattr(self, 'historical_particles'):
# Check if it is empty.
if self.historical_particles:
# Do the Update
# self_copy.particles = [particle.states for particle in self.particles]
self_copy.historical_particles = {key: val.states for key, val in self.historical_particles.items()}
return self_copy return self_copy
def save(self, **kwargs): def save(self, **kwargs):
@ -64,28 +83,32 @@ class Experiment(ABC):
with open(os.path.join(self.dir, f"{name}.dill"), "wb") as dill_file: with open(os.path.join(self.dir, f"{name}.dill"), "wb") as dill_file:
dill.dump(value, dill_file) dill.dump(value, dill_file)
def reset_log(self):
self.log_messages = list()
@abstractmethod @abstractmethod
def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs): def run_net(self, net, **kwargs):
raise NotImplementedError raise NotImplementedError
pass pass
def run_exp(self, network_generator, exp_iterations, prints=False, **kwargs): def run_exp(self, network_generator, reset_model=False, **kwargs):
# INFO Run_ID needs to be more than 0, so that exp stores the trajectories! # INFO Run_ID needs to be more than 0, so that exp stores the trajectories!
for run_id in range(exp_iterations): for run_id in range(self.params.get('exp_iterations')):
network = network_generator() network = network_generator()
self.run_net(network, 100, run_id=run_id + 1, **kwargs) self.run_net(network, **kwargs)
self.historical_particles[run_id] = network self.historical_particles[run_id] = network
if prints: if self.params.get('prints'):
print("Fixpoint? " + str(network.is_fixpoint())) print("Fixpoint? " + str(network.is_fixpoint()))
self.reset_model() self.iteration += 1
if reset_model:
self.reset_model()
def reset_all(self): def reset_all(self):
self.reset_log()
self.reset_model() self.reset_model()
class FixpointExperiment(Experiment): class FixpointExperiment(Experiment):
if kwargs.get('logging', False):
self.log(self.counters)
def __init__(self, **kwargs): def __init__(self, **kwargs):
kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name'] kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
@ -93,12 +116,22 @@ class FixpointExperiment(Experiment):
self.counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0) self.counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0)
self.interesting_fixpoints = [] self.interesting_fixpoints = []
def run_net(self, net, step_limit=100, run_id=0, **kwargs): def run_exp(self, network_generator, logging=True, reset_model=False, **kwargs):
i = 0 kwargs.update(reset_model=False)
while i < step_limit and not net.is_diverged() and not net.is_fixpoint(): super(FixpointExperiment, self).run_exp(network_generator, **kwargs)
net.self_attack() if logging:
i += 1 self.log(self.counters)
if run_id: if reset_model:
self.reset_model()
def run_net(self, net, **kwargs):
if len(kwargs):
raise IllegalArgumentError
for i in range(self.params.get('application_steps')):
if net.is_diverged() or net.is_fixpoint():
break
net.set_weights(net.apply_to_weights(net.get_weights()))
if self.iteration and hasattr(self, 'save_state'):
net.save_state(time=i) net.save_state(time=i)
self.count(net) self.count(net)
@ -128,44 +161,117 @@ class FixpointExperiment(Experiment):
class MixedFixpointExperiment(FixpointExperiment): class MixedFixpointExperiment(FixpointExperiment):
def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs): def __init__(self, **kwargs):
for i in range(step_limit): kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(MixedFixpointExperiment, self).__init__(**kwargs)
def run_net(self, net, **kwargs):
assert hasattr(net, 'train'), 'This Network must be trainable, i.e. use the "TrainingNeuralNetworkDecorator"!'
for application in range(self.params.get('application_steps')):
epoch_num = self.params.get('trains_per_application') * application
net.set_weights(net.apply_to_weights(net.get_weights()))
if net.is_diverged() or net.is_fixpoint(): if net.is_diverged() or net.is_fixpoint():
break break
net.self_attack() barformat = "Experiment Iteration: {postfix[iteration]} | "
with tqdm(postfix=["Loss", dict(value=0)]) as bar: barformat += "Evolution Step:{postfix[step]}| "
for _ in range(trains_per_application): barformat += "Training Epoch:{postfix[epoch]}| "
loss = net.compiled().train() barformat += "Loss: {postfix[loss]} | {bar}"
bar.postfix[1]["value"] = loss with tqdm(total=self.params.get('trains_per_application'),
postfix={'step': 0, 'loss': 0, 'iteration': self.iteration, 'epoch': 0, None: None},
bar_format=barformat) as bar:
# This iterates for self.trains_per_application times, the addition is just for epoch enumeration
for epoch in range(epoch_num, epoch_num + self.params.get('trains_per_application')):
if net.is_diverged():
print('Network diverged to either inf or nan... breaking')
break
loss = net.train(epoch=epoch)
if epoch % 10 == 0:
bar.postfix.update(step=application, epoch=epoch, loss=loss, iteration=self.iteration)
bar.update() bar.update()
if run_id: epoch_num += 1
if self.iteration and hasattr(net, 'save_sate'):
net.save_state() net.save_state()
self.count(net) self.count(net)
class SoupExperiment(Experiment): class TaskExperiment(MixedFixpointExperiment):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(SoupExperiment, self).__init__(name=kwargs.get('name', self.__class__.__name__)) kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(TaskExperiment, self).__init__(**kwargs)
def run_exp(self, network_generator, exp_iterations, soup_generator=None, soup_iterations=0, prints=False): def run_exp(self, network_generator, reset_model=False, logging=True, **kwargs):
for i in range(soup_iterations): kwargs.update(reset_model=False, logging=logging)
soup = soup_generator() super(FixpointExperiment, self).run_exp(network_generator, **kwargs)
if reset_model:
self.reset_model()
pass
def run_net(self, net, **kwargs):
assert hasattr(net, 'evaluate')
super(TaskExperiment, self).run_net(net, **kwargs)
# Get Performance without Training
task_performance = net.evaluate(*net.get_samples(task_samples=True),
batchsize=net.get_amount_of_weights())
self_performance = net.evaluate(*net.get_samples(self_samples=True),
batchsize=net.get_amount_of_weights())
current_performance = dict(task_performance=task_performance,
self_performance=self_performance,
counters=self.counters, id=self.iteration)
self.log(current_performance)
pass
class SoupExperiment(Experiment):
def __init__(self, soup_generator, **kwargs):
kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
self.soup_generator = soup_generator
super(SoupExperiment, self).__init__(**kwargs)
def run_exp(self, network_generator, **kwargs):
for i in range(self.params.get('exp_iterations')):
soup = self.soup_generator()
soup.seed() soup.seed()
for _ in tqdm(exp_iterations): for _ in tqdm(range(self.params.get('application_steps'))):
soup.evolve() soup.evolve()
self.log(soup.count()) self.log(soup.count())
self.save(soup=soup.without_particles()) self.save(soup=soup.without_particles())
K.clear_session()
def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs): def run_net(self, net, **kwargs):
raise NotImplementedError raise NotImplementedError
pass pass
class IdentLearningExperiment(Experiment): class TaskingSoupExperiment(Experiment):
def __init__(self): def __init__(self, soup_generator, **kwargs):
super(IdentLearningExperiment, self).__init__(name=self.__class__.__name__) kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(TaskingSoupExperiment, self).__init__(**kwargs)
self.soup_generator = soup_generator
def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs): def __copy__(self):
super(TaskingSoupExperiment, self).__copy__(self.soup_generator)
def run_exp(self, **kwargs):
for i in range(self.params.get('exp_iterations')):
soup = self.soup_generator()
soup.seed()
for _ in tqdm(range(self.params.get('application_steps'))):
soup.evolve()
self.log(soup.count())
self.save(soup=soup.without_particles())
K.clear_session()
def run_net(self, net, **kwargs):
raise NotImplementedError()
pass pass
if __name__ == '__main__':
pass

View File

@ -1,14 +1,26 @@
# Librarys
import numpy as np import numpy as np
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from typing import List, Union from typing import List, Tuple
from types import FunctionType from types import FunctionType
import warnings
import os
# Functions and Operators
from operator import mul
from functools import reduce
from itertools import accumulate
from copy import deepcopy
# Deep learning Framework
from tensorflow.python.keras.models import Sequential from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.callbacks import Callback from tensorflow.python.keras.callbacks import Callback
from tensorflow.python.keras.layers import SimpleRNN, Dense from tensorflow.python.keras.layers import SimpleRNN, Dense
from tensorflow.python.keras import backend as K
from experiment import * # Experiment Class
from task import TaskAdditionOfN
from experiment import TaskExperiment
# Supress warnings and info messages # Supress warnings and info messages
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
@ -28,104 +40,23 @@ class SaveStateCallback(Callback):
return return
class Weights: class EarlyStoppingByInfNanLoss(Callback):
def __init__(self, monitor='loss', verbose=0):
super(Callback, self).__init__()
self.monitor = monitor
self.verbose = verbose
@staticmethod def on_epoch_end(self, epoch, logs: dict = None):
def __reshape_flat_array__(array, shapes): logs = logs or dict()
sizes: List[int] = [int(np.prod(shape)) for shape in shapes] current = logs.get(self.monitor)
# Split the incoming array into slices for layers if current is None:
slices = [array[x: y] for x, y in zip(np.cumsum([0]+sizes), np.cumsum([0]+sizes)[1:])] warnings.warn(f'Early stopping requires {self.monitor} available!', RuntimeWarning)
# reshape them in accordance to the given shapes pass
weights = [np.reshape(weight_slice, shape) for weight_slice, shape in zip(slices, shapes)]
return weights
def __init__(self, weight_vector: Union[List[np.ndarray], np.ndarray], flat_array_shape=None): if np.isnan(current) or np.isinf(current):
""" if self.verbose > 0:
Weight class, for easy manipulation of weight vectors from Keras models print(f'Epoch {epoch}: early stopping THR')
self.model.stop_training = True
:param weight_vector: A numpy array holding weights
:type weight_vector: List[np.ndarray]
"""
self.__iter_idx = [0, 0]
if flat_array_shape:
weight_vector = self.__reshape_flat_array__(weight_vector, flat_array_shape)
self.layers = weight_vector
# TODO: implement a way to access the cells directly
# self.cells = len(self)
# TODO: implement a way to access the weights directly
# self.weights = self.to_flat_array() ?
def __iter__(self):
self.__iter_idx = [0, 0]
return self
def __getitem__(self, item):
return self.layers[item]
def max(self):
np.max(self.layers)
def avg(self):
return np.average(self.layers)
def __len__(self):
return sum([x.size for x in self.layers])
def shapes(self):
return [x.shape for x in self.layers]
def num_layers(self):
return len(self.layers)
def __copy__(self):
return copy.deepcopy(self)
def __next__(self):
# ToDo: Check iteration progress over layers
# ToDo: There is still a problem interation, currently only cell level is the last loop stage.
# Do we need this?
if self.__iter_idx[0] >= len(self.layers):
if self.__iter_idx[1] >= len(self.layers[self.__iter_idx[0]]):
raise StopIteration
result = self.layers[self.__iter_idx[0]][self.__iter_idx[1]]
if self.__iter_idx[1] >= len(self.layers[self.__iter_idx[0]]):
self.__iter_idx[0] += 1
self.__iter_idx[1] = 0
else:
self.__iter_idx[1] += 1
return result
def __repr__(self):
return f'Weights({self.to_flat_array().tolist()})'
def to_flat_array(self) -> np.ndarray:
return np.hstack([weight.flatten() for weight in self.layers])
def from_flat_array(self, array):
new_weights = self.__reshape_flat_array__(array, self.shapes())
return new_weights
def shuffle(self):
flat = self.to_flat_array()
np.random.shuffle(flat)
self.from_flat_array(flat)
return True
def are_diverged(self):
return any([np.isnan(x).any() for x in self.layers]) or any([np.isinf(x).any() for x in self.layers])
def are_within_bounds(self, lower_bound: float, upper_bound: float):
return bool(sum([((lower_bound < x) & (x > upper_bound)).size for x in self.layers]))
def aggregate_by(self, func: FunctionType, num_aggregates):
collection_sizes = len(self) // num_aggregates
weights = self.to_flat_array()[:collection_sizes * num_aggregates].reshape((num_aggregates, -1))
aggregated_weights = func(weights, num_aggregates)
left_overs = self.to_flat_array()[collection_sizes * num_aggregates:]
return aggregated_weights, left_overs
class NeuralNetwork(ABC): class NeuralNetwork(ABC):
@ -133,10 +64,50 @@ class NeuralNetwork(ABC):
This is the Base Network Class, including abstract functions that must be implemented. This is the Base Network Class, including abstract functions that must be implemented.
""" """
@staticmethod
def are_weights_diverged(weights: List[np.ndarray]) -> bool:
return any([any((np.isnan(x).any(), np.isinf(x).any())) for x in weights])
@staticmethod
def are_weights_within_bounds(weights: List[np.ndarray], lower_bound: float, upper_bound: float) -> bool:
return any([((lower_bound < x) & (x < upper_bound)).any() for x in weights])
@staticmethod
def get_weight_amount(weights: List[np.ndarray]):
return sum([x.size for x in weights])
@staticmethod
def shapes(weights: List[np.ndarray]):
return [x.shape for x in weights]
@staticmethod
def num_layers(weights: List[np.ndarray]):
return len(weights)
def repr(self, weights: List[np.ndarray]):
return f'Weights({self.weights_to_flat_array(weights).tolist()})'
@staticmethod
def weights_to_flat_array(weights: List[np.ndarray]) -> np.ndarray:
return np.concatenate([d.ravel() for d in weights])
@staticmethod
def reshape_flat_array(array, shapes: List[Tuple[int]]) -> List[np.ndarray]:
# Same thing, but with an additional np call
# sizes: List[int] = [int(np.prod(shape)) for shape in shapes]
sizes = [reduce(mul, shape) for shape in shapes]
# Split the incoming array into slices for layers
slices = [array[x: y] for x, y in zip(accumulate([0] + sizes), accumulate(sizes))]
# reshape them in accordance to the given shapes
weights = [np.reshape(weight_slice, shape) for weight_slice, shape in zip(slices, shapes)]
return weights
def __init__(self, **params): def __init__(self, **params):
super().__init__() super().__init__()
self.params = dict(epsilon=0.00000000000001) self.params = dict(epsilon=0.00000000000001, early_nan_stopping=True, store_states=False)
self.params.update(params) self.params.update(params)
self.name = params.get('name', self.__class__.__name__)
self.keras_params = dict(activation='linear', use_bias=False) self.keras_params = dict(activation='linear', use_bias=False)
self.states = [] self.states = []
self.model: Sequential self.model: Sequential
@ -155,76 +126,89 @@ class NeuralNetwork(ABC):
self.keras_params.update(kwargs) self.keras_params.update(kwargs)
return self return self
def get_weights(self) -> Weights: def print_weights(self, weights=None):
return Weights(self.model.get_weights()) print(self.repr(weights or self.get_weights()))
def get_amount_of_weights(self):
return self.get_weight_amount(self.get_weights())
def get_model(self):
return self.model
def get_weights(self) -> List[np.ndarray]:
return self.get_model().get_weights()
def get_weights_flat(self) -> np.ndarray: def get_weights_flat(self) -> np.ndarray:
return self.get_weights().to_flat_array() return self.weights_to_flat_array(self.get_weights())
def set_weights(self, new_weights: Weights): def reshape_flat_array_like(self, array, weights: List[np.ndarray]) -> List[np.ndarray]:
return self.model.set_weights(new_weights.layers) return self.reshape_flat_array(array, self.shapes(weights))
@abstractmethod def set_weights(self, new_weights: List[np.ndarray]):
def get_samples(self): return self.model.set_weights(new_weights)
# TODO: add a dogstring, telling the user what this does, e.g. what is a sample?
raise NotImplementedError
@abstractmethod def apply_to_network(self, other_network) -> List[np.ndarray]:
def apply_to_weights(self, old_weights) -> Weights: """
# TODO: add a dogstring, telling the user what this does, e.g. what is applied? Take a networks weights and apply _this_ networks function.
raise NotImplementedError :param other_network:
:return:
def apply_to_network(self, other_network) -> Weights: """
# TODO: add a dogstring, telling the user what this does, e.g. what is applied?
new_weights = self.apply_to_weights(other_network.get_weights()) new_weights = self.apply_to_weights(other_network.get_weights())
return new_weights return new_weights
def attack(self, other_network):
# TODO: add a dogstring, telling the user what this does, e.g. what is an attack?
other_network.set_weights(self.apply_to_network(other_network))
return self
def fuck(self, other_network):
# TODO: add a dogstring, telling the user what this does, e.g. what is fucking?
self.set_weights(self.apply_to_network(other_network))
return self
def self_attack(self, iterations=1):
# TODO: add a dogstring, telling the user what this does, e.g. what is self attack?
for _ in range(iterations):
self.attack(self)
return self
def meet(self, other_network):
# TODO: add a dogstring, telling the user what this does, e.g. what is meeting?
new_other_network = copy.deepcopy(other_network)
return self.attack(new_other_network)
def is_diverged(self): def is_diverged(self):
return self.get_weights().are_diverged() return self.are_weights_diverged(self.get_weights())
def is_zero(self, epsilon=None): def is_zero(self, epsilon=None):
epsilon = epsilon or self.get_params().get('epsilon') epsilon = epsilon or self.get_params().get('epsilon')
return self.get_weights().are_within_bounds(-epsilon, epsilon) return self.are_weights_within_bounds(self.get_weights(), -epsilon, epsilon)
def is_fixpoint(self, degree: int = 1, epsilon: float = None) -> bool: def is_fixpoint(self, degree: int = 1, epsilon: float = None) -> bool:
assert degree >= 1, "degree must be >= 1" assert degree >= 1, "degree must be >= 1"
epsilon = epsilon or self.get_params().get('epsilon') epsilon = epsilon or self.get_params().get('epsilon')
new_weights = copy.deepcopy(self.get_weights()) new_weights = deepcopy(self.get_weights())
for _ in range(degree): for _ in range(degree):
new_weights = self.apply_to_weights(new_weights) new_weights = self.apply_to_weights(new_weights)
if new_weights.are_diverged(): if self.are_weights_diverged(new_weights):
return False return False
biggerEpsilon = (np.abs(new_weights.to_flat_array() - self.get_weights().to_flat_array()) >= epsilon).any() flat_new = self.weights_to_flat_array(new_weights)
flat_old = self.weights_to_flat_array(self.get_weights())
biggerEpsilon = (np.abs(flat_new - flat_old) >= epsilon).any()
# Boolean Value needs to be flipped to answer "is_fixpoint" # Boolean Value needs to be flipped to answer "is_fixpoint"
return not biggerEpsilon return not biggerEpsilon
def print_weights(self, weights=None): def aggregate_weights_by(self, weights: List[np.ndarray], func: FunctionType, num_aggregates: int):
print(weights or self.get_weights()) collection_sizes = self.get_weight_amount(weights) // num_aggregates
flat = self.weights_to_flat_array(weights)
array_for_aggregation = flat[:collection_sizes * num_aggregates].reshape((num_aggregates, -1))
left_overs = flat[collection_sizes * num_aggregates:]
aggregated_weights = func(array_for_aggregation, num_aggregates)
return aggregated_weights, left_overs
def shuffle_weights(self, weights: List[np.ndarray]):
flat = self.weights_to_flat_array(weights)
np.random.shuffle(flat)
return self.reshape_flat_array_like(flat, weights)
@abstractmethod
def get_samples(self, **kwargs):
# TODO: add a dogstring, telling the user what this does, e.g. what is a sample?
raise NotImplementedError
@abstractmethod
def apply_to_weights(self, old_weights) -> List[np.ndarray]:
"""
Take weights as inputs; retunr the evaluation of _this_ network.
"Apply this function".
:param old_weights:
:return:
"""
raise NotImplementedError
class ParticleDecorator: class ParticleDecorator:
@ -273,6 +257,63 @@ class ParticleDecorator:
def get_states(self): def get_states(self):
return self.states return self.states
def attack(self, other_network, iterations: int = 1):
"""
Set a networks weights based on the output of the application of my function to its weights.
"Alter a networks weights based on my evaluation"
:param other_network:
:param iterations:
:return:
"""
for _ in range(iterations):
other_network.set_weights(self.apply_to_network(other_network))
return self
def self_attack(self, iterations: int = 1):
"""
Set my weights based on the output of the application of my function to its weights.
"Alter my network weights based on my evaluation"
:param iterations:
:return:
"""
for _ in range(iterations):
self.attack(self)
return self
class TaskDecorator(TaskAdditionOfN):
def __init__(self, network, **kwargs):
super(TaskDecorator, self).__init__(**kwargs)
self.network = network
self.batchsize = self.network.get_amount_of_weights()
def __getattr__(self, name):
return getattr(self.network, name)
def get_samples(self, task_samples=False, self_samples=False, **kwargs):
# XOR, cannot be true at the same time
assert not all([task_samples, self_samples])
if task_samples:
return super(TaskDecorator, self).get_samples()
elif self_samples:
return self.network.get_samples()
else:
self_x, self_y = self.network.get_samples()
# Super class = Task
task_x, task_y = super(TaskDecorator, self).get_samples()
amount_of_weights = self.network.get_amount_of_weights()
random_idx = np.random.choice(np.arange(amount_of_weights), amount_of_weights//2)
x = self_x[random_idx] = task_x[random_idx]
y = self_y[random_idx] = task_y[random_idx]
return x, y
class WeightwiseNeuralNetwork(NeuralNetwork): class WeightwiseNeuralNetwork(NeuralNetwork):
@ -291,23 +332,23 @@ class WeightwiseNeuralNetwork(NeuralNetwork):
# TODO: Write about it... What does it do? # TODO: Write about it... What does it do?
return self.model.predict(inputs) return self.model.predict(inputs)
def get_samples(self): def get_samples(self, **kwargs: List[np.ndarray]):
weights = self.get_weights() weights = kwargs.get('weights', self.get_weights())
sample = np.asarray([ sample = np.asarray([
[weight, idx, *x] for idx, layer in enumerate(weights.layers) for x, weight in np.ndenumerate(layer) [weight, idx, *x] for idx, layer in enumerate(weights) for x, weight in np.ndenumerate(layer)
]) ])
# normalize [layer, cell, position] # normalize [layer, cell, position]
for idx in range(1, sample.shape[1]): for idx in range(1, sample.shape[1]):
sample[:, idx] = sample[:, idx] / np.max(sample[:, idx]) sample[:, idx] = sample[:, idx] / np.max(sample[:, idx])
return sample, sample return sample, sample[:, 0]
def apply_to_weights(self, weights) -> Weights: def apply_to_weights(self, weights) -> List[np.ndarray]:
# ToDo: Insert DocString # ToDo: Insert DocString
# Transform the weight matrix in an horizontal stack as: array([[weight, layer, cell, position], ...]) # Transform the weight matrix in an horizontal stack as: array([[weight, layer, cell, position], ...])
transformed_weights = self.get_samples()[0] transformed_weights, _ = self.get_samples(weights=weights)
new_weights = self.apply(transformed_weights) new_flat_weights = self.apply(transformed_weights)
# use the original weight shape to transform the new tensor # use the original weight shape to transform the new tensor
return Weights(new_weights, flat_array_shape=weights.shapes()) return self.reshape_flat_array_like(new_flat_weights, weights)
class AggregatingNeuralNetwork(NeuralNetwork): class AggregatingNeuralNetwork(NeuralNetwork):
@ -329,11 +370,10 @@ class AggregatingNeuralNetwork(NeuralNetwork):
@staticmethod @staticmethod
def deaggregate_identically(aggregate, amount): def deaggregate_identically(aggregate, amount):
# ToDo: Find a better way than using the a hardcoded [0] return np.repeat(aggregate, amount, axis=0)
return np.hstack([aggregate for _ in range(amount)])[0]
@staticmethod @staticmethod
def shuffle_not(weights: Weights): def shuffle_not(weights: List[np.ndarray]):
""" """
Doesn't do a thing. f(x) Doesn't do a thing. f(x)
@ -344,9 +384,8 @@ class AggregatingNeuralNetwork(NeuralNetwork):
""" """
return weights return weights
@staticmethod def shuffle_random(self, weights: List[np.ndarray]):
def shuffle_random(weights: Weights): weights = self.shuffle_weights(weights)
assert weights.shuffle()
return weights return weights
def __init__(self, aggregates, width, depth, **kwargs): def __init__(self, aggregates, width, depth, **kwargs):
@ -369,17 +408,14 @@ class AggregatingNeuralNetwork(NeuralNetwork):
def get_shuffler(self): def get_shuffler(self):
return self.params.get('shuffler', self.shuffle_not) return self.params.get('shuffler', self.shuffle_not)
def get_amount_of_weights(self):
return len(self.get_weights())
def apply(self, inputs): def apply(self, inputs):
# You need to add an dimension here... "..." copies array values # You need to add an dimension here... "..." copies array values
return self.model.predict(inputs[None, ...]) return self.model.predict(inputs[None, ...])
def get_aggregated_weights(self): def get_aggregated_weights(self):
return self.get_weights().aggregate_by(self.get_aggregator(), self.aggregates) return self.aggregate_weights_by(self.get_weights(), self.get_aggregator(), self.aggregates)
def apply_to_weights(self, old_weights) -> Weights: def apply_to_weights(self, old_weights) -> List[np.ndarray]:
# build aggregations of old_weights # build aggregations of old_weights
old_aggregations, leftovers = self.get_aggregated_weights() old_aggregations, leftovers = self.get_aggregated_weights()
@ -390,14 +426,14 @@ class AggregatingNeuralNetwork(NeuralNetwork):
new_aggregations = self.deaggregate_identically(new_aggregations, collection_sizes) new_aggregations = self.deaggregate_identically(new_aggregations, collection_sizes)
# generate new weights # generate new weights
# only include leftovers if there are some then coonvert them to Weight on base of th old shape # only include leftovers if there are some then coonvert them to Weight on base of th old shape
new_weights = Weights(new_aggregations if not leftovers.shape[0] else np.hstack((new_aggregations, leftovers)), complete_weights = new_aggregations if not leftovers.shape[0] else np.hstack((new_aggregations, leftovers))
flat_array_shape=old_weights.shapes()) new_weights = self.reshape_flat_array_like(complete_weights, old_weights)
# maybe shuffle # maybe shuffle
new_weights = self.get_shuffler()(new_weights) new_weights = self.get_shuffler()(new_weights)
return new_weights return new_weights
def get_samples(self): def get_samples(self, **kwargs):
aggregations, _ = self.get_aggregated_weights() aggregations, _ = self.get_aggregated_weights()
# What did that do? # What did that do?
# sample = np.transpose(np.array([[aggregations[i]] for i in range(self.aggregates)])) # sample = np.transpose(np.array([[aggregations[i]] for i in range(self.aggregates)]))
@ -408,11 +444,11 @@ class AggregatingNeuralNetwork(NeuralNetwork):
epsilon = epsilon or self.get_params().get('epsilon') epsilon = epsilon or self.get_params().get('epsilon')
old_aggregations, _ = self.get_aggregated_weights() old_aggregations, _ = self.get_aggregated_weights()
new_weights = copy.deepcopy(self.get_weights()) new_weights = deepcopy(self.get_weights())
for _ in range(degree): for _ in range(degree):
new_weights = self.apply_to_weights(new_weights) new_weights = self.apply_to_weights(new_weights)
if new_weights.are_diverged(): if self.are_weights_diverged(new_weights):
return False return False
new_aggregations, leftovers = self.get_aggregated_weights() new_aggregations, leftovers = self.get_aggregated_weights()
@ -427,7 +463,8 @@ class AggregatingNeuralNetwork(NeuralNetwork):
class RecurrentNeuralNetwork(NeuralNetwork): class RecurrentNeuralNetwork(NeuralNetwork):
def __init__(self, width, depth, **kwargs): def __init__(self, width, depth, **kwargs):
super().__init__(**kwargs) raise NotImplementedError
super(RecurrentNeuralNetwork, self).__init__()
self.features = 1 self.features = 1
self.width = width self.width = width
self.depth = depth self.depth = depth
@ -443,7 +480,7 @@ class RecurrentNeuralNetwork(NeuralNetwork):
def apply_to_weights(self, old_weights): def apply_to_weights(self, old_weights):
# build list from old weights # build list from old weights
new_weights = copy.deepcopy(old_weights) new_weights = deepcopy(old_weights)
old_weights_list = [] old_weights_list = []
for layer_id, layer in enumerate(old_weights): for layer_id, layer in enumerate(old_weights):
for cell_id, cell in enumerate(layer): for cell_id, cell in enumerate(layer):
@ -500,7 +537,7 @@ class TrainingNeuralNetworkDecorator:
return self return self
def compile_model(self, **kwargs): def compile_model(self, **kwargs):
compile_params = copy.deepcopy(self.compile_params) compile_params = deepcopy(self.compile_params)
compile_params.update(kwargs) compile_params.update(kwargs)
return self.network.model.compile(**compile_params) return self.network.model.compile(**compile_params)
@ -510,13 +547,30 @@ class TrainingNeuralNetworkDecorator:
self.model_compiled = True self.model_compiled = True
return self return self
def train(self, batchsize=1, store_states=True, epoch=0): def train(self, batchsize=1, epoch=0):
self.compiled() self.compiled()
x, y = self.network.get_samples() x, y = self.network.get_samples()
savestatecallback = [SaveStateCallback(network=self, epoch=epoch)] if store_states else None callbacks = []
history = self.network.model.fit(x=x, y=y, epochs=epoch+1, verbose=0, if self.get_params().get('store_states'):
batch_size=batchsize, callbacks=savestatecallback, callbacks.append(SaveStateCallback(network=self, epoch=epoch))
initial_epoch=epoch) if self.get_params().get('early_nan_stopping'):
callbacks.append(EarlyStoppingByInfNanLoss())
# 'or' does not work on empty lists
callbacks = callbacks if callbacks else None
"""
Please Note:
epochs: Integer. Number of epochs to train the model.
An epoch is an iteration over the entire `x` and `y`
data provided.
Note that in conjunction with `initial_epoch`,
`epochs` is to be understood as "final epoch".
The model is not trained for a number of iterations
given by `epochs`, but merely until the epoch
of index `epochs` is reached."""
history = self.network.model.fit(x=x, y=y, initial_epoch=epoch, epochs=epoch+1, verbose=0,
batch_size=batchsize, callbacks=callbacks)
return history.history['loss'][-1] return history.history['loss'][-1]
def learn_from(self, other_network, batchsize=1): def learn_from(self, other_network, batchsize=1):
@ -524,68 +578,81 @@ class TrainingNeuralNetworkDecorator:
other_network.compiled() other_network.compiled()
x, y = other_network.network.get_samples() x, y = other_network.network.get_samples()
history = self.network.model.fit(x=x, y=y, verbose=0, batch_size=batchsize) history = self.network.model.fit(x=x, y=y, verbose=0, batch_size=batchsize)
return history.history['loss'][-1] return history.history['loss'][-1]
def evaluate(self, x=None, y=None, batchsize=1):
self.compiled()
x, y = x, y if x is not None and y is not None else self.network.get_samples()
"""
Please Note:
epochs: Integer. Number of epochs to train the model.
An epoch is an iteration over the entire `x` and `y`
data provided.
Note that in conjunction with `initial_epoch`,
`epochs` is to be understood as "final epoch".
The model is not trained for a number of iterations
given by `epochs`, but merely until the epoch
of index `epochs` is reached."""
loss = self.network.model.evaluate(x=x, y=y, verbose=0, batch_size=batchsize)
return loss
if __name__ == '__main__': if __name__ == '__main__':
if True: if True:
# WeightWise Neural Network # WeightWise Neural Network
net_generator = ParticleDecorator(WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation='linear')) with TaskExperiment().with_params(application_steps=10, trains_per_application=1000, exp_iterations=30) as exp:
with FixpointExperiment() as exp: net_generator = lambda: TrainingNeuralNetworkDecorator(TaskDecorator(
exp.run_exp(net_generator, 10, logging=True) WeightwiseNeuralNetwork(width=2, depth=2))
exp.reset_all() ).with_keras_params(activation='linear')
exp.run_exp(net_generator, reset_model=True)
if False: if False:
# Aggregating Neural Network # Aggregating Neural Network
net_generator = ParticleDecorator(AggregatingNeuralNetwork(aggregates=4, width=2, depth=2).with_keras_params()) net_generator = lambda: AggregatingNeuralNetwork(aggregates=4, width=2, depth=2)
with FixpointExperiment() as exp: with MixedFixpointExperiment() as exp:
exp.run_exp(net_generator, 10, logging=True) exp.run_exp(net_generator, 10)
exp.reset_all() exp.reset_all()
if False: if False:
# FFT Aggregation # FFT Aggregation
net_generator = lambda: ParticleDecorator( net_generator = lambda: AggregatingNeuralNetwork(
AggregatingNeuralNetwork( aggregates=4, width=2, depth=2, aggregator=AggregatingNeuralNetwork.aggregate_fft)
aggregates=4, width=2, depth=2, aggregator=AggregatingNeuralNetwork.aggregate_fft
).with_keras_params(activation='linear'))
with FixpointExperiment() as exp: with FixpointExperiment() as exp:
for run_id in tqdm(range(10)): exp.run_exp(net_generator, 10)
exp.run_exp(net_generator, 1) exp.log(exp.counters)
exp.log(exp.counters) exp.reset_model()
exp.reset_model()
exp.reset_all() exp.reset_all()
if True: if False:
# ok so this works quite realiably # ok so this works quite realiably
run_count = 10000 run_count = 1000
net_generator = TrainingNeuralNetworkDecorator( net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(
ParticleDecorator(WeightwiseNeuralNetwork(width=2, depth=2)) width=2, depth=2).with_params(epsilon=0.0001)).with_keras_params(optimizer='sgd')
).with_params(epsilon=0.0001).with_keras_params(optimizer='sgd')
with MixedFixpointExperiment() as exp: with MixedFixpointExperiment() as exp:
for run_id in tqdm(range(run_count+1)): for run_id in tqdm(range(run_count+1)):
exp.run_exp(net_generator, 1) exp.run_exp(net_generator, 1)
if run_id % 100 == 0: if run_id % 100 == 0:
exp.run_net(net_generator, 1) exp.run_exp(net_generator, 1)
K.clear_session() K.clear_session()
if False: if False:
with FixpointExperiment() as exp: with FixpointExperiment() as exp:
run_count = 1000 run_count = 100
net = TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, width=2, depth=2)).with_params(epsilon=0.1e-6) net = TrainingNeuralNetworkDecorator(
AggregatingNeuralNetwork(4, width=2, depth=2).with_params(epsilon=0.1e-6))
for run_id in tqdm(range(run_count+1)): for run_id in tqdm(range(run_count+1)):
loss = net.compiled().train() current_loss = net.compiled().train()
if run_id % 100 == 0: if run_id % 100 == 0:
net.print_weights() net.print_weights()
old_aggs, _ = net.net.get_aggregated_weights() old_aggs, _ = net.get_aggregated_weights()
print("old weights agg: " + str(old_aggs)) print("old weights agg: " + str(old_aggs))
fp, new_aggs = net.net.is_fixpoint_after_aggregation(epsilon=0.0001) fp, new_aggs = net.is_fixpoint_after_aggregation(epsilon=0.0001)
print("new weights agg: " + str(new_aggs)) print("new weights agg: " + str(new_aggs))
print("Fixpoint? " + str(net.is_fixpoint())) print("Fixpoint? " + str(net.is_fixpoint()))
print("Fixpoint after Agg? " + str(fp)) print("Fixpoint after Agg? " + str(fp))
print("Loss " + str(loss)) print("Loss " + str(current_loss))
print() print()
if False: if False:
@ -593,26 +660,13 @@ if __name__ == '__main__':
# TODO: Wtf is happening here? # TODO: Wtf is happening here?
with FixpointExperiment() as exp: with FixpointExperiment() as exp:
run_count = 10000 run_count = 10000
net = TrainingNeuralNetworkDecorator(RecurrentNeuralNetwork(width=2, depth=2))\ net = TrainingNeuralNetworkDecorator(RecurrentNeuralNetwork(width=2, depth=2)
.with_params(epsilon=0.1e-2).with_keras_params(optimizer='sgd', activation='linear') ).with_keras_params(optimizer='sgd', activation='linear')
for run_id in tqdm(range(run_count+1)): for run_id in tqdm(range(run_count+1)):
loss = net.compiled().train() current_loss = net.compiled().train()
if run_id % 500 == 0: if run_id % 500 == 0:
net.print_weights() net.print_weights()
# print(net.apply_to_network(net)) # print(net.apply_to_network(net))
print("Fixpoint? " + str(net.is_fixpoint())) print("Fixpoint? " + str(net.is_fixpoint()))
print("Loss " + str(loss)) print("Loss " + str(current_loss))
print() print()
if False:
# and this gets somewhat interesting... we can still achieve non-trivial fixpoints
# over multiple applications when training enough in-between
with MixedFixpointExperiment() as exp:
for run_id in range(10):
net = TrainingNeuralNetworkDecorator(FFTNeuralNetwork(2, width=2, depth=2))\
.with_params(epsilon=0.0001, activation='sigmoid')
exp.run_net(net, 500, 10)
net.print_weights()
print("Fixpoint? " + str(net.is_fixpoint()))
exp.log(exp.counters)

View File

@ -0,0 +1,66 @@
import os
from experiment import Experiment
# noinspection PyUnresolvedReferences
from soup import Soup
from argparse import ArgumentParser
import numpy as np
import plotly as pl
import plotly.graph_objs as go
import colorlover as cl
import dill
from sklearn.manifold.t_sne import TSNE, PCA
def build_args():
arg_parser = ArgumentParser()
arg_parser.add_argument('-i', '--in_file', nargs=1, type=str)
arg_parser.add_argument('-o', '--out_file', nargs='?', default='out', type=str)
return arg_parser.parse_args()
class DataPlotter:
def __init__(self, path=None):
self.path = path or os.getcwd()
pass
def search_and_apply(self, plotting_function, files_to_look_for=None, absolut_file_or_folder=None):
absolut_file_or_folder, files_to_look_for = self.path or absolut_file_or_folder, list() or files_to_look_for
if os.path.isdir(absolut_file_or_folder):
for sub_file_or_folder in os.scandir(absolut_file_or_folder):
self.search_and_apply(plotting_function, files_to_look_for=files_to_look_for,
absolut_file_or_folder=sub_file_or_folder.path)
elif absolut_file_or_folder.endswith('.dill'):
file_or_folder = os.path.split(absolut_file_or_folder)[-1]
if file_or_folder in files_to_look_for and not os.path.exists(
'{}.html'.format(absolut_file_or_folder[:-5])):
print('Apply Plotting function "{func}" on file "{file}"'.format(func=plotting_function.__name__,
file=absolut_file_or_folder)
)
with open(absolut_file_or_folder, 'rb') as in_f:
exp = dill.load(in_f)
names_dill_location = os.path.join(*os.path.split(absolut_file_or_folder)[:-1], 'all_names.dill')
with open(names_dill_location, 'rb') as in_f:
names = dill.load(in_f)
try:
plotting_function((names, exp), filename='{}.html'.format(absolut_file_or_folder[:-5]))
except ValueError:
pass
except AttributeError:
pass
else:
# This was either another FilyType or Plot.html already exists.
pass
if __name__ == '__main__':
plotter = DataPlotter
pass

View File

@ -0,0 +1,109 @@
import os
from collections import defaultdict
# noinspection PyUnresolvedReferences
from soup import Soup
from experiment import TaskExperiment
from argparse import ArgumentParser
import plotly as pl
import plotly.graph_objs as go
import colorlover as cl
import dill
import numpy as np
def build_args():
arg_parser = ArgumentParser()
arg_parser.add_argument('-i', '--in_file', nargs=1, type=str)
arg_parser.add_argument('-o', '--out_file', nargs='?', default='out', type=str)
return arg_parser.parse_args()
def line_plot(exp: TaskExperiment, filename='lineplot'):
assert isinstance(exp, TaskExperiment), ' This has to be a TaskExperiment!'
traces, data = [], defaultdict(list)
color_scale = cl.scales['3']['div']['RdYlBu']
# Sort data per Key
for message in exp.log_messages:
for key in message.keys():
try:
data[key].append(-0.1 if np.isnan(message[key]) or np.isinf(message[key]) else message[key])
except:
data[key].append(message[key])
for line_id, key in enumerate(data.keys()):
if key not in ['counters', 'id']:
trace = go.Scatter(
x=[x for x in range(len(data[key]))],
y=data[key],
name=key,
line=dict(
color=color_scale[line_id],
width=5
),
)
traces.append(trace)
else:
continue
layout = dict(xaxis=dict(title='Trains per self-application', titlefont=dict(size=20)),
yaxis=dict(title='Average amount of fixpoints found',
titlefont=dict(size=20),
# type='log',
# range=[0, 2]
),
legend=dict(orientation='h', x=0.3, y=-0.3),
# height=800, width=800,
margin=dict(b=0)
)
fig = go.Figure(data=traces, layout=layout)
pl.offline.plot(fig, auto_open=True, filename=filename)
pass
def search_and_apply(absolut_file_or_folder, plotting_function, files_to_look_for=None, override=False):
# ToDo: Clean this Mess
assert os.path.exists(absolut_file_or_folder), f'The given path does not exist! Given: {absolut_file_or_folder}'
files_to_look_for = files_to_look_for or list()
if os.path.isdir(absolut_file_or_folder):
for sub_file_or_folder in os.scandir(absolut_file_or_folder):
search_and_apply(sub_file_or_folder.path, plotting_function,
files_to_look_for=files_to_look_for, override=override)
elif absolut_file_or_folder.endswith('.dill'):
file_or_folder = os.path.split(absolut_file_or_folder)[-1]
if file_or_folder in files_to_look_for or not files_to_look_for:
if not os.path.exists('{}.html'.format(absolut_file_or_folder[:-5])) or override:
print('Apply Plotting function "{func}" on file "{file}"'.format(func=plotting_function.__name__,
file=absolut_file_or_folder)
)
with open(absolut_file_or_folder, 'rb') as in_f:
exp = dill.load(in_f)
try:
plotting_function(exp, filename='{}.html'.format(absolut_file_or_folder[:-5]))
except ValueError:
pass
except AttributeError:
pass
else:
# Plot.html already exists.
pass
else:
# This was a wrong FilyType.
pass
if __name__ == '__main__':
args = build_args()
in_file = args.in_file[0]
out_file = args.out_file
search_and_apply(in_file, line_plot, override=True)

View File

@ -7,12 +7,13 @@ sys.path += os.path.join('..', '.')
from experiment import * from experiment import *
from network import * from network import *
import keras.backend as K
def generate_counters(): def generate_counters():
return {'divergent': 0, 'fix_zero': 0, 'fix_other': 0, 'fix_sec': 0, 'other': 0} return {'divergent': 0, 'fix_zero': 0, 'fix_other': 0, 'fix_sec': 0, 'other': 0}
def count(counters, net, notable_nets=[]):
def count(counters, net, notable_nets: list=None):
notable_nets = notable_nets or list()
if net.is_diverged(): if net.is_diverged():
counters['divergent'] += 1 counters['divergent'] += 1
elif net.is_fixpoint(): elif net.is_fixpoint():
@ -31,7 +32,7 @@ def count(counters, net, notable_nets=[]):
if __name__ == '__main__': if __name__ == '__main__':
with Experiment('applying_fixpoint') as exp: with FixpointExperiment(name='applying_fixpoint') as exp:
exp.trials = 50 exp.trials = 50
exp.run_count = 100 exp.run_count = 100
exp.epsilon = 1e-4 exp.epsilon = 1e-4
@ -40,7 +41,7 @@ if __name__ == '__main__':
for use_bias in [False]: for use_bias in [False]:
net_generators += [lambda activation=activation, use_bias=use_bias: WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)] net_generators += [lambda activation=activation, use_bias=use_bias: WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)]
net_generators += [lambda activation=activation, use_bias=use_bias: AggregatingNeuralNetwork(aggregates=4, width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)] net_generators += [lambda activation=activation, use_bias=use_bias: AggregatingNeuralNetwork(aggregates=4, width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)]
net_generators += [lambda activation=activation, use_bias=use_bias: RecurrentNeuralNetwork(width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)] # net_generators += [lambda activation=activation, use_bias=use_bias: RecurrentNeuralNetwork(width=2, depth=2).with_keras_params(activation=activation, use_bias=use_bias)]
all_counters = [] all_counters = []
all_notable_nets = [] all_notable_nets = []
all_names = [] all_names = []
@ -50,14 +51,14 @@ if __name__ == '__main__':
for _ in tqdm(range(exp.trials)): for _ in tqdm(range(exp.trials)):
net = ParticleDecorator(net_generator()) net = ParticleDecorator(net_generator())
net.with_params(epsilon=exp.epsilon) net.with_params(epsilon=exp.epsilon)
name = str(net.net.__class__.__name__) + " activiation='" + str(net.get_keras_params().get('activation')) + "' use_bias=" + str(net.get_keras_params().get('use_bias')) name = str(net.name) + " activiation='" + str(net.get_keras_params().get('activation')) + "' use_bias=" + str(net.get_keras_params().get('use_bias'))
for run_id in range(exp.run_count): for run_id in range(exp.run_count):
loss = net.self_attack() loss = net.self_attack()
count(counters, net, notable_nets) count(counters, net, notable_nets)
all_counters += [counters] all_counters += [counters]
all_notable_nets += [notable_nets] all_notable_nets += [notable_nets]
all_names += [name] all_names += [name]
K.clear_session() exp.reset_model()
exp.save(all_counters=all_counters) exp.save(all_counters=all_counters)
exp.save(trajectorys=exp.without_particles()) exp.save(trajectorys=exp.without_particles())
# net types reached in the end # net types reached in the end

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
TrainingNeuralNetworkDecorator activiation='linear' use_bias=False
{'xs': [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], 'ys': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 'zs': [0.0, 1.2, 5.2, 7.4, 8.1, 9.1, 9.6, 9.8, 10.0, 9.9, 9.9]}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

View File

@ -11,7 +11,7 @@ from network import *
from soup import * from soup import *
import tensorflow.python.keras.backend as K from tensorflow.python.keras import backend as K
from statistics import mean from statistics import mean
avg = mean avg = mean
@ -59,7 +59,7 @@ def count(counters, soup, notable_nets=None):
if __name__ == '__main__': if __name__ == '__main__':
with SoupExperiment('learn-from-soup') as exp: with SoupExperiment(name='learn-from-soup') as exp:
exp.soup_size = 10 exp.soup_size = 10
exp.soup_life = 100 exp.soup_life = 100
exp.trials = 10 exp.trials = 10
@ -83,10 +83,10 @@ if __name__ == '__main__':
counters = generate_counters() counters = generate_counters()
results = [] results = []
for _ in tqdm(range(exp.trials)): for _ in tqdm(range(exp.trials)):
soup = Soup(exp.soup_size, lambda net_generator=net_generator,exp=exp: TrainingNeuralNetworkDecorator(net_generator()).with_params(epsilon=exp.epsilon)) soup = Soup(exp.soup_size, lambda net_generator=net_generator, exp=exp: TrainingNeuralNetworkDecorator(net_generator()).with_params(epsilon=exp.epsilon))
soup.with_params(attacking_rate=-1, learn_from_rate=0.1, train=0, learn_from_severity=learn_from_severity) soup.with_params(attacking_rate=-1, learn_from_rate=0.1, train=0, learn_from_severity=learn_from_severity)
soup.seed() soup.seed()
name = str(soup.particles[0].net.__class__.__name__) + " activiation='" + str(soup.particles[0].get_keras_params().get('activation')) + "' use_bias=" + str(soup.particles[0].get_keras_params().get('use_bias')) name = str(soup.particles[0].name) + " activiation='" + str(soup.particles[0].get_keras_params().get('activation')) + "' use_bias=" + str(soup.particles[0].get_keras_params().get('use_bias'))
for time in range(exp.soup_life): for time in range(exp.soup_life):
soup.evolve() soup.evolve()
count(counters, soup, notable_nets) count(counters, soup, notable_nets)

View File

@ -9,8 +9,6 @@ sys.path += os.path.join('..', '.')
from experiment import * from experiment import *
from network import * from network import *
import tensorflow.python.keras.backend as K
def generate_counters(): def generate_counters():
""" """
@ -84,7 +82,7 @@ if __name__ == '__main__':
if net.is_diverged() or net.is_fixpoint(): if net.is_diverged() or net.is_fixpoint():
break break
count(counters, net, notable_nets) count(counters, net, notable_nets)
keras.backend.clear_session() exp.reset_model()
xs += [trains_per_selfattack] xs += [trains_per_selfattack]
ys += [float(counters['fix_zero'] + counters['fix_other']) / float(exp.trials)] ys += [float(counters['fix_zero'] + counters['fix_other']) / float(exp.trials)]
all_names += [name] all_names += [name]

View File

@ -104,7 +104,7 @@ if __name__ == '__main__':
for run_id in range(10): for run_id in range(10):
net = TrainingNeuralNetworkDecorator(FFTNeuralNetwork(2, width=2, depth=2))\ net = TrainingNeuralNetworkDecorator(FFTNeuralNetwork(2, width=2, depth=2))\
.with_params(epsilon=0.0001, activation='sigmoid') .with_params(epsilon=0.0001, activation='sigmoid')
exp.run_net(net, 500, 10) exp.run_net(net)
net.print_weights() net.print_weights()

View File

@ -10,23 +10,23 @@ from experiment import *
if __name__ == '__main__': if __name__ == '__main__':
if True: if True:
with SoupExperiment("soup") as exp: with SoupExperiment(namne="soup") as exp:
for run_id in range(1): net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)) \
net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)) \ .with_keras_params(activation='linear').with_params(epsilon=0.0001)
.with_keras_params(activation='linear').with_params(epsilon=0.0001) # net_generator = lambda: TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, 2, 2))\
# net_generator = lambda: TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, 2, 2))\ # .with_keras_params(activation='linear')
# .with_keras_params(activation='linear') # net_generator = lambda: TrainingNeuralNetworkDecorator(FFTNeuralNetwork(4, 2, 2))\
# net_generator = lambda: TrainingNeuralNetworkDecorator(FFTNeuralNetwork(4, 2, 2))\ # .with_keras_params(activation='linear')
# .with_keras_params(activation='linear') # net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
# net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params() soup = Soup(20, net_generator).with_params(remove_divergent=True, remove_zero=True,
soup = Soup(20, net_generator).with_params(remove_divergent=True, remove_zero=True, train=30,
train=30, learn_from_rate=-1)
learn_from_rate=-1) soup.seed()
soup.seed() for _ in tqdm(range(100)):
for _ in tqdm(range(100)): soup.evolve()
soup.evolve() exp.log(soup.count())
exp.log(soup.count()) # you can access soup.historical_particles[particle_uid].states[time_step]['loss']
# you can access soup.historical_particles[particle_uid].states[time_step]['loss'] # or soup.historical_particles[particle_uid].states[time_step]['weights']
# or soup.historical_particles[particle_uid].states[time_step]['weights'] # from soup.dill
# from soup.dill exp.save(soup=soup.without_particles())
exp.save(soup=soup.without_particles()) K.clear_session()

View File

@ -1,85 +1,140 @@
import random import random
from tensorflow.python.keras.layers import Dense, Dropout, BatchNormalization
from tensorflow.python.keras.layers import Input, Layer, Concatenate, RepeatVector, Reshape
from tensorflow.python.keras.models import Sequential, Model
from tensorflow.python.keras import backend as K
from network import * from typing import List, Tuple
# Functions and Operators
from operator import mul
from functools import reduce
from itertools import accumulate
import numpy as np
from task import Task, TaskAdditionOfN
from copy import copy, deepcopy
from network import ParticleDecorator, WeightwiseNeuralNetwork, TrainingNeuralNetworkDecorator, \
EarlyStoppingByInfNanLoss
from experiment import TaskingSoupExperiment
from math import sqrt
def prng(): def prng():
return random.random() return random.random()
class SlicingLayer(Layer):
def __init__(self):
self.kernel: None
self.inputs: int
super(SlicingLayer, self).__init__()
def build(self, input_shape):
# Create a trainable weight variable for this layer.
self.kernel = None
self.inputs = input_shape[-1]
super(SlicingLayer, self).build(input_shape) # Be sure to call this at the end
def call(self, x, **kwargs):
concats = [Concatenate()([x[:, i][..., None]] * self.inputs) for i in range(x.shape[-1].value)]
return concats
def compute_output_shape(self, input_shape):
return [Concatenate()([(None, 1)] * 4) for _ in range(input_shape[-1])]
class Soup(object): class Soup(object):
def __init__(self, size, generator, **kwargs): def __init__(self, size, generator, **kwargs):
self.size = size self.size = size
self.generator = generator self.generator = generator
self.particles = [] self.particles = []
self.historical_particles = {} self.historical_particles = {}
self.params = dict(attacking_rate=0.1, learn_from_rate=0.1, train=0, learn_from_severity=1) self.soup_params = dict(attacking_rate=0.1, learn_from_rate=0.1, train=0, learn_from_severity=1)
self.params.update(kwargs) self.soup_params.update(kwargs)
self.time = 0 self.time = 0
self.is_seeded = False
self.is_compiled = False
def __len__(self):
return len(self.particles)
def __copy__(self): def __copy__(self):
copy_ = Soup(self.size, self.generator, **self.params) copy_ = Soup(self.size, self.generator, **self.soup_params)
copy_.__dict__ = {attr: self.__dict__[attr] for attr in self.__dict__ if copy_.__dict__ = {attr: self.__dict__[attr] for attr in self.__dict__ if
attr not in ['particles', 'historical_particles']} attr not in ['particles', 'historical_particles']}
return copy_ return copy_
def without_particles(self): def without_particles(self):
self_copy = copy.copy(self) self_copy = copy(self)
# self_copy.particles = [particle.states for particle in self.particles] # self_copy.particles = [particle.states for particle in self.particles]
self_copy.historical_particles = {key: val.states for key, val in self.historical_particles.items()} self_copy.historical_particles = {key: val.states for key, val in self.historical_particles.items()}
return self_copy return self_copy
def with_params(self, **kwargs): def with_soup_params(self, **kwargs):
self.params.update(kwargs) self.soup_params.update(kwargs)
return self return self
def generate_particle(self): def generate_particle(self):
new_particle = ParticleDecorator(self.generator()) new_particle = ParticleDecorator(self.generator())
self.historical_particles[new_particle.get_uid()] = new_particle self.historical_particles[new_particle.get_uid()] = new_particle
return new_particle return new_particle
def get_particle(self, uid, otherwise=None): def get_particle(self, uid, otherwise=None):
return self.historical_particles.get(uid, otherwise) return self.historical_particles.get(uid, otherwise)
def seed(self): def seed(self):
self.particles = [] if not self.is_seeded:
for _ in range(self.size): self.particles = []
self.particles += [self.generate_particle()] for _ in range(self.size):
return self self.particles += [self.generate_particle()]
else:
print('already seeded!')
self.is_seeded = True
return self
def evolve(self, iterations=1): def evolve(self, iterations=1):
for _ in range(iterations): for _ in range(iterations):
self.time += 1 self.time += 1
for particle_id, particle in enumerate(self.particles): for particle_id, particle in enumerate(self.particles):
description = {'time': self.time} description = {'time': self.time}
if prng() < self.params.get('attacking_rate'): if prng() < self.soup_params.get('attacking_rate'):
other_particle_id = int(prng() * len(self.particles)) other_particle_id = int(prng() * len(self.particles))
other_particle = self.particles[other_particle_id] other_particle = self.particles[other_particle_id]
particle.attack(other_particle) particle.attack(other_particle)
description['action'] = 'attacking' description['action'] = 'attacking'
description['counterpart'] = other_particle.get_uid() description['counterpart'] = other_particle.get_uid()
if prng() < self.params.get('learn_from_rate'):
if prng() < self.soup_params.get('learn_from_rate'):
other_particle_id = int(prng() * len(self.particles)) other_particle_id = int(prng() * len(self.particles))
other_particle = self.particles[other_particle_id] other_particle = self.particles[other_particle_id]
for _ in range(self.params.get('learn_from_severity', 1)): for _ in range(self.soup_params.get('learn_from_severity', 1)):
particle.learn_from(other_particle) particle.learn_from(other_particle)
description['action'] = 'learn_from' description['action'] = 'learn_from'
description['counterpart'] = other_particle.get_uid() description['counterpart'] = other_particle.get_uid()
for _ in range(self.params.get('train', 0)):
particle.compiled() for _ in range(self.soup_params.get('train', 0)):
# callbacks on save_state are broken for TrainingNeuralNetwork # callbacks on save_state are broken for TrainingNeuralNetwork
loss = particle.train(store_states=False) loss = particle.train(store_states=False)
description['fitted'] = self.params.get('train', 0) description['fitted'] = self.soup_params.get('train', 0)
description['loss'] = loss description['loss'] = loss
description['action'] = 'train_self' description['action'] = 'train_self'
description['counterpart'] = None description['counterpart'] = None
if self.params.get('remove_divergent') and particle.is_diverged():
if self.soup_params.get('remove_divergent') and particle.is_diverged():
new_particle = self.generate_particle() new_particle = self.generate_particle()
self.particles[particle_id] = new_particle self.particles[particle_id] = new_particle
description['action'] = 'divergent_dead' description['action'] = 'divergent_dead'
description['counterpart'] = new_particle.get_uid() description['counterpart'] = new_particle.get_uid()
if self.params.get('remove_zero') and particle.is_zero():
if self.soup_params.get('remove_zero') and particle.is_zero():
new_particle = self.generate_particle() new_particle = self.generate_particle()
self.particles[particle_id] = new_particle self.particles[particle_id] = new_particle
description['action'] = 'zweo_dead' description['action'] = 'zweo_dead'
@ -101,37 +156,221 @@ class Soup(object):
else: else:
counters['other'] += 1 counters['other'] += 1
return counters return counters
def print_all(self): def print_all(self):
for particle in self.particles: for particle in self.particles:
particle.print_weights() particle.print_weights()
print(particle.is_fixpoint()) print(particle.is_fixpoint())
class TaskingSoup(Soup):
@staticmethod
def weights_to_flat_array(weights: List[np.ndarray]) -> np.ndarray:
return np.concatenate([d.ravel() for d in weights])
@staticmethod
def reshape_flat_array(array, shapes: List[Tuple[int]]) -> List[np.ndarray]:
# Same thing, but with an additional np call
# sizes: List[int] = [int(np.prod(shape)) for shape in shapes]
sizes = [reduce(mul, shape) for shape in shapes]
# Split the incoming array into slices for layers
slices = [array[x: y] for x, y in zip(accumulate([0] + sizes), accumulate(sizes))]
# reshape them in accordance to the given shapes
weights = [np.reshape(weight_slice, shape) for weight_slice, shape in zip(slices, shapes)]
return weights
def __init__(self, population_size: int, task: Task, particle_generator, sparsity_rate=0.1, use_bias=False,
safe=True, **kwargs):
if safe:
input_shape_error_message = f'The population size must be devideable by {task.input_shape[-1]}'
assert population_size % task.input_shape[-1] == 0, input_shape_error_message
assert population_size % 2 == 0, 'The population size needs to be of even value'
super(TaskingSoup, self).__init__(population_size, particle_generator, **kwargs)
self.task = task
self.model: Sequential
self.network_params = dict(sparsity_rate=sparsity_rate, early_nan_stopping=True, use_bias=use_bias,
depth=population_size // task.input_shape[-1])
self.network_params.update(kwargs.get('network_params', {}))
self.compile_params = dict(loss='mse', optimizer='sgd')
self.compile_params.update(kwargs.get('compile_params', {}))
def with_network_params(self, **params):
self.network_params.update(params)
def _generate_model(self):
particle_idx_list = list(range(len(self)))
particles_per_layer = len(self) // self.network_params.get('depth')
task_input = Input(self.task.input_shape, name='Task_Input')
# First layer, which is conected to the input layer and independently trainable / not trainable at all.
input_neurons = particles_per_layer * self.task.output_shape
x = Dense(input_neurons, use_bias=self.network_params.get('use_bias'))(task_input)
x = SlicingLayer()(x)
for layer_num in range(self.network_params.get('depth')):
# This needs to be tensors, because particles come as keras models that applicable
x = [self.particles[layer_num*particles_per_layer + i].get_model()(x[i]) for
i in range(particles_per_layer)]
x = [RepeatVector(particles_per_layer)(x[i]) for i in range(particles_per_layer)]
x = [Reshape((particles_per_layer,))(x[i]) for i in range(particles_per_layer)]
x = Concatenate()(x)
x = Dense(self.task.output_shape, use_bias=self.network_params.get('use_bias'), activation='linear')(x)
model = Model(inputs=task_input, outputs=x)
return model
def get_weights(self):
return self.model.get_weights()
def set_weights(self, weights: List[np.ndarray]):
self.model.set_weights(weights)
def set_intermediate_weights(self, weights: List[np.ndarray]):
all_weights = self.get_weights()
all_weights[1:-1] = weights
self.set_weights(all_weights)
def get_intermediate_weights(self):
return self.get_weights()[1:-1]
def seed(self):
K.clear_session()
self.is_compiled = False
super(TaskingSoup, self).seed()
self.model = self._generate_model()
pass
def compile_model(self, **kwargs):
if not self.is_compiled:
compile_params = deepcopy(self.compile_params)
compile_params.update(kwargs)
return self.model.compile(**compile_params)
else:
raise BrokenPipeError('This Model is not compiled yet! Something went wrong in the Pipeline!')
def get_total_weight_amount(self):
if self.is_seeded:
return sum([x.get_amount_of_weights() for x in self.particles])
else:
return 0
def get_shapes(self):
return [x.shape for x in self.get_weights()]
def get_intermediate_shapes(self):
weights = [x.shape for x in self.get_weights()]
return weights[2:-2] if self.network_params.get('use_bias') else weights[1:-1]
def predict(self, x):
return self.model.predict(x)
def evolve(self, iterations=1):
for iteration in range(iterations):
super(TaskingSoup, self).evolve(iterations=1)
self.train_particles()
def get_particle_weights(self):
return np.concatenate([x.get_weights_flat() for x in self.particles])
def get_particle_input_shape(self):
if self.is_seeded:
return tuple([x if x else -1 for x in self.particles[0].get_model().input_shape])
else:
return -1
def set_particle_weights(self, weights):
particle_weight_shape = self.particles[0].shapes(self.particles[0].get_weights())
sizes = [x.get_amount_of_weights() for x in self.particles]
flat_weights = self.weights_to_flat_array(weights)
slices = [flat_weights[x: y] for x, y in zip(accumulate([0] + sizes), accumulate(sizes))]
for particle, slice in zip(self.particles, slices):
new_weights = self.reshape_flat_array(slice, particle_weight_shape)
particle.set_weights(new_weights)
return True
def compiled(self, **kwargs):
if not self.is_compiled:
self.compile_model(**kwargs)
self.is_compiled = True
return self
def train(self, batchsize=1, epoch=0):
self.compiled()
x, y = self.task.get_samples()
callbacks = []
if self.network_params.get('early_nan_stopping'):
callbacks.append(EarlyStoppingByInfNanLoss())
# 'or' does not work on empty lists
callbacks = callbacks if callbacks else None
"""
Please Note:
epochs: Integer. Number of epochs to train the model.
An epoch is an iteration over the entire `x` and `y`
data provided.
Note that in conjunction with `initial_epoch`,
`epochs` is to be understood as "final epoch".
The model is not trained for a number of iterations
given by `epochs`, but merely until the epoch
of index `epochs` is reached."""
history = self.model.fit(x=x, y=y, initial_epoch=epoch, epochs=epoch + 1, verbose=0,
batch_size=batchsize, callbacks=callbacks)
return history.history['loss'][-1]
def train_particles(self, **kwargs):
self.compiled()
weights = self.get_particle_weights()
shaped_weights = self.reshape_flat_array(weights, self.get_intermediate_shapes())
self.set_intermediate_weights(shaped_weights)
_ = self.train(**kwargs) # This returns the loss values
new_weights = self.get_intermediate_weights()
self.set_particle_weights(new_weights)
return
if __name__ == '__main__': if __name__ == '__main__':
if True: if True:
net_generator = lambda: WeightwiseNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params() from task import TaskAdditionOfN
soup_generator = Soup(100, net_generator).with_params(remove_divergent=True, remove_zero=True)
exp = SoupExperiment()
exp.run_exp(net_generator, 1000, soup_generator, 1, False)
# net_generator = lambda: FFTNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params() net_generator = lambda: TrainingNeuralNetworkDecorator(
# net_generator = lambda: AggregatingNeuralNetwork(4, 2, 2).with_keras_params(activation='sigmoid')\ WeightwiseNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random) )
# net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params() soup_generator = lambda: TaskingSoup(20, TaskAdditionOfN(4), net_generator)
with TaskingSoupExperiment(soup_generator, name='solving_soup') as exp:
exp.run_exp(reset_model=False)
if True: if False:
net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)) \ soup_generator = lambda: Soup(10, net_generator).with_soup_params(remove_divergent=True, remove_zero=True)
.with_keras_params(activation='linear').with_params(epsilon=0.0001) with SoupExperiment(soup_generator, name='soup') as exp:
soup_generator = lambda: Soup(100, net_generator).with_params(remove_divergent=True, remove_zero=True, train=20) net_generator = lambda: TrainingNeuralNetworkDecorator(
exp = SoupExperiment(name="soup") WeightwiseNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
)
exp.run_exp(net_generator, 100, soup_generator, 1, False) exp.run_exp(net_generator)
# net_generator = lambda: FFTNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
# net_generator = lambda: AggregatingNeuralNetwork(4, 2, 2).with_keras_params(activation='sigmoid')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
if False:
soup_generator = lambda: Soup(10, net_generator).with_soup_params(remove_divergent=True, remove_zero=True)
with SoupExperiment(soup_generator, name='soup') as exp:
net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)) \
.with_keras_params(activation='linear').with_params(epsilon=0.0001)
exp.run_exp(net_generator)
# net_generator = lambda: TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, 2, 2))
# .with_keras_params(activation='linear')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# net_generator = lambda: TrainingNeuralNetworkDecorator(FFTNeuralNetwork(4, 2, 2))\
# .with_keras_params(activation='linear')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()
# net_generator = lambda: TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, 2, 2))
# .with_keras_params(activation='linear')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# net_generator = lambda: TrainingNeuralNetworkDecorator(FFTNeuralNetwork(4, 2, 2))\
# .with_keras_params(activation='linear')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# net_generator = lambda: RecurrentNeuralNetwork(2, 2).with_keras_params(activation='linear').with_params()

32
code/task.py Normal file
View File

@ -0,0 +1,32 @@
from abc import ABC, abstractmethod
import numpy as np
from typing import Tuple
class Task(ABC):
def __init__(self, input_shape, output_shape, **kwargs):
assert any([x not in kwargs.keys() for x in ["input_shape", "output_shape"]]), 'Dublicated arguments were given'
self.input_shape = input_shape
self.output_shape = output_shape
self.batchsize = kwargs.get('batchsize', 100)
def get_samples(self) -> Tuple[np.ndarray, np.ndarray]:
raise NotImplementedError
class TaskAdditionOfN(Task):
def __init__(self, n: int, input_shape=(4,), output_shape=1, **kwargs):
assert any([x not in kwargs.keys() for x in ["input_shape", "output_shape"]]), 'Dublicated arguments were given'
assert n <= input_shape[0], f'You cannot Add more values (n={n}) than your input is long (in={input_shape}).'
kwargs.update(input_shape=input_shape, output_shape=output_shape)
super(TaskAdditionOfN, self).__init__(**kwargs)
self.n = n
def get_samples(self) -> Tuple[np.ndarray, np.ndarray]:
x = np.zeros((self.batchsize, *self.input_shape))
x[:, :self.n] = np.random.standard_normal((self.batchsize, self.n)) * 0.5
y = np.sum(x, axis=1)
return x, y