Skip to content

Commit 93c7ab4

Browse files
chongyouquanTensorflow Cloud maintainers
authored andcommitted
Remove overridden get_trial, make sure metrics of trials are updated locally, and
make sure trial.best_step is an integer PiperOrigin-RevId: 343406054
1 parent 3f5ec68 commit 93c7ab4

File tree

5 files changed

+66
-67
lines changed

5 files changed

+66
-67
lines changed

src/python/tensorflow_cloud/tuner/tests/unit/optimizer_client_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,17 @@ def test_get_trial(self):
393393
mock_get_trial.assert_called_once_with(name=self._trial_name)
394394
self.assertEqual(trial, expected_trial)
395395

396+
def test_get_trial_with_404_raises(self):
397+
mock_get_trial = mock.MagicMock()
398+
mock_get_trial.return_value.execute.side_effect = errors.HttpError(
399+
httplib2.Response(info={"status": 404}), b"")
400+
401+
self._mock_discovery.projects().locations().studies().trials(
402+
).get = mock_get_trial
403+
404+
with self.assertRaises(errors.HttpError):
405+
self._client.get_trial(trial_id="1")
406+
396407
def test_list_trials(self):
397408
mock_list_trials = mock.MagicMock()
398409
expected_trials = {

src/python/tensorflow_cloud/tuner/tests/unit/tuner_test.py

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def setUp(self):
8787
self._test_trial = trial_module.Trial(
8888
hyperparameters=self._test_hyperparameters,
8989
trial_id="1",
90-
status=trial_module.TrialStatus,
90+
status=trial_module.TrialStatus.RUNNING,
9191
)
9292
# TODO(b/170687807) Switch from using "{}".format() to f-string
9393
self._job_id = "{}_{}".format(self._study_id, self._test_trial.trial_id)
@@ -253,7 +253,8 @@ def test_create_trial_after_early_stopping(self):
253253
self.assertEqual(trial.hyperparameters.values, {})
254254
self.assertEqual(trial.status, trial_module.TrialStatus.STOPPED)
255255

256-
def test_update_trial(self):
256+
@mock.patch.object(oracle_module.Oracle, "update_trial", auto_spec=True)
257+
def test_update_trial(self, mock_super_update_trial):
257258
self._tuner_with_hparams()
258259

259260
self.mock_client.should_trial_stop.return_value = True
@@ -277,6 +278,9 @@ def test_update_trial(self):
277278
)
278279
self.mock_client.should_trial_stop.assert_called_once_with("1")
279280
self.assertEqual(status, trial_module.TrialStatus.STOPPED)
281+
mock_super_update_trial.assert_called_once_with(
282+
"1", {"val_acc": 0.8}, 3
283+
)
280284

281285
def test_end_trial_success(self):
282286
self._tuner_with_hparams()
@@ -285,17 +289,29 @@ def test_end_trial_success(self):
285289
"state": "COMPLETED",
286290
"parameters": [{"parameter": "learning_rate", "floatValue": 0.01}],
287291
"finalMeasurement": {
288-
"stepCount": 3,
292+
"stepCount": "3",
289293
"metrics": [{"metric": "val_acc", "value": 0.7}],
290294
},
291295
"trial_infeasible": False,
292296
"infeasible_reason": None,
293297
}
294-
298+
mock_save_trial = mock.Mock()
299+
self.tuner.oracle._save_trial = mock_save_trial
295300
self.tuner.oracle.ongoing_trials = {"tuner_0": self._test_trial}
301+
expected_trial = trial_module.Trial(
302+
hyperparameters=self._test_hyperparameters,
303+
trial_id="1",
304+
status=trial_module.TrialStatus.COMPLETED,
305+
)
306+
expected_trial.best_step = 3
307+
expected_trial.score = 0.7
308+
296309
self.tuner.oracle.end_trial(trial_id="1")
310+
297311
self.mock_client.complete_trial.assert_called_once_with(
298312
"1", False, None)
313+
self.assertEqual(repr(mock_save_trial.call_args[0][0].get_state()),
314+
repr(expected_trial.get_state()))
299315

300316
def test_end_trial_infeasible_trial(self):
301317
self._tuner_with_hparams()
@@ -319,35 +335,6 @@ def test_end_trial_invalid_status(self):
319335
with self.assertRaises(ValueError):
320336
self.tuner.oracle.end_trial(trial_id="1", status="FOO")
321337

