Прохождение простого рабочего процесса машинного обучения

Я хотел объединить несколько вещей, которые я изучал, и хотел пройти через базовый процесс машинного обучения. Поэтому я написал класс Python, MLModel, для изучения. Конечно, это игрушка, но она дает общее представление о переходе от данных к предсказанию. Итак, мы собираемся изучить некоторые основные концепции машинного обучения через призму этого простого инструмента машинного обучения.

Этот класс использует pandas для обработки данных и scikit-learn, популярную библиотеку машинного обучения в Python. Мы рассмотрим каждый метод в классе и свяжем код с такими понятиями, как вменение, одно горячее кодирование, регрессия случайного леса, перекрестная проверка и выбор количества сгибов для перекрестной проверки. Полный код доступен на GitHub.

Загрузка и подготовка данных

Метод load_data считывает данные из указанного пути к файлу, используя pandas. Он идентифицирует целевой столбец (который мы пытаемся предсказать) и отделяет его от остальной части набора данных. Он также детализирует столбцы числовых и категориальных данных.

def load_data(self, train_path, test_path, target_column):
    self._df = pd.read_csv(train_path)
    self._df = self._df.dropna(subset=[target_column])
    self._y = self._df[target_column]
    self._df = self._df.drop(columns=[target_column])
    # Load test data
    self._test_df = pd.read_csv(test_path)
    # Identify numeric and categorical features
    self._numeric_features = self._df.select_dtypes(
    include=["int64", "float64"]
    ).columns
    self._categorical_features = self._df.select_dtypes(include=["object"]).columns

Теперь, когда мы загрузили наши данные, нам нужно очистить их и правильно отформатировать. Вот тут-то и появляется метод `preprocess_data`.

Предварительная обработка и обработка пропущенных значений

Метод preprocess_data применяет преобразования к числовым и категориальным функциям. Числовые характеристики — это числа, такие как «возраст» или «доход», а категориальные — все остальное, часто в виде строк.

Вменение

Вменение — это красивое слово для «заполнения пропущенных значений». Например, если у нас есть набор данных о ценах на жилье, но в некоторых домах не указано количество комнат, нам нужно каким-то образом заполнить эти недостающие значения. Это связано с тем, что алгоритмы машинного обучения обычно плохо работают с отсутствующими данными.

Класс SimpleImputer из sklearn предоставляет стратегии вменения. Для числовых признаков мы по умолчанию используем «медианную» стратегию, которая заполняет пропущенные значения медианой столбца. Для категориальных признаков мы по умолчанию используем стратегию «наиболее частые», заполняя отсутствующие значения наиболее частым значением в столбце. Обе стратегии можно настроить.

Горячее кодирование

Категориальные переменные также сложны. Например, если у нас есть функция «страна», алгоритм машинного обучения не поймет, как работать с «США», «Великобритания», «Индия» и т. д. Нам нужно преобразовать эти категории в формат, который он может понять. : числа. Одним из распространенных способов сделать это является горячее кодирование.

В горячем кодировании каждая категория в объекте превращается в свой собственный объект (или столбец), который принимает значение 0 или 1. Если бы у нас было три категории «США», «Великобритания», «Индия», мы бы в конечном итоге с тремя новыми функциями: «Is_USA», «Is_UK», «Is_India». Если в строке изначально было «США» для функции «страна», теперь она будет иметь 1 для функции «Является».

_USA» и 0 для «Is_UK» и «Is_India».

За нас это делает класс OneHotEncoder из sklearn. Аргумент handle_unknown=’ignore’ говорит ему игнорировать категории в тестовых данных, которых не было в обучающих данных.

