Вторая запись, поскольку я продолжаю свой класс TensorFlow по GCP, неделя №2 курса 5: Искусство и наука машинного обучения.
В классе мы сначала строим простую модель линейной регрессии, затем строим задачу для обучения модели с входными параметрами, такими как каталог для вывода и т. д.
Среди входных параметров некоторые считаются «гиперпараметрами», например, learning_rate и batch_size. По-видимому, эти гиперпараметры имеют решающее значение для производительности нашей модели.
Конечно, можно попробовать разные комбинации разных значений вручную. Но для человека было бы утомительно пробовать все возможные комбинации/значения для каждого параметра, что меня радует, так это то, что GCP ML-Engine предоставляет возможность настраивать эти параметры самостоятельно.
В этом посте я собираюсь обучить модель локально с выбранными вручную гиперпараметрами, а затем проделать магию автоматической настройки.
Готовый ? Давай сделаем это !
Набор данных, над которым мы работаем, является общедоступным набором данных о ценах на жилье в Калифорнии. Любой желающий может нажать на ссылку и загрузить файл csv.
Каждая запись имеет геолокацию (долгота/широта), возраст, количество комнат, … и, конечно же, цену дома (median_house_value).
Наша цель: на основе всей информации о доме спрогнозировать стоимость дома.
Просто заглянув в csv, вы заметите, что количество комнат указано не в каждом доме, а в сумме всех домов в этой области долготы/широты. Да, нам нужно немного помассировать данные, как показано ниже:
df = pd.read_csv(“...csv", sep = “,”) df[‘num_rooms’] = df[‘total_rooms’] / df[‘households’] # Define feature columns features = [tf.feature_column.numeric_column('num_rooms')]
Для простоты у нас есть только одна функция: num_rooms
на дом (на самом деле это не очень хорошая идея для нашей модели, но это не главное для класса)
При обучении нам понадобятся как обучающие данные, так и тестовые данные, чтобы увидеть, как работает наша модель, имея под рукой единственный csv, мы можем использовать 80% данных во время обучения, а остальные 20% оставить в качестве теста.
np.random.seed(seed = 1) #makes split reproducible msk = np.random.rand(len(df)) < 0.8 traindf = df[msk] evaldf = df[~msk]
Вы могли заметить, что стоимость дома находится в масштабе 100000, чтобы сделать лучшую модель, мы хотим, чтобы все функции и метки были в одном масштабе, давайте сделаем это:
# Train and eval input functions SCALE = 100000 def train_input_fn(df, batch_size): return tf.estimator.inputs.pandas_input_fn(x = traindf[[“num_rooms”]], y = traindf[“median_house_value”] / SCALE, # note the scaling num_epochs = None, batch_size = batch_size, # note the batch size shuffle = True) def eval_input_fn(df, batch_size): return tf.estimator.inputs.pandas_input_fn(x = evaldf[[“num_rooms”]], y = evaldf[“median_house_value”] / SCALE, # note the scaling num_epochs = 1, batch_size = batch_size, shuffle = False)
Чтобы оценить, как работает наша модель на каждом шаге, мы используем rmse, как показано ниже:
def rmse(labels, predictions): pred_values = tf.cast(predictions[‘predictions’], tf.float64) return {‘rmse’: tf.metrics.root_mean_squared_error(labels * SCALE, pred_values * SCALE)} estimator = tf.contrib.estimator.add_metrics(estimator, rmse)
Обратите внимание, что мы возвращаемся к исходному диапазону чисел. Почему ? Что ж, если мы этого не сделаем, мы получим число вроде 1.xxx, что само по себе хорошо, но если мы хотим сравнить это с реальной ценой дома, нам нужно выполнить эту математику.
Собрав все вместе, мы имеем очень простую модель линейной регрессии.
//house_prediction_module/trainer/model.py import ... # Read dataset and split into train and eval df = pd.read_csv(“...", sep = “,”) df[‘num_rooms’] = df[‘total_rooms’] / df[‘households’] np.random.seed(seed = 1) #makes split reproducible msk = np.random.rand(len(df)) < 0.8 traindf = df[msk] evaldf = df[~msk] #traindf.describe() evaldf.describe() # Train and eval input functions SCALE = 100000 def train_input_fn(df, batch_size): ... def eval_input_fn(df, batch_size): ... # Define feature columns features = [tf.feature_column.numeric_column(‘num_rooms’)] def train_and_evaluate(args): # Compute appropriate number of steps num_steps = (len(traindf) / args[‘batch_size’]) / args[‘learning_rate’] # if learning_rate=0.01, hundred epochs print(“train_and_evaluate, args”, args) print(“num_steps=”,num_steps) # Create custom optimizer myopt = tf.train.FtrlOptimizer(learning_rate = args[‘learning_rate’]) # note the learning rate # Create rest of the estimator as usual estimator = tf.estimator.LinearRegressor( model_dir = args[‘output_dir’], feature_columns = features, optimizer = myopt) #Add rmse evaluation metric def rmse(labels, predictions): pred_values = tf.cast(predictions[‘predictions’], tf.float64) return {‘rmse’: tf.metrics.root_mean_squared_error(labels * SCALE, pred_values * SCALE)} estimator = tf.contrib.estimator.add_metrics(estimator, rmse) train_spec = tf.estimator.TrainSpec( input_fn = train_input_fn( df = traindf, batch_size = args[‘batch_size’]), max_steps = num_steps) eval_spec = tf.estimator.EvalSpec( input_fn = eval_input_fn( df = evaldf, batch_size = len(evaldf)), steps = None) # Finally, let's train ! tf.estimator.train_and_evaluate(estimator, train_spec , eval_spec )
Я упоминал, что нам также нужно создать задачу для получения параметров? Да, давайте сделаем это очень быстро:
//house_prediction_module/trainer/task.py import ... from . import model if __name__ == ‘__main__’ and “get_ipython” not in dir(): parser = argparse.ArgumentParser() parser.add_argument( ‘ — learning_rate’, type = float, default = 0.01 ) parser.add_argument( ‘ — batch_size’, type = int, default = 30 ) parser.add_argument( ‘ — output_dir’, help = ‘GCS location to write checkpoints and export models.’, required = True ) parser.add_argument( ‘ — job-dir’, help = ‘this model ignores this field, but it is required by gcloud’, default = ‘junk’ ) args = parser.parse_args() arguments = args.__dict__ # Unused args provided by service arguments.pop(‘job_dir’, None) arguments.pop(‘job-dir’, None) # Append trial_id to path if we are doing hptuning # This code can be removed if you are not using hyperparameter tuning arguments[‘output_dir’] = os.path.join( arguments[‘output_dir’], json.loads( os.environ.get(‘TF_CONFIG’, ‘{}’) ).get(‘task’, {}).get(‘trial’, ‘’) ) # Run the training shutil.rmtree(arguments[‘output_dir’], ignore_errors=True) # start fresh each time # Pass the command line arguments to our model’s train_and_evaluate function model.train_and_evaluate(arguments)
По сути, это парсер командной строки Python.
Теперь мы обучаем модель локально через ml-engine.
%%bash rm -rf house_trained export PYTHONPATH=${PYTHONPATH}:${PWD}/house_prediction_module gcloud ml-engine local train \ — module-name=trainer.task \ — job-dir=house_trained \ — package-path=$(pwd)/trainer \ — \ — batch_size=16 \ — learning_rate=0.02 \ — output_dir=house_trained
Заметили, как я передаю batch_size
и learning_rate
? Как прошло ?
Хм, у нас есть rmse 112976.37, что не так уж и хорошо. Если мы попросим панду описать тестовый набор данных, стандартное значение median_house_value равно 113802, мы делаем немного лучше, чем случайное угадывание ??
В любом случае, это не главное в классе.
Вопрос: как мне узнать, какие batch_size и training_rate являются лучшими?
К счастью для нас, ML-Engine предлагает «черный ящик» для их настройки. Все, что нам нужно, это определить файл yaml, указать, какие параметры мы хотели бы настроить, какова наша метрика, как показано ниже:
//hyperparam.yaml trainingInput: hyperparameters: goal: MINIMIZE maxTrials: 20 maxParallelTrials: 5 hyperparameterMetricTag: rmse params: — parameterName: batch_size type: INTEGER minValue: 8 maxValue: 56 scaleType: UNIT_LINEAR_SCALE — parameterName: learning_rate type: DOUBLE minValue: 0.005 maxValue: 0.05 scaleType: UNIT_LINEAR_SCALE
Если у вас, как и у меня, есть вопросы по поводу того, что такое scaleType
(линейное или логарифмическое), вот немного почитания.
Теперь мы можем попросить ML-Engine настроить их:
%%bash OUTDIR=gs://${BUCKET}/house_trained # CHANGE bucket name appropriately gsutil rm -rf $OUTDIR export PYTHONPATH=${PYTHONPATH}:${PWD}/house_prediction_module gcloud ml-engine jobs submit training house_1218 \ — config=hyperparam.yaml \ — module-name=trainer.task \ — package-path=$(pwd)/house_prediction_module/trainer \ — job-dir=$OUTDIR \ — runtime-version=$TFVERSION \ — \ — output_dir=$OUTDIR \
house_1218
- это идентификатор задания, вы можете передать любую строку, которую хотите, просто к вашему сведению.
Как у нас дела? Давайте перейдем на страницу списка вакансий GCP AI Platform, нажмите на вакансию house_1218
, здесь ~ мы ~ идем.
На самом деле он дает несколько «хороший» результат в испытании № 5, где всего 5994 шага обучения. Помните, раньше с локальным подходом мы тренировались около 40000 шагов, чтобы получить аналогичный результат?
И это дает нам лучшие значения batch_size: 49 и learning_rate: 0,046.
к вашему сведению: Идея этого черного ящика называется Визирь.
Официальный документ находится здесь: https://cloud.google.com/ml-engine/docs/tensorflow/using-hyperparameter-tuning