TaskDecorator, Tasks and Experiments

This commit is contained in:
Si11ium
2019-06-26 13:40:34 +02:00
parent a12577465c
commit 320c5c26bc
4 changed files with 288 additions and 98 deletions

View File

@@ -24,7 +24,7 @@ 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.next_iteration = 0
@@ -73,11 +73,11 @@ class Experiment(ABC):
raise NotImplementedError raise NotImplementedError
pass pass
def run_exp(self, network_generator, exp_iterations, step_limit=100, prints=False, reset_model=False): def run_exp(self, network_generator, exp_iterations, step_limit=100, prints=False, 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(exp_iterations):
network = network_generator() network = network_generator()
self.run_net(network, step_limit, run_id=run_id + 1) self.run_net(network, step_limit, run_id=run_id + 1, **kwargs)
self.historical_particles[run_id] = network self.historical_particles[run_id] = network
if prints: if prints:
print("Fixpoint? " + str(network.is_fixpoint())) print("Fixpoint? " + str(network.is_fixpoint()))
@@ -96,11 +96,12 @@ 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_exp(self, network_generator, exp_iterations, logging=True, **kwargs): def run_exp(self, network_generator, exp_iterations, logging=True, reset_model=False, **kwargs):
kwargs.update(reset_model=False) kwargs.update(reset_model=False)
super(FixpointExperiment, self).run_exp(network_generator, exp_iterations, **kwargs) super(FixpointExperiment, self).run_exp(network_generator, exp_iterations, **kwargs)
if logging: if logging:
self.log(self.counters) self.log(self.counters)
if reset_model:
self.reset_model() self.reset_model()
def run_net(self, net, step_limit=100, run_id=0, **kwargs): def run_net(self, net, step_limit=100, run_id=0, **kwargs):
@@ -109,7 +110,7 @@ class FixpointExperiment(Experiment):
for i in range(step_limit): for i in range(step_limit):
if net.is_diverged() or net.is_fixpoint(): if net.is_diverged() or net.is_fixpoint():
break break
net.self_attack() net.set_weights(net.apply_to_weights(net.get_weights()))
if run_id: if run_id:
net.save_state(time=i) net.save_state(time=i)
self.count(net) self.count(net)
@@ -141,30 +142,69 @@ class FixpointExperiment(Experiment):
class MixedFixpointExperiment(FixpointExperiment): class MixedFixpointExperiment(FixpointExperiment):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MixedFixpointExperiment, self).__init__(name=kwargs.get('name', self.__class__.__name__)) kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(MixedFixpointExperiment, self).__init__(**kwargs)
def run_net(self, net, step_limit=100, run_id=0, **kwargs): def run_net(self, net, step_limit=100, run_id=0, trains_per_application=100, **kwargs):
for i in range(step_limit): assert hasattr(net, 'train'), 'This Network must be trainable, i.e. use the "TrainingNeuralNetworkDecorator"!'
for evolution_step in range(step_limit):
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() epoch_num = run_id * trains_per_application * evolution_step
with tqdm(postfix=["Loss", dict(value=0)]) as bar: with tqdm(postfix={"epoch": 0, "loss": 0, None: None},
for _ in range(kwargs.get('trains_per_application', 100)): bar_format="This Epoch:{postfix[epoch]} Loss: {postfix[loss]}%|{r_bar}") as bar:
loss = net.train() for epoch in range(epoch_num, epoch_num + trains_per_application):
bar.postfix[1]["value"] = loss loss = net.train(epoch=epoch)
bar.postfix.update(epoch=epoch, loss=loss)
bar.update() bar.update()
if run_id: if run_id and hasattr(net, 'save_sate'):
net.save_state() net.save_state()
self.count(net) self.count(net)
class TaskExperiment(MixedFixpointExperiment):
def __init__(self, **kwargs):
kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(TaskExperiment, self).__init__(**kwargs)
self.task_performance = []
self.self_performance = []
def run_exp(self, network_generator, exp_iterations, logging=True, reset_model=False, **kwargs):
kwargs.update(reset_model=False, logging=logging)
super(FixpointExperiment, self).run_exp(network_generator, exp_iterations, **kwargs)
if reset_model:
self.reset_model()
pass
def run_net(self, net, step_limit=100, run_id=0, **kwargs):
assert hasattr(net, 'evaluate')
kwargs.update(step_limit=step_limit, run_id=run_id)
super(TaskExperiment, self).run_net(net, **kwargs)
# Get Performance without Training
selfX, selfY = net.get_samples(self_samples=True)
self.task_performance.append(net.evaluate(*net.get_samples(task_samples=True),
batchsize=net.get_amount_of_weights()))
self.self_performance.append(net.evaluate(*net.get_samples(self_samples=True),
batchsize=net.get_amount_of_weights()))
pass
class SoupExperiment(Experiment): class SoupExperiment(Experiment):
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(SoupExperiment, 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, exp_iterations,
soup_generator=None, soup_iterations=0, prints=False, **kwargs):
for i in range(soup_iterations): for i in range(soup_iterations):
if not soup_generator:
raise ValueError('A Soup Generator needs to be given!')
soup = soup_generator() soup = soup_generator()
soup.seed() soup.seed()
for _ in tqdm(range(exp_iterations)): for _ in tqdm(range(exp_iterations)):
@@ -181,7 +221,8 @@ class SoupExperiment(Experiment):
class IdentLearningExperiment(Experiment): class IdentLearningExperiment(Experiment):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(IdentLearningExperiment, self).__init__(name=kwargs.get('name', self.__class__.__name__)) kwargs['name'] = self.__class__.__name__ if 'name' not in kwargs else kwargs['name']
super(IdentLearningExperiment, self).__init__(**kwargs)
def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs): def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0, **kwargs):
pass pass

