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

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

Что такое машинное обучение?

Во-первых, давайте начнем с того, что такое машинное обучение. Машинное обучение – это метод создания программ, которые могут обучаться на основе данных и разрабатывать собственный алгоритм (обычно называемый моделью) для решения проблемы. Это отличается от обычного программирования, когда вы диктуете ряд шагов, которым должна следовать программа. Машинное обучение — более сложная задача, но если все сделано правильно, оно также может дать вам широкую гибкость, а вашим пользователям — индивидуальный подход.

Что такое Каггл?

Kaggle — отличное место для изучения машинного обучения. Для тех из вас, кто понятия не имеет, что это такое, это онлайн-сообщество для энтузиастов и практиков машинного обучения и науки о данных. Вы можете найти тысячи наборов данных и задач, размещенных на нем, для практики машинного обучения. У них даже есть онлайн-курс, который вы можете пройти, чтобы изучить Python и машинное обучение — я настоятельно рекомендую проверить это, если вы думаете заняться машинным обучением.

Проблема/Цель

Набор данных Titanic чем-то похож на Kaggle «Hello World!» проект. Это не слишком сложно, что идеально подходит для начинающих, таких как я, и это то, с чего они рекомендуют вам начать. Целью проекта является создание модели, которая может предсказывать пассажиров, выживших после кораблекрушения Титаника. Задачи такого рода называются «классификацией», поскольку мы будем классифицировать, выжил ли человек или нет, а не вычислять числовое значение, которое называется «регрессией». . (Хорошо, я знаю, что мы можем просто погуглить, кто выжил на Титанике, но мы ничего не узнаем, делая это, так что мы не будем прибегать к этому).

Сбор данных

Первым шагом в любом проекте машинного обучения (после того, как проблема сформулирована) является сбор данных. Это неотъемлемая часть любого проекта — без данных было бы нечего анализировать. Иногда это даже считается самой сложной частью. Kaggle экономит наше время и усилия, уже предоставляя нам данные. Вы можете перейти на их веб-сайт, чтобы загрузить и просмотреть данные для себя.

Исследование данных

Получив данные, мы можем приступить к их исследованию. Kaggle предоставляет нам 2 файла: «поезд» и «тест» в формате CSV. Мы можем проверить данные, используя любой инструмент для работы с электронными таблицами. Я использовал LibreOffice Calc здесь. Когда вы впервые читаете имена столбцов, это не кажется интуитивным, Kaggle предоставляет нам для этого таблицу определений. Это упрощение в именах столбцов сделано для облегчения манипуляций в дальнейшем.

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

Теперь, когда у нас есть представление о наборе данных, мы можем углубиться, используя Python и Jupyter Notebook. Jupyter Notebook предоставляет вам рабочую среду для запуска сценариев Python, и вы можете установить ее бесплатно с помощью Anaconda. Когда вы откроете его в первый раз, он будет выглядеть так.

Сначала мы импортируем модуль Pandas и используем его для загрузки данных в виде объекта Python, которым мы можем манипулировать. Эти объекты Python называются DataFrames.

import pandas as pd
titanic_train = pd.read_csv("train.csv")
titanic_test = pd.read_csv("test.csv") # Set aside for now, we will come back to it after we have our final model

Мы можем просмотреть первые две записи наших данных, используя следующую команду. Он должен выглядеть так же, как в вашей электронной таблице.

titanic = titanic_train
titanic.head(2)

Вывод:

Есть несколько команд, которые помогут вам погрузиться в данные. Давайте воспользуемся командой "info".

titanic.info()

Вывод:

RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

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

Если вы посмотрите на столбец "dtype", вы увидите, что есть три вида значений: int64, float, object. Int64 означает целое число. Столбцы int64 и float в наборе данных представляют собой числовые значения, а столбцы объектов — категориальные значения. Когда мы начинаем обучать нашу модель, все наши столбцы должны иметь числовые значения, чтобы ее можно было понять. Существует несколько способов преобразования категорийных значений в числовые значения. Давайте попробуем это на колонке Секс. В настоящее время значения для него либо мужские, либо женские, мы можем фактически представить их как 1 и 0, что называется бинарными данными. На этот раз мы будем импортировать другой модуль под названием Scikit-learn (или sklearn), чтобы сделать это. Введите следующие команды.

