This commit is contained in:
Si11ium 2019-03-07 12:05:58 +01:00
parent bae997feab
commit 95c2ff4200
4 changed files with 128 additions and 48 deletions

View File

@ -2,8 +2,7 @@ import os
import time
import dill
from tqdm import tqdm
from collections import defaultdict
import copy
class Experiment:
@ -19,7 +18,7 @@ class Experiment:
self.base_dir = self.experiment_name
self.next_iteration = 0
self.log_messages = []
self.data_storage = defaultdict(list)
self.historical_particles = dict()
def __enter__(self):
self.dir = os.path.join(self.base_dir, 'experiments', 'exp-{name}-{id}-{it}'.format(
@ -31,7 +30,7 @@ class Experiment:
return self
def __exit__(self, exc_type, exc_value, traceback):
self.save(experiment=self)
self.save(experiment=self.without_particles())
self.save_log()
self.next_iteration += 1
@ -44,13 +43,25 @@ class Experiment:
for log_message in self.log_messages:
print(str(log_message), file=log_file)
def __copy__(self):
copy_ = Experiment(name=self.experiment_name,)
copy_.__dict__ = {attr: self.__dict__[attr] for attr in self.__dict__ if
attr not in ['particles', 'historical_particles']}
return copy_
def without_particles(self):
self_copy = copy.copy(self)
# 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
def save(self, **kwargs):
for name, value in kwargs.items():
with open(os.path.join(self.dir, "{name}.dill".format(name=name)), "wb") as dill_file:
dill.dump(value, dill_file)
def add_trajectory_segment(self, run_id, trajectory):
self.data_storage[run_id].append(trajectory)
self.historical_particles[run_id].append(trajectory)
return

View File

@ -3,7 +3,9 @@ import copy
import numpy as np
from keras.models import Sequential
from keras.callbacks import Callback
from keras.layers import SimpleRNN, Dense
import keras.backend as K
from util import *
from experiment import *
@ -12,6 +14,20 @@ from experiment import *
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
class SaveStateCallback(Callback):
def __init__(self, net, epoch=0):
super(SaveStateCallback, self).__init__()
self.net = net
self.init_epoch = epoch
def on_epoch_end(self, epoch, logs={}):
description = dict(time=epoch+self.init_epoch)
description['action'] = 'train_self'
description['counterpart'] = None
self.net.save_state(**description)
return
class NeuralNetwork(PrintingObject):
@staticmethod
@ -64,6 +80,7 @@ class NeuralNetwork(PrintingObject):
self.params = dict(epsilon=0.00000000000001)
self.params.update(params)
self.keras_params = dict(activation='linear', use_bias=False)
self.states = []
def get_model(self):
raise NotImplementedError
@ -147,6 +164,23 @@ class NeuralNetwork(PrintingObject):
def print_weights(self, weights=None):
print(self.repr_weights(weights))
def make_state(self, **kwargs):
weights = self.get_weights_flat()
state = {'class': self.__class__.__name__, 'weights': weights}
if any(np.isinf(weights)):
return None
state.update(kwargs)
return state
def save_state(self, **kwargs):
state = self.make_state(**kwargs)
if state is not None:
self.states += [state]
else:
pass
def get_states(self):
return self.states
class WeightwiseNeuralNetwork(NeuralNetwork):
@ -600,10 +634,11 @@ class TrainingNeuralNetworkDecorator():
self.model_compiled = True
return self
def train(self, batchsize=1):
def train(self, batchsize=1, store_states=True, epoch=0):
self.compiled()
x, y = self.net.compute_samples()
history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize)
savestatecallback = SaveStateCallback(net=self.net, epoch=epoch) if store_states else None
history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize, callbacks=[savestatecallback])
return history.history['loss'][-1]
def train_other(self, other_network, batchsize=1):
@ -611,6 +646,7 @@ class TrainingNeuralNetworkDecorator():
other_network.compiled()
x, y = other_network.net.compute_samples()
history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize)
return history.history['loss'][-1]
@ -648,17 +684,21 @@ if __name__ == '__main__':
if True:
# ok so this works quite realiably
with FixpointExperiment() as exp:
run_count = 1000
net = TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(width=2, depth=2))\
.with_params(epsilon=0.0001).with_keras_params(optimizer='sgd')
for run_id in tqdm(range(run_count+1)):
loss = net.compiled().train()
if run_id % 100 == 0:
net.print_weights()
# print(net.apply_to_network(net))
print("Fixpoint? " + str(net.is_fixpoint()))
print("Loss " + str(loss))
print()
for i in range(10):
run_count = 1000
net = TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(width=2, depth=2))\
.with_params(epsilon=0.0001).with_keras_params(optimizer='sgd')
for run_id in tqdm(range(run_count+1)):
loss = net.compiled().train(epoch=run_id)
if run_id % 100 == 0:
net.print_weights()
# print(net.apply_to_network(net))
print("Fixpoint? " + str(net.is_fixpoint()))
print("Loss " + str(loss))
print()
exp.historical_particles[i] = net
K.clear_session()
if False:
# this does not work as the aggregation function screws over the fixpoint computation....
# TODO: check for fixpoint in aggregated space...

View File

