71 KiB
Inżynieria uczenia maszynowego
7. Sacred [laboratoria]
Tomasz Ziętkiewicz (2023)
Sacred
Every experiment is sacred
Every experiment is great
If an experiment is wasted
God gets quite irate
—https://github.com/IDSIA/sacred / Sens życia według Monty Pythona
- https://sacred.readthedocs.io/ - dokumentacja
- https://github.com/IDSIA/sacred - Github
- Open source
- Prosty w użyciu
- Wiele webowych frontendów
- Przeprowadzanie eksperymentów (zmiana parametrów, trenowanie, ewaluacja) uczenia maszynowego jest kosztowne i czasochłonne
- Dlatego warto przeprowadzać je w zorganizowany sposób
- I tak, żebyśmy mogli powtórzyć / odtworzyć raz uzyskane wyniki
Sacred is a tool to help you:
- configure
- organize
- log
- reproduce experiments.
It is designed to do all the tedious overhead work that you need to do around your actual experiment in order to:
- keep track of all the parameters of your experiment
- easily run your experiment for different settings
- save configurations for individual runs in a database
- reproduce your results
- ConfigScopes A very convenient way of the local variables in a function to define the parameters your experiment uses.
- Config Injection You can access all parameters of your configuration from every function. They are automatically injected by name.
- Command-line interface You get a powerful command-line interface for each experiment that you can use to change parameters and run different variants.
- Observers Sacred provides Observers that log all kinds of information about your experiment, its dependencies, the configuration you used, the machine it is run on, and of course the result. These can be saved to a MongoDB, for easy access later.
- Automatic seeding helps controlling the randomness in your experiments, such that the results remain reproducible.
Instalacja
!pip3 install sacred
Collecting sacred Downloading sacred-0.8.4-py2.py3-none-any.whl (107 kB) [K |████████████████████████████████| 107 kB 637 kB/s eta 0:00:01 [?25hCollecting py-cpuinfo>=4.0 Downloading py_cpuinfo-9.0.0-py3-none-any.whl (22 kB) Collecting GitPython Downloading GitPython-3.1.31-py3-none-any.whl (184 kB) [K |████████████████████████████████| 184 kB 617 kB/s eta 0:00:01 [?25hCollecting munch<3.0,>=2.5 Downloading munch-2.5.0-py2.py3-none-any.whl (10 kB) Collecting wrapt<2.0,>=1.0 Downloading wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (78 kB) [K |████████████████████████████████| 78 kB 508 kB/s eta 0:00:01 [?25hCollecting colorama>=0.4 Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB) Collecting docopt<1.0,>=0.3 Downloading docopt-0.6.2.tar.gz (25 kB) Collecting jsonpickle>=1.2 Downloading jsonpickle-3.0.1-py2.py3-none-any.whl (40 kB) [K |████████████████████████████████| 40 kB 518 kB/s eta 0:00:01 [?25hRequirement already satisfied: packaging>=18.0 in /home/tomek/miniconda3/lib/python3.9/site-packages (from sacred) (23.0) Requirement already satisfied: six in /home/tomek/miniconda3/lib/python3.9/site-packages (from munch<3.0,>=2.5->sacred) (1.16.0) Collecting gitdb<5,>=4.0.1 Downloading gitdb-4.0.10-py3-none-any.whl (62 kB) [K |████████████████████████████████| 62 kB 430 kB/s eta 0:00:01 [?25hCollecting smmap<6,>=3.0.1 Using cached smmap-5.0.0-py3-none-any.whl (24 kB) Building wheels for collected packages: docopt Building wheel for docopt (setup.py) ... [?25ldone [?25h Created wheel for docopt: filename=docopt-0.6.2-py2.py3-none-any.whl size=13723 sha256=9ae6354916cc7817db186faf89036b67a6562e0391c04e901e72698d9ce5bd8b Stored in directory: /home/tomek/.cache/pip/wheels/70/4a/46/1309fc853b8d395e60bafaf1b6df7845bdd82c95fd59dd8d2b Successfully built docopt Installing collected packages: smmap, gitdb, wrapt, py-cpuinfo, munch, jsonpickle, GitPython, docopt, colorama, sacred Successfully installed GitPython-3.1.31 colorama-0.4.6 docopt-0.6.2 gitdb-4.0.10 jsonpickle-3.0.1 munch-2.5.0 py-cpuinfo-9.0.0 sacred-0.8.4 smmap-5.0.0 wrapt-1.15.0
Funkcja main
%%writefile sacred_hello.py
from sacred import Experiment
ex = Experiment()
@ex.automain
def my_main():
print('Witaj świecie!')
Overwriting sacred_hello.py
!python3 IUM_07/sacred_hello.py
WARNING - sacred_hello - No observers have been added to this run INFO - sacred_hello - Running command 'my_main' INFO - sacred_hello - Started Witaj świecie! INFO - sacred_hello - Completed after 0:00:00
Co się dzieje w kodzie powyżej?
- Tworzymy obiekt klasy Experiment
- Dekorujemy funkcję "my_main" dekoratorem automain
Dzięki temu:
- otrzymujemy interfejs CLI, m.in. do kontrolowania poziomu logowania, przekazywania parametrów itp.
- oznaczamy funkcję "my_main" jako główną funkcję, która będzie wywoływana podczas wykonywania eksperymentu
- funkcja oznaczona jako główna musi być ostatnią funkcją zdefiniowaną w pliku!
Co nam daje interejs CLI:
!python3 IUM_07/sacred_hello.py -h
Usage: sacred_hello.py [(with UPDATE...)] [options] sacred_hello.py help [COMMAND] sacred_hello.py (-h | --help) sacred_hello.py COMMAND [(with UPDATE...)] [options] Options: -b VALUE --beat-interval=VALUE Set the heart-beat interval for this run. Time between two heartbeat events is measured in seconds. -C VALUE --capture=VALUE Control the way stdout and stderr are captured. The argument value must be one of [no, sys, fd] -c VALUE --comment=VALUE Add a comment to this run. -d --debug Set this run to debug mode. Suppress warnings about missing observers and don't filter the stacktrace. Also enables usage with ipython `--pdb`. -e --enforce_clean Fail if any version control repository is dirty. -F VALUE --file_storage=VALUE Add a file-storage observer to the experiment. The value of the arguement should be the base- directory to write the runs to -f --force Disable warnings about suspicious changes for this run. -h --help Print this help message and exit. -l VALUE --loglevel=VALUE Set the LogLevel. Loglevel either as 0 - 50 or as string: DEBUG(10), INFO(20), WARNING(30), ERROR(40), CRITICAL(50) -m VALUE --mongo_db=VALUE Add a MongoDB Observer to the experiment. The argument value is the database specification. Should be in the form: `[host:port:]db_name[.c ollection[:id]][!priority]` -n VALUE --name=VALUE Set the name for this run. -D --pdb Automatically enter post-mortem debugging with pdb on failure. -p --print-config Always print the configuration first. -P VALUE --priority=VALUE Sets the priority for a queued up experiment. `--priority=NUMBER` The number represent the priority for this run. -q --queue Only queue this run, do not start it. -S VALUE --s3=VALUE Add a S3 File observer to the experiment. The argument value should be `s3://<bucket>/path/to/exp`. -s VALUE --sql=VALUE Add a SQL Observer to the experiment. The typical form is: dialect://username:password@host:port/database -t VALUE --tiny_db=VALUE Add a TinyDB Observer to the experiment. The argument is the path to be given to the TinyDbObserver. -u --unobserved Ignore all observers for this run. Arguments: COMMAND Name of command to run (see below for list of commands) UPDATE Configuration assignments of the form foo.bar=17 Commands: print_config Print the updated configuration and exit. print_dependencies Print the detected source-files and dependencies. save_config Store the updated configuration in a file. print_named_configs Print the available named configs and exit. my_main
Konfiguracje
- Konfiguracje pozwalają nam sparametryzować wywołania eksperymentu.
- Ułatwiają przekazywanie parametrów - zmienne z konfiguracji są wstrzykiwane do funkcji wywoływanych
- Mogą być automatycznie zapisywane (dzięki czemu możemy śledzić jak zmieniały się parametry i jaki miały wpływ na wyniki)
- Konfigurację można stworzyć w jeden z 3 sposobów:
- używając config scopes (z dekoratorem
@config
) - jako słownik
- wczytując ją z pliku
- używając config scopes (z dekoratorem
Konfiguracje - config scopes
Jeśli oznaczymy jakąś funkcję dekoratorem @config
, to zostanie ona uruchoniona przed wywołaniem eksperymentu i wszystkie jej lokalne zmienne, które da się zserializować jako json, zostaną dodane do konfiguracji. Potem ich wartości zostaną wstrzyknięte do innych funkcji wywoływanych w eksperymencie.
from sacred import Experiment
exint = Experiment("sacred_scopes", interactive=True) #Jeśli wykonujemy interaktywnie (w konsoli Pythona albo w Jupyter):
# - musimy podać nazwę eksperymentu (domyślnie jako nazwa używana jest nazwa pliku źródłowego)
# - musimy dodać parametr "interactive=True"
# - zamiast dekoratora "@ex.automain" używamy "@ex.main"
@exint.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
message = "{0} {1}!".format(greeting, recipient)
@exint.main
def my_main(message):
print(message)
exint.run()
WARNING - sacred_scopes - No observers have been added to this run INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started INFO - sacred_scopes - Completed after 0:00:00
Witaj Świecie!
<sacred.run.Run at 0x7f641e9ddba8>
Możemy podejrzeć wartości zmiennych w konfiguracji:
my_config()
{'recipient': 'Świecie', 'greeting': 'Witaj', 'message': 'Witaj Świecie!'}
Parametry możemy podejrzeć i modyfikować z poziomu CLI
- wartości podane w CLI nadpiszą te podane w kodzie
# %load IUM_07/sacred_scopes.py
from sacred import Experiment
ex = Experiment()
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
message = "{0} {1}!".format(greeting, recipient)
@ex.automain
def my_main(message):
print(message)
!python3 IUM_07/sacred_scopes.py with 'recipient=Przygodo'
WARNING - sacred_scopes - No observers have been added to this run INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started Witaj Przygodo! INFO - sacred_scopes - Completed after 0:00:00
!python3 IUM_07/sacred_scopes.py print_config
INFO - sacred_scopes - Running command 'print_config' INFO - sacred_scopes - Started Configuration ([34mmodified[0m, [32madded[0m, [31mtypechanged[0m, [2mdoc[0m): greeting = 'Witaj' message = 'Witaj Świecie!' recipient = 'Świecie' seed = 539001265 [2m# the random seed for this experiment[0m INFO - sacred_scopes - Completed after 0:00:00
!python IUM_07/sacred_scopes.py print_config with 'recipient=Przygodo'
INFO - sacred_scopes - Running command 'print_config' INFO - sacred_scopes - Started Configuration ([34mmodified[0m, [32madded[0m, [31mtypechanged[0m, [2mdoc[0m): greeting = 'Witaj' message = 'Witaj Przygodo!' [34m recipient = 'Przygodo'[0m seed = 215765170 [2m# the random seed for this experiment[0m INFO - sacred_scopes - Completed after 0:00:00
Wczytywanie konfiguracji z pliku
# %load IUM_07/config.json
{
"recipient": "samotności",
"greeting": "Żegnaj"
}
{'recipient': 'samotności', 'greeting': 'Żegnaj'}
from sacred import Experiment
ex = Experiment("sacred_scopes", interactive=True) #Jeśli wykonujemy interaktywnie (w konsoli Pythona albo w Jupyter):
# - musimy podać nazwę eksperymentu (domyślnie jako nazwa używana jest nazwa pliku źródłowego)
# - musimy dodać parametr "interactive=True"
# - zamiast "automain" używamy parametru "main"
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
ex.add_config("IUM_07/config.json")
@ex.main
def my_main(recipient, greeting):
print("{0} {1}!".format(greeting, recipient))
r = ex.run()
WARNING - sacred_scopes - No observers have been added to this run INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started INFO - sacred_scopes - Completed after 0:00:00
Żegnaj samotności!
r.config
{'recipient': 'samotności', 'greeting': 'Żegnaj', 'seed': 529757761}
Możemy modyfikować części konfiguracji bezpośrednio przed wywołaniem
r = ex.run(config_updates={"recipient":"nudo"})
WARNING - sacred_scopes - No observers have been added to this run INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started INFO - sacred_scopes - Completed after 0:00:00
Żegnaj nudo!
Wstrzykiwanie zależności
- Oprócz funkcji głównej, wartości z konfiguracji są też wstrzykiwane do funkcji udekorowanych dekoratorem
@ex.capture
- Możemy korzystać w nich ze specjalnych parametrów, np.:
_log
- daje nam dostęp do obiektu logera (więcej: logowanie)_run
- daje dostęp do obiektu reprezentującego aktualne wywołanie eksperymentu (przykład później)
from sacred import Experiment
ex = Experiment("sacred_scopes", interactive=True)
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
@ex.capture
def prepare_message(recipient, greeting, _log):
_log.info("Enterred prepare_message")
return "{0} {1}!".format(greeting, recipient)
@ex.main
def my_main():
print(prepare_message()) ## Nie musimy przekazywać wartości
ex.run()
WARNING - sacred_scopes - No observers have been added to this run INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started INFO - prepare_message - Enterred prepare_message INFO - sacred_scopes - Completed after 0:00:00
Witaj Świecie!
<sacred.run.Run at 0x7f423c40d820>
Obserwowanie eksperymentów
Sacred zapisuje szereg informacji na temat każdego eksperymentu:
- czas wykonania
- konfigurację
- tekst zwrócony na stdout/stderr
- błędy, jeśli wystąpiły
- podstawowe informacje o środowisku (maszynie), na której przeprowadzono eksperyment
- użyte pliki źródłowe
- użyte zależności i ich wersje
- pliki otwarte za pomocą ex.open_resource() albo ex.add_resource()
- pliki dodane za pomocą ex.add_artifact()
!ls -l runs
Obserwowane infromacje mogą zostać zapisane za pomocą jednego z obserwatorów:
- Mongo Observer - zapisuje dane w MongoDB
- File Storage Observer - zapisuje dane lokalnie w pliku
- TinyDB Observer - korzysta z lokalnej bazy zapisanej w pliku JSON
- SQL Observer - przechowuje informacje w bazie SQL
- S3 Observer - korzysta z AWS S3
- gcs_observer - korzysta z Google Cloud Storage
- Queue Observer - rodzaj lokalnego bufora nakładanego na jeden z powyższych
- Slack Observer - używany do powiadomień wysyłanych na komunikator Slack
- Telegram Observer - używany do powiadomień wysyłanych na komunikator Telegram
File storage observer
- zapisuje informacje o eksperymencie w lokalnych plikach
- można go dodać tak:
ex.observers.append(FileStorageObserver('my_runs_directory'))
, gdziemy_runs_directory
to ścieżka, gdzie będą zapisywane informacje o eksperymentach
%%writefile IUM_07/file_observer.py
from sacred.observers import FileStorageObserver
from sacred import Experiment
ex = Experiment("file_observer")
ex.observers.append(FileStorageObserver('my_runs'))
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
@ex.capture
def prepare_message(recipient, greeting):
return "{0} {1}!".format(greeting, recipient)
@ex.automain
def my_main(recipient, greeting):
print(prepare_message()) ## Nie musimy przekazywać wartości
Overwriting IUM_07/file_observer.py
!python3 IUM_07/file_observer.py
INFO - file_observer - Running command 'my_main' INFO - file_observer - Started run with ID "1" Witaj Świecie! INFO - file_observer - Completed after 0:00:00
Zobaczmy jakie informacje zostały zapisane
!ls -l my_runs
total 0 drwxr-xr-x 1 tomek tomek 512 Apr 25 09:51 1 drwxr-xr-x 1 tomek tomek 512 Apr 25 09:51 _sources
!ls -l my_runs/1
total 4 -rw-r--r-- 1 tomek tomek 77 Apr 25 09:51 config.json -rw-r--r-- 1 tomek tomek 159 Apr 25 09:51 cout.txt -rw-r--r-- 1 tomek tomek 2 Apr 25 09:51 metrics.json -rw-r--r-- 1 tomek tomek 1659 Apr 25 09:51 run.json
# %load my_runs/1/config.json
{
"greeting": "Witaj",
"recipient": "\u015awiecie",
"seed": 805857632
}
!cat my_runs/1/cout.txt
INFO - file_observer - Running command 'my_main' INFO - file_observer - Started run with ID "1" Witaj Świecie! INFO - file_observer - Completed after 0:00:00
# %load my_runs/1/run.json
{
"artifacts": [],
"command": "my_main",
"experiment": {
"base_dir": "/home/tomek/repos/aitech/aitech-ium/IUM_07",
"dependencies": [
"sacred==0.8.2"
],
"mainfile": "file_observer.py",
"name": "file_observer",
"repositories": [
{
"commit": "3055a4f1c2ef06ea1c29e3d41d862827cede7e2a",
"dirty": true,
"url": "git@git.wmi.amu.edu.pl:tzietkiewicz/aitech-ium.git"
}
],
"sources": [
[
"file_observer.py",
"_sources/file_observer_cd34a0ef4a32fb0a966eaa01ea6371ad.py"
]
]
},
"heartbeat": "2022-04-25T07:51:37.853633",
"host": {
"ENV": {},
"cpu": "Intel(R) Core(TM) i5-4200H CPU @ 2.80GHz",
"hostname": "ASUSEK",
"os": [
"Linux",
"Linux-4.4.0-19041-Microsoft-x86_64-with-Ubuntu-18.04-bionic"
],
"python_version": "3.6.9"
},
"meta": {
"command": "my_main",
"options": {
"--beat-interval": null,
"--capture": null,
"--comment": null,
"--debug": false,
"--enforce_clean": false,
"--file_storage": null,
"--force": false,
"--help": false,
"--loglevel": null,
"--mongo_db": null,
"--name": null,
"--pdb": false,
"--print-config": false,
"--priority": null,
"--queue": false,
"--s3": null,
"--sql": null,
"--tiny_db": null,
"--unobserved": false,
"COMMAND": null,
"UPDATE": [],
"help": false,
"with": false
}
},
"resources": [],
"result": null,
"start_time": "2022-04-25T07:51:37.831461",
"status": "COMPLETED",
"stop_time": "2022-04-25T07:51:37.849334"
}
! ls -l my_runs/_sources
## W run.json możemy znaleźć ścieżkę do pliku z źródłami: "_sources/file_observer_bb0a5c4720d1072b641d23da080696b6.py"
total 4 -rw-rw-r-- 1 tomek tomek 463 kwi 26 10:21 file_observer_bb0a5c4720d1072b641d23da080696b6.py
## Źródła zostały zapisane
# %load my_runs/_sources/file_observer_bb0a5c4720d1072b641d23da080696b6.py
from sacred.observers import FileStorageObserver
from sacred import Experiment
ex = Experiment("file_observer")
ex.observers.append(FileStorageObserver('my_runs'))
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
@ex.capture
def prepare_message(recipient, greeting):
return "{0} {1}!".format(greeting, recipient)
@ex.automain
def my_main(recipient, greeting):
print(prepare_message()) ## Nie musimy przekazywać wartości
Dodawanie własnych informacji
from sacred.observers import FileStorageObserver
from sacred import Experiment
from datetime import datetime
ex = Experiment("file_observer", interactive=True)
ex.observers.append(FileStorageObserver('my_runs'))
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
### - Do "przechwyconej" przez @ex.capture funkcji prepare_message dodaliśmy specjalny parametr _run
### - Daje on dostęp do obiektu wywołania eksperymentu w trakcie jego wywołania
### - umożliwia m.in. zapisywanie dodatkowych informacji w słowniku info
@ex.capture
def prepare_message(recipient, greeting, _run):
_run.info["prepare_message_ts"] = str(datetime.now())
return "{0} {1}!".format(greeting, recipient)
@ex.main
def my_main(recipient, greeting):
print(prepare_message()) ## Nie musimy przekazywać wartości
r = ex.run()
INFO - file_observer - Running command 'my_main' INFO - file_observer - Started run with ID "2" INFO - file_observer - Completed after 0:00:00
Witaj Świecie!
cat my_runs/6/info.json
{ "prepare_message_ts": "2021-04-26 10:39:59.268539" }
Artefakty
- Artefakty służą do zapisywania plików, np. z wytrenowanym modelem
- Plik można zapisać jako artefakt korzystając z : ex.add_artifact()
ex.add_artifact("model.pb")
Otwieranie zasobów
- Zmiana danych wejściowych wpłwa w oczywisty sposób na wyniki
- Dlatego warto śledzić te zmiany za pomocą:
from sacred import Experiment
from sacred.observers import FileStorageObserver
ex = Experiment("resources", interactive=True)
ex.observers.append(FileStorageObserver('my_runs'))
@ex.main
def my_main():
f = ex.open_resource("Iris.csv", "r")
print(f.readline())
ex.run()
INFO - resources - Running command 'my_main' INFO - resources - Started run with ID "3" INFO - resources - Completed after 0:00:00
Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species
<sacred.run.Run at 0x7f9b8821cd90>
!ls -l my_runs/_resources
total 8 -rw-r--r-- 1 tomek tomek 5107 Apr 12 15:11 Iris_717820ef0af287ff346c5cabfb4c612c.csv
!grep -e "resources" -R my_runs/3
my_runs/3/run.json: "name": "resources", my_runs/3/run.json: "resources": [ my_runs/3/run.json: "my_runs/_resources/Iris_717820ef0af287ff346c5cabfb4c612c.csv"
Obserwator mongo
- Żeby skorzystać z obserwatora Mongo, musimy mieć dostęp do bazy Mongo.
- Można ją łatwo "postawić" za pomocą docker-compose .
- W tym celu wystarczy skopiować katalog examples/docker z repozytorium SACRED i uruchomić
docker-compose up
- dostaniemy uruchomioną bazę MongoDB i dodatkowo Omniboard . Więcej informacji w dokumentacji - Baza taka została już postawiona na serwerze Jenkins, więc pracując na Jenkinsie można skorzystać z lokalnej bazy (
localhost:27017
)
!pip3 install pymongo
Collecting pymongo Downloading https://files.pythonhosted.org/packages/10/3b/46541b4ee3000019b8ef5b1847292ddc77f492c162bc4d49c424db7fc97a/pymongo-4.1.1-cp36-cp36m-manylinux1_x86_64.whl (464kB) [K 100% |████████████████████████████████| 471kB 959kB/s ta 0:00:01 [?25hInstalling collected packages: pymongo Successfully installed pymongo-4.1.1
from sacred.observers import MongoObserver
from sacred import Experiment
ex = Experiment("sacred_scopes", interactive=True)
ex.observers.append(MongoObserver(url='mongodb://mongo_user:mongo_password@localhost:27017',
db_name='sacred')) # Tutaj podajemy dane uwierzytelniające i nazwę bazy skonfigurowane w pliku .env podczas uruchamiania bazy.
# W przypadku instancji na Jenkinsie url będzie wyglądał następująco: mongodb://mongo_user:mongo_password_IUM_2021@localhost:27017
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
@ex.capture
def prepare_message(recipient, greeting):
return "{0} {1}!".format(greeting, recipient)
@ex.main
def my_main(recipient, greeting):
print(prepare_message()) ## Nie musimy przekazywać wartości
ex.run()
INFO - sacred_scopes - Running command 'my_main' ERROR - sacred_scopes - Failed after 0:00:30! ERROR - sacred_scopes - Traceback (most recent call last): File "/home/tomek/.local/lib/python3.6/site-packages/sacred/run.py", line 235, in __call__ self._emit_started() File "/home/tomek/.local/lib/python3.6/site-packages/sacred/run.py", line 333, in _emit_started _id=self._id, File "/home/tomek/.local/lib/python3.6/site-packages/sacred/observers/mongo.py", line 258, in started_event self.insert() File "/home/tomek/.local/lib/python3.6/site-packages/sacred/observers/mongo.py", line 367, in insert c.next()["_id"] + 1 if self.runs.count_documents({}, limit=1) else 1 File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/collection.py", line 1811, in count_documents return self._retryable_non_cursor_read(_cmd, session) File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/collection.py", line 1816, in _retryable_non_cursor_read with client._tmp_session(session) as s: File "/usr/lib/python3.6/contextlib.py", line 81, in __enter__ return next(self.gen) File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/mongo_client.py", line 1676, in _tmp_session s = self._ensure_session(session) File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/mongo_client.py", line 1663, in _ensure_session return self.__start_session(True, causal_consistency=False) File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/mongo_client.py", line 1608, in __start_session self._topology._check_implicit_session_support() File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/topology.py", line 519, in _check_implicit_session_support self._check_session_support() File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/topology.py", line 536, in _check_session_support readable_server_selector, self._settings.server_selection_timeout, None File "/home/tomek/.local/lib/python3.6/site-packages/pymongo/topology.py", line 229, in _select_servers_loop % (self._error_message(selector), timeout, self.description) pymongo.errors.ServerSelectionTimeoutError: localhost:27017: [Errno 111] Connection refused, Timeout: 30s, Topology Description: <TopologyDescription id: 6266603d3269f60a67f61383, topology_type: Unknown, servers: [<ServerDescription ('localhost', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('localhost:27017: [Errno 111] Connection refused',)>]> During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/tomek/.local/lib/python3.6/site-packages/sacred/run.py", line 429, in _final_call getattr(observer, method)(**kwargs) File "/home/tomek/.local/lib/python3.6/site-packages/sacred/observers/mongo.py", line 283, in failed_event self.final_save(attempts=1) File "/home/tomek/.local/lib/python3.6/site-packages/sacred/observers/mongo.py", line 421, in final_save os.makedirs(self.failure_dir, exist_ok=True) File "/usr/lib/python3.6/os.py", line 205, in makedirs head, tail = path.split(name) File "/usr/lib/python3.6/posixpath.py", line 107, in split p = os.fspath(p) TypeError: expected str, bytes or os.PathLike object, not NoneType
[0;31m---------------------------------------------------------------------------[0m [0;31mServerSelectionTimeoutError[0m Traceback (most recent call last) [0;32m<ipython-input-23-95f1b404dde7>[0m in [0;36m<module>[0;34m[0m [1;32m 19[0m [0mprint[0m[0;34m([0m[0mprepare_message[0m[0;34m([0m[0;34m)[0m[0;34m)[0m [0;31m## Nie musimy przekazywać wartości[0m[0;34m[0m[0;34m[0m[0m [1;32m 20[0m [0;34m[0m[0m [0;32m---> 21[0;31m [0mex[0m[0;34m.[0m[0mrun[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m [0;32m~/.local/lib/python3.6/site-packages/sacred/experiment.py[0m in [0;36mrun[0;34m(self, command_name, config_updates, named_configs, info, meta_info, options)[0m [1;32m 274[0m [0mcommand_name[0m[0;34m,[0m [0mconfig_updates[0m[0;34m,[0m [0mnamed_configs[0m[0;34m,[0m [0minfo[0m[0;34m,[0m [0mmeta_info[0m[0;34m,[0m [0moptions[0m[0;34m[0m[0;34m[0m[0m [1;32m 275[0m ) [0;32m--> 276[0;31m [0mrun[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 277[0m [0;32mreturn[0m [0mrun[0m[0;34m[0m[0;34m[0m[0m [1;32m 278[0m [0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/sacred/run.py[0m in [0;36m__call__[0;34m(self, *args)[0m [1;32m 233[0m [0;32mtry[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 234[0m [0;32mwith[0m [0mcapture_stdout[0m[0;34m([0m[0;34m)[0m [0;32mas[0m [0mself[0m[0;34m.[0m[0m_output_file[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m--> 235[0;31m [0mself[0m[0;34m.[0m[0m_emit_started[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 236[0m [0mself[0m[0;34m.[0m[0m_start_heartbeat[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [1;32m 237[0m [0mself[0m[0;34m.[0m[0m_execute_pre_run_hooks[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/sacred/run.py[0m in [0;36m_emit_started[0;34m(self)[0m [1;32m 331[0m [0mconfig[0m[0;34m=[0m[0mself[0m[0;34m.[0m[0mconfig[0m[0;34m,[0m[0;34m[0m[0;34m[0m[0m [1;32m 332[0m [0mmeta_info[0m[0;34m=[0m[0mself[0m[0;34m.[0m[0mmeta_info[0m[0;34m,[0m[0;34m[0m[0;34m[0m[0m [0;32m--> 333[0;31m [0m_id[0m[0;34m=[0m[0mself[0m[0;34m.[0m[0m_id[0m[0;34m,[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 334[0m ) [1;32m 335[0m [0;32mif[0m [0mself[0m[0;34m.[0m[0m_id[0m [0;32mis[0m [0;32mNone[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/sacred/observers/mongo.py[0m in [0;36mstarted_event[0;34m(self, ex_info, command, host_info, start_time, config, meta_info, _id)[0m [1;32m 256[0m [0;31m# save sources[0m[0;34m[0m[0;34m[0m[0;34m[0m[0m [1;32m 257[0m [0mself[0m[0;34m.[0m[0mrun_entry[0m[0;34m[[0m[0;34m"experiment"[0m[0;34m][0m[0;34m[[0m[0;34m"sources"[0m[0;34m][0m [0;34m=[0m [0mself[0m[0;34m.[0m[0msave_sources[0m[0;34m([0m[0mex_info[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0;32m--> 258[0;31m [0mself[0m[0;34m.[0m[0minsert[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 259[0m [0;32mreturn[0m [0mself[0m[0;34m.[0m[0mrun_entry[0m[0;34m[[0m[0;34m"_id"[0m[0;34m][0m[0;34m[0m[0;34m[0m[0m [1;32m 260[0m [0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/sacred/observers/mongo.py[0m in [0;36minsert[0;34m(self)[0m [1;32m 365[0m [0mc[0m [0;34m=[0m [0mc[0m[0;34m.[0m[0msort[0m[0;34m([0m[0;34m"_id"[0m[0;34m,[0m [0mpymongo[0m[0;34m.[0m[0mDESCENDING[0m[0;34m)[0m[0;34m.[0m[0mlimit[0m[0;34m([0m[0;36m1[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [1;32m 366[0m self.run_entry["_id"] = ( [0;32m--> 367[0;31m [0mc[0m[0;34m.[0m[0mnext[0m[0;34m([0m[0;34m)[0m[0;34m[[0m[0;34m"_id"[0m[0;34m][0m [0;34m+[0m [0;36m1[0m [0;32mif[0m [0mself[0m[0;34m.[0m[0mruns[0m[0;34m.[0m[0mcount_documents[0m[0;34m([0m[0;34m{[0m[0;34m}[0m[0;34m,[0m [0mlimit[0m[0;34m=[0m[0;36m1[0m[0;34m)[0m [0;32melse[0m [0;36m1[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 368[0m ) [1;32m 369[0m [0;32mtry[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/collection.py[0m in [0;36mcount_documents[0;34m(self, filter, session, comment, **kwargs)[0m [1;32m 1809[0m [0;32mreturn[0m [0mresult[0m[0;34m[[0m[0;34m"n"[0m[0;34m][0m[0;34m[0m[0;34m[0m[0m [1;32m 1810[0m [0;34m[0m[0m [0;32m-> 1811[0;31m [0;32mreturn[0m [0mself[0m[0;34m.[0m[0m_retryable_non_cursor_read[0m[0;34m([0m[0m_cmd[0m[0;34m,[0m [0msession[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 1812[0m [0;34m[0m[0m [1;32m 1813[0m [0;32mdef[0m [0m_retryable_non_cursor_read[0m[0;34m([0m[0mself[0m[0;34m,[0m [0mfunc[0m[0;34m,[0m [0msession[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/collection.py[0m in [0;36m_retryable_non_cursor_read[0;34m(self, func, session)[0m [1;32m 1814[0m [0;34m"""Non-cursor read helper to handle implicit session creation."""[0m[0;34m[0m[0;34m[0m[0m [1;32m 1815[0m [0mclient[0m [0;34m=[0m [0mself[0m[0;34m.[0m[0m__database[0m[0;34m.[0m[0mclient[0m[0;34m[0m[0;34m[0m[0m [0;32m-> 1816[0;31m [0;32mwith[0m [0mclient[0m[0;34m.[0m[0m_tmp_session[0m[0;34m([0m[0msession[0m[0;34m)[0m [0;32mas[0m [0ms[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 1817[0m [0;32mreturn[0m [0mclient[0m[0;34m.[0m[0m_retryable_read[0m[0;34m([0m[0mfunc[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0m_read_preference_for[0m[0;34m([0m[0ms[0m[0;34m)[0m[0;34m,[0m [0ms[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [1;32m 1818[0m [0;34m[0m[0m [0;32m/usr/lib/python3.6/contextlib.py[0m in [0;36m__enter__[0;34m(self)[0m [1;32m 79[0m [0;32mdef[0m [0m__enter__[0m[0;34m([0m[0mself[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 80[0m [0;32mtry[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m---> 81[0;31m [0;32mreturn[0m [0mnext[0m[0;34m([0m[0mself[0m[0;34m.[0m[0mgen[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 82[0m [0;32mexcept[0m [0mStopIteration[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 83[0m [0;32mraise[0m [0mRuntimeError[0m[0;34m([0m[0;34m"generator didn't yield"[0m[0;34m)[0m [0;32mfrom[0m [0;32mNone[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/mongo_client.py[0m in [0;36m_tmp_session[0;34m(self, session, close)[0m [1;32m 1674[0m [0;32mreturn[0m[0;34m[0m[0;34m[0m[0m [1;32m 1675[0m [0;34m[0m[0m [0;32m-> 1676[0;31m [0ms[0m [0;34m=[0m [0mself[0m[0;34m.[0m[0m_ensure_session[0m[0;34m([0m[0msession[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 1677[0m [0;32mif[0m [0ms[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 1678[0m [0;32mtry[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/mongo_client.py[0m in [0;36m_ensure_session[0;34m(self, session)[0m [1;32m 1661[0m [0;31m# Don't make implicit sessions causally consistent. Applications[0m[0;34m[0m[0;34m[0m[0;34m[0m[0m [1;32m 1662[0m [0;31m# should always opt-in.[0m[0;34m[0m[0;34m[0m[0;34m[0m[0m [0;32m-> 1663[0;31m [0;32mreturn[0m [0mself[0m[0;34m.[0m[0m__start_session[0m[0;34m([0m[0;32mTrue[0m[0;34m,[0m [0mcausal_consistency[0m[0;34m=[0m[0;32mFalse[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 1664[0m [0;32mexcept[0m [0;34m([0m[0mConfigurationError[0m[0;34m,[0m [0mInvalidOperation[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 1665[0m [0;31m# Sessions not supported.[0m[0;34m[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/mongo_client.py[0m in [0;36m__start_session[0;34m(self, implicit, **kwargs)[0m [1;32m 1606[0m [0;31m# Raises ConfigurationError if sessions are not supported.[0m[0;34m[0m[0;34m[0m[0;34m[0m[0m [1;32m 1607[0m [0;32mif[0m [0mimplicit[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m-> 1608[0;31m [0mself[0m[0;34m.[0m[0m_topology[0m[0;34m.[0m[0m_check_implicit_session_support[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 1609[0m [0mserver_session[0m [0;34m=[0m [0m_EmptyServerSession[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [1;32m 1610[0m [0;32melse[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/topology.py[0m in [0;36m_check_implicit_session_support[0;34m(self)[0m [1;32m 517[0m [0;32mdef[0m [0m_check_implicit_session_support[0m[0;34m([0m[0mself[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 518[0m [0;32mwith[0m [0mself[0m[0;34m.[0m[0m_lock[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m--> 519[0;31m [0mself[0m[0;34m.[0m[0m_check_session_support[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 520[0m [0;34m[0m[0m [1;32m 521[0m [0;32mdef[0m [0m_check_session_support[0m[0;34m([0m[0mself[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/topology.py[0m in [0;36m_check_session_support[0;34m(self)[0m [1;32m 534[0m [0;32melif[0m [0;32mnot[0m [0mself[0m[0;34m.[0m[0m_description[0m[0;34m.[0m[0mreadable_servers[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m [1;32m 535[0m self._select_servers_loop( [0;32m--> 536[0;31m [0mreadable_server_selector[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0m_settings[0m[0;34m.[0m[0mserver_selection_timeout[0m[0;34m,[0m [0;32mNone[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 537[0m ) [1;32m 538[0m [0;34m[0m[0m [0;32m~/.local/lib/python3.6/site-packages/pymongo/topology.py[0m in [0;36m_select_servers_loop[0;34m(self, selector, timeout, address)[0m [1;32m 227[0m raise ServerSelectionTimeoutError( [1;32m 228[0m [0;34m"%s, Timeout: %ss, Topology Description: %r"[0m[0;34m[0m[0;34m[0m[0m [0;32m--> 229[0;31m [0;34m%[0m [0;34m([0m[0mself[0m[0;34m.[0m[0m_error_message[0m[0;34m([0m[0mselector[0m[0;34m)[0m[0;34m,[0m [0mtimeout[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mdescription[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m [0m[1;32m 230[0m ) [1;32m 231[0m [0;34m[0m[0m [0;31mServerSelectionTimeoutError[0m: localhost:27017: [Errno 111] Connection refused, Timeout: 30s, Topology Description: <TopologyDescription id: 6266603d3269f60a67f61383, topology_type: Unknown, servers: [<ServerDescription ('localhost', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('localhost:27017: [Errno 111] Connection refused',)>]>
- Informacje o eksperymencie można obejrzeć na Omniboard: http://127.0.0.1:9000/sacred
- Instancja na Jenkinsie: http://tzietkiewicz.vm.wmi.amu.edu.pl:9000/sacred
Metryki
- W trakcie eksperymentu możemy śledzić metryki, np. aktualny loss
- W tym celu wystarczy:
- dodać do funkcji udekorowanej
@ex.main
albo@ex.capure
parametr_run
- potem wywołać np.
_run.log_scalar()
- dodać do funkcji udekorowanej
from sacred.observers import MongoObserver
from sacred import Experiment
import random
import time
ex = Experiment("sacred_scopes", interactive=True)
ex.observers.append(MongoObserver(url='mongodb://mongo_user:mongo_password@localhost:27017',
db_name='sacred')) # Tutaj podajemy dane uwierzytelniające i nazwę bazy skonfigurowane w pliku .env podczas uruchamiania bazy.
# W przypadku instancji na Jenkinsie url będzie wyglądał następująco: mongodb://mongo_user:mongo_password_IUM_2021@localhost:27017
@ex.config
def my_config():
recipient = "Świecie"
greeting = "Witaj"
@ex.capture
def prepare_message(recipient, greeting):
return "{0} {1}!".format(greeting, recipient)
@ex.main
def my_main(recipient, greeting, _run):
print(prepare_message()) ## Nie musimy przekazywać wartości
counter = 0
while counter < 20:
counter+=1
value = counter
ms_to_wait = random.randint(5, 5000)
time.sleep(ms_to_wait/1000)
noise = 1.0 + 0.1 * (random.randint(0, 10) - 5)
# This will add an entry for training.loss metric in every second iteration.
# The resulting sequence of steps for training.loss will be 0, 2, 4, ...
if counter % 2 == 0:
_run.log_scalar("training.loss", value * 1.5 * noise, counter)
# Implicit step counter (0, 1, 2, 3, ...)
# incremented with each call for training.accuracy:
_run.log_scalar("training.accuracy", value * 2 * noise)
ex.run()
INFO - sacred_scopes - Running command 'my_main' INFO - sacred_scopes - Started run with ID "9"
Witaj Świecie!
INFO - sacred_scopes - Completed after 0:00:50
<sacred.run.Run at 0x7f423c2de550>
Zadanie [15 pkt] (do 2023-05-12)
- "Owiń" wywołanie swojego eksperymentu za pomocą Sacred, w ten sposób, żeby zapisane zostały [10pkt]:
parametry, z którymi wywołany był trening
powstały plik z modelem (jako artefakt)
kod źródłowy użyty do przeprowadzenia treningu
pliki wejściowe otwarte za pomocą open_resource
metryki
Jako nazwę eksperymentu użyj swojego numeru indeksu tak, żebyś mogła/mógł je odnaleźć w Omniboard
- Wykorzystaj 2 obserwatory [5pkt]:
- MongoObserver, skorzytaj nastęþującego URL:
mongodb://admin:IUM_2021@172.17.0.1:27017
(będziesz mógł przeglądać wyniki na http://tzietkiewicz.vm.wmi.amu.edu.pl:9000/sacred) - FileObserver - zapisane pliki zarchiwizuj na Jenkinsie jako jego artefakty