from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer()
titanic["Sex"] = lb.fit_transform(titanic["Sex"])

Когда вы оглядываетесь назад на свой набор данных, теперь вы должны видеть вместо этого 1 и 0.

На самом деле нам не нужно использовать все столбцы при создании нашей модели. Мы можем удалить некоторые из них, такие как «PassengerId» и «Name», потому что они не будут иметь никакого отношения к модели. Преобразование и удаление данных лучше оставить в части подготовки данных. На данный момент мы больше озабочены изучением данных, чтобы получить представление о том, как мы будем их подготавливать.

Двигаясь дальше, мы можем сгенерировать описательную статистику с помощью команды describe. Он покажет вам число, среднее значение, стандартное отклонение, минимум, максимум и 25-й, 50-й, 75-й процентиль для каждого столбца. Как правило, данные показывают диапазон ваших значений и то, куда попадает большая часть данных.

titanic.describe()

Вывод:

Если у вас есть солидный опыт работы со статистикой, извлечь информацию из таблицы не составит труда. Я не. Мне больше нравится визуализировать данные, чтобы понять их, мы можем построить их с помощью команды "hist".

titanic.hist()

Вывод:

Каждый столбец числовых значений отображается в виде гистограммы, показывающей распределение значений. На изображении видно, что большая часть наших данных искажается в одну сторону графика, а диапазон значений для каждого столбца разный (т. е. столбец «Возраст» показывает значения от 0 до 80, а столбец «СибСп» содержит только значения от 0 до 8). Нам нужно будет преобразовать значения каждого столбца, чтобы они находились в одинаковом диапазоне, чтобы ни один столбец не мог чрезмерно влиять на всю модель позже, когда мы ее обучаем. Это называется стандартизацией.

Наконец (при исследовании данных), давайте проверим корреляцию наших столбцов друг с другом и с нашей «меткой». Корреляция измеряет, насколько сильно две переменные связаны друг с другом. Мы можем наблюдать это, построив матрицу корреляции с помощью следующих команд. Если вы хотите визуализировать это, вы также можете использовать для этого тепловую карту.

corr = titanic.corr()
corr

Вывод:

Глядя на таблицу, мы видим, что существует видимая связь между столбцами «Тариф» и «Pclass». Это ожидаемо, «Pclass» представляет собой социально-экономический статус человека. Если мы проверим гистограммы, мы увидим, что большая часть «Pclass» приходится на значение 3, что означает более низкий класс, а большая часть «Fare» приходится на более дешевую сторону. Мы можем сделать предположение, что люди с более низким экономическим статусом, как правило, имеют более дешевые билеты. Основываясь на этом предположении, мы можем фактически удалить любой из них, потому что включение обоих может быть избыточным для нашей модели, поскольку они оба имеют схожие последствия. Иногда простота лучше, когда дело доходит до обучения нашей модели.

Подготовка данных

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

При подготовке к обучению нам нужно будет выделить подмножество данных поезда, которые будут использоваться для тестирования позже. Мы можем считать это отличным от тестовых данных, предоставленных Kaggle, потому что мы фактически будем использовать их для оценки того, как работает наша модель. Позже это будет иметь гораздо больше смысла, когда мы будем оценивать нашу модель. А пока я разделю свои данные о поездах. Я отложу 10% в качестве дополнительных тестовых данных, и я хочу убедиться, что получу тот же коэффициент «выжившего», что и общие данные.

from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.1, 
                               random_state=42)
for train_index, test_index in split.split(titanic, 
    titanic["Survived"]):
    strat_train_set = titanic.loc[train_index]
    strat_test_set = titanic.loc[test_index]

Мы также хотим отделить метку от предикторов в данных поезда, потому что мы будем выполнять подготовку данных только для предикторов.

titanic = strat_train_set.drop("Survived", axis=1)
titanic_label = strat_train_set["Survived"].copy()

Наши числовые данные будут обрабатываться иначе, чем наши категориальные данные. Мы можем создать отдельный конвейер для обоих, а затем объединить два конвейера.