@ -57,28 +57,30 @@ class Soup:
other_particle_id = int(prng() * len(self.particles))
other_particle = self.particles[other_particle_id]
particle.attack(other_particle)
description['attacking'] = other_particle.get_uid()
description['action'] = 'attacking'
description['counterpart'] = other_particle.get_uid()
if prng() < self.params.get('train_other_rate'):
other_particle_id = int(prng() * len(self.particles))
other_particle = self.particles[other_particle_id]
particle.train_other(other_particle)
description['training'] = other_particle.get_uid()
description['action'] = 'train_other'
description['counterpart'] = other_particle.get_uid()
for _ in range(self.params.get('train', 0)):
loss = particle.compiled().train()
description['fitted'] = self.params.get('train', 0)
description['loss'] = loss
description['action'] = 'train_self'
description['counterpart'] = None
if self.params.get('remove_divergent') and particle.is_diverged():
new_particle = self.generate_particle()
self.particles[particle_id] = new_particle
description['died'] = True
description['cause'] = 'divergent'
description['substitute'] = new_particle.get_uid()
description['action'] = 'divergent_dead'
description['counterpart'] = new_particle.get_uid()
if self.params.get('remove_zero') and particle.is_zero():
new_particle = self.generate_particle()
self.particles[particle_id] = new_particle
description['died'] = True
description['cause'] = 'zero'
description['substitute'] = new_particle.get_uid()
description['action'] = 'zweo_dead'
description['counterpart'] = new_particle.get_uid()
particle.save_state(**description)
def count(self):
@ -120,15 +122,22 @@ class ParticleDecorator:
return self.uid
def make_state(self, **kwargs):
state = {'class': self.net.__class__.__name__, 'weights': self.net.get_weights()}
weights = self.net.get_weights_flat()
if any(np.isinf(weights)):
return None
state = {'class': self.net.__class__.__name__, 'weights': weights}
state.update(kwargs)
return state
def save_state(self, **kwargs):
state = self.make_state(**kwargs)
self.states += [state]
if state is not None:
self.states += [state]
else:
pass
def update_state(self, number, **kwargs):
raise NotImplementedError('Result is vague')
if number < len(self.states):
self.states[number] = self.make_state(**kwargs)
else:

View File

@ -24,20 +24,24 @@ def build_args():
return arg_parser.parse_args()
def build_from_soup(soup):
def build_from_soup_or_exp(soup):
particles = soup.historical_particles
particle_dict = [dict(trajectory=[timestamp['weights'] for timestamp in particle],
fitted=[timestamp['fitted'] for timestamp in particle],
loss=[timestamp['loss'] for timestamp in particle],
time=[timestamp['time'] for timestamp in particle]) for particle in particles.values()]
return particle_dict
particle_list = []
for particle in particles.values():
particle_dict = dict(
trajectory=[event['weights'] for event in particle],
time=[event['time'] for event in particle],
action=[event['action'] for event in particle],
counterpart=[event['counterpart'] for event in particle]
)
particle_list.append(particle_dict)
return particle_list
def plot_latent_trajectories(soup_or_experiment, filename='latent_trajectory_plot'):
assert isinstance(soup_or_experiment, (Experiment, Soup))
bupu = cl.scales['11']['div']['RdYlGn']
data_dict = soup_or_experiment.data_storage if isinstance(soup_or_experiment, Experiment) \
else build_from_soup(soup_or_experiment)
data_dict = build_from_soup_or_exp(soup_or_experiment)
scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins
# Fit the mebedding space
@ -91,25 +95,22 @@ def plot_latent_trajectories_3D(soup_or_experiment, filename='plot'):
def norm(val, a=0, b=0.25):
return (val - a) / (b - a)
data_dict = soup_or_experiment.data_storage if isinstance(soup_or_experiment, Experiment) \
else build_from_soup(soup_or_experiment)
data_list = build_from_soup_or_exp(soup_or_experiment)
bupu = cl.scales['11']['div']['RdYlGn']
scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins
scale = cl.interp(bupu, len(data_list)+1) # Map color scale to N bins
# Fit the embedding space
transformer = TSNE()
for particle_dict in data_dict:
array = np.asarray([np.hstack([x.flatten() for x in timestamp]).flatten()
for timestamp in particle_dict['trajectory']])
particle_dict['trajectory'] = array
for particle_dict in data_list:
array = np.asarray(particle_dict['trajectory'])
transformer.fit(array)
# Transform data accordingly and plot it
data = []
for p_id, particle_dict in enumerate(data_dict):
for p_id, particle_dict in enumerate(data_list):
transformed = transformer._fit(particle_dict['trajectory'])
trace = go.Scatter3d(
line_trace = go.Scatter3d(
x=transformed[:, 0],
y=transformed[:, 1],
z=np.asarray(particle_dict['time']),
@ -120,9 +121,28 @@ def plot_latent_trajectories_3D(soup_or_experiment, filename='plot'):
# showlegend=True,
hoverinfo='text',
mode='lines')
data.append(trace)
layout = go.Layout(scene=dict(aspectratio=dict(x=2, y=2, z=1),
line_start = go.Scatter3d(mode='markers', x=[transformed[0, 0]], y=[transformed[0, 1]],
z=np.asarray(particle_dict['time'][0]),
marker=dict(
color='rgb(255, 0, 0)',
size=4
),
showlegend=False
)
line_end = go.Scatter3d(mode='markers', x=[transformed[-1, 0]], y=[transformed[-1, 1]],
z=np.asarray(particle_dict['time'][-1]),
marker=dict(
color='rgb(0, 0, 0)',
size=4
),
showlegend=False
)
data.extend([line_trace, line_start, line_end])
layout = go.Layout(scene=dict(aspectratio=dict(x=2, y=2, z=2),
xaxis=dict(tickwidth=1, title='Transformed X'),
yaxis=dict(tickwidth=1, title='transformed Y'),
zaxis=dict(tickwidth=1, title='Epoch')),