Skip to content

Commit

Permalink
Merge pull request maxpumperla#189 from CyberZHG/master
Browse files Browse the repository at this point in the history
Support model creating function with multi-line arguments
  • Loading branch information
maxpumperla authored Sep 6, 2018
2 parents 1570c9f + acd60c1 commit 0151f59
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 11 deletions.
8 changes: 5 additions & 3 deletions hyperas/optim.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .utils import (
remove_imports, remove_all_comments, extract_imports, temp_string,
write_temp_files, determine_indent, with_line_numbers, unpack_hyperopt_vals,
eval_hyperopt_space)
eval_hyperopt_space, find_signature_end)

sys.path.append(".")

Expand Down Expand Up @@ -281,8 +281,10 @@ def augmented_names(parts):


def hyperopt_keras_model(model_string, parts, aug_parts, verbose=True):
first_line = model_string.split("\n")[0]
model_string = model_string.replace(first_line, "def keras_fmin_fnct(space):\n")
colon_index = find_signature_end(model_string)
func_sign_line_end = model_string.count("\n", 0, colon_index) + 1
func_sign_lines = "\n".join(model_string.split("\n")[:func_sign_line_end])
model_string = model_string.replace(func_sign_lines, "def keras_fmin_fnct(space):\n")
result = re.sub(r"(\{\{[^}]+}\})", lambda match: aug_parts.pop(0), model_string, count=len(parts))
if verbose:
print('>>> Resulting replaced keras model:\n')
Expand Down
48 changes: 48 additions & 0 deletions hyperas/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,51 @@ def eval_hyperopt_space(space, vals):
"""
unpacked_vals = unpack_hyperopt_vals(vals)
return space_eval(space, unpacked_vals)


def find_signature_end(model_string):
"""
Find the index of the colon in the function signature.
:param model_string: string
source code of the model
:return: int
the index of the colon
"""
index, brace_depth = 0, 0
while index < len(model_string):
ch = model_string[index]
if brace_depth == 0 and ch == ':':
break
if ch == '#': # Ignore comments
index += 1
while index < len(model_string) and model_string[index] != '\n':
index += 1
index += 1
elif ch in ['"', "'"]: # Skip strings
string_depth = 0
while index < len(model_string) and model_string[index] == ch:
string_depth += 1
index += 1
if string_depth == 2:
string_depth = 1
index += string_depth
while index < len(model_string):
if model_string[index] == '\\':
index += 2
elif model_string[index] == ch:
string_depth -= 1
if string_depth == 0:
break
index += 1
else:
index += 1
index += 1
elif ch == '(':
brace_depth += 1
index += 1
elif ch == ')':
brace_depth -= 1
index += 1
else:
index += 1
return index
2 changes: 1 addition & 1 deletion tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def model(X_train, Y_train, X_test, Y_test):

model.fit(X_train, Y_train,
batch_size={{choice([64, 128])}},
nb_epoch=1,
epochs=1,
verbose=2,
validation_data=(X_test, Y_test))
score, acc = model.evaluate(X_test, Y_test, verbose=0)
Expand Down
32 changes: 29 additions & 3 deletions tests/test_functional_api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from __future__ import print_function
from hyperopt import Trials, STATUS_OK, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform
from hyperas.distributions import choice

from keras.models import Model
from keras.layers import Dense, Input
from keras.optimizers import RMSprop

from keras.datasets import mnist
from keras.utils import np_utils
from hyperopt import rand


def data():
Expand Down Expand Up @@ -39,7 +38,28 @@ def model(X_train, Y_train, X_test, Y_test):

model.fit(X_train, Y_train,
batch_size={{choice([64, 128])}},
nb_epoch=1,
epochs=1,
verbose=2,
validation_data=(X_test, Y_test))
score, acc = model.evaluate(X_test, Y_test, verbose=0)
print('Test accuracy:', acc)
return {'loss': -acc, 'status': STATUS_OK, 'model': model}


def model_multi_line_arguments(X_train, Y_train,
X_test, Y_test):
inputs = Input(shape=(784,))

x = Dense({{choice([20, 30, 40])}}, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)
model = Model(inputs=inputs, outputs=predictions)

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(X_train, Y_train,
batch_size={{choice([64, 128])}},
epochs=1,
verbose=2,
validation_data=(X_test, Y_test))
score, acc = model.evaluate(X_test, Y_test, verbose=0)
Expand All @@ -55,3 +75,9 @@ def test_functional_api():
max_evals=1,
trials=Trials(),
verbose=False)
best_run, best_model = optim.minimize(model=model_multi_line_arguments,
data=data,
algo=tpe.suggest,
max_evals=1,
trials=Trials(),
verbose=False)
5 changes: 2 additions & 3 deletions tests/test_lr_plateau.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
from __future__ import print_function
from hyperopt import Trials, STATUS_OK, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform
from hyperas.distributions import choice

from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import RMSprop

from keras.datasets import mnist
from keras.utils import np_utils
from keras.callbacks import ReduceLROnPlateau, EarlyStopping
from hyperopt import rand


def data():
Expand Down Expand Up @@ -46,6 +44,7 @@ def create_model(x_train, y_train, x_test, y_test):
print('MAE:', mae)
return {'loss': mae, 'status': STATUS_OK, 'model': model}


def test_advanced_callbacks():
X_train, Y_train, X_test, Y_test = data()
best_run, best_model = optim.minimize(model=create_model,
Expand Down
2 changes: 2 additions & 0 deletions tests/test_optim.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def test_data():
Y_test = np_utils.to_categorical(y_test, nb_classes_return)
return X_train, Y_train, X_test, Y_test


def test_data_function():
result = retrieve_data_string(test_data, verbose=False)
assert 'return X_train, Y_train, X_test, Y_test' not in result
Expand All @@ -25,5 +26,6 @@ def test_data_function():
assert '(X_train, y_train), (X_test, y_test) = mnist.load_data()' in result
assert 'Y_test = np_utils.to_categorical(y_test, nb_classes_return)' in result


if __name__ == '__main__':
test_data_function()
17 changes: 16 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from hyperas.utils import (
extract_imports, remove_imports, remove_all_comments, temp_string,
write_temp_files, with_line_numbers, determine_indent, unpack_hyperopt_vals,
eval_hyperopt_space)
eval_hyperopt_space, find_signature_end)

TEST_SOURCE = """
from __future__ import print_function
Expand Down Expand Up @@ -31,6 +31,16 @@ def foo():
bar()
"""

TEST_SOURCE_4 = """
@foo_bar(bar_foo)
def foo(train_x=')\\':', train_y=")\\":", # ):
test_x=lambda x: bar, test_y=bar[:, 0],
foo='''
):):
\\'''', bar="") :
pass
"""


def test_extract_imports():
result = extract_imports(TEST_SOURCE)
Expand Down Expand Up @@ -172,3 +182,8 @@ def test_eval_hyperopt_space():

assert eval_hyperopt_space(space, test_vals) == result
assert eval_hyperopt_space(space, test_vals_unpacked) == result


def test_find_signature_end():
index = find_signature_end(TEST_SOURCE_4)
assert len(TEST_SOURCE_4) - 10, index

0 comments on commit 0151f59

Please sign in to comment.