# numerical data
num_attribs = ["Age", "SibSp", "Parch", "Pclass"]
# categorical data
cat_attribs = ["Sex", "Embarked"]

Я включил только интересующие меня столбцы. Я удалил «PassengerId», «Имя» и «Билет», потому что эта информация кажется неактуальной. Я удалил «Cabin», потому что в нем было слишком много пропущенных значений, чтобы быть полезным. Я также удалил «Fare», потому что думаю, что «Pclass» будет достаточно.

У нас есть два шага в обработке наших числовых данных. Во-первых, нам нужно заполнить пропущенное значение в столбце «Возраст», а затем нам нужно нормализовать диапазон данных. Мы можем использовать SimpleImputer и StandardScaler от sklearn.

# num pipeline
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
num_pipeline = Pipeline([
   ("imputer", SimpleImputer(strategy="median")),
   ("std_scaler", StandardScaler())
])

Для наших категорийных данных у нас есть столбцы «Пол» и «Посадка». Мы умеем преобразовывать столбец «Пол» в двоичные данные, но как насчет столбца «Отправлено». Что ж, если мы посмотрим на столбец, то узнаем, что в нем есть 3 значения на выбор. На самом деле мы можем создать для него 3 разных столбца двоичных данных. Для этого мы будем использовать OneHotEncoder. До этого в столбце «Embarked» отсутствует значение, которое нам нужно заполнить. Мы просто будем использовать наиболее частое значение для его заполнения.