322-
def test_get_trial_success(self):
323-
self._tuner_with_hparams()
324-
self.mock_client.get_trial.return_value = {
325-
"name": "1",
326-
"state": "COMPLETED",
327-
"parameters": [{"parameter": "learning_rate", "floatValue": 0.01}],
328-
"finalMeasurement": {
329-
"stepCount": 3,
330-
"metrics": [{"metric": "val_acc", "value": 0.7}],
331-
},
332-
"trial_infeasible": False,
333-
"infeasible_reason": None,
334-
}
335-
trial = self.tuner.oracle.get_trial(trial_id="1")
336-
self.mock_client.get_trial.assert_called_once_with("1")
337-
self.assertEqual(trial.trial_id, "1")
338-
self.assertEqual(trial.score, 0.7)
339-
self.assertEqual(trial.status, trial_module.TrialStatus.COMPLETED)
340-
self.assertEqual(trial.hyperparameters.values, {"learning_rate": 0.01})
341-
342-
def test_get_trial_failed(self):
343-
self._tuner_with_hparams()
344-
self.mock_client.get_trial.return_value = {
345-
"name": "1",
346-
"state": "FOO"
347-
}
348-
with self.assertRaises(ValueError):
349-
self.tuner.oracle.get_trial(trial_id="1")
350-
351338
def test_get_best_trials(self):
352339
self._tuner_with_hparams()
353340

@@ -358,7 +345,7 @@ def test_get_best_trials(self):
358345
"parameters":
359346
[{"parameter": "learning_rate", "floatValue": 0.01}],
360347
"finalMeasurement": {
361-
"stepCount": 3,
348+
"stepCount": "3",
362349
"metrics": [{"metric": "val_acc", "value": 0.7}],
363350
},
364351
"trial_infeasible": False,
@@ -370,7 +357,7 @@ def test_get_best_trials(self):
370357
"parameters":
371358
[{"parameter": "learning_rate", "floatValue": 0.001}],
372359
"finalMeasurement": {
373-
"stepCount": 3,
360+
"stepCount": "3",
374361
"metrics": [{"metric": "val_acc", "value": 0.9}],
375362
},
376363
"trial_infeasible": False,
@@ -425,7 +412,7 @@ def test_get_best_trials_multi_tuners(self):
425412
"parameters":
426413
[{"parameter": "learning_rate", "floatValue": 0.01}],
427414
"finalMeasurement": {
428-
"stepCount": 3,
415+
"stepCount": "3",
429416
"metrics": [{"metric": "val_acc", "value": 0.7}],
430417
},
431418
"trial_infeasible": False,
@@ -437,7 +424,7 @@ def test_get_best_trials_multi_tuners(self):
437424
"parameters":
438425
[{"parameter": "learning_rate", "floatValue": 0.001}],
439426
"finalMeasurement": {
440-
"stepCount": 3,
427+
"stepCount": "3",
441428
"metrics": [{"metric": "val_acc", "value": 0.9}],
442429
},
443430
"trial_infeasible": False,
@@ -458,6 +445,11 @@ def test_get_best_trials_multi_tuners(self):
458445
self.assertEqual(best_trials_1[0].score, 0.9)
459446
self.assertEqual(best_trials_1[0].best_step, 3)
460447

448+
def test_get_single_objective(self):
449+
self._tuner_with_hparams()
450+
self.assertEqual([self.tuner.oracle.objective],
451+
self.tuner.oracle._get_objective())
452+
461453
@mock.patch.object(super_tuner.Tuner, "__init__", auto_spec=True)
462454
@mock.patch.object(tf.summary, "create_file_writer", auto_spec=True)
463455
@mock.patch.object(hparams_api, "hparams", auto_spec=True)

src/python/tensorflow_cloud/tuner/tests/unit/utils_test.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189
{"parameter": "learning_rate", "floatValue": 0.0001},
190190
],
191191
"finalMeasurement": {
192-
"stepCount": 1,
192+
"stepCount": "1",
193193
"metrics": [{"value": 0.9}],
194194
},
195195
}
@@ -305,13 +305,14 @@ def test_convert_optimizer_trial_to_hps(self):
305305
trial_hps = utils.convert_optimizer_trial_to_hps(hps, OPTIMIZER_TRIAL)
306306
self.assertDictEqual(trial_hps.values, EXPECTED_TRIAL_HPS)
307307