def preprocess_data(
  self,
  numeric_imputer_strategy="median",
  numeric_fill_value=None,
  categorical_imputer_strategy="most_frequent",
  categorical_fill_value=None,
  handle_unknown="ignore"):

  numeric_transformer = Pipeline(
    steps=[
      ("imputer", SimpleImputer(strategy=numeric_imputer_strategy, 
          fill_value=numeric_fill_value),)
    ])

  categorical_transformer = Pipeline(
    steps=[
      ("imputer", SimpleImputer(strategy=categorical_imputer_strategy,
          fill_value=categorical_fill_value,),),
      ("onehot", OneHotEncoder(handle_unknown=handle_unknown)),
    ])

  self._preprocessor = ColumnTransformer(
    transformers=[
      ("num", numeric_transformer, self._numeric_features),
      ("cat", categorical_transformer, self._categorical_features),
    ])

  categorical_transformer = Pipeline(
    steps=[
      ("imputer", SimpleImputer(strategy=categorical_imputer_strategy,
          fill_value=categorical_fill_value,),),
      ("onehot", OneHotEncoder(handle_unknown=handle_unknown)),
    ])

  self._preprocessor = ColumnTransformer(
    transformers=[
      ("num", numeric_transformer, self._numeric_features),
      ("cat", categorical_transformer, self._categorical_features),
    ])

Обучение модели

Как только наши данные будут готовы и предварительно обработаны, мы можем передать их алгоритму машинного обучения. В этом случае мы используем регрессор случайного леса.

Регрессия случайного леса

Регрессия случайного леса — это метод ансамблевого обучения, в котором группа «слабых учеников» объединяется, чтобы сформировать «сильного ученика». В этом подходе слабыми учениками являются деревья решений. Деревья решений — это простые структуры, в которых каждый узел дерева принимает решение на основе функции, и эти решения следуют вниз по дереву, пока мы не достигнем прогноза.

Регрессия случайного леса создает группу этих деревьев (отсюда и «лес»), каждое из которых дает несколько разные прогнозы на основе случайного подмножества данных. Окончательный прогноз случайного леса представляет собой среднее значение прогнозов всех деревьев.

def train_model(self, n_estimators=100, random_state=0):
  model = RandomForestRegressor(
    n_estimators=n_estimators, random_state=random_state)
  self._pipeline = Pipeline(
    steps=[("preprocessor", self._preprocessor), ("model", model)])
  self._pipeline.fit(self._df, self._y)

Оценка модели

Теперь, когда мы обучили нашу модель, мы хотим посмотреть, насколько хорошо она работает. Но мы не можем просто протестировать его на данных, которые мы использовали для его обучения — это все равно, что дать студенту тест с точно такими же вопросами, которые он использовал для изучения. Вместо этого нам нужен свежий набор данных для оценки нашей модели. Здесь вступает в действие перекрестная проверка.

### Перекрестная проверка

Перекрестная проверка — это метод, при котором мы делим наши обучающие данные на «складки», обучаем нашу модель на некоторых из этих сгибов, а затем тестируем ее на оставшихся сгибах. Мы делаем это несколько раз, каждый раз с другим сгибом в качестве тестового набора. Параметр cv определяет, сколько складок мы делаем. По умолчанию мы используем 5-кратную перекрестную проверку (cv=5), что является распространенным выбором.

Функция cross_val_score из sklearn выполняет для нас перекрестную проверку. Он возвращает оценку для каждого сгиба, которую мы затем усредняем, чтобы получить общую оценку.

def evaluate_model(self, cv=5, scoring="neg_mean_absolute_error"):
  scores = -1 * cross_val_score(
    self._pipeline, self._df, self._y, cv=cv, scoring=scoring)
  return scores.mean()

Выбор количества сгибов для перекрестной проверки

Как мы выбираем количество сгибов для перекрестной проверки? Слишком мало, и мы не сможем получить надежную оценку производительности нашей модели. Слишком много, и мы тратим вычислительные ресурсы впустую. В этом нам помогает метод check_cv_values. Он выполняет перекрестную проверку для диапазона возможных значений cv и возвращает значение, обеспечивающее наилучшую производительность (в данном случае наименьшую среднюю абсолютную ошибку).

def check_cv_values(self, cv_values, scoring="neg_mean_absolute_error"):
  results = {}
  for cv in cv_values:
    mae = self.evaluate_model(cv=cv, scoring=scoring)
    results[cv] = mae

  # Find the cv value with the lowest MAE
  best_cv = min(results, key=results.get)
  results["best"] = best_cv
  return results

Заключение

И вот оно! Мы прошли через базовый конвейер машинного обучения, от загрузки и предварительной обработки данных до обучения и оценки модели. Мы узнали о вменении, горячем кодировании, регрессии случайного леса и перекрестной проверке — все в контексте этого класса MLModel. Конечно, машинное обучение — это гораздо больше, чем это, но, надеюсь, это введение было полезным!