from sklearn.preprocessing import OneHotEncoder
embarked_pipeline = Pipeline([
   ("imputer", SimpleImputer(strategy="most_frequent"),
   ("one_hot_encode", OneHotEncoder())
])

Теперь мы можем объединить все наши конвейеры, чтобы сформировать только один полный конвейер. После запуска полного конвейера данных вы должны увидеть, что они были преобразованы со всеми нашими настройками.

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OrdinalEncoder
full_pipeline = ColumnTransformer([
   ("num", num_pipeline, num_attribs),
   ("sex", OrdinalEncoder(), ["Sex"]),
   ("embarked", embarked_pipeline, ["Embarked"])
])
titanic_prepared = full_pipeline.fit_transform(titanic)
titanic_prepared

Вывод:

Обучение и оценка моделей

После того, как наши данные были подготовлены, теперь нужно обучить различные алгоритмы обучения и выбрать тот, который в конечном итоге станет нашей моделью. Это сложная часть. Почему мы должны обучать разные алгоритмы, почему мы не можем просто остановиться на одном сейчас? Что ж, несколько алгоритмов работают по-разному, и хотя у них одна и та же цель, математика, стоящая за ними, служит разным целям. Как новичок, вы не сразу поймете, какой из них будет лучше всего работать с вашими данными, не попробовав каждый из них. Я даже не думаю, что профессионалы могут знать сразу, не пробуя разные алгоритмы.

Я буду пробовать четыре разных алгоритма SGDClassifier, RandomForestClassifier, SVC и KNeighborsClassifier. Каждый из них заслуживает отдельной статьи, но пока все, что вам нужно знать, это то, что каждый из них может предсказывать классификацию. Чтобы оценить производительность, я буду использовать показатель под названием "точность". Точность определяет количество правильных прогнозов среди всех сделанных прогнозов. Есть и другие метрики на выбор, но пока мы можем остановиться на «точности».

Одна из распространенных проблем при обучении моделей возникает, когда модель очень хорошо работает на данных, на которых мы ее обучали, и очень плохо на невидимых данных. Это называется подгонкой модели. Лучший способ, которым я могу попытаться описать это, — запоминание против понимания. Модель запомнила ответы, но на самом деле не понимала их, когда дело доходило до того, чтобы дать ей новые данные для работы, она легко терялась. Чтобы этого не произошло, мы будем использовать метод под названием «перекрестная проверка». Перекрестная проверка работает путем обучения модели несколько раз на одном и том же наборе данных, но каждый раз она выделяет разные подмножества данных для тестирования и улучшения модели. Таким образом модель не может «запоминать» ответы. Вы заметите, что он выводит 3 разных результата, потому что он запускается 3 раза каждый.

from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
sgd_clf = SGDClassifier(random_state=42)
cross_val_score(sgd_clf, titanic_prepared, 
                titanic_label, cv=3, scoring="accuracy")
# OUTPUT SCORES: [0.75655431, 0.73033708, 0.68539326]
forest_clf = RandomForestClassifier(random_state=42)
cross_val_score(forest_clf, titanic_prepared,
                titanic_label, cv=3, scoring="accuracy")
# OUTPUT SCORES: [0.80524345, 0.77153558, 0.79026217]
svm_clf = SVC(random_state=42)
cross_val_score(svm_clf, titanic_prepared, 
                titanic_label, cv=3, scoring="accuracy")
# OUTPUT SCORES: [0.84269663, 0.81273408, 0.82397004]
knn_clf = KNeighborsClassifier()
cross_val_score(knn_clf, titanic_prepared, 
                titanic_label, cv=3, scoring="accuracy")
# OUTPUT SCORES: [0.82771536, 0.79775281, 0.80524345]

Из четырех моделей SVC работает лучше всего, поэтому мы выберем эту, но давайте посмотрим, сможем ли мы еще больше улучшить модель. Все эти модели на самом деле дают нам доступ к набору гиперпараметров, которые мы можем настроить, чтобы как-то контролировать процесс обучения. Раньше мы просто устанавливали его по умолчанию, но давайте посмотрим, сможем ли мы еще больше улучшить оценку после корректировки. Давайте воспользуемся GridSearchCV, который в основном ищет для нас лучшие гиперпараметры.

from sklearn.model_selection import GridSearchCV
param_grid = [
   {'C': [1,10,100], 'gamma': [1,0.1,0.001], 'kernel': 
   ['linear','rbf']}
]
svm_clf = SVC(random_state=42)
grid_search = GridSearchCV(svm_clf, param_grid, cv=3, 
                           scoring="accuracy",
                           return_train_score=True, verbose=10)
grid_search.fit(titanic_prepared, titanic_label)
cross_val_score(grid_search.best_estimator_, titanic_prepared, 
                titanic_label, cv=3, scoring="accuracy")
# OUTPUT SCORES: [0.84644195, 0.8164794 , 0.82397004]

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

И последнее, прежде чем мы классифицируем тестовые данные Kaggle, давайте оценим модель на невидимых данных. Помните данные с метками, которые мы отложили ранее? Что ж, теперь мы будем использовать модель, чтобы классифицировать ее и сравнить с реальной этикеткой.

from sklearn.metrics import accuracy_score
final_model = grid_search.best_estimator_
unseen = strat_test_set.drop("Survived", axis=1)
unseen_label = strat_test_set["Survived"].copy()
unseen_prepared = full_pipeline.fit_transform(unseen)
predictions = final_model.predict(unseen_prepared)
accuracy_score(unseen_label, predictions)
# OUTPUT SCORE: 0.8

Я получил 0,8 балла, что неплохо, и он все еще близок к предыдущим баллам, так что теперь мы знаем, что модель не подгоняет данные!

Отправка на Kaggle

Мы можем продолжить и подключить тестовые данные из Kaggle к модели. Мы еще не сможем узнать наш счет в Jupyter Notebook, так как данные не имеют меток. Давайте сохраним его как файл CSV и отправим на сайт Kaggle.

test = titanic_test
test_prepared = full_pipeline.fit_transform(test)
final_predictions = final_model.predict(test_prepared)
test["Survived"] = final_predictions
test[["PassengerId", "Survived"]].to_csv("submission.csv", 
                                         index=False)

Как только вы загрузите свое представление, оно покажет вам ваш счет. Мой окончательный результат — 0,78, что, на мой взгляд, неплохо, немного ниже ожидаемого, но все же близко к нашему 0,8, что является хорошей отправной точкой. Kaggle позволяет вам еще больше улучшить свою модель и отправлять заявки несколько раз, чтобы улучшить свой результат. Я оставлю это вам, если вы заинтересованы в дальнейшем создании модели, представленной здесь, но пока, если вы следите за своей собственной записной книжкой, дайте себе пять, потому что вы только что представили свою первую запись Kaggle!

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

Ваше здоровье!