Вторая запись, поскольку я продолжаю свой класс 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