-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feature: export experiment results #2706
Changes from 7 commits
1362b00
a128bc0
e862c1a
d3e03a7
7ac572b
8379c0f
194679e
3b23df0
ff3eea9
8d9713f
dea1c23
4316393
7c7dba8
5ef17ff
74827a5
6ff03d0
a58a588
ca195da
135b7b2
7d115a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ nnictl support commands: | |
* [nnictl package](#package) | ||
* [nnictl ss_gen](#ss_gen) | ||
* [nnictl --version](#version) | ||
* [nnictl export_results](#export-results) | ||
|
||
### Manage an experiment | ||
|
||
|
@@ -465,13 +466,14 @@ Debug mode will disable version check function in Trialkeeper. | |
|id| False| |ID of the experiment | | ||
|--filename, -f| True| |File path of the output file | | ||
|--type| True| |Type of output file, only support "csv" and "json"| | ||
|--intermediate, -i|False||Is intermediate results required| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are intermediate results included There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
* Examples | ||
|
||
> export all trial data in an experiment as json format | ||
|
||
```bash | ||
nnictl experiment export [experiment_id] --filename [file_path] --type json | ||
nnictl experiment export [experiment_id] --filename [file_path] --type json --intermediate True | ||
``` | ||
|
||
* __nnictl experiment import__ | ||
|
@@ -850,4 +852,3 @@ Debug mode will disable version check function in Trialkeeper. | |
```bash | ||
nnictl --version | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,14 +9,15 @@ | |
import re | ||
import shutil | ||
import subprocess | ||
from functools import reduce | ||
from datetime import datetime, timezone | ||
from pathlib import Path | ||
from subprocess import Popen | ||
from pyhdfs import HdfsClient | ||
from nni.package_utils import get_nni_installation_path | ||
from nni_annotation import expand_annotations | ||
from .rest_utils import rest_get, rest_delete, check_rest_server_quick, check_response | ||
from .url_utils import trial_jobs_url, experiment_url, trial_job_id_url, export_data_url | ||
from .url_utils import trial_jobs_url, experiment_url, trial_job_id_url, export_data_url, metric_data_url | ||
from .config_utils import Config, Experiments | ||
from .constants import NNICTL_HOME_DIR, EXPERIMENT_INFORMATION_FORMAT, EXPERIMENT_DETAIL_FORMAT, \ | ||
EXPERIMENT_MONITOR_INFO, TRIAL_MONITOR_HEAD, TRIAL_MONITOR_CONTENT, TRIAL_MONITOR_TAIL, REST_TIME_OUT | ||
|
@@ -681,30 +682,53 @@ def monitor_experiment(args): | |
set_monitor(False, args.time) | ||
|
||
def export_trials_data(args): | ||
'''export experiment metadata to csv | ||
'''export experiment metadata and intermediate results to json or csv | ||
''' | ||
def groupby_trial_id(intermediate_results): | ||
sorted(intermediate_results, key=lambda x: x['timestamp']) | ||
groupby = dict() | ||
for content in intermediate_results: | ||
groupby.setdefault(content['trialJobId'], []).append(content['data'][2:-2]) | ||
return groupby | ||
|
||
def trans_intermediate_dict(record): | ||
return {'intermediate': str(reduce(lambda x, y: x + y, record))} | ||
|
||
nni_config = Config(get_config_filename(args)) | ||
rest_port = nni_config.get_config('restServerPort') | ||
rest_pid = nni_config.get_config('restServerPid') | ||
print(vars(args)) | ||
tabVersion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not detect_process(rest_pid): | ||
print_error('Experiment is not running...') | ||
return | ||
running, response = check_rest_server_quick(rest_port) | ||
if running: | ||
tabVersion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
response = rest_get(export_data_url(rest_port), 20) | ||
if args.intermediate: | ||
intermediate_results = rest_get(metric_data_url(rest_port), REST_TIME_OUT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in my point, use |
||
if not intermediate_results or not check_response(intermediate_results): | ||
print_error('Error getting intermediate results.') | ||
return | ||
intermediate_results = groupby_trial_id(json.loads(intermediate_results.text)) | ||
if response is not None and check_response(response): | ||
content = json.loads(response.text) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the content of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if args.intermediate: | ||
for record in content: | ||
tabVersion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
record['intermediate'] = intermediate_results[record['id']] | ||
if args.type == 'json': | ||
with open(args.path, 'w') as file: | ||
file.write(response.text) | ||
file.write(json.dumps(content)) | ||
elif args.type == 'csv': | ||
content = json.loads(response.text) | ||
trial_records = [] | ||
for record in content: | ||
print(record) | ||
record_value = json.loads(record['value']) | ||
if not isinstance(record_value, (float, int)): | ||
formated_record = {**record['parameter'], **record_value, **{'id': record['id']}} | ||
formated_record = {**record['parameter'], **record_value, **{'id': record['id']}, | ||
**trans_intermediate_dict(record['intermediate'])} | ||
else: | ||
formated_record = {**record['parameter'], **{'reward': record_value, 'id': record['id']}} | ||
formated_record = {**record['parameter'], **{'reward': record_value, 'id': record['id']}, | ||
**trans_intermediate_dict(record['intermediate'])} | ||
trial_records.append(formated_record) | ||
if not trial_records: | ||
print_error('No trial results collected! Please check your trial log...') | ||
|
@@ -736,3 +760,64 @@ def search_space_auto_gen(args): | |
print_warning('Expected search space file \'{}\' generated, but not found.'.format(file_path)) | ||
else: | ||
print_normal('Generate search space done: \'{}\'.'.format(file_path)) | ||
|
||
def export_results(args): | ||
tabVersion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
'''dump all intermediate results and final results to json file | ||
''' | ||
|
||
def groupby_trial_id(intermediate_results): | ||
sorted(intermediate_results, key=lambda x: x['timestamp']) | ||
groupby = dict() | ||
for content in intermediate_results: | ||
groupby.setdefault(content['trialJobId'], []).append(content) | ||
return groupby | ||
|
||
update_experiment() | ||
|
||
nni_config = Config(get_config_filename(args)) | ||
rest_port = nni_config.get_config('restServerPort') | ||
rest_pid = nni_config.get_config('restServerPid') | ||
if not detect_process(rest_pid): | ||
print_error('Experiment is not running...') | ||
return | ||
running, _ = check_rest_server_quick(rest_port) | ||
if running: | ||
experiment_info = rest_get(experiment_url(rest_port), REST_TIME_OUT) | ||
trial_info = rest_get(metric_data_url(rest_port), REST_TIME_OUT) | ||
trial_jobs = rest_get(trial_jobs_url(rest_port), REST_TIME_OUT) | ||
|
||
if experiment_info and check_response(experiment_info) and \ | ||
trial_info and check_response(trial_info) and \ | ||
trial_jobs and check_response(trial_jobs): | ||
results = {} | ||
experiment_info = json.loads(experiment_info.text) | ||
results['experimentParameters'] = experiment_info | ||
experiment_id = experiment_info.get('id') | ||
trial_info = json.loads(trial_info.text) | ||
group_trial_info = groupby_trial_id(trial_info) | ||
trial_jobs = json.loads(trial_jobs.text) | ||
trial_message = [] | ||
for trial in trial_jobs: | ||
trial_id = trial['id'] | ||
trial['intermediate'] = group_trial_info[trial_id] | ||
trial_message.append(trial) | ||
results['trialMessage'] = trial_message | ||
else: | ||
print_error('Ops. Error occured connecting to the server.') | ||
exit(1) | ||
else: | ||
args = vars(args) | ||
experiment_config = Experiments() | ||
experiment_dict = experiment_config.get_all_experiments() | ||
print_error('Ops. The experiment to export isn\'t running now. Please use \n \ | ||
nnictl resume {}\nto restart.'.format(args.id if args.id in experiment_dict else '')) | ||
exit(1) | ||
args = vars(args) | ||
filename = 'exp_' + str(experiment_id) + '_' + str(time.time()) + '.json' \ | ||
if args.get('name') == '' else args.get('name') | ||
if os.path.exists(filename): | ||
print_error('File {0} has existed.'.format(filename)) | ||
exit(1) | ||
with open(filename, 'w') as f: | ||
f.write(json.dumps(results)) | ||
print_normal('Export expriment {0} done: {1}'.format(experiment_id, filename)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the difference of this command from
nnictl experiment export
in https://nni.readthedocs.io/en/latest/Tutorial/Nnictl.html#manage-experiment-informationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It dumps not only experiment settings, trial final results, but also all intermediate results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should combine the commands, otherwise, the commands are messy. please try to merge your command to
nnictl experiment export
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will add a CLI option to specify whether intermediate results are required or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove unused command in doc.