Unverified Commit 611a45fc authored by chicm-ms's avatar chicm-ms Committed by GitHub
Browse files

Merge pull request #19 from microsoft/master

pull code
parents 841d4677 e267a737
...@@ -3,7 +3,7 @@ experimentName: default_test ...@@ -3,7 +3,7 @@ experimentName: default_test
maxExecDuration: 5m maxExecDuration: 5m
maxTrialNum: 4 maxTrialNum: 4
trialConcurrency: 2 trialConcurrency: 2
searchSpacePath: ../../../examples/trials/mnist-cascading-search-space/search_space.json searchSpacePath: ../../../examples/trials/mnist-nested-search-space/search_space.json
tuner: tuner:
#choice: TPE, Random, Anneal, Evolution #choice: TPE, Random, Anneal, Evolution
...@@ -13,7 +13,7 @@ assessor: ...@@ -13,7 +13,7 @@ assessor:
classArgs: classArgs:
optimize_mode: maximize optimize_mode: maximize
trial: trial:
codeDir: ../../../examples/trials/mnist-cascading-search-space codeDir: ../../../examples/trials/mnist-nested-search-space
command: python3 mnist.py --batch_num 100 command: python3 mnist.py --batch_num 100
gpuNum: 0 gpuNum: 0
......
jobs:
- job: 'integration_test_remote_windows'
steps:
- script: python -m pip install --upgrade pip setuptools
displayName: 'Install python tools'
- task: CopyFilesOverSSH@0
inputs:
sshEndpoint: $(end_point)
targetFolder: /tmp/nnitest/$(Build.BuildId)/nni-remote
overwrite: true
displayName: 'Copy all files to remote machine'
- script: |
powershell.exe -file install.ps1
displayName: 'Install nni toolkit via source code'
- script: |
python -m pip install scikit-learn==0.20.1 --user
displayName: 'Install dependencies for integration tests'
- task: SSH@0
inputs:
sshEndpoint: $(end_point)
runOptions: inline
inline: cd /tmp/nnitest/$(Build.BuildId)/nni-remote/deployment/pypi;make build
continueOnError: true
displayName: 'build nni bdsit_wheel'
- task: SSH@0
inputs:
sshEndpoint: $(end_point)
runOptions: commands
commands: python3 /tmp/nnitest/$(Build.BuildId)/nni-remote/test/remote_docker.py --mode start --name $(Build.BuildId) --image nni/nni --os windows
displayName: 'Start docker'
- powershell: |
Write-Host "Downloading Putty..."
(New-Object Net.WebClient).DownloadFile("https://the.earth.li/~sgtatham/putty/latest/w64/pscp.exe", "$(Agent.TempDirectory)\pscp.exe")
$(Agent.TempDirectory)\pscp.exe -hostkey $(hostkey) -pw $(pscp_pwd) $(remote_user)@$(remote_host):/tmp/nnitest/$(Build.BuildId)/port test\port
Get-Content test\port
displayName: 'Get docker port'
- powershell: |
cd test
python generate_ts_config.py --ts remote --remote_user $(docker_user) --remote_host $(remote_host) --remote_port $(Get-Content port) --remote_pwd $(docker_pwd) --nni_manager_ip $(nni_manager_ip)
Get-Content training_service.yml
python config_test.py --ts remote --exclude cifar10,smac,bohb
displayName: 'integration test'
- task: SSH@0
inputs:
sshEndpoint: $(end_point)
runOptions: commands
commands: python3 /tmp/nnitest/$(Build.BuildId)/nni-remote/test/remote_docker.py --mode stop --name $(Build.BuildId) --os windows
displayName: 'Stop docker'
...@@ -30,18 +30,33 @@ def find_wheel_package(dir): ...@@ -30,18 +30,33 @@ def find_wheel_package(dir):
return file_name return file_name
return None return None
def start_container(image, name): def start_container(image, name, nnimanager_os):
'''Start docker container, generate a port in /tmp/nnitest/{name}/port file''' '''Start docker container, generate a port in /tmp/nnitest/{name}/port file'''
port = find_port() port = find_port()
source_dir = '/tmp/nnitest/' + name source_dir = '/tmp/nnitest/' + name
run_cmds = ['docker', 'run', '-d', '-p', str(port) + ':22', '--name', name, '--mount', 'type=bind,source=' + source_dir + ',target=/tmp/nni', image] run_cmds = ['docker', 'run', '-d', '-p', str(port) + ':22', '--name', name, '--mount', 'type=bind,source=' + source_dir + ',target=/tmp/nni', image]
output = check_output(run_cmds) output = check_output(run_cmds)
commit_id = output.decode('utf-8') commit_id = output.decode('utf-8')
wheel_name = find_wheel_package(os.path.join(source_dir, 'dist'))
if nnimanager_os == 'windows':
wheel_name = find_wheel_package(os.path.join(source_dir, 'nni-remote/deployment/pypi/dist'))
else:
wheel_name = find_wheel_package(os.path.join(source_dir, 'dist'))
if not wheel_name: if not wheel_name:
print('Error: could not find wheel package in {0}'.format(source_dir)) print('Error: could not find wheel package in {0}'.format(source_dir))
exit(1) exit(1)
sdk_cmds = ['docker', 'exec', name, 'python3', '-m', 'pip', 'install', '/tmp/nni/dist/{0}'.format(wheel_name)]
def get_dist(wheel_name):
'''get the wheel package path'''
if nnimanager_os == 'windows':
return '/tmp/nni/nni-remote/deployment/pypi/dist/{0}'.format(wheel_name)
else:
return '/tmp/nni/dist/{0}'.format(wheel_name)
pip_cmds = ['docker', 'exec', name, 'python3', '-m', 'pip', 'install', '--upgrade', 'pip']
check_call(pip_cmds)
sdk_cmds = ['docker', 'exec', name, 'python3', '-m', 'pip', 'install', get_dist(wheel_name)]
check_call(sdk_cmds) check_call(sdk_cmds)
with open(source_dir + '/port', 'w') as file: with open(source_dir + '/port', 'w') as file:
file.write(str(port)) file.write(str(port))
...@@ -58,8 +73,9 @@ if __name__ == '__main__': ...@@ -58,8 +73,9 @@ if __name__ == '__main__':
parser.add_argument('--mode', required=True, choices=['start', 'stop'], dest='mode', help='start or stop a container') parser.add_argument('--mode', required=True, choices=['start', 'stop'], dest='mode', help='start or stop a container')
parser.add_argument('--name', required=True, dest='name', help='the name of container to be used') parser.add_argument('--name', required=True, dest='name', help='the name of container to be used')
parser.add_argument('--image', dest='image', help='the image to be used') parser.add_argument('--image', dest='image', help='the image to be used')
parser.add_argument('--os', dest='os', default='unix', choices=['unix', 'windows'], help='nniManager os version')
args = parser.parse_args() args = parser.parse_args()
if args.mode == 'start': if args.mode == 'start':
start_container(args.image, args.name) start_container(args.image, args.name, args.os)
else: else:
stop_container(args.name) stop_container(args.name)
...@@ -54,4 +54,4 @@ NNI CTL 模块用来控制 Neural Network Intelligence,包括开始新 Experim ...@@ -54,4 +54,4 @@ NNI CTL 模块用来控制 Neural Network Intelligence,包括开始新 Experim
## 开始使用 NNI CTL ## 开始使用 NNI CTL
参考 [NNI CTL 文档](../docs/zh_CN/NNICTLDOC.md) 参考 [NNI CTL 文档](../docs/zh_CN/Nnictl.md)
\ No newline at end of file \ No newline at end of file
...@@ -25,6 +25,94 @@ from nni_cmd.common_utils import print_warning ...@@ -25,6 +25,94 @@ from nni_cmd.common_utils import print_warning
# pylint: disable=unidiomatic-typecheck # pylint: disable=unidiomatic-typecheck
def parse_annotation_mutable_layers(code, lineno):
"""Parse the string of mutable layers in annotation.
Return a list of AST Expr nodes
code: annotation string (excluding '@')
"""
module = ast.parse(code)
assert type(module) is ast.Module, 'internal error #1'
assert len(module.body) == 1, 'Annotation mutable_layers contains more than one expression'
assert type(module.body[0]) is ast.Expr, 'Annotation is not expression'
call = module.body[0].value
nodes = []
mutable_id = 'mutable_block_' + str(lineno)
mutable_layer_cnt = 0
for arg in call.args:
fields = {'layer_choice': False,
'fixed_inputs': False,
'optional_inputs': False,
'optional_input_size': False,
'layer_output': False}
for k, value in zip(arg.keys, arg.values):
if k.id == 'layer_choice':
assert not fields['layer_choice'], 'Duplicated field: layer_choice'
assert type(value) is ast.List, 'Value of layer_choice should be a list'
call_funcs_keys = []
call_funcs_values = []
call_kwargs_values = []
for call in value.elts:
assert type(call) is ast.Call, 'Element in layer_choice should be function call'
call_name = astor.to_source(call).strip()
call_funcs_keys.append(ast.Str(s=call_name))
call_funcs_values.append(call.func)
assert not call.args, 'Number of args without keyword should be zero'
kw_args = []
kw_values = []
for kw in call.keywords:
kw_args.append(kw.arg)
kw_values.append(kw.value)
call_kwargs_values.append(ast.Dict(keys=kw_args, values=kw_values))
call_funcs = ast.Dict(keys=call_funcs_keys, values=call_funcs_values)
call_kwargs = ast.Dict(keys=call_funcs_keys, values=call_kwargs_values)
fields['layer_choice'] = True
elif k.id == 'fixed_inputs':
assert not fields['fixed_inputs'], 'Duplicated field: fixed_inputs'
assert type(value) is ast.List, 'Value of fixed_inputs should be a list'
fixed_inputs = value
fields['fixed_inputs'] = True
elif k.id == 'optional_inputs':
assert not fields['optional_inputs'], 'Duplicated field: optional_inputs'
assert type(value) is ast.List, 'Value of optional_inputs should be a list'
var_names = [ast.Str(s=astor.to_source(var).strip()) for var in value.elts]
optional_inputs = ast.Dict(keys=var_names, values=value.elts)
fields['optional_inputs'] = True
elif k.id == 'optional_input_size':
assert not fields['optional_input_size'], 'Duplicated field: optional_input_size'
assert type(value) is ast.Num, 'Value of optional_input_size should be a number'
optional_input_size = value
fields['optional_input_size'] = True
elif k.id == 'layer_output':
assert not fields['layer_output'], 'Duplicated field: layer_output'
assert type(value) is ast.Name, 'Value of layer_output should be ast.Name type'
layer_output = value
fields['layer_output'] = True
else:
raise AssertionError('Unexpected field in mutable layer')
# make call for this mutable layer
assert fields['layer_choice'], 'layer_choice must exist'
assert fields['layer_output'], 'layer_output must exist'
mutable_layer_id = 'mutable_layer_' + str(mutable_layer_cnt)
mutable_layer_cnt += 1
target_call_attr = ast.Attribute(value=ast.Name(id='nni', ctx=ast.Load()), attr='mutable_layer', ctx=ast.Load())
target_call_args = [ast.Str(s=mutable_id),
ast.Str(s=mutable_layer_id),
call_funcs,
call_kwargs]
if fields['fixed_inputs']:
target_call_args.append(fixed_inputs)
else:
target_call_args.append(ast.NameConstant(value=None))
if fields['optional_inputs']:
target_call_args.append(optional_inputs)
assert fields['optional_input_size'], 'optional_input_size must exist when optional_inputs exists'
target_call_args.append(optional_input_size)
else:
target_call_args.append(ast.NameConstant(value=None))
target_call = ast.Call(func=target_call_attr, args=target_call_args, keywords=[])
node = ast.Assign(targets=[layer_output], value=target_call)
nodes.append(node)
return nodes
def parse_annotation(code): def parse_annotation(code):
"""Parse an annotation string. """Parse an annotation string.
...@@ -235,6 +323,9 @@ class Transformer(ast.NodeTransformer): ...@@ -235,6 +323,9 @@ class Transformer(ast.NodeTransformer):
or string.startswith('@nni.get_next_parameter('): or string.startswith('@nni.get_next_parameter('):
return parse_annotation(string[1:]) # expand annotation string to code return parse_annotation(string[1:]) # expand annotation string to code
if string.startswith('@nni.mutable_layers('):
return parse_annotation_mutable_layers(string[1:], node.lineno)
if string.startswith('@nni.variable(') \ if string.startswith('@nni.variable(') \
or string.startswith('@nni.function_choice('): or string.startswith('@nni.function_choice('):
self.stack[-1] = string[1:] # mark that the next expression is annotated self.stack[-1] = string[1:] # mark that the next expression is annotated
......
...@@ -38,7 +38,8 @@ _ss_funcs = [ ...@@ -38,7 +38,8 @@ _ss_funcs = [
'qnormal', 'qnormal',
'lognormal', 'lognormal',
'qlognormal', 'qlognormal',
'function_choice' 'function_choice',
'mutable_layer'
] ]
...@@ -50,6 +51,18 @@ class SearchSpaceGenerator(ast.NodeTransformer): ...@@ -50,6 +51,18 @@ class SearchSpaceGenerator(ast.NodeTransformer):
self.search_space = {} self.search_space = {}
self.last_line = 0 # last parsed line, useful for error reporting self.last_line = 0 # last parsed line, useful for error reporting
def generate_mutable_layer_search_space(self, args):
mutable_block = args[0].s
mutable_layer = args[1].s
if mutable_block not in self.search_space:
self.search_space[mutable_block] = dict()
self.search_space[mutable_block][mutable_layer] = {
'layer_choice': [key.s for key in args[2].keys],
'optional_inputs': [key.s for key in args[5].keys],
'optional_input_size': args[6].n
}
def visit_Call(self, node): # pylint: disable=invalid-name def visit_Call(self, node): # pylint: disable=invalid-name
self.generic_visit(node) self.generic_visit(node)
...@@ -68,6 +81,10 @@ class SearchSpaceGenerator(ast.NodeTransformer): ...@@ -68,6 +81,10 @@ class SearchSpaceGenerator(ast.NodeTransformer):
self.last_line = node.lineno self.last_line = node.lineno
if func == 'mutable_layer':
self.generate_mutable_layer_search_space(node.args)
return node
if node.keywords: if node.keywords:
# there is a `name` argument # there is a `name` argument
assert len(node.keywords) == 1, 'Smart parameter has keyword argument other than "name"' assert len(node.keywords) == 1, 'Smart parameter has keyword argument other than "name"'
......
import time
def add_one(inputs):
return inputs + 1
def add_two(inputs):
return inputs + 2
def add_three(inputs):
return inputs + 3
def add_four(inputs):
return inputs + 4
def main():
images = 5
"""@nni.mutable_layers(
{
layer_choice: [add_one(), add_two(), add_three(), add_four()],
optional_inputs: [images],
optional_input_size: 1,
layer_output: layer_1_out
},
{
layer_choice: [add_one(), add_two(), add_three(), add_four()],
optional_inputs: [layer_1_out],
optional_input_size: 1,
layer_output: layer_2_out
},
{
layer_choice: [add_one(), add_two(), add_three(), add_four()],
optional_inputs: [layer_1_out, layer_2_out],
optional_input_size: 1,
layer_output: layer_3_out
}
)"""
"""@nni.report_intermediate_result(layer_1_out)"""
time.sleep(2)
"""@nni.report_intermediate_result(layer_2_out)"""
time.sleep(2)
"""@nni.report_intermediate_result(layer_3_out)"""
time.sleep(2)
layer_3_out = layer_3_out + 10
"""@nni.report_final_result(layer_3_out)"""
if __name__ == '__main__':
main()
...@@ -63,7 +63,9 @@ common_schema = { ...@@ -63,7 +63,9 @@ common_schema = {
Optional('advisor'): dict, Optional('advisor'): dict,
Optional('assessor'): dict, Optional('assessor'): dict,
Optional('localConfig'): { Optional('localConfig'): {
Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!') Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!'),
Optional('maxTrialNumPerGpu'): setType('maxTrialNumPerGpu', int),
Optional('useActiveGpu'): setType('useActiveGpu', bool)
} }
} }
tuner_schema_dict = { tuner_schema_dict = {
...@@ -310,26 +312,30 @@ frameworkcontroller_config_schema = { ...@@ -310,26 +312,30 @@ frameworkcontroller_config_schema = {
}) })
} }
machine_list_schima = { machine_list_schema = {
Optional('machineList'):[Or({ Optional('machineList'):[Or({
'ip': setType('ip', str), 'ip': setType('ip', str),
Optional('port'): setNumberRange('port', int, 1, 65535), Optional('port'): setNumberRange('port', int, 1, 65535),
'username': setType('username', str), 'username': setType('username', str),
'passwd': setType('passwd', str), 'passwd': setType('passwd', str),
Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!') Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!'),
Optional('maxTrialNumPerGpu'): setType('maxTrialNumPerGpu', int),
Optional('useActiveGpu'): setType('useActiveGpu', bool)
},{ },{
'ip': setType('ip', str), 'ip': setType('ip', str),
Optional('port'): setNumberRange('port', int, 1, 65535), Optional('port'): setNumberRange('port', int, 1, 65535),
'username': setType('username', str), 'username': setType('username', str),
'sshKeyPath': setPathCheck('sshKeyPath'), 'sshKeyPath': setPathCheck('sshKeyPath'),
Optional('passphrase'): setType('passphrase', str), Optional('passphrase'): setType('passphrase', str),
Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!') Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!'),
Optional('maxTrialNumPerGpu'): setType('maxTrialNumPerGpu', int),
Optional('useActiveGpu'): setType('useActiveGpu', bool)
})] })]
} }
LOCAL_CONFIG_SCHEMA = Schema({**common_schema, **common_trial_schema}) LOCAL_CONFIG_SCHEMA = Schema({**common_schema, **common_trial_schema})
REMOTE_CONFIG_SCHEMA = Schema({**common_schema, **common_trial_schema, **machine_list_schima}) REMOTE_CONFIG_SCHEMA = Schema({**common_schema, **common_trial_schema, **machine_list_schema})
PAI_CONFIG_SCHEMA = Schema({**common_schema, **pai_trial_schema, **pai_config_schema}) PAI_CONFIG_SCHEMA = Schema({**common_schema, **pai_trial_schema, **pai_config_schema})
......
...@@ -119,4 +119,4 @@ class Experiments: ...@@ -119,4 +119,4 @@ class Experiments:
return json.load(file) return json.load(file)
except ValueError: except ValueError:
return {} return {}
return {} return {}
\ No newline at end of file
...@@ -86,12 +86,13 @@ TUNERS_SUPPORTING_IMPORT_DATA = { ...@@ -86,12 +86,13 @@ TUNERS_SUPPORTING_IMPORT_DATA = {
'Anneal', 'Anneal',
'GridSearch', 'GridSearch',
'MetisTuner', 'MetisTuner',
'BOHB' 'BOHB',
'SMAC',
'BatchTuner'
} }
TUNERS_NO_NEED_TO_IMPORT_DATA = { TUNERS_NO_NEED_TO_IMPORT_DATA = {
'Random', 'Random',
'Batch_tuner',
'Hyperband' 'Hyperband'
} }
......
...@@ -125,18 +125,17 @@ def start_rest_server(port, platform, mode, config_file_name, experiment_id=None ...@@ -125,18 +125,17 @@ def start_rest_server(port, platform, mode, config_file_name, experiment_id=None
if mode == 'resume': if mode == 'resume':
cmds += ['--experiment_id', experiment_id] cmds += ['--experiment_id', experiment_id]
stdout_full_path, stderr_full_path = get_log_path(config_file_name) stdout_full_path, stderr_full_path = get_log_path(config_file_name)
stdout_file = open(stdout_full_path, 'a+') with open(stdout_full_path, 'a+') as stdout_file, open(stderr_full_path, 'a+') as stderr_file:
stderr_file = open(stderr_full_path, 'a+') time_now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
time_now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) #add time information in the header of log files
#add time information in the header of log files log_header = LOG_HEADER % str(time_now)
log_header = LOG_HEADER % str(time_now) stdout_file.write(log_header)
stdout_file.write(log_header) stderr_file.write(log_header)
stderr_file.write(log_header) if sys.platform == 'win32':
if sys.platform == 'win32': from subprocess import CREATE_NEW_PROCESS_GROUP
from subprocess import CREATE_NEW_PROCESS_GROUP process = Popen(cmds, cwd=entry_dir, stdout=stdout_file, stderr=stderr_file, creationflags=CREATE_NEW_PROCESS_GROUP)
process = Popen(cmds, cwd=entry_dir, stdout=stdout_file, stderr=stderr_file, creationflags=CREATE_NEW_PROCESS_GROUP) else:
else: process = Popen(cmds, cwd=entry_dir, stdout=stdout_file, stderr=stderr_file)
process = Popen(cmds, cwd=entry_dir, stdout=stdout_file, stderr=stderr_file)
return process, str(time_now) return process, str(time_now)
def set_trial_config(experiment_config, port, config_file_name): def set_trial_config(experiment_config, port, config_file_name):
...@@ -160,9 +159,13 @@ def set_local_config(experiment_config, port, config_file_name): ...@@ -160,9 +159,13 @@ def set_local_config(experiment_config, port, config_file_name):
request_data = dict() request_data = dict()
if experiment_config.get('localConfig'): if experiment_config.get('localConfig'):
request_data['local_config'] = experiment_config['localConfig'] request_data['local_config'] = experiment_config['localConfig']
if request_data['local_config'] and request_data['local_config'].get('gpuIndices') \ if request_data['local_config']:
and isinstance(request_data['local_config'].get('gpuIndices'), int): if request_data['local_config'].get('gpuIndices') and isinstance(request_data['local_config'].get('gpuIndices'), int):
request_data['local_config']['gpuIndices'] = str(request_data['local_config'].get('gpuIndices')) request_data['local_config']['gpuIndices'] = str(request_data['local_config'].get('gpuIndices'))
if request_data['local_config'].get('maxTrialNumOnEachGpu'):
request_data['local_config']['maxTrialNumOnEachGpu'] = request_data['local_config'].get('maxTrialNumOnEachGpu')
if request_data['local_config'].get('useActiveGpu'):
request_data['local_config']['useActiveGpu'] = request_data['local_config'].get('useActiveGpu')
response = rest_put(cluster_metadata_url(port), json.dumps(request_data), REST_TIME_OUT) response = rest_put(cluster_metadata_url(port), json.dumps(request_data), REST_TIME_OUT)
err_message = '' err_message = ''
if not response or not check_response(response): if not response or not check_response(response):
...@@ -343,6 +346,13 @@ def set_experiment(experiment_config, mode, port, config_file_name): ...@@ -343,6 +346,13 @@ def set_experiment(experiment_config, mode, port, config_file_name):
def launch_experiment(args, experiment_config, mode, config_file_name, experiment_id=None): def launch_experiment(args, experiment_config, mode, config_file_name, experiment_id=None):
'''follow steps to start rest server and start experiment''' '''follow steps to start rest server and start experiment'''
nni_config = Config(config_file_name) nni_config = Config(config_file_name)
# check execution policy in powershell
if sys.platform == 'win32':
execution_policy = check_output(['powershell.exe','Get-ExecutionPolicy']).decode('ascii').strip()
if execution_policy == 'Restricted':
print_error('PowerShell execution policy error, please run PowerShell as administrator with this command first:\r\n'\
+ '\'Set-ExecutionPolicy -ExecutionPolicy Unrestricted\'')
exit(1)
# check packages for tuner # check packages for tuner
package_name, module_name = None, None package_name, module_name = None, None
if experiment_config.get('tuner') and experiment_config['tuner'].get('builtinTunerName'): if experiment_config.get('tuner') and experiment_config['tuner'].get('builtinTunerName'):
......
...@@ -26,8 +26,8 @@ import datetime ...@@ -26,8 +26,8 @@ import datetime
import time import time
from subprocess import call, check_output from subprocess import call, check_output
from .rest_utils import rest_get, rest_delete, check_rest_server_quick, check_response 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 .config_utils import Config, Experiments from .config_utils import Config, Experiments
from .url_utils import trial_jobs_url, experiment_url, trial_job_id_url
from .constants import NNICTL_HOME_DIR, EXPERIMENT_INFORMATION_FORMAT, EXPERIMENT_DETAIL_FORMAT, \ 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 EXPERIMENT_MONITOR_INFO, TRIAL_MONITOR_HEAD, TRIAL_MONITOR_CONTENT, TRIAL_MONITOR_TAIL, REST_TIME_OUT
from .common_utils import print_normal, print_error, print_warning, detect_process from .common_utils import print_normal, print_error, print_warning, detect_process
...@@ -450,30 +450,9 @@ def monitor_experiment(args): ...@@ -450,30 +450,9 @@ def monitor_experiment(args):
print_error(exception) print_error(exception)
exit(1) exit(1)
def parse_trial_data(content):
"""output: List[Dict]"""
trial_records = []
for trial_data in content:
for phase_i in range(len(trial_data['hyperParameters'])):
hparam = json.loads(trial_data['hyperParameters'][phase_i])['parameters']
hparam['id'] = trial_data['id']
if 'finalMetricData' in trial_data.keys() and phase_i < len(trial_data['finalMetricData']):
reward = json.loads(trial_data['finalMetricData'][phase_i]['data'])
if isinstance(reward, (float, int)):
dict_tmp = {**hparam, **{'reward': reward}}
elif isinstance(reward, dict):
dict_tmp = {**hparam, **reward}
else:
raise ValueError("Invalid finalMetricsData format: {}/{}".format(type(reward), reward))
else:
dict_tmp = hparam
trial_records.append(dict_tmp)
return trial_records
def export_trials_data(args): def export_trials_data(args):
"""export experiment metadata to csv '''export experiment metadata to csv
""" '''
nni_config = Config(get_config_filename(args)) nni_config = Config(get_config_filename(args))
rest_port = nni_config.get_config('restServerPort') rest_port = nni_config.get_config('restServerPort')
rest_pid = nni_config.get_config('restServerPid') rest_pid = nni_config.get_config('restServerPid')
...@@ -482,26 +461,28 @@ def export_trials_data(args): ...@@ -482,26 +461,28 @@ def export_trials_data(args):
return return
running, response = check_rest_server_quick(rest_port) running, response = check_rest_server_quick(rest_port)
if running: if running:
response = rest_get(trial_jobs_url(rest_port), 20) response = rest_get(export_data_url(rest_port), 20)
if response is not None and check_response(response): if response is not None and check_response(response):
content = json.loads(response.text)
# dframe = pd.DataFrame.from_records([parse_trial_data(t_data) for t_data in content])
# dframe.to_csv(args.csv_path, sep='\t')
records = parse_trial_data(content)
if args.type == 'json': if args.type == 'json':
json_records = [] with open(args.path, 'w') as file:
for trial in records: file.write(response.text)
value = trial.pop('reward', None) elif args.type == 'csv':
trial_id = trial.pop('id', None) content = json.loads(response.text)
json_records.append({'parameter': trial, 'value': value, 'id': trial_id}) trial_records = []
with open(args.path, 'w') as file: for record in content:
if args.type == 'csv': if not isinstance(record['value'], (float, int)):
writer = csv.DictWriter(file, set.union(*[set(r.keys()) for r in records])) formated_record = {**record['parameter'], **record['value'], **{'id': record['id']}}
else:
formated_record = {**record['parameter'], **{'reward': record['value'], 'id': record['id']}}
trial_records.append(formated_record)
with open(args.path, 'w') as file:
writer = csv.DictWriter(file, set.union(*[set(r.keys()) for r in trial_records]))
writer.writeheader() writer.writeheader()
writer.writerows(records) writer.writerows(trial_records)
else: else:
json.dump(json_records, file) print_error('Unknown type: %s' % args.type)
exit(1)
else: else:
print_error('Export failed...') print_error('Export failed...')
else: else:
print_error('Restful server is not Running') print_error('Restful server is not Running')
\ No newline at end of file
...@@ -94,11 +94,9 @@ def start_tensorboard_process(args, nni_config, path_list, temp_nni_path): ...@@ -94,11 +94,9 @@ def start_tensorboard_process(args, nni_config, path_list, temp_nni_path):
if detect_port(args.port): if detect_port(args.port):
print_error('Port %s is used by another process, please reset port!' % str(args.port)) print_error('Port %s is used by another process, please reset port!' % str(args.port))
exit(1) exit(1)
with open(os.path.join(temp_nni_path, 'tensorboard_stdout'), 'a+') as stdout_file, open(os.path.join(temp_nni_path, 'tensorboard_stderr'), 'a+') as stderr_file:
stdout_file = open(os.path.join(temp_nni_path, 'tensorboard_stdout'), 'a+') cmds = ['tensorboard', '--logdir', format_tensorboard_log_path(path_list), '--port', str(args.port)]
stderr_file = open(os.path.join(temp_nni_path, 'tensorboard_stderr'), 'a+') tensorboard_process = Popen(cmds, stdout=stdout_file, stderr=stderr_file)
cmds = ['tensorboard', '--logdir', format_tensorboard_log_path(path_list), '--port', str(args.port)]
tensorboard_process = Popen(cmds, stdout=stdout_file, stderr=stderr_file)
url_list = get_local_urls(args.port) url_list = get_local_urls(args.port)
print_normal(COLOR_GREEN_FORMAT % 'Start tensorboard success!\n' + 'Tensorboard urls: ' + ' '.join(url_list)) print_normal(COLOR_GREEN_FORMAT % 'Start tensorboard success!\n' + 'Tensorboard urls: ' + ' '.join(url_list))
tensorboard_process_pid_list = nni_config.get_config('tensorboardPidList') tensorboard_process_pid_list = nni_config.get_config('tensorboardPidList')
......
...@@ -35,6 +35,8 @@ CHECK_STATUS_API = '/check-status' ...@@ -35,6 +35,8 @@ CHECK_STATUS_API = '/check-status'
TRIAL_JOBS_API = '/trial-jobs' TRIAL_JOBS_API = '/trial-jobs'
EXPORT_DATA_API = '/export-data'
TENSORBOARD_API = '/tensorboard' TENSORBOARD_API = '/tensorboard'
...@@ -68,6 +70,11 @@ def trial_job_id_url(port, job_id): ...@@ -68,6 +70,11 @@ def trial_job_id_url(port, job_id):
return '{0}:{1}{2}{3}/:{4}'.format(BASE_URL, port, API_ROOT_URL, TRIAL_JOBS_API, job_id) return '{0}:{1}{2}{3}/:{4}'.format(BASE_URL, port, API_ROOT_URL, TRIAL_JOBS_API, job_id)
def export_data_url(port):
'''get export_data url'''
return '{0}:{1}{2}{3}'.format(BASE_URL, port, API_ROOT_URL, EXPORT_DATA_API)
def tensorboard_url(port): def tensorboard_url(port):
'''get tensorboard url''' '''get tensorboard url'''
return '{0}:{1}{2}{3}'.format(BASE_URL, port, API_ROOT_URL, TENSORBOARD_API) return '{0}:{1}{2}{3}'.format(BASE_URL, port, API_ROOT_URL, TENSORBOARD_API)
......
$NNI_DEPENDENCY_FOLDER = [System.IO.Path]::GetTempPath()+$env:USERNAME
$NNI_DEPENDENCY_FOLDER = "C:\tmp\$env:USERNAME"
$env:PYTHONIOENCODING = "UTF-8" $env:PYTHONIOENCODING = "UTF-8"
if($env:VIRTUAL_ENV){ if($env:VIRTUAL_ENV){
...@@ -27,4 +26,4 @@ Remove-Item "src/nni_manager/node_modules" -Recurse -Force ...@@ -27,4 +26,4 @@ Remove-Item "src/nni_manager/node_modules" -Recurse -Force
Remove-Item "src/webui/build" -Recurse -Force Remove-Item "src/webui/build" -Recurse -Force
Remove-Item "src/webui/node_modules" -Recurse -Force Remove-Item "src/webui/node_modules" -Recurse -Force
Remove-Item $NNI_YARN_FOLDER -Recurse -Force Remove-Item $NNI_YARN_FOLDER -Recurse -Force
Remove-Item $NNI_NODE_FOLDER -Recurse -Force Remove-Item $NNI_NODE_FOLDER -Recurse -Force
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment