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 time
import dill import dill
from tqdm import tqdm from tqdm import tqdm
import copy
from collections import defaultdict
class Experiment: class Experiment:
@ -19,7 +18,7 @@ class Experiment:
self.base_dir = self.experiment_name self.base_dir = self.experiment_name
self.next_iteration = 0 self.next_iteration = 0
self.log_messages = [] self.log_messages = []
self.data_storage = defaultdict(list) self.historical_particles = dict()
def __enter__(self): def __enter__(self):
self.dir = os.path.join(self.base_dir, 'experiments', 'exp-{name}-{id}-{it}'.format( self.dir = os.path.join(self.base_dir, 'experiments', 'exp-{name}-{id}-{it}'.format(
@ -31,7 +30,7 @@ class Experiment:
return self return self
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
self.save(experiment=self) self.save(experiment=self.without_particles())
self.save_log() self.save_log()
self.next_iteration += 1 self.next_iteration += 1
@ -43,14 +42,26 @@ class Experiment:
with open(os.path.join(self.dir, "{name}.txt".format(name=log_name)), "w") as log_file: with open(os.path.join(self.dir, "{name}.txt".format(name=log_name)), "w") as log_file:
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):
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): def save(self, **kwargs):
for name, value in kwargs.items(): for name, value in kwargs.items():
with open(os.path.join(self.dir, "{name}.dill".format(name=name)), "wb") as dill_file: with open(os.path.join(self.dir, "{name}.dill".format(name=name)), "wb") as dill_file:
dill.dump(value, dill_file) dill.dump(value, dill_file)
def add_trajectory_segment(self, run_id, trajectory): def add_trajectory_segment(self, run_id, trajectory):
self.data_storage[run_id].append(trajectory) self.historical_particles[run_id].append(trajectory)
return return

View File

@ -3,7 +3,9 @@ import copy
import numpy as np import numpy as np
from keras.models import Sequential from keras.models import Sequential
from keras.callbacks import Callback
from keras.layers import SimpleRNN, Dense from keras.layers import SimpleRNN, Dense
import keras.backend as K
from util import * from util import *
from experiment import * from experiment import *
@ -12,6 +14,20 @@ from experiment import *
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' 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): class NeuralNetwork(PrintingObject):
@staticmethod @staticmethod
@ -64,6 +80,7 @@ class NeuralNetwork(PrintingObject):
self.params = dict(epsilon=0.00000000000001) self.params = dict(epsilon=0.00000000000001)
self.params.update(params) self.params.update(params)
self.keras_params = dict(activation='linear', use_bias=False) self.keras_params = dict(activation='linear', use_bias=False)
self.states = []
def get_model(self): def get_model(self):
raise NotImplementedError raise NotImplementedError
@ -147,6 +164,23 @@ class NeuralNetwork(PrintingObject):
def print_weights(self, weights=None): def print_weights(self, weights=None):
print(self.repr_weights(weights)) 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): class WeightwiseNeuralNetwork(NeuralNetwork):
@ -600,10 +634,11 @@ class TrainingNeuralNetworkDecorator():
self.model_compiled = True self.model_compiled = True
return self return self
def train(self, batchsize=1): def train(self, batchsize=1, store_states=True, epoch=0):
self.compiled() self.compiled()
x, y = self.net.compute_samples() 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] return history.history['loss'][-1]
def train_other(self, other_network, batchsize=1): def train_other(self, other_network, batchsize=1):
@ -611,6 +646,7 @@ class TrainingNeuralNetworkDecorator():
other_network.compiled() other_network.compiled()
x, y = other_network.net.compute_samples() x, y = other_network.net.compute_samples()
history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize) history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize)
return history.history['loss'][-1] return history.history['loss'][-1]
@ -648,17 +684,21 @@ if __name__ == '__main__':
if True: if True:
# ok so this works quite realiably # ok so this works quite realiably
with FixpointExperiment() as exp: with FixpointExperiment() as exp:
run_count = 1000 for i in range(10):
net = TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(width=2, depth=2))\
.with_params(epsilon=0.0001).with_keras_params(optimizer='sgd') run_count = 1000
for run_id in tqdm(range(run_count+1)): net = TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(width=2, depth=2))\
loss = net.compiled().train() .with_params(epsilon=0.0001).with_keras_params(optimizer='sgd')
if run_id % 100 == 0: for run_id in tqdm(range(run_count+1)):
net.print_weights() loss = net.compiled().train(epoch=run_id)
# print(net.apply_to_network(net)) if run_id % 100 == 0:
print("Fixpoint? " + str(net.is_fixpoint())) net.print_weights()
print("Loss " + str(loss)) # print(net.apply_to_network(net))
print() print("Fixpoint? " + str(net.is_fixpoint()))
print("Loss " + str(loss))
print()
exp.historical_particles[i] = net
K.clear_session()
if False: if False:
# this does not work as the aggregation function screws over the fixpoint computation.... # this does not work as the aggregation function screws over the fixpoint computation....
# TODO: check for fixpoint in aggregated space... # 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_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['attacking'] = other_particle.get_uid() description['action'] = 'attacking'
description['counterpart'] = other_particle.get_uid()
if prng() < self.params.get('train_other_rate'): if prng() < self.params.get('train_other_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.train_other(other_particle) 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)): for _ in range(self.params.get('train', 0)):
loss = particle.compiled().train() loss = particle.compiled().train()
description['fitted'] = self.params.get('train', 0) description['fitted'] = self.params.get('train', 0)
description['loss'] = loss description['loss'] = loss
description['action'] = 'train_self'
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['died'] = True description['action'] = 'divergent_dead'
description['cause'] = 'divergent' description['counterpart'] = new_particle.get_uid()
description['substitute'] = 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
description['died'] = True description['action'] = 'zweo_dead'
description['cause'] = 'zero' description['counterpart'] = new_particle.get_uid()
description['substitute'] = new_particle.get_uid()
particle.save_state(**description) particle.save_state(**description)
def count(self): def count(self):
@ -120,15 +122,22 @@ class ParticleDecorator:
return self.uid return self.uid
def make_state(self, **kwargs): 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) state.update(kwargs)
return state return state
def save_state(self, **kwargs): def save_state(self, **kwargs):
state = self.make_state(**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): def update_state(self, number, **kwargs):
raise NotImplementedError('Result is vague')
if number < len(self.states): if number < len(self.states):
self.states[number] = self.make_state(**kwargs) self.states[number] = self.make_state(**kwargs)
else: else:

View File

@ -24,20 +24,24 @@ def build_args():
return arg_parser.parse_args() return arg_parser.parse_args()
def build_from_soup(soup): def build_from_soup_or_exp(soup):
particles = soup.historical_particles particles = soup.historical_particles
particle_dict = [dict(trajectory=[timestamp['weights'] for timestamp in particle], particle_list = []
fitted=[timestamp['fitted'] for timestamp in particle], for particle in particles.values():
loss=[timestamp['loss'] for timestamp in particle], particle_dict = dict(
time=[timestamp['time'] for timestamp in particle]) for particle in particles.values()] trajectory=[event['weights'] for event in particle],
return particle_dict 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'): def plot_latent_trajectories(soup_or_experiment, filename='latent_trajectory_plot'):
assert isinstance(soup_or_experiment, (Experiment, Soup)) assert isinstance(soup_or_experiment, (Experiment, Soup))
bupu = cl.scales['11']['div']['RdYlGn'] bupu = cl.scales['11']['div']['RdYlGn']
data_dict = soup_or_experiment.data_storage if isinstance(soup_or_experiment, Experiment) \ data_dict = build_from_soup_or_exp(soup_or_experiment)
else build_from_soup(soup_or_experiment)
scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins
# Fit the mebedding space # 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): def norm(val, a=0, b=0.25):
return (val - a) / (b - a) return (val - a) / (b - a)
data_dict = soup_or_experiment.data_storage if isinstance(soup_or_experiment, Experiment) \ data_list = build_from_soup_or_exp(soup_or_experiment)
else build_from_soup(soup_or_experiment)
bupu = cl.scales['11']['div']['RdYlGn'] 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 # Fit the embedding space
transformer = TSNE() transformer = TSNE()
for particle_dict in data_dict: for particle_dict in data_list:
array = np.asarray([np.hstack([x.flatten() for x in timestamp]).flatten() array = np.asarray(particle_dict['trajectory'])
for timestamp in particle_dict['trajectory']])
particle_dict['trajectory'] = array
transformer.fit(array) transformer.fit(array)
# Transform data accordingly and plot it # Transform data accordingly and plot it
data = [] 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']) transformed = transformer._fit(particle_dict['trajectory'])
trace = go.Scatter3d( line_trace = go.Scatter3d(
x=transformed[:, 0], x=transformed[:, 0],
y=transformed[:, 1], y=transformed[:, 1],
z=np.asarray(particle_dict['time']), z=np.asarray(particle_dict['time']),
@ -120,9 +121,28 @@ def plot_latent_trajectories_3D(soup_or_experiment, filename='plot'):
# showlegend=True, # showlegend=True,
hoverinfo='text', hoverinfo='text',
mode='lines') 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'), xaxis=dict(tickwidth=1, title='Transformed X'),
yaxis=dict(tickwidth=1, title='transformed Y'), yaxis=dict(tickwidth=1, title='transformed Y'),
zaxis=dict(tickwidth=1, title='Epoch')), zaxis=dict(tickwidth=1, title='Epoch')),