Когда вы думаете о том, как подойти к проекту машинного обучения с учителем (ML), процесс, начиная с получения ваших данных и заканчивая развертыванием вашей модели, часто может показаться написанным всего за несколько шагов:

  1. Получите ваши данные и конвертируйте файл данных в желаемый формат
  2. Разделите данные на обучающий и тестовый наборы и выберите показатель производительности.
  3. Исследуйте и визуализируйте свои данные
  4. Очистите свои данные и выполните проектирование/выбор функций
  5. Обучайте многочисленные модели и выбирайте самые эффективные
  6. Выполните настройку гиперпараметров и выберите наиболее эффективную модель
  7. Разверните эту модель

Те, кто только начинает работать с ML, склонны думать, что есть только один правильный способ выполнения некоторых из вышеперечисленных шагов. Например, думать, что функциональность SKLearn train_test_split() — это единственный способ создать поезд и тестовый набор. (Несмотря на то, что существует множество других способов сделать это). В этой статье будут рассмотрены некоторые важные функции машинного обучения, которые часто упускают из виду, но которые могут значительно улучшить различные аспекты сквозного проекта.

1) Использование StratifiedShuffleSplit() для разделения данных в соответствии с распределением

Обычно при разделении набора данных мы либо используем функцию train_test_split, либо вручную разделяем данные, как показано ниже:

#X and y can be any dataframe for this example. 
X_train, X_test, y_train, y_test = X[:5], X[5:], y[:5], y[5:]
X = [[1, 2], [3, 4], [5, 6], [7, 8]]
y = [0, 1, 0, 1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Оба этих подхода хороши, когда у нас есть большие и сбалансированные наборы данных, поскольку размер и сбалансированность набора данных повышают вероятность того, что мы получим репрезентативные наборы для обучения и тестирования. Однако этот подход становится проблематичным, когда у нас есть меньшие и/или несбалансированные наборы данных. Например, предположим, что у нас есть данные для 100 выпускников компьютерных наук, такие как их средний балл бакалавриата, наиболее известная компания, в которой они проходили стажировку, их начальная зарплата (которая в этом примере является ярлыком) и т. д. На основе этих данных мы должны обучить модель машинного обучения, которая может предсказать среднюю начальную зарплату будущих выпускников компьютерных наук. Использование train_test_split может легко внести смещение в нашу модель, создавая случайные экземпляры наборов для обучения и/или тестирования, которые сильно отдают предпочтение определенным атрибутам. Возвращаясь к нашему примеру с выпускниками компьютерных наук, предположим, что train_test_split() дал нам обучающий набор, в котором 80% строк были выпускниками, прошедшими стажировку в компании FAANG. Наша модель будет плохо обобщать и делать неточные прогнозы на основе данных о новых выпускниках, потому что наша модель плохо подготовлена ​​для выпускников, которые не проходили стажировку в компаниях FAANG, и выпускников, которые не проходили стажировку ни в одной компании. Нам нужен способ разделения наших обучающих и тестовых наборов таким образом, чтобы выполнялись определенные критерии (пример: только 20% выпускников могут пройти стажировку в FAANG), чтобы предотвратить недостаточное представление определенных групп данных и плохое обобщение новых данных. Этого можно добиться с помощью функции StratifiedShuffleSplit().

Вместо стратификации по самой известной стажерской компании, давайте стратифицируем по целочисленному признаку среднего балла бакалавриата, с которым гораздо проще работать, чем по категориальному признаку стажерской компании.

#cs_grads is the name of the dataframe
#cs_grades is the name of the column containing grades from 0.0-4.0
from sklearn.model_selection import StratifiedShuffleSplit
cs_grads["grade_dist"] = pd.cut(cs_grads["grades"],
                         bins = [0., 1.0, 2.0, 3.0, 4.0],
                         labels = ["F", "D", "C-B", "B-A"])
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train, test in split.split(cs_grads, cs_grads["grade_dist"]):
    stratified_train_set = cs_grads.loc[train]
    stratified_test_set = cs_grads.loc[test]

В приведенном выше коде мы сначала превращаем числовой атрибут среднего балла бакалавриата в категориальный атрибут, который примерно соответствует их средней буквенной оценке. Затем мы используем StratifiedShuffleSplit() с 0,2% данных, поступающих в тестовый набор и 0,8% процентов в обучающий набор, со случайным состоянием 42 для обеспечения воспроизводимости, а также с циклом для создания обучения и тестирования. наборы.

2) Использование параметра n_jobs для значительного ускорения процессов