308-
def test_convert_optimizer_trial_to_keras_trial(self):
308+
def test_convert_completed_optimizer_trial_to_keras_trial(self):
309309
hps = hp_module.HyperParameters()
310310
hps.Choice("learning_rate", [1e-4, 1e-3, 1e-2])
311-
trial = utils.convert_optimizer_trial_to_keras_trial(
311+
trial = utils.convert_completed_optimizer_trial_to_keras_trial(
312312
COMPLETED_OPTIMIZER_TRIAL, hps)
313313
self.assertEqual(trial.trial_id, "trial_1")
314314
self.assertEqual(trial.score, 0.9)
315+
self.assertEqual(trial.best_step, 1)
315316
self.assertEqual(trial.status, trial_module.TrialStatus.COMPLETED)
316317
self.assertEqual(
317318
trial.hyperparameters.values, {"learning_rate": 0.0001})

src/python/tensorflow_cloud/tuner/tuner.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ def __init__(
123123
raise ValueError('"region" is not found.')
124124
self._region = region
125125

126-
self.objective = utils.format_objective(objective)
126+
# If it's just single objective, let it be an Objective instead of a
127+
# list, to keep it consistent with how KerasTuner formats objectives
128+
obj = utils.format_objective(objective)
129+
self.objective = obj[0] if len(obj) == 1 else obj
130+
127131
self.hyperparameters = hyperparameters
128132
self.max_trials = max_trials
129133

@@ -218,6 +222,7 @@ def update_trial(self,
218222
"""Used by a worker to report the status of a trial."""
219223
# Constructs the measurement.
220224
# Adds the measurement of the objective functions to a trial.
225+
super(CloudOracle, self).update_trial(trial_id, metrics, step)
221226
elapsed_secs = time.time() - self._start_time
222227
if elapsed_secs < 0 or step < 0:
223228
raise ValueError(
@@ -227,7 +232,7 @@ def update_trial(self,
227232
"At least one of {elapsed_secs, step} must be positive")
228233

229234
metric_list = []
230-
for ob in self.objective:
235+
for ob in self._get_objective():
231236
if ob.name not in metrics:
232237
tf.get_logger().info(
233238
'Objective "{}" is not found in metrics.'.format(ob.name)
@@ -293,22 +298,6 @@ def end_trial(self, trial_id: Text, status: Text = "COMPLETED"):
293298
self._save_trial(kerastuner_trial)
294299
self.save()
295300

296-
def get_trial(self, trial_id: Text) -> trial_module.Trial:
297-
"""Returns a completed KerasTuner Trial given the trial_id."""
298-
# Note that this is called in Tuner.on_trial_end.
299-
300-
optimizer_trial = self.service.get_trial(trial_id)
301-
302-
if optimizer_trial["state"] != "COMPLETED":
303-
raise ValueError("The trial status is not COMPLETED, found {}"
304-
.format(optimizer_trial["state"]))
305-
306-
# Convert a completed Optimizer trial to KerasTuner Trial instance.
307-
kerastuner_trial = utils.convert_optimizer_trial_to_keras_trial(
308-
optimizer_trial,
309-
self.hyperparameters.copy())
310-
return kerastuner_trial
311-
312301
def get_best_trials(self, num_trials: int = 1) -> List[trial_module.Trial]:
313302
"""Returns the trials with the best objective values found so far.
314303
@@ -317,14 +306,14 @@ def get_best_trials(self, num_trials: int = 1) -> List[trial_module.Trial]:
317306
Returns:
318307
List of KerasTuner Trials.
319308
"""
320-
if len(self.objective) > 1:
309+
objective = self._get_objective()
310+
if len(objective) > 1:
321311
raise ValueError(
322312
"Getting the best trials for multi-objective optimization "
323313
"is not supported."
324314
)
325315

326-
maximizing = (
327-
utils.format_goal(self.objective[0].direction) == "MAXIMIZE")
316+
maximizing = (utils.format_goal(objective[0].direction) == "MAXIMIZE")
328317

329318
# List all trials associated with the same study
330319
trial_list = self.service.list_trials()
@@ -336,20 +325,26 @@ def get_best_trials(self, num_trials: int = 1) -> List[trial_module.Trial]:
336325

337326
sorted_trials = sorted(
338327
optimizer_trials,
339-
key=lambda t: t["finalMeasurement"]["metrics"][0]["value"],
328+
key=lambda t: t["finalMeasurement"]["metrics"][0].get("value"),
340329
reverse=maximizing,
341330
)
342331
best_optimizer_trials = sorted_trials[:num_trials]
343332

344333
best_trials = []
345334
# Convert completed Optimizer trials to KerasTuner Trial instances.
346335
for optimizer_trial in best_optimizer_trials:
347-
kerastuner_trial = utils.convert_optimizer_trial_to_keras_trial(
348-
optimizer_trial,
349-
self.hyperparameters.copy())
336+
kerastuner_trial = (
337+
utils.convert_completed_optimizer_trial_to_keras_trial(
338+
optimizer_trial,
339+
self.hyperparameters.copy()))
350340
best_trials.append(kerastuner_trial)
351341
return best_trials
352342

343+
def _get_objective(self):
344+
"""Returns the Objective(s) as a list."""
345+
return self.objective if isinstance(self.objective,
346+
list) else [self.objective]
347+
353348

354349
class CloudTuner(tuner_module.Tuner):
355350
"""KerasTuner interface implementation backed by CAIP Optimizer Service."""

src/python/tensorflow_cloud/tuner/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ def convert_optimizer_trial_to_hps(
509509
return hps
510510

511511

512-
def convert_optimizer_trial_to_keras_trial(
512+
def convert_completed_optimizer_trial_to_keras_trial(
513513
optimizer_trial: Dict[Text, Any],
514514
hyperparameter_space: hp_module.HyperParameters,
515515
) -> trial_module.Trial:
@@ -537,7 +537,7 @@ def convert_optimizer_trial_to_keras_trial(
537537
raise ValueError('"finalMeasurement" not found in this trial {}'
538538
.format(optimizer_trial))
539539

540-
kerastuner_trial.best_step = final_measurement.get("stepCount", 0)
540+
kerastuner_trial.best_step = int(final_measurement.get("stepCount", 0))
541541
kerastuner_trial.score = final_measurement["metrics"][0].get("value")
542542
return kerastuner_trial
543543

0 commit comments

Comments
 (0)