View File

@@ -1,16 +1,24 @@
# Librarys
import numpy as np import numpy as np
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from typing import List, Union, Tuple from typing import List, Union, Tuple
from types import FunctionType from types import FunctionType
# Functions and Operators
from operator import mul from operator import mul
from functools import reduce from functools import reduce
from itertools import accumulate
from statistics import mean
from random import random as prng
# 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
# Experiment Class
from experiment import * from experiment import *
from task import *
# 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'
@@ -35,14 +43,6 @@ 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 max(weights: List[np.ndarray]):
np.max(weights)
@staticmethod
def avg(weights: List[np.ndarray]):
return np.average(weights)
@staticmethod @staticmethod
def are_weights_diverged(weights: List[np.ndarray]) -> bool: def are_weights_diverged(weights: List[np.ndarray]) -> bool:
return any([any((np.isnan(x).any(), np.isinf(x).any())) for x in weights]) return any([any((np.isnan(x).any(), np.isinf(x).any())) for x in weights])
@@ -52,7 +52,7 @@ class NeuralNetwork(ABC):
return any([((lower_bound < x) & (x < upper_bound)).any() for x in weights]) return any([((lower_bound < x) & (x < upper_bound)).any() for x in weights])
@staticmethod @staticmethod
def weight_amount(weights: List[np.ndarray]): def get_weight_amount(weights: List[np.ndarray]):
return sum([x.size for x in weights]) return sum([x.size for x in weights])
@staticmethod @staticmethod
@@ -72,11 +72,12 @@ class NeuralNetwork(ABC):
@staticmethod @staticmethod
def reshape_flat_array(array, shapes: List[Tuple[int]]) -> List[np.ndarray]: def reshape_flat_array(array, shapes: List[Tuple[int]]) -> List[np.ndarray]:
sizes: List[int] = [int(np.prod(shape)) for shape in shapes] # 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] sizes = [reduce(mul, shape) for shape in shapes]
# Split the incoming array into slices for layers # Split the incoming array into slices for layers
slices = [array[x: y] for x, y in zip(np.cumsum([0] + sizes), np.cumsum([0] + sizes)[1:])] slices = [array[x: y] for x, y in zip(accumulate([0] + sizes), accumulate(sizes))]
# reshape them in accordance to the given shapes # reshape them in accordance to the given shapes
weights = [np.reshape(weight_slice, shape) for weight_slice, shape in zip(slices, shapes)] weights = [np.reshape(weight_slice, shape) for weight_slice, shape in zip(slices, shapes)]
return weights return weights
@@ -107,6 +108,9 @@ class NeuralNetwork(ABC):
def print_weights(self, weights=None): def print_weights(self, weights=None):
print(self.repr(weights or self.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_weights(self) -> List[np.ndarray]: def get_weights(self) -> List[np.ndarray]:
return self.model.get_weights() return self.model.get_weights()
@@ -120,31 +124,14 @@ class NeuralNetwork(ABC):
return self.model.set_weights(new_weights) return self.model.set_weights(new_weights)
def apply_to_network(self, other_network) -> List[np.ndarray]: def apply_to_network(self, other_network) -> List[np.ndarray]:
# TODO: add a dogstring, telling the user what this does, e.g. what is applied? """
Take a networks weights and apply _this_ networks function.
:param other_network:
:return:
"""
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.are_weights_diverged(self.get_weights()) return self.are_weights_diverged(self.get_weights())
@@ -171,11 +158,11 @@ class NeuralNetwork(ABC):
return not biggerEpsilon return not biggerEpsilon
def aggregate_weights_by(self, weights: List[np.ndarray], func: FunctionType, num_aggregates: int): def aggregate_weights_by(self, weights: List[np.ndarray], func: FunctionType, num_aggregates: int):
collection_sizes = self.weight_amount(weights) // num_aggregates collection_sizes = self.get_weight_amount(weights) // num_aggregates
flat = self.weights_to_flat_array(weights) flat = self.weights_to_flat_array(weights)
weights = flat[:collection_sizes * num_aggregates].reshape((num_aggregates, -1)) array_for_aggregation = flat[:collection_sizes * num_aggregates].reshape((num_aggregates, -1))
left_overs = flat[collection_sizes * num_aggregates:] left_overs = flat[collection_sizes * num_aggregates:]
aggregated_weights = func(weights, num_aggregates) aggregated_weights = func(array_for_aggregation, num_aggregates)
return aggregated_weights, left_overs return aggregated_weights, left_overs
def shuffle_weights(self, weights: List[np.ndarray]): def shuffle_weights(self, weights: List[np.ndarray]):
@@ -184,13 +171,19 @@ class NeuralNetwork(ABC):
return self.reshape_flat_array_like(flat, weights) return self.reshape_flat_array_like(flat, weights)
@abstractmethod @abstractmethod
def get_samples(self): def get_samples(self, **kwargs):
# TODO: add a dogstring, telling the user what this does, e.g. what is a sample? # TODO: add a dogstring, telling the user what this does, e.g. what is a sample?
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def apply_to_weights(self, old_weights) -> List[np.ndarray]: def apply_to_weights(self, old_weights) -> List[np.ndarray]:
# TODO: add a dogstring, telling the user what this does, e.g. what is applied? """
Take weights as inputs; retunr the evaluation of _this_ network.
"Apply this function".
:param old_weights:
:return:
"""
raise NotImplementedError raise NotImplementedError
@@ -240,6 +233,51 @@ class ParticleDecorator:
def get_states(self): def get_states(self):
return self.states return self.states
def attack(self, other_network):
"""
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:
:return:
"""
other_network.set_weights(self.apply_to_network(other_network))
return self
def self_attack(self, iterations=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 other_network:
:return:
"""
for _ in range(iterations):
self.attack(self)
return self
class TaskDecorator(TaskAdditionOf2):
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()
elif prng() >= kwargs.get('split', 0.5):
return super(TaskDecorator, self).get_samples()
else:
return self.network.get_samples()
class WeightwiseNeuralNetwork(NeuralNetwork): class WeightwiseNeuralNetwork(NeuralNetwork):
@@ -258,8 +296,8 @@ 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, weights: List[np.ndarray] = None): def get_samples(self, **kwargs: List[np.ndarray]):
weights = weights or 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) for x, weight in np.ndenumerate(layer) [weight, idx, *x] for idx, layer in enumerate(weights) for x, weight in np.ndenumerate(layer)
]) ])
@@ -271,7 +309,7 @@ class WeightwiseNeuralNetwork(NeuralNetwork):
def apply_to_weights(self, weights) -> List[np.ndarray]: 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(weights) transformed_weights, _ = self.get_samples(weights=weights)
new_flat_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 self.reshape_flat_array_like(new_flat_weights, weights) return self.reshape_flat_array_like(new_flat_weights, weights)
@@ -334,9 +372,6 @@ 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 self.weight_amount(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, ...])
@@ -362,7 +397,7 @@ class AggregatingNeuralNetwork(NeuralNetwork):
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)]))
@@ -475,13 +510,23 @@ 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, store_states=False, 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 savestatecallback = [SaveStateCallback(network=self, epoch=epoch)] if store_states else None
history = self.network.model.fit(x=x, y=y, epochs=epoch+1, verbose=0, """
batch_size=batchsize, callbacks=savestatecallback, Please Note:
initial_epoch=epoch)
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=savestatecallback)
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):
@@ -489,48 +534,59 @@ 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 False: if True:
# WeightWise Neural Network # WeightWise Neural Network
net_generator = lambda: ParticleDecorator( net_generator = lambda: TrainingNeuralNetworkDecorator(TaskDecorator(
WeightwiseNeuralNetwork(width=2, depth=2 WeightwiseNeuralNetwork(width=2, depth=2))).with_keras_params(activation='linear')
).with_keras_params(activation='linear')) with TaskExperiment() as exp:
with FixpointExperiment() as exp: exp.run_exp(net_generator, 10, trains_per_application=10)
exp.run_exp(net_generator, 10, logging=True)
exp.reset_all() exp.reset_all()
if True: if False:
# Aggregating Neural Network # Aggregating Neural Network
net_generator = lambda: ParticleDecorator( net_generator = lambda: AggregatingNeuralNetwork(aggregates=4, width=2, depth=2)
AggregatingNeuralNetwork(aggregates=4, width=2, depth=2 with MixedFixpointExperiment() as exp:
).with_keras_params()) exp.run_exp(net_generator, 10)
with FixpointExperiment() as exp:
exp.run_exp(net_generator, 10, logging=True)
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:
exp.run_exp(net_generator, 10) exp.run_exp(net_generator, 10)
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 = lambda: TrainingNeuralNetworkDecorator( net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(
ParticleDecorator(WeightwiseNeuralNetwork(width=2, depth=2) width=2, depth=2).with_params(epsilon=0.0001, steplimit=2, trains_per_application=10
)).with_params(epsilon=0.0001).with_keras_params(optimizer='sgd') )).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)
@@ -538,17 +594,18 @@ if __name__ == '__main__':
exp.run_exp(net_generator, 1) exp.run_exp(net_generator, 1)
K.clear_session() K.clear_session()
if True: if False:
with FixpointExperiment() as exp: with FixpointExperiment() as exp:
run_count = 100 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() 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))
@@ -560,8 +617,8 @@ 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() loss = net.compiled().train()
if run_id % 500 == 0: if run_id % 500 == 0:

View File

@@ -1,4 +1,8 @@
import random import random
from operator import mul
from functools import reduce
from tensorflow.python.keras.layers import Dense, Dropout, BatchNormalization
from network import * from network import *
@@ -17,6 +21,7 @@ class Soup(object):
self.params = dict(attacking_rate=0.1, learn_from_rate=0.1, train=0, learn_from_severity=1) self.params = dict(attacking_rate=0.1, learn_from_rate=0.1, train=0, learn_from_severity=1)
self.params.update(kwargs) self.params.update(kwargs)
self.time = 0 self.time = 0
self.is_seeded = False
def __copy__(self): def __copy__(self):
copy_ = Soup(self.size, self.generator, **self.params) copy_ = Soup(self.size, self.generator, **self.params)
@@ -43,9 +48,13 @@ class Soup(object):
return self.historical_particles.get(uid, otherwise) return self.historical_particles.get(uid, otherwise)
def seed(self): def seed(self):
if not self.is_seeded:
self.particles = [] self.particles = []
for _ in range(self.size): for _ in range(self.size):
self.particles += [self.generate_particle()] self.particles += [self.generate_particle()]
else:
print('already seeded!')
self.is_seeded = True
return self return self
def evolve(self, iterations=1): def evolve(self, iterations=1):
@@ -59,6 +68,7 @@ class Soup(object):
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.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]
@@ -66,6 +76,7 @@ class Soup(object):
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)): for _ in range(self.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)
@@ -73,11 +84,13 @@ class Soup(object):
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.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.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
@@ -107,6 +120,56 @@ class Soup(object):
print(particle.is_fixpoint()) print(particle.is_fixpoint())
class SolvingSoup(Soup):
def __init__(self, task: Task, particle_amount: int, particle_generator, depth: int=None, **kwargs):
super(SolvingSoup, self).__init__(particle_amount, particle_generator, **kwargs)
self.model = Sequential()
self.depth = depth or particle_amount - 1
self.task = task
self.network_params = dict()
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 seed(self):
super(SolvingSoup, self).seed()
# Static First Layer
self.model.add(Dense(self.network_params.get('first_layer_units', 10), input_shape=self.task.input_shape))
self.model.add(BatchNormalization())
for layer_num in range(self.depth):
# ToDo !!!!!!!!!!
self.model.add(Dense())
self.model.add(Dropout(rate=self.params.get('sparsity_rate', 0.1)))
has_to_be_zero =
if has_to_be_zero:
raise ValueError(f'This Combination does not Work!, There are still {has_to_be_zero} unnassigned Weights!')
self.model.add(Dense(left_over_units))
self.model.add(Dense(self.task.output_shape))
pass
def compile_model(self, **kwargs):
compile_params = copy.deepcopy(self.compile_params)
compile_params.update(kwargs)
return self.model.compile(**compile_params)
def get_total_weight_amount(self):
if self.is_seeded:
return sum([x.get_amount_of_weights for x in self.particles])
def predict(self, x):
return self.model.predict(x)
if __name__ == '__main__': if __name__ == '__main__':
if True: if True:
with SoupExperiment(name='soup') as exp: with SoupExperiment(name='soup') as exp:
@@ -136,3 +199,4 @@ if __name__ == '__main__':
# .with_keras_params(activation='linear')\ # .with_keras_params(activation='linear')\
# .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random) # .with_params(shuffler=AggregatingNeuralNetwork.shuffle_random)
# 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()

28
code/task.py Normal file
View File

@@ -0,0 +1,28 @@
from abc import ABC, abstractmethod
import numpy as np
from typing import Tuple, List, Union
class Task(ABC):
def __init__(self, input_shape, output_shape, **kwargs):
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 TaskAdditionOf2(Task):
def __init__(self, **kwargs):
super(TaskAdditionOf2, self).__init__(input_shape=(4,), output_shape=(1, ), **kwargs)
def get_samples(self) -> Tuple[np.ndarray, np.ndarray]:
x = np.zeros((self.batchsize, *self.input_shape))
x[:, :2] = np.random.standard_normal((self.batchsize, 2)) * 0.5
y = np.zeros_like(x)
y[:, -1] = np.sum(x, axis=1)
return x, y