diff --git a/.gitignore b/.gitignore index 125fe8f..e52f977 100644 --- a/.gitignore +++ b/.gitignore @@ -1,573 +1,573 @@ -# Created by https://www.gitignore.io/api/data,linux,macos,python,windows,pycharm,database,jupyternotebook -# Edit at https://www.gitignore.io/?templates=data,linux,macos,python,windows,pycharm,database,jupyternotebook - - -### Local Datasets ### -code/raw_runs -code/processed_runs -experiments/ - - -### Data ### -*.csv -*.dat -*.efx -*.gbr -*.key -*.pps -*.ppt -*.pptx -*.sdf -*.tax2010 -*.vcf -*.xml - -### Database ### -*.accdb -*.db -*.dbf -*.mdb -*.pdb -*.sqlite3 - -### JupyterNotebook ### -.ipynb_checkpoints -*/.ipynb_checkpoints/* - -# Remove previous ipynb_checkpoints -# git rm -r .ipynb_checkpoints/ -# - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### PyCharm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### PyCharm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -.idea/sonarlint - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -### Python Patch ### -.venv/ - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# pycharm -.idea/ - -####################################### -#### Tex related -## Core latex/pdflatex auxiliary files: -*.aux -*.lof -*.log -*.lot -*.fls -*.out -*.toc -*.fmt -*.fot -*.cb -*.cb2 -.*.lb - -## Intermediate documents: -*.dvi -*.xdv -*-converted-to.* -# these rules might exclude image files for figures etc. -# *.ps -# *.eps -# *.pdf - -## Generated if empty string is given at "Please type another file name for output:" -.pdf - -## Bibliography auxiliary files (bibtex/biblatex/biber): -*.bbl -*.bcf -*.blg -*-blx.aux -*-blx.bib -*.run.xml - -## Build tool auxiliary files: -*.fdb_latexmk -*.synctex -*.synctex(busy) -*.synctex.gz -*.synctex.gz(busy) -*.pdfsync - -## Build tool directories for auxiliary files -# latexrun -latex.out/ - -## Auxiliary and intermediate files from other packages: -# algorithms -*.alg -*.loa - -# achemso -acs-*.bib - -# amsthm -*.thm - -# beamer -*.nav -*.pre -*.snm -*.vrb - -# changes -*.soc - -# comment -*.cut - -# cprotect -*.cpt - -# elsarticle (documentclass of Elsevier journals) -*.spl - -# endnotes -*.ent - -# fixme -*.lox - -# feynmf/feynmp -*.mf -*.mp -*.t[1-9] -*.t[1-9][0-9] -*.tfm - -#(r)(e)ledmac/(r)(e)ledpar -*.end -*.?end -*.[1-9] -*.[1-9][0-9] -*.[1-9][0-9][0-9] -*.[1-9]R -*.[1-9][0-9]R -*.[1-9][0-9][0-9]R -*.eledsec[1-9] -*.eledsec[1-9]R -*.eledsec[1-9][0-9] -*.eledsec[1-9][0-9]R -*.eledsec[1-9][0-9][0-9] -*.eledsec[1-9][0-9][0-9]R - -# glossaries -*.acn -*.acr -*.glg -*.glo -*.gls -*.glsdefs - -# gnuplottex -*-gnuplottex-* - -# gregoriotex -*.gaux -*.gtex - -# htlatex -*.4ct -*.4tc -*.idv -*.lg -*.trc -*.xref - -# hyperref -*.brf - -# knitr -*-concordance.tex -# TODO Comment the next line if you want to keep your tikz graphics files -*.tikz -*-tikzDictionary - -# listings -*.lol - -# makeidx -*.idx -*.ilg -*.ind -*.ist - -# minitoc -*.maf -*.mlf -*.mlt -*.mtc[0-9]* -*.slf[0-9]* -*.slt[0-9]* -*.stc[0-9]* - -# minted -_minted* -*.pyg - -# morewrites -*.mw - -# nomencl -*.nlg -*.nlo -*.nls - -# pax -*.pax - -# pdfpcnotes -*.pdfpc - -# sagetex -*.sagetex.sage -*.sagetex.py -*.sagetex.scmd - -# scrwfile -*.wrt - -# sympy -*.sout -*.sympy -sympy-plots-for-*.tex/ - -# pdfcomment -*.upa -*.upb - -# pythontex -*.pytxcode -pythontex-files-*/ - -# tcolorbox -*.listing - -# thmtools -*.loe - -# TikZ & PGF -*.dpth -*.md5 -*.auxlock - -# todonotes -*.tdo - -# vhistory -*.hst -*.ver - -# easy-todo -*.lod - -# xcolor -*.xcp - -# xmpincl -*.xmpi - -# xindy -*.xdy - -# xypic precompiled matrices -*.xyc - -# endfloat -*.ttt -*.fff - -# Latexian -TSWLatexianTemp* - -## Editors: -# WinEdt -*.bak -*.sav - -# Texpad -.texpadtmp - -# LyX -*.lyx~ - -# Kile -*.backup - -# KBibTeX -*~[0-9]* - -# auto folder when using emacs and auctex -./auto/* -*.el - -# expex forward references with \gathertags -*-tags.tex - -# standalone packages -*.sta - - -# End of https://www.gitignore.io/api/data,linux,macos,python,windows,pycharm,database,jupyternotebook +# Created by https://www.gitignore.io/api/data,linux,macos,python,windows,pycharm,database,jupyternotebook +# Edit at https://www.gitignore.io/?templates=data,linux,macos,python,windows,pycharm,database,jupyternotebook + + +### Local Datasets ### +code/raw_runs +code/processed_runs +experiments/ + + +### Data ### +*.csv +*.dat +*.efx +*.gbr +*.key +*.pps +*.ppt +*.pptx +*.sdf +*.tax2010 +*.vcf +*.xml + +### Database ### +*.accdb +*.db +*.dbf +*.mdb +*.pdb +*.sqlite3 + +### JupyterNotebook ### +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ +# + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### Python Patch ### +.venv/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# pycharm +.idea/ + +####################################### +#### Tex related +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# makeidx +*.idx +*.ilg +*.ind +*.ist + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + + +# End of https://www.gitignore.io/api/data,linux,macos,python,windows,pycharm,database,jupyternotebook diff --git a/README.md b/README.md index 8f3a59a..086618e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# bannana-networks - +# bannana-networks + diff --git a/code/experiment.py b/code/experiment.py index 1117603..38dd3c2 100644 --- a/code/experiment.py +++ b/code/experiment.py @@ -1,113 +1,113 @@ -import os -import time -import dill -from tqdm import tqdm - -from collections import defaultdict - - -class Experiment: - - @staticmethod - def from_dill(path): - with open(path, "rb") as dill_file: - return dill.load(dill_file) - - def __init__(self, name=None, ident=None): - self.experiment_id = ident or time.time() - self.experiment_name = name or 'unnamed_experiment' - self.base_dir = self.experiment_name - self.next_iteration = 0 - self.log_messages = [] - self.data_storage = defaultdict(list) - - def __enter__(self): - self.dir = os.path.join(self.base_dir, 'experiments', 'exp-{name}-{id}-{it}'.format( - name=self.experiment_name, id=self.experiment_id, it=self.next_iteration) - ) - - os.makedirs(self.dir) - print("** created {dir} **".format(dir=self.dir)) - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.save(experiment=self) - self.save_log() - self.next_iteration += 1 - - def log(self, message, **kwargs): - self.log_messages.append(message) - print(message, **kwargs) - - def save_log(self, log_name="log"): - with open(os.path.join(self.dir, "{name}.txt".format(name=log_name)), "w") as log_file: - for log_message in self.log_messages: - print(str(log_message), file=log_file) - - 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) - return - - -class FixpointExperiment(Experiment): - - def __init__(self): - super().__init__(name=self.__class__.__name__) - self.counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0) - self.interesting_fixpoints = [] - - def run_net(self, net, step_limit=100, run_id=0): - i = 0 - while i < step_limit and not net.is_diverged() and not net.is_fixpoint(): - net.self_attack() - i += 1 - if run_id: - weights = net.get_weights_flat() - self.add_trajectory_segment(run_id, weights) - self.count(net) - - def count(self, net): - if net.is_diverged(): - self.counters['divergent'] += 1 - elif net.is_fixpoint(): - if net.is_zero(): - self.counters['fix_zero'] += 1 - else: - self.counters['fix_other'] += 1 - self.interesting_fixpoints.append(net.get_weights()) - elif net.is_fixpoint(2): - self.counters['fix_sec'] += 1 - else: - self.counters['other'] += 1 - - -class MixedFixpointExperiment(FixpointExperiment): - - def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0): - # TODO Where to place the trajectory storage ? - # weights = net.get_weights() - # self.add_trajectory_segment(run_id, weights) - - i = 0 - while i < step_limit and not net.is_diverged() and not net.is_fixpoint(): - net.self_attack() - with tqdm(postfix=["Loss", dict(value=0)]) as bar: - for _ in range(trains_per_application): - loss = net.compiled().train() - bar.postfix[1]["value"] = loss - bar.update() - i += 1 - self.count(net) - - -class SoupExperiment(Experiment): - pass - - -class IdentLearningExperiment(Experiment): - pass +import os +import time +import dill +from tqdm import tqdm + +from collections import defaultdict + + +class Experiment: + + @staticmethod + def from_dill(path): + with open(path, "rb") as dill_file: + return dill.load(dill_file) + + def __init__(self, name=None, ident=None): + self.experiment_id = ident or time.time() + self.experiment_name = name or 'unnamed_experiment' + self.base_dir = self.experiment_name + self.next_iteration = 0 + self.log_messages = [] + self.data_storage = defaultdict(list) + + def __enter__(self): + self.dir = os.path.join(self.base_dir, 'experiments', 'exp-{name}-{id}-{it}'.format( + name=self.experiment_name, id=self.experiment_id, it=self.next_iteration) + ) + + os.makedirs(self.dir) + print("** created {dir} **".format(dir=self.dir)) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.save(experiment=self) + self.save_log() + self.next_iteration += 1 + + def log(self, message, **kwargs): + self.log_messages.append(message) + print(message, **kwargs) + + def save_log(self, log_name="log"): + with open(os.path.join(self.dir, "{name}.txt".format(name=log_name)), "w") as log_file: + for log_message in self.log_messages: + print(str(log_message), file=log_file) + + 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) + return + + +class FixpointExperiment(Experiment): + + def __init__(self): + super().__init__(name=self.__class__.__name__) + self.counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0) + self.interesting_fixpoints = [] + + def run_net(self, net, step_limit=100, run_id=0): + i = 0 + while i < step_limit and not net.is_diverged() and not net.is_fixpoint(): + net.self_attack() + i += 1 + if run_id: + weights = net.get_weights_flat() + self.add_trajectory_segment(run_id, weights) + self.count(net) + + def count(self, net): + if net.is_diverged(): + self.counters['divergent'] += 1 + elif net.is_fixpoint(): + if net.is_zero(): + self.counters['fix_zero'] += 1 + else: + self.counters['fix_other'] += 1 + self.interesting_fixpoints.append(net.get_weights()) + elif net.is_fixpoint(2): + self.counters['fix_sec'] += 1 + else: + self.counters['other'] += 1 + + +class MixedFixpointExperiment(FixpointExperiment): + + def run_net(self, net, trains_per_application=100, step_limit=100, run_id=0): + # TODO Where to place the trajectory storage ? + # weights = net.get_weights() + # self.add_trajectory_segment(run_id, weights) + + i = 0 + while i < step_limit and not net.is_diverged() and not net.is_fixpoint(): + net.self_attack() + with tqdm(postfix=["Loss", dict(value=0)]) as bar: + for _ in range(trains_per_application): + loss = net.compiled().train() + bar.postfix[1]["value"] = loss + bar.update() + i += 1 + self.count(net) + + +class SoupExperiment(Experiment): + pass + + +class IdentLearningExperiment(Experiment): + pass diff --git a/code/latent_trajectory_plot.html b/code/latent_trajectory_plot.html index 5d6eef6..1433788 100644 --- a/code/latent_trajectory_plot.html +++ b/code/latent_trajectory_plot.html @@ -1,7 +1,7 @@ -
\ No newline at end of file diff --git a/code/methods.py b/code/methods.py index dd2497b..39e8dd6 100644 --- a/code/methods.py +++ b/code/methods.py @@ -1,191 +1,191 @@ -import tensorflow as tf -from keras.models import Sequential, Model -from keras.layers import SimpleRNN, Dense -from keras.layers import Input, TimeDistributed -from tqdm import tqdm -import time -import os -import dill - -from experiment import Experiment - -import itertools - -from typing import Union -import numpy as np - -class Network(object): - def __init__(self, features, cells, layers, bias=False, recurrent=False): - self.features = features - self.cells = cells - self.num_layer = layers - bias_params = cells if bias else 0 - - # Recurrent network - if recurrent: - # First RNN - p_layer_1 = (self.features * self.cells + self.cells ** 2 + bias_params) - # All other RNN Layers - p_layer_n = (self.cells * self.cells + self.cells ** 2 + bias_params) * (self.num_layer - 1) - else: - # First Dense - p_layer_1 = (self.features * self.cells + bias_params) - # All other Dense Layers - p_layer_n = (self.cells * self.cells + bias_params) * (self.num_layer - 1) - # Final Dense - p_layer_out = self.features * self.cells + bias_params - self.parameters = np.sum([p_layer_1, p_layer_n, p_layer_out]) - # Build network - cell = SimpleRNN if recurrent else Dense - self.inputs, x = Input(shape=(self.parameters // self.features, - self.features) if recurrent else (self.features,)), None - - for layer in range(self.num_layer): - if recurrent: - x = SimpleRNN(self.cells, activation=None, use_bias=False, - return_sequences=True)(self.inputs if layer == 0 else x) - else: - x = Dense(self.cells, activation=None, use_bias=False, - )(self.inputs if layer == 0 else x) - self.outputs = Dense(self.features if recurrent else 1, activation=None, use_bias=False)(x) - print('Network initialized, i haz {p} params @:{e}Features: {f}{e}Cells: {c}{e}Layers: {l}'.format( - p=self.parameters, l=self.num_layer, c=self.cells, f=self.features, e='\n{}'.format(' ' * 5)) - ) - pass - - def get_inputs(self): - return self.inputs - - def get_outputs(self): - return self.outputs - - -class _BaseNetwork(Model): - - def __init__(self, **kwargs): - super(_BaseNetwork, self).__init__(**kwargs) - # This is dirty - self.features = None - - def get_weights_flat(self): - weights = super().get_weights() - flat = np.asarray(np.concatenate([x.flatten() for x in weights])) - return flat - - def step(self, x): - pass - - def step_other(self, other: Union[Sequential, Model]) -> bool: - pass - - def get_parameter_count(self): - return np.sum([np.prod(x.shape) for x in self.get_weights()]) - - def train_on_batch(self, *args, **kwargs): - raise NotImplementedError - - def compile(self, *args, **kwargs): - raise NotImplementedError - - @staticmethod - def mean_abs_error(labels, predictions): - return np.mean(np.abs(predictions - labels), axis=-1) - - @staticmethod - def mean_sqrd_error(labels, predictions): - return np.mean(np.square(predictions - labels), axis=-1) - - -class RecurrentNetwork(_BaseNetwork): - def __init__(self, network: Network, *args, **kwargs): - super().__init__(inputs=network.inputs, outputs=network.outputs) - self.features = network.features - self.parameters = network.parameters - assert self.parameters == self.get_parameter_count() - - def step(self, x): - shaped = np.reshape(x, (1, -1, self.features)) - return self.predict(shaped).flatten() - - def fit(self, epochs=500, **kwargs): - losses = [] - with tqdm(total=epochs, ascii=True, - desc='Type: {t}'. format(t=self.__class__.__name__), - postfix=["Loss", dict(value=0)]) as bar: - for _ in range(epochs): - x = self.get_weights_flat() - y = self.step(x) - weights = self.get_weights() - global_idx = 0 - for idx, weight_matrix in enumerate(weights): - flattened = weight_matrix.flatten() - new_weights = y[global_idx:global_idx + flattened.shape[0]] - weights[idx] = np.reshape(new_weights, weight_matrix.shape) - global_idx += flattened.shape[0] - losses.append(self.mean_sqrd_error(y.flatten(), self.get_weights_flat())) - self.set_weights(weights) - bar.postfix[1]["value"] = losses[-1] - bar.update() - return losses - - -class FeedForwardNetwork(_BaseNetwork): - def __init__(self, network:Network, **kwargs): - super().__init__(inputs=network.inputs, outputs=network.outputs, **kwargs) - self.features = network.features - self.parameters = network.parameters - self.num_layer = network.num_layer - self.num_cells = network.cells - # assert self.parameters == self.get_parameter_count() - - def step(self, x): - return self.predict(x) - - def step_other(self, x): - return self.predict(x) - - def fit(self, epochs=500, **kwargs): - losses = [] - with tqdm(total=epochs, ascii=True, - desc='Type: {t} @ Epoch:'. format(t=self.__class__.__name__), - postfix=["Loss", dict(value=0)]) as bar: - for _ in range(epochs): - all_weights = self.get_weights_flat() - cell_idx = np.apply_along_axis(lambda x: x/self.num_cells, 0, np.arange(int(self.get_parameter_count()))) - xc = np.concatenate((all_weights[..., None], cell_idx[..., None]), axis=1) - - y = self.step(xc) - - weights = self.get_weights() - global_idx = 0 - - for idx, weight_matrix in enumerate(weights): - - # UPDATE THE WEIGHTS - flattened = weight_matrix.flatten() - new_weights = y[global_idx:global_idx + flattened.shape[0], 0] - weights[idx] = np.reshape(new_weights, weight_matrix.shape) - global_idx += flattened.shape[0] - - losses.append(self.mean_sqrd_error(y[:, 0].flatten(), self.get_weights_flat())) - self.set_weights(weights) - bar.postfix[1]["value"] = losses[-1] - bar.update() - return losses - - -if __name__ == '__main__': - with Experiment() as exp: - features, cells, layers = 2, 2, 2 - use_recurrent = False - if use_recurrent: - network = Network(features, cells, layers, recurrent=use_recurrent) - r = RecurrentNetwork(network) - loss = r.fit(epochs=10) - exp.save(rnet=r) - else: - network = Network(features, cells, layers, recurrent=use_recurrent) - ff = FeedForwardNetwork(network) - loss = ff.fit(epochs=10) - exp.save(ffnet=ff) - print(loss) +import tensorflow as tf +from keras.models import Sequential, Model +from keras.layers import SimpleRNN, Dense +from keras.layers import Input, TimeDistributed +from tqdm import tqdm +import time +import os +import dill + +from experiment import Experiment + +import itertools + +from typing import Union +import numpy as np + +class Network(object): + def __init__(self, features, cells, layers, bias=False, recurrent=False): + self.features = features + self.cells = cells + self.num_layer = layers + bias_params = cells if bias else 0 + + # Recurrent network + if recurrent: + # First RNN + p_layer_1 = (self.features * self.cells + self.cells ** 2 + bias_params) + # All other RNN Layers + p_layer_n = (self.cells * self.cells + self.cells ** 2 + bias_params) * (self.num_layer - 1) + else: + # First Dense + p_layer_1 = (self.features * self.cells + bias_params) + # All other Dense Layers + p_layer_n = (self.cells * self.cells + bias_params) * (self.num_layer - 1) + # Final Dense + p_layer_out = self.features * self.cells + bias_params + self.parameters = np.sum([p_layer_1, p_layer_n, p_layer_out]) + # Build network + cell = SimpleRNN if recurrent else Dense + self.inputs, x = Input(shape=(self.parameters // self.features, + self.features) if recurrent else (self.features,)), None + + for layer in range(self.num_layer): + if recurrent: + x = SimpleRNN(self.cells, activation=None, use_bias=False, + return_sequences=True)(self.inputs if layer == 0 else x) + else: + x = Dense(self.cells, activation=None, use_bias=False, + )(self.inputs if layer == 0 else x) + self.outputs = Dense(self.features if recurrent else 1, activation=None, use_bias=False)(x) + print('Network initialized, i haz {p} params @:{e}Features: {f}{e}Cells: {c}{e}Layers: {l}'.format( + p=self.parameters, l=self.num_layer, c=self.cells, f=self.features, e='\n{}'.format(' ' * 5)) + ) + pass + + def get_inputs(self): + return self.inputs + + def get_outputs(self): + return self.outputs + + +class _BaseNetwork(Model): + + def __init__(self, **kwargs): + super(_BaseNetwork, self).__init__(**kwargs) + # This is dirty + self.features = None + + def get_weights_flat(self): + weights = super().get_weights() + flat = np.asarray(np.concatenate([x.flatten() for x in weights])) + return flat + + def step(self, x): + pass + + def step_other(self, other: Union[Sequential, Model]) -> bool: + pass + + def get_parameter_count(self): + return np.sum([np.prod(x.shape) for x in self.get_weights()]) + + def train_on_batch(self, *args, **kwargs): + raise NotImplementedError + + def compile(self, *args, **kwargs): + raise NotImplementedError + + @staticmethod + def mean_abs_error(labels, predictions): + return np.mean(np.abs(predictions - labels), axis=-1) + + @staticmethod + def mean_sqrd_error(labels, predictions): + return np.mean(np.square(predictions - labels), axis=-1) + + +class RecurrentNetwork(_BaseNetwork): + def __init__(self, network: Network, *args, **kwargs): + super().__init__(inputs=network.inputs, outputs=network.outputs) + self.features = network.features + self.parameters = network.parameters + assert self.parameters == self.get_parameter_count() + + def step(self, x): + shaped = np.reshape(x, (1, -1, self.features)) + return self.predict(shaped).flatten() + + def fit(self, epochs=500, **kwargs): + losses = [] + with tqdm(total=epochs, ascii=True, + desc='Type: {t}'. format(t=self.__class__.__name__), + postfix=["Loss", dict(value=0)]) as bar: + for _ in range(epochs): + x = self.get_weights_flat() + y = self.step(x) + weights = self.get_weights() + global_idx = 0 + for idx, weight_matrix in enumerate(weights): + flattened = weight_matrix.flatten() + new_weights = y[global_idx:global_idx + flattened.shape[0]] + weights[idx] = np.reshape(new_weights, weight_matrix.shape) + global_idx += flattened.shape[0] + losses.append(self.mean_sqrd_error(y.flatten(), self.get_weights_flat())) + self.set_weights(weights) + bar.postfix[1]["value"] = losses[-1] + bar.update() + return losses + + +class FeedForwardNetwork(_BaseNetwork): + def __init__(self, network:Network, **kwargs): + super().__init__(inputs=network.inputs, outputs=network.outputs, **kwargs) + self.features = network.features + self.parameters = network.parameters + self.num_layer = network.num_layer + self.num_cells = network.cells + # assert self.parameters == self.get_parameter_count() + + def step(self, x): + return self.predict(x) + + def step_other(self, x): + return self.predict(x) + + def fit(self, epochs=500, **kwargs): + losses = [] + with tqdm(total=epochs, ascii=True, + desc='Type: {t} @ Epoch:'. format(t=self.__class__.__name__), + postfix=["Loss", dict(value=0)]) as bar: + for _ in range(epochs): + all_weights = self.get_weights_flat() + cell_idx = np.apply_along_axis(lambda x: x/self.num_cells, 0, np.arange(int(self.get_parameter_count()))) + xc = np.concatenate((all_weights[..., None], cell_idx[..., None]), axis=1) + + y = self.step(xc) + + weights = self.get_weights() + global_idx = 0 + + for idx, weight_matrix in enumerate(weights): + + # UPDATE THE WEIGHTS + flattened = weight_matrix.flatten() + new_weights = y[global_idx:global_idx + flattened.shape[0], 0] + weights[idx] = np.reshape(new_weights, weight_matrix.shape) + global_idx += flattened.shape[0] + + losses.append(self.mean_sqrd_error(y[:, 0].flatten(), self.get_weights_flat())) + self.set_weights(weights) + bar.postfix[1]["value"] = losses[-1] + bar.update() + return losses + + +if __name__ == '__main__': + with Experiment() as exp: + features, cells, layers = 2, 2, 2 + use_recurrent = False + if use_recurrent: + network = Network(features, cells, layers, recurrent=use_recurrent) + r = RecurrentNetwork(network) + loss = r.fit(epochs=10) + exp.save(rnet=r) + else: + network = Network(features, cells, layers, recurrent=use_recurrent) + ff = FeedForwardNetwork(network) + loss = ff.fit(epochs=10) + exp.save(ffnet=ff) + print(loss) diff --git a/code/network.py b/code/network.py index 8c4e53a..de2e3bd 100644 --- a/code/network.py +++ b/code/network.py @@ -1,698 +1,698 @@ -import math -import copy -import numpy as np -from tqdm import tqdm - -from keras.models import Sequential -from keras.layers import SimpleRNN, Dense - -from util import * -from experiment import * - -# Supress warnings and info messages -os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' - - -class NeuralNetwork(PrintingObject): - - @staticmethod - def weights_to_string(weights): - s = "" - for layer_id, layer in enumerate(weights): - for cell_id, cell in enumerate(layer): - s += "[ " - for weight_id, weight in enumerate(cell): - s += str(weight) + " " - s += "]" - s += "\n" - return s - - @staticmethod - def are_weights_diverged(network_weights): - for layer_id, layer in enumerate(network_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - if math.isnan(weight): - return True - if math.isinf(weight): - return True - return False - - @staticmethod - def are_weights_within(network_weights, lower_bound, upper_bound): - for layer_id, layer in enumerate(network_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - if not (lower_bound <= weight and weight <= upper_bound): - return False - return True - - @staticmethod - def fill_weights(old_weights, new_weights_list): - new_weights = copy.deepcopy(old_weights) - current_weight_id = 0 - for layer_id, layer in enumerate(new_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - new_weight = new_weights_list[current_weight_id] - new_weights[layer_id][cell_id][weight_id] = new_weight - current_weight_id += 1 - return new_weights - - def __init__(self, **params): - super().__init__() - self.model = Sequential() - self.params = dict(epsilon=0.00000000000001) - self.params.update(params) - self.keras_params = dict(activation='linear', use_bias=False) - - def get_params(self): - return self.params - - def get_keras_params(self): - return self.keras_params - - def with_params(self, **kwargs): - self.params.update(kwargs) - return self - - def with_keras_params(self, **kwargs): - self.keras_params.update(kwargs) - return self - - def get_weights(self): - return self.model.get_weights() - - def get_weights_flat(self): - return np.hstack([weight.flatten() for weight in self.get_weights()]) - - def set_weights(self, new_weights): - return self.model.set_weights(new_weights) - - def apply_to_weights(self, old_weights): - raise NotImplementedError - - def apply_to_network(self, other_network): - new_weights = self.apply_to_weights(other_network.get_weights()) - return new_weights - - def attack(self, other_network): - other_network.set_weights(self.apply_to_network(other_network)) - return self - - def fuck(self, other_network): - self.set_weights(self.apply_to_network(other_network)) - return self - - def self_attack(self, iterations=1): - for _ in range(iterations): - self.attack(self) - return self - - def meet(self, other_network): - new_other_network = copy.deepcopy(other_network) - return self.attack(new_other_network) - - def is_diverged(self): - return NeuralNetwork.are_weights_diverged(self.get_weights()) - - def is_zero(self, epsilon=None): - epsilon = epsilon or self.params.get('epsilon') - return NeuralNetwork.are_weights_within(self.get_weights(), -epsilon, epsilon) - - def is_fixpoint(self, degree=1, epsilon=None): - assert degree >= 1, "degree must be >= 1" - epsilon = epsilon or self.get_params().get('epsilon') - old_weights = self.get_weights() - new_weights = copy.deepcopy(old_weights) - - for _ in range(degree): - new_weights = self.apply_to_weights(new_weights) - - if NeuralNetwork.are_weights_diverged(new_weights): - return False - for layer_id, layer in enumerate(old_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - new_weight = new_weights[layer_id][cell_id][weight_id] - if abs(new_weight - weight) >= epsilon: - return False - return True - - def repr_weights(self, weights=None): - return self.weights_to_string(weights or self.get_weights()) - - def print_weights(self, weights=None): - print(self.repr_weights(weights)) - - -class WeightwiseNeuralNetwork(NeuralNetwork): - - @staticmethod - def normalize_id(value, norm): - if norm > 1: - return float(value) / float(norm) - else: - return float(value) - - def __init__(self, width, depth, **kwargs): - super().__init__(**kwargs) - self.width = width - self.depth = depth - self.model.add(Dense(units=self.width, input_dim=4, **self.keras_params)) - for _ in range(self.depth-1): - self.model.add(Dense(units=self.width, **self.keras_params)) - self.model.add(Dense(units=1, **self.keras_params)) - - def apply(self, *inputs): - stuff = np.transpose(np.array([[inputs[0]], [inputs[1]], [inputs[2]], [inputs[3]]])) - return self.model.predict(stuff)[0][0] - - @classmethod - def compute_all_duplex_weight_points(cls, old_weights): - points = [] - normal_points = [] - max_layer_id = len(old_weights) - 1 - for layer_id, layer in enumerate(old_weights): - max_cell_id = len(layer) - 1 - for cell_id, cell in enumerate(layer): - max_weight_id = len(cell) - 1 - for weight_id, weight in enumerate(cell): - normal_layer_id = cls.normalize_id(layer_id, max_layer_id) - normal_cell_id = cls.normalize_id(cell_id, max_cell_id) - normal_weight_id = cls.normalize_id(weight_id, max_weight_id) - - points += [[weight, layer_id, cell_id, weight_id]] - normal_points += [[weight, normal_layer_id, normal_cell_id, normal_weight_id]] - return points, normal_points - - @classmethod - def compute_all_weight_points(cls, all_weights): - return cls.compute_all_duplex_weight_points(all_weights)[0] - - @classmethod - def compute_all_normal_weight_points(cls, all_weights): - return cls.compute_all_duplex_weight_points(all_weights)[1] - - def apply_to_weights(self, old_weights): - new_weights = copy.deepcopy(self.get_weights()) - for (weight_point, normal_weight_point) in zip(*self.__class__.compute_all_duplex_weight_points(old_weights)): - weight, layer_id, cell_id, weight_id = weight_point - _, normal_layer_id, normal_cell_id, normal_weight_id = normal_weight_point - - new_weight = self.apply(*normal_weight_point) - new_weights[layer_id][cell_id][weight_id] = new_weight - - if self.params.get("print_all_weight_updates", False) and not self.is_silent(): - print("updated old weight {weight}\t @ ({layer},{cell},{weight_id}) " - "to new value {new_weight}\t calling @ ({normal_layer},{normal_cell},{normal_weight_id})").format( - weight=weight, layer=layer_id, cell=cell_id, weight_id=weight_id, new_weight=new_weight, - normal_layer=normal_layer_id, normal_cell=normal_cell_id, normal_weight_id=normal_weight_id) - return new_weights - - def compute_samples(self): - samples = [] - for normal_weight_point in self.__class__.compute_all_normal_weight_points(self.get_weights()): - weight, normal_layer_id, normal_cell_id, normal_weight_id = normal_weight_point - - sample = np.transpose(np.array([[weight], [normal_layer_id], [normal_cell_id], [normal_weight_id]])) - samples += [sample[0]] - samples_array = np.asarray(samples) - return samples_array, samples_array[:, 0] - - -class AggregatingNeuralNetwork(NeuralNetwork): - - @staticmethod - def aggregate_average(weights): - total = 0 - count = 0 - for weight in weights: - total += float(weight) - count += 1 - return total / float(count) - - @staticmethod - def aggregate_max(weights): - max_found = weights[0] - for weight in weights: - max_found = weight > max_found and weight or max_found - return max_found - - @staticmethod - def deaggregate_identically(aggregate, amount): - return [aggregate for _ in range(amount)] - - @staticmethod - def shuffle_not(weights_list): - return weights_list - - @staticmethod - def shuffle_random(weights_list): - import random - random.shuffle(weights_list) - return weights_list - - def __init__(self, aggregates, width, depth, **kwargs): - super().__init__(**kwargs) - self.aggregates = aggregates - self.width = width - self.depth = depth - self.model.add(Dense(units=width, input_dim=self.aggregates, **self.keras_params)) - for _ in range(depth-1): - self.model.add(Dense(units=width, **self.keras_params)) - self.model.add(Dense(units=self.aggregates, **self.keras_params)) - - def get_aggregator(self): - return self.params.get('aggregator', self.aggregate_average) - - def get_deaggregator(self): - return self.params.get('deaggregator', self.deaggregate_identically) - - def get_shuffler(self): - return self.params.get('shuffler', self.shuffle_not) - - def get_amount_of_weights(self): - total_weights = 0 - for layer_id, layer in enumerate(self.get_weights()): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - total_weights += 1 - return total_weights - - def apply(self, *inputs): - stuff = np.transpose(np.array([[inputs[i]] for i in range(self.aggregates)])) - return self.model.predict(stuff)[0] - - def apply_to_weights(self, old_weights): - # build aggregations from old_weights - collection_size = self.get_amount_of_weights() // self.aggregates - collections, leftovers = self.collect_weights(old_weights, collection_size) - - # call network - old_aggregations = [self.get_aggregator()(collection) for collection in collections] - new_aggregations = self.apply(*old_aggregations) - - # generate list of new weights - new_weights_list = [] - for aggregation_id, aggregation in enumerate(new_aggregations): - if aggregation_id == self.aggregates - 1: - new_weights_list += self.get_deaggregator()(aggregation, collection_size + leftovers) - else: - new_weights_list += self.get_deaggregator()(aggregation, collection_size) - new_weights_list = self.get_shuffler()(new_weights_list) - - # write back new weights - new_weights = self.fill_weights(old_weights, new_weights_list) - - # return results - if self.params.get("print_all_weight_updates", False) and not self.is_silent(): - print("updated old weight aggregations " + str(old_aggregations)) - print("to new weight aggregations " + str(new_aggregations)) - print("resulting in network weights ...") - print(self.weights_to_string(new_weights)) - return new_weights - - @staticmethod - def collect_weights(all_weights, collection_size): - collections = [] - next_collection = [] - current_weight_id = 0 - for layer_id, layer in enumerate(all_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - next_collection += [weight] - if (current_weight_id + 1) % collection_size == 0: - collections += [next_collection] - next_collection = [] - current_weight_id += 1 - collections[-1] += next_collection - leftovers = len(next_collection) - return collections, leftovers - - def get_collected_weights(self): - collection_size = self.get_amount_of_weights() // self.aggregates - return self.__class__.collect_weights(self.get_weights(), collection_size) - - def get_aggregated_weights(self): - collections, leftovers = self.get_collected_weights() - aggregations = [self.get_aggregator()(collection) for collection in collections] - return aggregations, leftovers - - def compute_samples(self): - aggregations, _ = self.get_aggregated_weights() - sample = np.transpose(np.array([[aggregations[i]] for i in range(self.aggregates)])) - return [sample], [sample] - - def is_fixpoint_after_aggregation(self, degree=1, epsilon=None): - assert degree >= 1, "degree must be >= 1" - epsilon = epsilon or self.get_params().get('epsilon') - - old_weights = self.get_weights() - old_aggregations, _ = self.get_aggregated_weights() - - new_weights = copy.deepcopy(old_weights) - for _ in range(degree): - new_weights = self.apply_to_weights(new_weights) - if NeuralNetwork.are_weights_diverged(new_weights): - return False - collection_size = self.get_amount_of_weights() // self.aggregates - collections, leftovers = self.__class__.collect_weights(new_weights, collection_size) - new_aggregations = [self.get_aggregator()(collection) for collection in collections] - - for aggregation_id, old_aggregation in enumerate(old_aggregations): - new_aggregation = new_aggregations[aggregation_id] - if abs(new_aggregation - old_aggregation) >= epsilon: - return False, new_aggregations - return True, new_aggregations - - -class FFTNeuralNetwork(NeuralNetwork): - - @staticmethod - def aggregate_fft(weights, dims): - flat = np.hstack([weight.flatten() for weight in weights]) - fft_reduction = np.fft.fftn(flat, dims)[None, ...] - return fft_reduction - - @staticmethod - def deaggregate_identically(aggregate, dims): - fft_inverse = np.fft.ifftn(aggregate, dims) - return fft_inverse - - @staticmethod - def shuffle_not(weights_list): - return weights_list - - @staticmethod - def shuffle_random(weights_list): - import random - random.shuffle(weights_list) - return weights_list - - def __init__(self, aggregates, width, depth, **kwargs): - super().__init__(**kwargs) - self.aggregates = aggregates - self.width = width - self.depth = depth - self.model.add(Dense(units=width, input_dim=self.aggregates, **self.keras_params)) - for _ in range(depth-1): - self.model.add(Dense(units=width, **self.keras_params)) - self.model.add(Dense(units=self.aggregates, **self.keras_params)) - - def get_shuffler(self): - return self.params.get('shuffler', self.shuffle_not) - - def get_amount_of_weights(self): - total_weights = 0 - for layer_id, layer in enumerate(self.get_weights()): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - total_weights += 1 - return total_weights - - def apply(self, inputs): - sample = np.asarray(inputs) - return self.model.predict(sample)[0] - - def apply_to_weights(self, old_weights): - # build aggregations from old_weights - weights = self.get_weights() - - # call network - old_aggregation = self.aggregate_fft(weights, self.aggregates) - new_aggregation = self.apply(old_aggregation) - - # generate list of new weights - new_weights_list = self.deaggregate_identically(new_aggregation, self.get_amount_of_weights()) - - new_weights_list = self.get_shuffler()(new_weights_list) - - # write back new weights - new_weights = self.fill_weights(old_weights, new_weights_list) - - # return results - if self.params.get("print_all_weight_updates", False) and not self.is_silent(): - print("updated old weight aggregations " + str(old_aggregation)) - print("to new weight aggregations " + str(new_aggregation)) - print("resulting in network weights ...") - print(self.__class__.weights_to_string(new_weights)) - return new_weights - - def compute_samples(self): - weights = self.get_weights() - sample = np.asarray(weights)[None, ...] - return [sample], [sample] - - -class RecurrentNeuralNetwork(NeuralNetwork): - - def __init__(self, width, depth, **kwargs): - super().__init__(**kwargs) - self.features = 1 - self.width = width - self.depth = depth - self.model.add(SimpleRNN(units=width, input_dim=self.features, return_sequences=True, **self.keras_params)) - for _ in range(depth-1): - self.model.add(SimpleRNN(units=width, return_sequences=True, **self.keras_params)) - self.model.add(SimpleRNN(units=self.features, return_sequences=True, **self.keras_params)) - - def apply(self, *inputs): - stuff = np.transpose(np.array([[[inputs[i]] for i in range(len(inputs))]])) - return self.model.predict(stuff)[0].flatten() - - def apply_to_weights(self, old_weights): - # build list from old weights - new_weights = copy.deepcopy(old_weights) - old_weights_list = [] - for layer_id, layer in enumerate(old_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - old_weights_list += [weight] - - # call network - new_weights_list = self.apply(*old_weights_list) - - # write back new weights from list of rnn returns - current_weight_id = 0 - for layer_id, layer in enumerate(new_weights): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - new_weight = new_weights_list[current_weight_id] - new_weights[layer_id][cell_id][weight_id] = new_weight - current_weight_id += 1 - return new_weights - - def compute_samples(self): - # build list from old weights - old_weights_list = [] - for layer_id, layer in enumerate(self.get_weights()): - for cell_id, cell in enumerate(layer): - for weight_id, weight in enumerate(cell): - old_weights_list += [weight] - sample = np.asarray(old_weights_list)[None, ..., None] - return sample, sample - - -class LearningNeuralNetwork(NeuralNetwork): - - @staticmethod - def mean_reduction(weights, features): - single_dim_weights = np.hstack([w.flatten() for w in weights]) - shaped_weights = np.reshape(single_dim_weights, (1, features, -1)) - x = np.mean(shaped_weights, axis=-1) - return x - - @staticmethod - def fft_reduction(weights, features): - single_dim_weights = np.hstack([w.flatten() for w in weights]) - x = np.fft.fft(single_dim_weights, n=features)[None, ...] - return x - - @staticmethod - def random_reduction(_, features): - x = np.random.rand(features)[None, ...] - return x - - def __init__(self, width, depth, features, **kwargs): - super().__init__(**kwargs) - self.width = width - self.depth = depth - self.features = features - self.compile_params = dict(loss='mse', optimizer='sgd') - self.model.add(Dense(units=self.width, input_dim=self.features, **self.keras_params)) - for _ in range(self.depth-1): - self.model.add(Dense(units=self.width, **self.keras_params)) - self.model.add(Dense(units=self.features, **self.keras_params)) - self.model.compile(**self.compile_params) - - def apply_to_weights(self, old_weights): - raise NotImplementedError - - def with_compile_params(self, **kwargs): - self.compile_params.update(kwargs) - return self - - def learn(self, epochs, reduction, batchsize=1): - with tqdm(total=epochs, ascii=True, - desc='Type: {t} @ Epoch:'.format(t=self.__class__.__name__), - postfix=["Loss", dict(value=0)]) as bar: - for epoch in range(epochs): - old_weights = self.get_weights() - x = reduction(old_weights, self.features) - history = self.model.fit(x=x, y=x, verbose=0, batch_size=batchsize) - bar.postfix[1]["value"] = history.history['loss'][-1] - bar.update() - - -class TrainingNeuralNetworkDecorator(NeuralNetwork): - - def __init__(self, net, **kwargs): - super().__init__(**kwargs) - self.net = net - self.compile_params = dict(loss='mse', optimizer='sgd') - self.model_compiled = False - - def get_params(self): - return self.net.get_params() - - def get_keras_params(self): - return self.net.get_keras_params() - - def get_compile_params(self): - return self.net.get_compile_params() - - def with_params(self, **kwargs): - self.net.with_params(**kwargs) - return self - - def with_keras_params(self, **kwargs): - self.net.with_keras_params(**kwargs) - return self - - def with_compile_params(self, **kwargs): - self.compile_params.update(kwargs) - return self - - def get_model(self): - return self.net.get_model() - - def apply_to_weights(self, old_weights): - return self.net.apply_to_weights(old_weights) - - def compile_model(self, **kwargs): - compile_params = copy.deepcopy(self.compile_params) - compile_params.update(kwargs) - return self.get_model().compile(**compile_params) - - def compiled(self, **kwargs): - if not self.model_compiled: - self.compile_model(**kwargs) - self.model_compiled = True - return self - - def train(self, batchsize=1): - self.compiled() - x, y = self.net.compute_samples() - history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize) - return history.history['loss'][-1] - - def train_other(self, other_network, batchsize=1): - self.compiled() - 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] - - -if __name__ == '__main__': - if True: - with FixpointExperiment() as exp: - for run_id in tqdm(range(100)): - # net = WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation='linear') - # net = AggregatingNeuralNetwork(aggregates=4, width=2, depth=2)\ - net = FFTNeuralNetwork(aggregates=4, width=2, depth=2) \ - .with_params(print_all_weight_updates=False, use_bias=False) - # net = RecurrentNeuralNetwork(width=2, depth=2).with_keras_params(activation='linear')\ - # .with_params(print_all_weight_updates=True) - # net.print_weights() - - # INFO Run_ID needs to be more than 0, so that exp stores the trajectories! - exp.run_net(net, 100, run_id=run_id+1) - exp.log(exp.counters) - if False: - # is_fixpoint was wrong because it trivially returned the old weights - with IdentLearningExperiment() as exp: - net = LearningNeuralNetwork(width=2, depth=2, features=2, )\ - .with_keras_params(activation='sigmoid', use_bias=False, ) \ - .with_params(print_all_weight_updates=False) - net.print_weights() - time.sleep(0.1) - print(net.is_fixpoint(epsilon=0.1e-6)) - print() - net.learn(1, reduction=LearningNeuralNetwork.fft_reduction) - import time - time.sleep(0.1) - net.print_weights() - time.sleep(0.1) - print(net.is_fixpoint(epsilon=0.1e-6)) - if False: - # 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() - if False: - # this does not work as the aggregation function screws over the fixpoint computation.... - # TODO: check for fixpoint in aggregated space... - with FixpointExperiment() as exp: - run_count = 1000 - net = TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, width=2, depth=2)).with_params(epsilon=0.1e-6) - for run_id in tqdm(range(run_count+1)): - loss = net.compiled().train() - if run_id % 100 == 0: - net.print_weights() - old_aggs, _ = net.net.get_aggregated_weights() - print("old weights agg: " + str(old_aggs)) - fp, new_aggs = net.net.is_fixpoint_after_aggregation(epsilon=0.0001) - print("new weights agg: " + str(new_aggs)) - print("Fixpoint? " + str(net.is_fixpoint())) - print("Fixpoint after Agg? " + str(fp)) - print("Loss " + str(loss)) - print() - if False: - # this explodes in our faces completely... NAN everywhere - # TODO: Wtf is happening here? - with FixpointExperiment() as exp: - run_count = 10000 - net = TrainingNeuralNetworkDecorator(RecurrentNeuralNetwork(width=2, depth=2))\ - .with_params(epsilon=0.1e-2).with_keras_params(optimizer='sgd', activation='linear') - for run_id in tqdm(range(run_count+1)): - loss = net.compiled().train() - if run_id % 500 == 0: - net.print_weights() - # print(net.apply_to_network(net)) - print("Fixpoint? " + str(net.is_fixpoint())) - print("Loss " + str(loss)) - 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) +import math +import copy +import numpy as np +from tqdm import tqdm + +from keras.models import Sequential +from keras.layers import SimpleRNN, Dense + +from util import * +from experiment import * + +# Supress warnings and info messages +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + + +class NeuralNetwork(PrintingObject): + + @staticmethod + def weights_to_string(weights): + s = "" + for layer_id, layer in enumerate(weights): + for cell_id, cell in enumerate(layer): + s += "[ " + for weight_id, weight in enumerate(cell): + s += str(weight) + " " + s += "]" + s += "\n" + return s + + @staticmethod + def are_weights_diverged(network_weights): + for layer_id, layer in enumerate(network_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + if math.isnan(weight): + return True + if math.isinf(weight): + return True + return False + + @staticmethod + def are_weights_within(network_weights, lower_bound, upper_bound): + for layer_id, layer in enumerate(network_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + if not (lower_bound <= weight and weight <= upper_bound): + return False + return True + + @staticmethod + def fill_weights(old_weights, new_weights_list): + new_weights = copy.deepcopy(old_weights) + current_weight_id = 0 + for layer_id, layer in enumerate(new_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + new_weight = new_weights_list[current_weight_id] + new_weights[layer_id][cell_id][weight_id] = new_weight + current_weight_id += 1 + return new_weights + + def __init__(self, **params): + super().__init__() + self.model = Sequential() + self.params = dict(epsilon=0.00000000000001) + self.params.update(params) + self.keras_params = dict(activation='linear', use_bias=False) + + def get_params(self): + return self.params + + def get_keras_params(self): + return self.keras_params + + def with_params(self, **kwargs): + self.params.update(kwargs) + return self + + def with_keras_params(self, **kwargs): + self.keras_params.update(kwargs) + return self + + def get_weights(self): + return self.model.get_weights() + + def get_weights_flat(self): + return np.hstack([weight.flatten() for weight in self.get_weights()]) + + def set_weights(self, new_weights): + return self.model.set_weights(new_weights) + + def apply_to_weights(self, old_weights): + raise NotImplementedError + + def apply_to_network(self, other_network): + new_weights = self.apply_to_weights(other_network.get_weights()) + return new_weights + + def attack(self, other_network): + other_network.set_weights(self.apply_to_network(other_network)) + return self + + def fuck(self, other_network): + self.set_weights(self.apply_to_network(other_network)) + return self + + def self_attack(self, iterations=1): + for _ in range(iterations): + self.attack(self) + return self + + def meet(self, other_network): + new_other_network = copy.deepcopy(other_network) + return self.attack(new_other_network) + + def is_diverged(self): + return NeuralNetwork.are_weights_diverged(self.get_weights()) + + def is_zero(self, epsilon=None): + epsilon = epsilon or self.params.get('epsilon') + return NeuralNetwork.are_weights_within(self.get_weights(), -epsilon, epsilon) + + def is_fixpoint(self, degree=1, epsilon=None): + assert degree >= 1, "degree must be >= 1" + epsilon = epsilon or self.get_params().get('epsilon') + old_weights = self.get_weights() + new_weights = copy.deepcopy(old_weights) + + for _ in range(degree): + new_weights = self.apply_to_weights(new_weights) + + if NeuralNetwork.are_weights_diverged(new_weights): + return False + for layer_id, layer in enumerate(old_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + new_weight = new_weights[layer_id][cell_id][weight_id] + if abs(new_weight - weight) >= epsilon: + return False + return True + + def repr_weights(self, weights=None): + return self.weights_to_string(weights or self.get_weights()) + + def print_weights(self, weights=None): + print(self.repr_weights(weights)) + + +class WeightwiseNeuralNetwork(NeuralNetwork): + + @staticmethod + def normalize_id(value, norm): + if norm > 1: + return float(value) / float(norm) + else: + return float(value) + + def __init__(self, width, depth, **kwargs): + super().__init__(**kwargs) + self.width = width + self.depth = depth + self.model.add(Dense(units=self.width, input_dim=4, **self.keras_params)) + for _ in range(self.depth-1): + self.model.add(Dense(units=self.width, **self.keras_params)) + self.model.add(Dense(units=1, **self.keras_params)) + + def apply(self, *inputs): + stuff = np.transpose(np.array([[inputs[0]], [inputs[1]], [inputs[2]], [inputs[3]]])) + return self.model.predict(stuff)[0][0] + + @classmethod + def compute_all_duplex_weight_points(cls, old_weights): + points = [] + normal_points = [] + max_layer_id = len(old_weights) - 1 + for layer_id, layer in enumerate(old_weights): + max_cell_id = len(layer) - 1 + for cell_id, cell in enumerate(layer): + max_weight_id = len(cell) - 1 + for weight_id, weight in enumerate(cell): + normal_layer_id = cls.normalize_id(layer_id, max_layer_id) + normal_cell_id = cls.normalize_id(cell_id, max_cell_id) + normal_weight_id = cls.normalize_id(weight_id, max_weight_id) + + points += [[weight, layer_id, cell_id, weight_id]] + normal_points += [[weight, normal_layer_id, normal_cell_id, normal_weight_id]] + return points, normal_points + + @classmethod + def compute_all_weight_points(cls, all_weights): + return cls.compute_all_duplex_weight_points(all_weights)[0] + + @classmethod + def compute_all_normal_weight_points(cls, all_weights): + return cls.compute_all_duplex_weight_points(all_weights)[1] + + def apply_to_weights(self, old_weights): + new_weights = copy.deepcopy(self.get_weights()) + for (weight_point, normal_weight_point) in zip(*self.__class__.compute_all_duplex_weight_points(old_weights)): + weight, layer_id, cell_id, weight_id = weight_point + _, normal_layer_id, normal_cell_id, normal_weight_id = normal_weight_point + + new_weight = self.apply(*normal_weight_point) + new_weights[layer_id][cell_id][weight_id] = new_weight + + if self.params.get("print_all_weight_updates", False) and not self.is_silent(): + print("updated old weight {weight}\t @ ({layer},{cell},{weight_id}) " + "to new value {new_weight}\t calling @ ({normal_layer},{normal_cell},{normal_weight_id})").format( + weight=weight, layer=layer_id, cell=cell_id, weight_id=weight_id, new_weight=new_weight, + normal_layer=normal_layer_id, normal_cell=normal_cell_id, normal_weight_id=normal_weight_id) + return new_weights + + def compute_samples(self): + samples = [] + for normal_weight_point in self.__class__.compute_all_normal_weight_points(self.get_weights()): + weight, normal_layer_id, normal_cell_id, normal_weight_id = normal_weight_point + + sample = np.transpose(np.array([[weight], [normal_layer_id], [normal_cell_id], [normal_weight_id]])) + samples += [sample[0]] + samples_array = np.asarray(samples) + return samples_array, samples_array[:, 0] + + +class AggregatingNeuralNetwork(NeuralNetwork): + + @staticmethod + def aggregate_average(weights): + total = 0 + count = 0 + for weight in weights: + total += float(weight) + count += 1 + return total / float(count) + + @staticmethod + def aggregate_max(weights): + max_found = weights[0] + for weight in weights: + max_found = weight > max_found and weight or max_found + return max_found + + @staticmethod + def deaggregate_identically(aggregate, amount): + return [aggregate for _ in range(amount)] + + @staticmethod + def shuffle_not(weights_list): + return weights_list + + @staticmethod + def shuffle_random(weights_list): + import random + random.shuffle(weights_list) + return weights_list + + def __init__(self, aggregates, width, depth, **kwargs): + super().__init__(**kwargs) + self.aggregates = aggregates + self.width = width + self.depth = depth + self.model.add(Dense(units=width, input_dim=self.aggregates, **self.keras_params)) + for _ in range(depth-1): + self.model.add(Dense(units=width, **self.keras_params)) + self.model.add(Dense(units=self.aggregates, **self.keras_params)) + + def get_aggregator(self): + return self.params.get('aggregator', self.aggregate_average) + + def get_deaggregator(self): + return self.params.get('deaggregator', self.deaggregate_identically) + + def get_shuffler(self): + return self.params.get('shuffler', self.shuffle_not) + + def get_amount_of_weights(self): + total_weights = 0 + for layer_id, layer in enumerate(self.get_weights()): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + total_weights += 1 + return total_weights + + def apply(self, *inputs): + stuff = np.transpose(np.array([[inputs[i]] for i in range(self.aggregates)])) + return self.model.predict(stuff)[0] + + def apply_to_weights(self, old_weights): + # build aggregations from old_weights + collection_size = self.get_amount_of_weights() // self.aggregates + collections, leftovers = self.collect_weights(old_weights, collection_size) + + # call network + old_aggregations = [self.get_aggregator()(collection) for collection in collections] + new_aggregations = self.apply(*old_aggregations) + + # generate list of new weights + new_weights_list = [] + for aggregation_id, aggregation in enumerate(new_aggregations): + if aggregation_id == self.aggregates - 1: + new_weights_list += self.get_deaggregator()(aggregation, collection_size + leftovers) + else: + new_weights_list += self.get_deaggregator()(aggregation, collection_size) + new_weights_list = self.get_shuffler()(new_weights_list) + + # write back new weights + new_weights = self.fill_weights(old_weights, new_weights_list) + + # return results + if self.params.get("print_all_weight_updates", False) and not self.is_silent(): + print("updated old weight aggregations " + str(old_aggregations)) + print("to new weight aggregations " + str(new_aggregations)) + print("resulting in network weights ...") + print(self.weights_to_string(new_weights)) + return new_weights + + @staticmethod + def collect_weights(all_weights, collection_size): + collections = [] + next_collection = [] + current_weight_id = 0 + for layer_id, layer in enumerate(all_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + next_collection += [weight] + if (current_weight_id + 1) % collection_size == 0: + collections += [next_collection] + next_collection = [] + current_weight_id += 1 + collections[-1] += next_collection + leftovers = len(next_collection) + return collections, leftovers + + def get_collected_weights(self): + collection_size = self.get_amount_of_weights() // self.aggregates + return self.__class__.collect_weights(self.get_weights(), collection_size) + + def get_aggregated_weights(self): + collections, leftovers = self.get_collected_weights() + aggregations = [self.get_aggregator()(collection) for collection in collections] + return aggregations, leftovers + + def compute_samples(self): + aggregations, _ = self.get_aggregated_weights() + sample = np.transpose(np.array([[aggregations[i]] for i in range(self.aggregates)])) + return [sample], [sample] + + def is_fixpoint_after_aggregation(self, degree=1, epsilon=None): + assert degree >= 1, "degree must be >= 1" + epsilon = epsilon or self.get_params().get('epsilon') + + old_weights = self.get_weights() + old_aggregations, _ = self.get_aggregated_weights() + + new_weights = copy.deepcopy(old_weights) + for _ in range(degree): + new_weights = self.apply_to_weights(new_weights) + if NeuralNetwork.are_weights_diverged(new_weights): + return False + collection_size = self.get_amount_of_weights() // self.aggregates + collections, leftovers = self.__class__.collect_weights(new_weights, collection_size) + new_aggregations = [self.get_aggregator()(collection) for collection in collections] + + for aggregation_id, old_aggregation in enumerate(old_aggregations): + new_aggregation = new_aggregations[aggregation_id] + if abs(new_aggregation - old_aggregation) >= epsilon: + return False, new_aggregations + return True, new_aggregations + + +class FFTNeuralNetwork(NeuralNetwork): + + @staticmethod + def aggregate_fft(weights, dims): + flat = np.hstack([weight.flatten() for weight in weights]) + fft_reduction = np.fft.fftn(flat, dims)[None, ...] + return fft_reduction + + @staticmethod + def deaggregate_identically(aggregate, dims): + fft_inverse = np.fft.ifftn(aggregate, dims) + return fft_inverse + + @staticmethod + def shuffle_not(weights_list): + return weights_list + + @staticmethod + def shuffle_random(weights_list): + import random + random.shuffle(weights_list) + return weights_list + + def __init__(self, aggregates, width, depth, **kwargs): + super().__init__(**kwargs) + self.aggregates = aggregates + self.width = width + self.depth = depth + self.model.add(Dense(units=width, input_dim=self.aggregates, **self.keras_params)) + for _ in range(depth-1): + self.model.add(Dense(units=width, **self.keras_params)) + self.model.add(Dense(units=self.aggregates, **self.keras_params)) + + def get_shuffler(self): + return self.params.get('shuffler', self.shuffle_not) + + def get_amount_of_weights(self): + total_weights = 0 + for layer_id, layer in enumerate(self.get_weights()): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + total_weights += 1 + return total_weights + + def apply(self, inputs): + sample = np.asarray(inputs) + return self.model.predict(sample)[0] + + def apply_to_weights(self, old_weights): + # build aggregations from old_weights + weights = self.get_weights() + + # call network + old_aggregation = self.aggregate_fft(weights, self.aggregates) + new_aggregation = self.apply(old_aggregation) + + # generate list of new weights + new_weights_list = self.deaggregate_identically(new_aggregation, self.get_amount_of_weights()) + + new_weights_list = self.get_shuffler()(new_weights_list) + + # write back new weights + new_weights = self.fill_weights(old_weights, new_weights_list) + + # return results + if self.params.get("print_all_weight_updates", False) and not self.is_silent(): + print("updated old weight aggregations " + str(old_aggregation)) + print("to new weight aggregations " + str(new_aggregation)) + print("resulting in network weights ...") + print(self.__class__.weights_to_string(new_weights)) + return new_weights + + def compute_samples(self): + weights = self.get_weights() + sample = np.asarray(weights)[None, ...] + return [sample], [sample] + + +class RecurrentNeuralNetwork(NeuralNetwork): + + def __init__(self, width, depth, **kwargs): + super().__init__(**kwargs) + self.features = 1 + self.width = width + self.depth = depth + self.model.add(SimpleRNN(units=width, input_dim=self.features, return_sequences=True, **self.keras_params)) + for _ in range(depth-1): + self.model.add(SimpleRNN(units=width, return_sequences=True, **self.keras_params)) + self.model.add(SimpleRNN(units=self.features, return_sequences=True, **self.keras_params)) + + def apply(self, *inputs): + stuff = np.transpose(np.array([[[inputs[i]] for i in range(len(inputs))]])) + return self.model.predict(stuff)[0].flatten() + + def apply_to_weights(self, old_weights): + # build list from old weights + new_weights = copy.deepcopy(old_weights) + old_weights_list = [] + for layer_id, layer in enumerate(old_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + old_weights_list += [weight] + + # call network + new_weights_list = self.apply(*old_weights_list) + + # write back new weights from list of rnn returns + current_weight_id = 0 + for layer_id, layer in enumerate(new_weights): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + new_weight = new_weights_list[current_weight_id] + new_weights[layer_id][cell_id][weight_id] = new_weight + current_weight_id += 1 + return new_weights + + def compute_samples(self): + # build list from old weights + old_weights_list = [] + for layer_id, layer in enumerate(self.get_weights()): + for cell_id, cell in enumerate(layer): + for weight_id, weight in enumerate(cell): + old_weights_list += [weight] + sample = np.asarray(old_weights_list)[None, ..., None] + return sample, sample + + +class LearningNeuralNetwork(NeuralNetwork): + + @staticmethod + def mean_reduction(weights, features): + single_dim_weights = np.hstack([w.flatten() for w in weights]) + shaped_weights = np.reshape(single_dim_weights, (1, features, -1)) + x = np.mean(shaped_weights, axis=-1) + return x + + @staticmethod + def fft_reduction(weights, features): + single_dim_weights = np.hstack([w.flatten() for w in weights]) + x = np.fft.fft(single_dim_weights, n=features)[None, ...] + return x + + @staticmethod + def random_reduction(_, features): + x = np.random.rand(features)[None, ...] + return x + + def __init__(self, width, depth, features, **kwargs): + super().__init__(**kwargs) + self.width = width + self.depth = depth + self.features = features + self.compile_params = dict(loss='mse', optimizer='sgd') + self.model.add(Dense(units=self.width, input_dim=self.features, **self.keras_params)) + for _ in range(self.depth-1): + self.model.add(Dense(units=self.width, **self.keras_params)) + self.model.add(Dense(units=self.features, **self.keras_params)) + self.model.compile(**self.compile_params) + + def apply_to_weights(self, old_weights): + raise NotImplementedError + + def with_compile_params(self, **kwargs): + self.compile_params.update(kwargs) + return self + + def learn(self, epochs, reduction, batchsize=1): + with tqdm(total=epochs, ascii=True, + desc='Type: {t} @ Epoch:'.format(t=self.__class__.__name__), + postfix=["Loss", dict(value=0)]) as bar: + for epoch in range(epochs): + old_weights = self.get_weights() + x = reduction(old_weights, self.features) + history = self.model.fit(x=x, y=x, verbose=0, batch_size=batchsize) + bar.postfix[1]["value"] = history.history['loss'][-1] + bar.update() + + +class TrainingNeuralNetworkDecorator(NeuralNetwork): + + def __init__(self, net, **kwargs): + super().__init__(**kwargs) + self.net = net + self.compile_params = dict(loss='mse', optimizer='sgd') + self.model_compiled = False + + def get_params(self): + return self.net.get_params() + + def get_keras_params(self): + return self.net.get_keras_params() + + def get_compile_params(self): + return self.net.get_compile_params() + + def with_params(self, **kwargs): + self.net.with_params(**kwargs) + return self + + def with_keras_params(self, **kwargs): + self.net.with_keras_params(**kwargs) + return self + + def with_compile_params(self, **kwargs): + self.compile_params.update(kwargs) + return self + + def get_model(self): + return self.net.model + + def apply_to_weights(self, old_weights): + return self.net.apply_to_weights(old_weights) + + def compile_model(self, **kwargs): + compile_params = copy.deepcopy(self.compile_params) + compile_params.update(kwargs) + return self.get_model().compile(**compile_params) + + def compiled(self, **kwargs): + if not self.model_compiled: + self.compile_model(**kwargs) + self.model_compiled = True + return self + + def train(self, batchsize=1): + self.compiled() + x, y = self.net.compute_samples() + history = self.net.model.fit(x=x, y=y, verbose=0, batch_size=batchsize) + return history.history['loss'][-1] + + def train_other(self, other_network, batchsize=1): + self.compiled() + 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] + + +if __name__ == '__main__': + if False: + with FixpointExperiment() as exp: + for run_id in tqdm(range(100)): + # net = WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation='linear') + # net = AggregatingNeuralNetwork(aggregates=4, width=2, depth=2)\ + # net = FFTNeuralNetwork(aggregates=4, width=2, depth=2) \ + # .with_params(print_all_weight_updates=False, use_bias=False) + # net = RecurrentNeuralNetwork(width=2, depth=2).with_keras_params(activation='linear')\ + # .with_params(print_all_weight_updates=True) + # net.print_weights() + + # INFO Run_ID needs to be more than 0, so that exp stores the trajectories! + exp.run_net(net, 100, run_id=run_id+1) + exp.log(exp.counters) + if False: + # is_fixpoint was wrong because it trivially returned the old weights + with IdentLearningExperiment() as exp: + net = LearningNeuralNetwork(width=2, depth=2, features=2, )\ + .with_keras_params(activation='sigmoid', use_bias=False, ) \ + .with_params(print_all_weight_updates=False) + net.print_weights() + time.sleep(0.1) + print(net.is_fixpoint(epsilon=0.1e-6)) + print() + net.learn(1, reduction=LearningNeuralNetwork.fft_reduction) + import time + time.sleep(0.1) + net.print_weights() + time.sleep(0.1) + print(net.is_fixpoint(epsilon=0.1e-6)) + if True: + # ok so this works quite realiably + with FixpointExperiment() as exp: + run_count = 100 + 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() + if False: + # this does not work as the aggregation function screws over the fixpoint computation.... + # TODO: check for fixpoint in aggregated space... + with FixpointExperiment() as exp: + run_count = 1000 + net = TrainingNeuralNetworkDecorator(AggregatingNeuralNetwork(4, width=2, depth=2)).with_params(epsilon=0.1e-6) + for run_id in tqdm(range(run_count+1)): + loss = net.compiled().train() + if run_id % 100 == 0: + net.print_weights() + old_aggs, _ = net.net.get_aggregated_weights() + print("old weights agg: " + str(old_aggs)) + fp, new_aggs = net.net.is_fixpoint_after_aggregation(epsilon=0.0001) + print("new weights agg: " + str(new_aggs)) + print("Fixpoint? " + str(net.is_fixpoint())) + print("Fixpoint after Agg? " + str(fp)) + print("Loss " + str(loss)) + print() + if False: + # this explodes in our faces completely... NAN everywhere + # TODO: Wtf is happening here? + with FixpointExperiment() as exp: + run_count = 10000 + net = TrainingNeuralNetworkDecorator(RecurrentNeuralNetwork(width=2, depth=2))\ + .with_params(epsilon=0.1e-2).with_keras_params(optimizer='sgd', activation='linear') + for run_id in tqdm(range(run_count+1)): + loss = net.compiled().train() + if run_id % 500 == 0: + net.print_weights() + # print(net.apply_to_network(net)) + print("Fixpoint? " + str(net.is_fixpoint())) + print("Loss " + str(loss)) + 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) diff --git a/code/plot.html b/code/plot.html index c519f39..e28ba6b 100644 --- a/code/plot.html +++ b/code/plot.html @@ -1,7 +1,7 @@ -
\ No newline at end of file diff --git a/code/soup.py b/code/soup.py index ba4b80e..8d46b6f 100644 --- a/code/soup.py +++ b/code/soup.py @@ -1,104 +1,104 @@ -import random -import copy - -from tqdm import tqdm - -from experiment import * -from network import * - - -def prng(): - return random.random() - - -class Soup: - - def __init__(self, size, generator, **kwargs): - self.size = size - self.generator = generator - self.particles = [] - self.params = dict(meeting_rate=0.1, train_other_rate=0.1, train=0) - self.params.update(kwargs) - - def with_params(self, **kwargs): - self.params.update(kwargs) - return self - - def seed(self): - self.particles = [] - for _ in range(self.size): - self.particles += [self.generator()] - return self - - def evolve(self, iterations=1): - for _ in range(iterations): - for particle_id, particle in enumerate(self.particles): - if prng() < self.params.get('meeting_rate'): - other_particle_id = int(prng() * len(self.particles)) - other_particle = self.particles[other_particle_id] - particle.attack(other_particle) - 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) - try: - for _ in range(self.params.get('train', 0)): - particle.compiled().train() - except AttributeError: - pass - if self.params.get('remove_divergent') and particle.is_diverged(): - self.particles[particle_id] = self.generator() - if self.params.get('remove_zero') and particle.is_zero(): - self.particles[particle_id] = self.generator() - - def count(self): - counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0) - for particle in self.particles: - if particle.is_diverged(): - counters['divergent'] += 1 - elif particle.is_fixpoint(): - if particle.is_zero(): - counters['fix_zero'] += 1 - else: - counters['fix_other'] += 1 - elif particle.is_fixpoint(2): - counters['fix_sec'] += 1 - else: - counters['other'] += 1 - return counters - - -class LearningSoup(Soup): - - def __init__(self, *args, **kwargs): - super(LearningSoup, self).__init__(**kwargs) - - -if __name__ == '__main__': - if False: - with SoupExperiment() as exp: - for run_id in range(1): - net_generator = lambda: WeightwiseNeuralNetwork(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() - soup = Soup(100, net_generator).with_params(remove_divergent=True, remove_zero=True) - soup.seed() - for _ in tqdm(range(100)): - soup.evolve() - exp.log(soup.count()) - - if True: - with SoupExperiment() as exp: - for run_id in range(1): - net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)).with_keras_params( - activation='linear') - # 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() - soup = Soup(10, net_generator).with_params(remove_divergent=True, remove_zero=True).with_params(train=500) - soup.seed() - for _ in tqdm(range(10)): - soup.evolve() - exp.log(soup.count()) - +import random +import copy + +from tqdm import tqdm + +from experiment import * +from network import * + + +def prng(): + return random.random() + + +class Soup: + + def __init__(self, size, generator, **kwargs): + self.size = size + self.generator = generator + self.particles = [] + self.params = dict(meeting_rate=0.1, train_other_rate=0.1, train=0) + self.params.update(kwargs) + + def with_params(self, **kwargs): + self.params.update(kwargs) + return self + + def seed(self): + self.particles = [] + for _ in range(self.size): + self.particles += [self.generator()] + return self + + def evolve(self, iterations=1): + for _ in range(iterations): + for particle_id, particle in enumerate(self.particles): + if prng() < self.params.get('meeting_rate'): + other_particle_id = int(prng() * len(self.particles)) + other_particle = self.particles[other_particle_id] + particle.attack(other_particle) + 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) + try: + for _ in range(self.params.get('train', 0)): + particle.compiled().train() + except AttributeError: + pass + if self.params.get('remove_divergent') and particle.is_diverged(): + self.particles[particle_id] = self.generator() + if self.params.get('remove_zero') and particle.is_zero(): + self.particles[particle_id] = self.generator() + + def count(self): + counters = dict(divergent=0, fix_zero=0, fix_other=0, fix_sec=0, other=0) + for particle in self.particles: + if particle.is_diverged(): + counters['divergent'] += 1 + elif particle.is_fixpoint(): + if particle.is_zero(): + counters['fix_zero'] += 1 + else: + counters['fix_other'] += 1 + elif particle.is_fixpoint(2): + counters['fix_sec'] += 1 + else: + counters['other'] += 1 + return counters + + +class LearningSoup(Soup): + + def __init__(self, *args, **kwargs): + super(LearningSoup, self).__init__(**kwargs) + + +if __name__ == '__main__': + if False: + with SoupExperiment() as exp: + for run_id in range(1): + net_generator = lambda: WeightwiseNeuralNetwork(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() + soup = Soup(100, net_generator).with_params(remove_divergent=True, remove_zero=True) + soup.seed() + for _ in tqdm(range(100)): + soup.evolve() + exp.log(soup.count()) + + if True: + with SoupExperiment() as exp: + for run_id in range(1): + net_generator = lambda: TrainingNeuralNetworkDecorator(WeightwiseNeuralNetwork(2, 2)).with_keras_params( + activation='linear') + # 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() + soup = Soup(10, net_generator).with_params(remove_divergent=True, remove_zero=True).with_params(train=500) + soup.seed() + for _ in tqdm(range(10)): + soup.evolve() + exp.log(soup.count()) + diff --git a/code/test.py b/code/test.py index 5395921..f82d3bc 100644 --- a/code/test.py +++ b/code/test.py @@ -1,33 +1,33 @@ -from experiment import * -from network import * -from soup import * -import numpy as np - -def vary(e=0.0, f=0.0): - return [ - np.array([[1.0+e, 0.0+f], [0.0+f, 0.0+f], [0.0+f, 0.0+f], [0.0+f, 0.0+f]], dtype=np.float32), - np.array([[1.0+e, 0.0+f], [0.0+f, 0.0+f]], dtype=np.float32), - np.array([[1.0+e], [0.0+f]], dtype=np.float32) - ] - -if __name__ == '__main__': - - net = WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation='sigmoid') - if False: - net.set_weights([ - np.array([[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], dtype=np.float32), - np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.float32), - np.array([[1.0], [0.0]], dtype=np.float32) - ]) - print(net.get_weights()) - net.self_attack(100) - print(net.get_weights()) - print(net.is_fixpoint()) - - if True: - net.set_weights(vary(0.01, 0.0)) - print(net.get_weights()) - for _ in range(5): - net.self_attack() - print(net.get_weights()) - print(net.is_fixpoint()) +from experiment import * +from network import * +from soup import * +import numpy as np + +def vary(e=0.0, f=0.0): + return [ + np.array([[1.0+e, 0.0+f], [0.0+f, 0.0+f], [0.0+f, 0.0+f], [0.0+f, 0.0+f]], dtype=np.float32), + np.array([[1.0+e, 0.0+f], [0.0+f, 0.0+f]], dtype=np.float32), + np.array([[1.0+e], [0.0+f]], dtype=np.float32) + ] + +if __name__ == '__main__': + + net = WeightwiseNeuralNetwork(width=2, depth=2).with_keras_params(activation='sigmoid') + if False: + net.set_weights([ + np.array([[1.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], dtype=np.float32), + np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.float32), + np.array([[1.0], [0.0]], dtype=np.float32) + ]) + print(net.get_weights()) + net.self_attack(100) + print(net.get_weights()) + print(net.is_fixpoint()) + + if True: + net.set_weights(vary(0.01, 0.0)) + print(net.get_weights()) + for _ in range(5): + net.self_attack() + print(net.get_weights()) + print(net.is_fixpoint()) diff --git a/code/util.py b/code/util.py index 2180900..a8604c4 100644 --- a/code/util.py +++ b/code/util.py @@ -1,39 +1,39 @@ -class PrintingObject: - - class SilenceSignal(): - def __init__(self, obj, value): - self.obj = obj - self.new_silent = value - def __enter__(self): - self.old_silent = self.obj.get_silence() - self.obj.set_silence(self.new_silent) - def __exit__(self, exception_type, exception_value, traceback): - self.obj.set_silence(self.old_silent) - - def __init__(self): - self.silent = True - - def is_silent(self): - return self.silent - - def get_silence(self): - return self.is_silent() - - def set_silence(self, value=True): - self.silent = value - return self - - def unset_silence(self): - self.silent = False - return self - - def with_silence(self, value=True): - self.set_silence(value) - return self - - def silence(self, value=True): - return self.__class__.SilenceSignal(self, value) - - def _print(self, *args, **kwargs): - if not self.silent: +class PrintingObject: + + class SilenceSignal(): + def __init__(self, obj, value): + self.obj = obj + self.new_silent = value + def __enter__(self): + self.old_silent = self.obj.get_silence() + self.obj.set_silence(self.new_silent) + def __exit__(self, exception_type, exception_value, traceback): + self.obj.set_silence(self.old_silent) + + def __init__(self): + self.silent = True + + def is_silent(self): + return self.silent + + def get_silence(self): + return self.is_silent() + + def set_silence(self, value=True): + self.silent = value + return self + + def unset_silence(self): + self.silent = False + return self + + def with_silence(self, value=True): + self.set_silence(value) + return self + + def silence(self, value=True): + return self.__class__.SilenceSignal(self, value) + + def _print(self, *args, **kwargs): + if not self.silent: print(*args, **kwargs) \ No newline at end of file diff --git a/code/visualization.py b/code/visualization.py index 30e6571..79d000d 100644 --- a/code/visualization.py +++ b/code/visualization.py @@ -1,197 +1,197 @@ -import os - -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 - - -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 plot_latent_trajectories(data_dict, filename='latent_trajectory_plot'): - - bupu = cl.scales['9']['seq']['BuPu'] - scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins - - # Fit the mebedding space - transformer = TSNE() - for trajectory_id in data_dict: - transformer.fit(np.asarray(data_dict[trajectory_id])) - - # Transform data accordingly and plot it - data = [] - for trajectory_id in data_dict: - transformed = transformer._fit(np.asarray(data_dict[trajectory_id])) - line_trace = go.Scatter( - x=transformed[:, 0], - y=transformed[:, 1], - text='Hovertext goes here'.format(), - line=dict(color=scale[trajectory_id]), - # legendgroup='Position -{}'.format(pos), - # name='Position -{}'.format(pos), - showlegend=False, - # hoverinfo='text', - mode='lines') - line_start = go.Scatter(mode='markers', x=[transformed[0, 0]], y=[transformed[0, 1]], - marker=dict( - color='rgb(255, 0, 0)', - size=4 - ), - showlegend=False - ) - line_end = go.Scatter(mode='markers', x=[transformed[-1, 0]], y=[transformed[-1, 1]], - marker=dict( - color='rgb(0, 0, 0)', - size=4 - ), - showlegend=False - ) - data.extend([line_trace, line_start, line_end]) - - layout = dict(title='{} - Latent Trajectory Movement'.format('Penis'), - height=800, width=800, margin=dict(l=0, r=0, t=0, b=0)) - # import plotly.io as pio - # pio.write_image(fig, filename) - fig = go.Figure(data=data, layout=layout) - pl.offline.plot(fig, auto_open=True, filename=filename) - pass - - -def plot_latent_trajectories_3D(data_dict, filename='plot'): - def norm(val, a=0, b=0.25): - return (val - a) / (b - a) - - bupu = cl.scales['9']['seq']['BuPu'] - scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins - - max_len = max([len(trajectory) for trajectory in data_dict.values()]) - - # Fit the mebedding space - transformer = TSNE() - for trajectory_id in data_dict: - transformer.fit(data_dict[trajectory_id]) - - # Transform data accordingly and plot it - data = [] - for trajectory_id in data_dict: - transformed = transformer._fit(np.asarray(data_dict[trajectory_id])) - trace = go.Scatter3d( - x=transformed[:, 0], - y=transformed[:, 1], - z=np.arange(transformed.shape[0]), - text='Hovertext goes here'.format(), - line=dict(color=scale[trajectory_id]), - # legendgroup='Position -{}'.format(pos), - # name='Position -{}'.format(pos), - showlegend=False, - # hoverinfo='text', - mode='lines') - data.append(trace) - - layout = go.Layout(scene=dict(aspectratio=dict(x=2, y=2, z=1), - xaxis=dict(tickwidth=1, title='Transformed X'), - yaxis=dict(tickwidth=1, title='transformed Y'), - zaxis=dict(tickwidth=1, title='Epoch')), - title='{} - Latent Trajectory Movement'.format('Penis'), - width=800, height=800, - margin=dict(l=0, r=0, b=0, t=0)) - - fig = go.Figure(data=data, layout=layout) - pl.offline.plot(fig, auto_open=True, filename=filename) - pass - - -def plot_histogram(bars_dict_list, filename='histogram_plot'): - # catagorical - ryb = cl.scales['10']['div']['RdYlBu'] - - data = [] - for bar_id, bars_dict in bars_dict_list: - hist = go.Histogram( - histfunc="count", - y=bars_dict.get('value', 14), - x=bars_dict.get('name', 'gimme a name'), - showlegend=False, - marker=dict( - color=ryb[bar_id] - ), - ) - data.append(hist) - - layout=dict(title='{} Histogram Plot'.format('Experiment Name Penis'), - height=400, width=400, margin=dict(l=0, r=0, t=0, b=0)) - - fig = go.Figure(data=data, layout=layout) - pl.offline.plot(fig, auto_open=True, filename=filename) - - pass - - -def line_plot(line_dict_list, filename='lineplot'): - # lines with standard deviation - # Transform data accordingly and plot it - data = [] - rdylgn = cl.scales['10']['div']['RdYlGn'] - rdylgn_background = [scale + (0.4,) for scale in cl.to_numeric(rdylgn)] - for line_id, line_dict in enumerate(line_dict_list): - name = line_dict.get('name', 'gimme a name') - - upper_bound = go.Scatter( - name='Upper Bound', - x=line_dict['x'], - y=line_dict['upper_y'], - mode='lines', - marker=dict(color="#444"), - line=dict(width=0), - fillcolor=rdylgn_background[line_id], - ) - - trace = go.Scatter( - x=line_dict['x'], - y=line_dict['main_y'], - mode='lines', - name=name, - line=dict(color=line_id), - fillcolor=rdylgn_background[line_id], - fill='tonexty') - - lower_bound = go.Scatter( - name='Lower Bound', - x=line_dict['x'], - y=line_dict['lower_y'], - marker=dict(color="#444"), - line=dict(width=0), - mode='lines') - - data.extend([upper_bound, trace, lower_bound]) - - layout=dict(title='{} Line Plot'.format('Experiment Name Penis'), - height=800, width=800, margin=dict(l=0, r=0, t=0, b=0)) - - fig = go.Figure(data=data, layout=layout) - pl.offline.plot(fig, auto_open=True, filename=filename) - pass - - -if __name__ == '__main__': - args = build_args() - in_file = args.in_file[0] - out_file = args.out_file - - with open(in_file, 'rb') as in_f: - experiment = dill.load(in_f) - plot_latent_trajectories_3D(experiment.data_storage) - - print('aha') +import os + +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 + + +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 plot_latent_trajectories(data_dict, filename='latent_trajectory_plot'): + + bupu = cl.scales['9']['seq']['BuPu'] + scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins + + # Fit the mebedding space + transformer = TSNE() + for trajectory_id in data_dict: + transformer.fit(np.asarray(data_dict[trajectory_id])) + + # Transform data accordingly and plot it + data = [] + for trajectory_id in data_dict: + transformed = transformer._fit(np.asarray(data_dict[trajectory_id])) + line_trace = go.Scatter( + x=transformed[:, 0], + y=transformed[:, 1], + text='Hovertext goes here'.format(), + line=dict(color=scale[trajectory_id]), + # legendgroup='Position -{}'.format(pos), + # name='Position -{}'.format(pos), + showlegend=False, + # hoverinfo='text', + mode='lines') + line_start = go.Scatter(mode='markers', x=[transformed[0, 0]], y=[transformed[0, 1]], + marker=dict( + color='rgb(255, 0, 0)', + size=4 + ), + showlegend=False + ) + line_end = go.Scatter(mode='markers', x=[transformed[-1, 0]], y=[transformed[-1, 1]], + marker=dict( + color='rgb(0, 0, 0)', + size=4 + ), + showlegend=False + ) + data.extend([line_trace, line_start, line_end]) + + layout = dict(title='{} - Latent Trajectory Movement'.format('Penis'), + height=800, width=800, margin=dict(l=0, r=0, t=0, b=0)) + # import plotly.io as pio + # pio.write_image(fig, filename) + fig = go.Figure(data=data, layout=layout) + pl.offline.plot(fig, auto_open=True, filename=filename) + pass + + +def plot_latent_trajectories_3D(data_dict, filename='plot'): + def norm(val, a=0, b=0.25): + return (val - a) / (b - a) + + bupu = cl.scales['9']['seq']['BuPu'] + scale = cl.interp(bupu, len(data_dict)+1) # Map color scale to N bins + + max_len = max([len(trajectory) for trajectory in data_dict.values()]) + + # Fit the mebedding space + transformer = TSNE() + for trajectory_id in data_dict: + transformer.fit(data_dict[trajectory_id]) + + # Transform data accordingly and plot it + data = [] + for trajectory_id in data_dict: + transformed = transformer._fit(np.asarray(data_dict[trajectory_id])) + trace = go.Scatter3d( + x=transformed[:, 0], + y=transformed[:, 1], + z=np.arange(transformed.shape[0]), + text='Hovertext goes here'.format(), + line=dict(color=scale[trajectory_id]), + # legendgroup='Position -{}'.format(pos), + # name='Position -{}'.format(pos), + showlegend=False, + # hoverinfo='text', + mode='lines') + data.append(trace) + + layout = go.Layout(scene=dict(aspectratio=dict(x=2, y=2, z=1), + xaxis=dict(tickwidth=1, title='Transformed X'), + yaxis=dict(tickwidth=1, title='transformed Y'), + zaxis=dict(tickwidth=1, title='Epoch')), + title='{} - Latent Trajectory Movement'.format('Penis'), + width=800, height=800, + margin=dict(l=0, r=0, b=0, t=0)) + + fig = go.Figure(data=data, layout=layout) + pl.offline.plot(fig, auto_open=True, filename=filename) + pass + + +def plot_histogram(bars_dict_list, filename='histogram_plot'): + # catagorical + ryb = cl.scales['10']['div']['RdYlBu'] + + data = [] + for bar_id, bars_dict in bars_dict_list: + hist = go.Histogram( + histfunc="count", + y=bars_dict.get('value', 14), + x=bars_dict.get('name', 'gimme a name'), + showlegend=False, + marker=dict( + color=ryb[bar_id] + ), + ) + data.append(hist) + + layout=dict(title='{} Histogram Plot'.format('Experiment Name Penis'), + height=400, width=400, margin=dict(l=0, r=0, t=0, b=0)) + + fig = go.Figure(data=data, layout=layout) + pl.offline.plot(fig, auto_open=True, filename=filename) + + pass + + +def line_plot(line_dict_list, filename='lineplot'): + # lines with standard deviation + # Transform data accordingly and plot it + data = [] + rdylgn = cl.scales['10']['div']['RdYlGn'] + rdylgn_background = [scale + (0.4,) for scale in cl.to_numeric(rdylgn)] + for line_id, line_dict in enumerate(line_dict_list): + name = line_dict.get('name', 'gimme a name') + + upper_bound = go.Scatter( + name='Upper Bound', + x=line_dict['x'], + y=line_dict['upper_y'], + mode='lines', + marker=dict(color="#444"), + line=dict(width=0), + fillcolor=rdylgn_background[line_id], + ) + + trace = go.Scatter( + x=line_dict['x'], + y=line_dict['main_y'], + mode='lines', + name=name, + line=dict(color=line_id), + fillcolor=rdylgn_background[line_id], + fill='tonexty') + + lower_bound = go.Scatter( + name='Lower Bound', + x=line_dict['x'], + y=line_dict['lower_y'], + marker=dict(color="#444"), + line=dict(width=0), + mode='lines') + + data.extend([upper_bound, trace, lower_bound]) + + layout=dict(title='{} Line Plot'.format('Experiment Name Penis'), + height=800, width=800, margin=dict(l=0, r=0, t=0, b=0)) + + fig = go.Figure(data=data, layout=layout) + pl.offline.plot(fig, auto_open=True, filename=filename) + pass + + +if __name__ == '__main__': + args = build_args() + in_file = args.in_file[0] + out_file = args.out_file + + with open(in_file, 'rb') as in_f: + experiment = dill.load(in_f) + plot_latent_trajectories_3D(experiment.data_storage) + + print('aha')