При выполнении кода для обучения любой модели или настройки гиперпараметров по умолчанию используется только 1 ЦП, что подходит для небольших наборов данных. Однако, если у нас есть набор данных с 250 000 строк, обучение модели только с 1 процессором займет много часов, если не больше. Здесь в игру вступает параметр n_jobs. Этот параметр позволяет определить количество одновременно работающих ЦП/воркеров, что значительно ускорит обучение моделей и настройку гиперпараметров. Приведенный ниже код и изображение иллюстрируют, как использование увеличенных ЦП для подбора модели может ускорить процессы подбора и настройки.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
import time

breast_cancer = load_breast_cancer()

execution_times = []
n_jobs_values = range(1, 7)  # Test values from 1 to 6 for n_jobs

for n_jobs in n_jobs_values:
    start_time = time.time()
    model = RandomForestClassifier(n_jobs=n_jobs)
    model.fit(breast_cancer.data, breast_cancer.target)
    end_time = time.time()
    execution_time = end_time - start_time
    execution_times.append(execution_time)

plt.plot(n_jobs_values, execution_times, marker='o')
plt.xlabel('n_jobs')
plt.ylabel('Execution Time (seconds)')
plt.title('Random Forest Classifier Fitting Time vs. n_jobs')
plt.grid(True)
plt.show()

Приведенный выше код загружает набор данных рака молочной железы, популярный набор данных для классификации. Затем я обучаю классификатор случайного леса с параметром n_jobs, изменяющимся от одного до шести, где шесть — это все процессоры на моем компьютере. Удивительно, но 3 ЦП дают наименьшее время подгонки вместо 6 ЦП. При добавлении большего количества параллельных процессов становится довольно сложно предсказать, сколько времени потребуется для запуска каждого процесса и всего выполнения кода. Из-за этого количество ЦП не обязательно линейно коррелирует с уменьшением времени подбора. На приведенном выше графике 3 процессора, по-видимому, сокращают время работы только на 0,01 секунды, что связано с небольшим размером набора данных о раке молочной железы. Однако, если мы посмотрим на 8%-ное сокращение времени работы между 1 и 3 ЦП, мы увидим, почему параметр n_jobs может быть настолько полезен для больших наборов данных, где увеличение числа ЦП может сократить время работы на много минут или даже часов. .

3) Используйте перекрестную проверку вместо создания набора проверки

Обычно мы создаем обучающий, тестовый и проверочный наборы для обучения, оптимизации и тестирования нашей модели соответственно. Хотя наборы для обучения и тестирования абсолютно необходимы, набор для проверки часто может показаться громоздким и неэффективным. Создается впечатление, что мы не используем данные в полной мере во время обучения, когда мы просто оставляем набор проверки в ожидании до самых последних этапов улучшения нашей модели, прежде чем попробовать модель на тестовых данных. Что, если бы существовал способ иметь только наборы для обучения и тестирования, но при этом иметь возможность оценивать модель на невидимых комбинациях наборов? Оказывается, перекрестная проверка — это наше решение! Короче говоря, перекрестная проверка разбивает ваши данные на k сгибов, где каждый сгиб содержит случайное подмножество обучения и тестирования. Затем перекрестная проверка обучает модель на каждом наборе обучающих данных и возвращает оценку для этих тестовых данных. Приведенный ниже фрагмент кода является частью замечательной книги Практическое машинное обучение с помощью Scikit-Learn, Keras и Tensorflow, и я призываю вас прочитать ее, как это делал я, чтобы узнать все подробности машинного обучения Python. Чтобы просмотреть код из этой книги, посетите этот GitHub: https://github.com/ageron/handson ml2/blob/master/02_end_to_end_machine_learning_project.ipynb.

lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels,
                             scoring="neg_mean_squared_error", cv=10)
lin_rmse_scores = np.sqrt(-lin_scores)
display_scores(lin_rmse_scores)

def display_scores(scores):
    print("Scores:", scores)
    print("Mean:", scores.mean())
    print("Standard deviation:", scores.std())

display_scores(tree_rmse_scores)

Сегмент кода использует cross_val_score для разделения данных на 10 групп, каждая из которых содержит обучающие и тестовые подмножества из вышележащего обучающего набора Housing_prepared и перекрывающего тестового набора Housing_labels. Затем функция display_scores используется для вывода оценки для каждой складки и среднего значения + стандартное отклонение для всех 10 сгибов.

Я надеюсь, что эти три важные концепции помогут вам улучшить ваши сквозные проекты машинного обучения. Спасибо за прочтение!