В этой статье продолжим рассмотрение практических примеров автоматизированного машинного обучение в задачах классификации с помощью TPOT, взятых из книги "Radečić Dario. Machine Learning Automation with TPOT: Build, validate, and deploy fully automated machine learning models with Python". Рассмотрим основные темы, такие как загрузка набора данных, очистка и предварительный анализ данных, создадим базовую модель классификации с помощью логистической регрессии и пакета sklean.Затем углубимся в классификацию с помощью ТPОТ. Узнаем, как обучать и оценивать автоматизированные модели классификации.
Перед автоматическим обучением моделей увидим, как можно получить хорошие модели с базовыми алгоритмами классификации, такими как логистическая регрессия. Эта модель будет служить в качестве базового уровня, который должен превзойти TPOT. Будут рассмотрены следующие темы: • Применение автоматизированного классификационного моделирования к набору данных Iris. • Применение автоматизированного классификационного моделирования к набору данных Titanic.
В начале, как обычно, импортируем необходимые библиотеки
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rcParams
rcParams['axes.spines.top'] = False
rcParams['axes.spines.right'] = False
sepal_length | sepal_width | petal_length | petal_width | species | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
sepal_length 0
sepal_width 0
petal_length 0
petal_width 0
species 0
dtype: int64
Пропущенных значений нет. Проверим распределение классов в целевой переменной.
Это относится к количество экземпляров, принадлежащих к каждому классу – setosa,
versicolor и virginica. Как известно, модели машинного обучения работают плохо, если
присутствует серьезный дисбаланс классов. Визуализируем распределение классов:
ax = df.groupby('species').count().plot(kind='bar',
figsize=(10, 6), fontsize=13, color='#4f4f4f')
ax.set_title('Iris Dataset target variable distribution',
size=20, pad=30)
ax.set_ylabel('Count', fontsize=14)
ax.set_xlabel('Species', fontsize=14)
ax.get_legend().remove()
Заключительным этапом исследовательского анализа и подготовки данных является
проверка корреляция. Высокая корреляция между функциями обычно означает, что есть некоторые избыточность в наборе данных. Следующий фрагмент кода строит
корреляционную матрицу :
plt.figure(figsize=(12, 9))
plt.title('Correlation matrix', size=20)
sns.heatmap(df.corr(), annot=True, cmap='Blues');
Как и ожидалось, существует сильная корреляция между большинством функций.
Теперь мы знакомы с набором данных Iris, и можем перейти к моделированию. Сначала построим базовую модель с помощью алгоритма логистической регрессии. Она будет
служить стартовой моделью, которую TPOT должен превзойти. Первым шагом в этом
процессе является разделение на обучения/тестирования.
from sklearn.model_selection import train_test_split
X = df.drop('species', axis=1)
y = df['species']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=3)
y_train.shape, y_test.shape
Количество экземпляров в обучающих и тестовых наборах
((112,), (38,))Строим модель логистической регрессии, делаем прогнозы на тестовом наборе и
печатаем матрицу ошибок фактических и прогнозируемых значений:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
lm = LogisticRegression(random_state=42)
lm.fit(X_train, y_train)
lm_preds = lm.predict(X_test)
print(confusion_matrix(y_test, lm_preds))
[[15 0 0] [ 0 11 1] [ 0 0 11]]Как видим, есть только одна неправильная классификация — ложное срабатывание для virginica.
Базовая модель сработала исключительно хорошо. Выведем точность для базовой
модели:
from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, lm_preds))
print(f'accuracy = {accuracy_score(y_test, lm_preds)):.2f}')
accuracy= 0.97
Точность тестового набора с логистической регрессией 97% и только одна
неправильная классификация. Посмотрим, сможет ли TPOT превзойти логистическую
регрессию. Построим модель автоматической классификации, оптимизируем точность и тренируем в течение 10 минут.
from tpot import TPOTClassifier pipeline_optimizer = TPOTClassifier( scoring='accuracy', max_time_mins=10, random_state=42, verbosity=2) pipeline_optimizer.fit(X_train, y_train)
TPOT удалось за 10 минут установить на мою машину 9 поколений
Generation 1 - Current best internal CV score: 0.9826086956521738 Generation 2 - Current best internal CV score: 0.9826086956521738 Generation 3 - Current best internal CV score: 0.9826086956521738 Generation 4 - Current best internal CV score: 0.9826086956521738 Generation 5 - Current best internal CV score: 0.9826086956521738 Generation 6 - Current best internal CV score: 0.9826086956521738 Generation 7 - Current best internal CV score: 0.9826086956521738 Generation 8 - Current best internal CV score: 0.9826086956521738 Generation 9 - Current best internal CV score: 0.9826086956521738 10.00 minutes have elapsed. TPOT will close down. TPOT closed during evaluation in one generation. WARNING: TPOT may not provide a good pipeline if TPOT is stopped/interrupted in a early generation. TPOT closed prematurely. Will use the current best pipeline. Best pipeline: MLPClassifier(input_matrix, alpha=0.01, learning_rate_init=0.001)Выведем полученную точность
tpot_preds = pipeline_optimizer.predict(X_test) accuracy_score(y_test, tpot_preds)
print(f'accuracy = {accuracy_score(y_test, tpot_preds)):.2f}')
accuracy= 0.97
Как видим, точность на тестовом наборе не улучшилась. Если сделать точечную
диаграмма целевой переменной и функций, можно некоторое перекрытие для классы
virginica и versicolor. Здесь, скорее всего, дело обстоит так, что никакая попытка
обучения не даст правильно классифицировать этот единственный экземпляр.
Посмотри что TPOT объявил оптимальным конвейером после
10 минут обучения, следующий фрагмент кода выведет этот конвейер :
pipeline_optimizer.fitted_pipeline_
Pipeline(steps=[('mlpclassifier', MLPClassifier(alpha=0.01, random_state=42))])
Переходим к следующему набору данных - набору данных катастрофы «Титаника», он
принадлежит к числу самых популярных наборов данных машинного обучения.
Цель состоит в том, чтобы построить автоматизированную модель, способную
прогнозировать, пережил бы пассажир аварию, основываясь на различных входных
характеристиках, таких как класс пассажира, пол, возраст, каюта.
Загружаем и знакомимся с данными :
df = pd.read_csv('titanic_train.csv')
df.head()
Проверяем наличие пропусков
df.isnull().sum()
PassengerId 0 Survived 0 Pclass 0 Name 0 Sex 0 Age 177 SibSp 0 Parch 0 Ticket 0 Fare 0 Cabin 687 Embarked 2 dtype: int64Как видим, в наборе данных много пропущенных значений. Большинство из
недостающих значений находятся в атрибутах Age и Cabin. Это легко понять для Cabin – значение отсутствует, если у пассажира не было собственной каюты. Мы разберемся с этими отсутствующими значениями позже, а пока давайте сосредоточимся на данных и используем для этого визуализацию. Для этого определим одну функцию для
отображения гистограммы.
def make_bar_chart(column, title, ylabel, xlabel, y_offset=10, x_offset=0.2):
ax = df.groupby(column).count()[['PassengerId']].plot(kind='bar',
figsize=(10, 6), fontsize=13,color='#4f4f4f' )
ax.set_title(title, size=20, pad=30)
ax.set_ylabel(ylabel, fontsize=14)
ax.set_xlabel(xlabel, fontsize=14)
ax.get_legend().remove()
for i in ax.patches:
ax.text(i.get_x() + x_offset, i.get_height() + y_offset, i.get_height(), fontsize=20)
return ax
Цель состоит в том, чтобы визуализировать, как распределяются категориальные
переменные, чтобы мы могли лучше понимать набор данных. Для начала
представим, сколько пассажиров выжило, а сколько нет.
make_bar_chart(column='Survived',
title='Distribution of the Survived variable',ylabel='Count',
xlabel='Has the passenger survived? (0 = No, 1 = Yes)');
Как видно, большинство пассажиров не выжили при крушении Титаника. Этот самапо себе информация мало поможет нашему анализу, потому что мы не знаемраспределение выживших пассажиров по полу, классу пассажиров и другим признакам.Продолжим визуализацию данных и покажем количество пассажиров каждогопассажирского класса. Чем ниже номер класса, тем лучше — дороже билет, лучшесервис и может быть, больше шансов на выживание:
make_bar_chart(column='Pclass',
title='Distirbution of the Passenger Class variable',
ylabel='Count',xlabel='Passenger Class (smaller is better)',
x_offset=0.15);
Как видим, большинство пассажиров относятся к третьему классу. Это ожидаемо,
так как на борту было больше рабочих, чем богатых людей.
В качестве следующего шага на этапе визуализации
посмотрим как выглядит распределение по полу. Это даст нам представление о том,
было ли больше женщин или мужчин на борту и насколько велика разница.
make_bar_chart(column='Sex',
title='Distirbution of the Sex variable',
ylabel='Count',xlabel='Gender');
Как видим, на борту было больше мужчин. Это связано с вывод, сделанный в
предыдущей визуализации, где мы пришли к выводу, что много рабочих на борту.
Далее визуализируем изменение непрерывной переменной. Цель состоит в том, чтобы сделать гистограмму атрибута Fare, которая покажет распределение сумм, уплаченных за билет.
plt.figure(figsize=(12, 7))
plt.title('Fare cost distribution', size=20)
plt.xlabel('Cost', size=14)
plt.ylabel('Count', size=14)
plt.hist(df['Fare'], bins=15, color='#4f4f4f',ec='#040404');
бывают крайние случаи.
strip().split(' ')[0])
make_bar_chart(column='Title',
title='Distirbution of the Passenger Title variable',
ylabel='Count',xlabel='Title',x_offset=-0.2);
Как видно, большинство пассажиров имеют общие титулы, такие как мистер и мисс и
всего несколько с уникальными титулами. Удобнее было бы превратить его в двоичный столбец — значение равно нулю, если заголовок общий, и единица в противном случае.
Пришло время подготовить набор данных для машинного обучения и сделать
следующие шаги :
1)Отбросить бесполезные столбцы — Ticket и PassengerId. Первый представляет
собой просто набор фиктивных букв и цифр и бесполезен для прогнозирования.
Второй — произвольный идентификатор, скорее всего, сгенерированный с помощью
последовательности базы данных. Удаляем их вызвав функцию drop().
2) Переназначить значения атрибута Пол на целые числа. Текстовые значения пола
не могут быть передана алгоритму машинного обучения напрямую, поэтому заменим
мужчин на 0, а женщин на 1. Функция replace() — идеальный кандидат на эту работу.
3) Используем ранее сгенерированный столбец Title и преобразуем его в двоичный
показатель – значение равно нулю, если титул является общим (например, Мистер,
Мисс и Миссис) и иначе единица. Затем переименуем столбец в Title_Unusal. Столбец
Name больше не нужен, мы его удаляем.
4) Обработаем отсутствующие значения в столбце Cabin, превратив этот атрибут
в двоичный – значение равно нулю, если значение для кабины отсутствует, и единице в противном случае. Назовем этот новый столбец Cabin_Known. После этого можно
удалить столбец Cabin, потому что он также больше не нужен 5) Создайте фиктивные переменные с атрибутом Embarked. Этот атрибут указывает
порт, в котором пассажиры прибыли на судно. Сложно судить о его полезности, оставим это на усмотрение TPOT.
6) Отсутствующие значения в атрибуте Age для простоты заменим средним
значениемВ следующем фрагменте кода показано, как применить все эти преобразования:
df.drop(['Ticket', 'PassengerId'], axis=1, inplace=True)
gender_mapper = {'male': 0, 'female': 1}
df['Sex'].replace(gender_mapper, inplace=True)
pd.concat([df.head(3),df.tail(3)])
Survived Pclass Name Sex Age SibSp Parch Fare Cabin Embarked Title 0 0 3 Braund, Mr. Owen Harris 0 22.0 1 0 7.2500 NaN S Mr. 1 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... 1 38.0 1 0 71.2833 C85 C Mrs. 2 1 3 Heikkinen, Miss. Laina 1 26.0 0 0 7.9250 NaN S Miss. 888 0 3 Johnston, Miss. Catherine Helen "Carrie" 1 NaN 1 2 23.4500 NaN S Miss. 889 1 1 Behr, Mr. Karl Howell 0 26.0 0 0 30.0000 C148 C Mr. 890 0 3 Dooley, Mr. Patrick 0 32.0 0 0 7.7500 NaN Q Mr.
df['Title'] = [0 if x in ['Mr.', 'Miss.', 'Mrs.'] else 1 for x in df['Title']]
df = df.rename(columns={'Title': 'Title_Unusual'})
df.drop('Name', axis=1, inplace=True)
pd.concat([df.head(3),df.tail(3)])
Survived Pclass Sex Age SibSp Parch Fare Cabin Embarked Title_Unusual 0 0 3 0 22.0 1 0 7.2500 NaN S 0 1 1 1 1 38.0 1 0 71.2833 C85 C 0 2 1 3 1 26.0 0 0 7.9250 NaN S 0 888 0 3 1 NaN 1 2 23.4500 NaN S 0 889 1 1 0 26.0 0 0 30.0000 C148 C 0 890 0 3 0 32.0 0 0 7.7500 NaN Q 0
df['Cabin_Known'] = [0 if str(x) == 'nan' else 1 for x in df['Cabin']]
df.drop('Cabin', axis=1, inplace=True)
pd.concat([df.head(3),df.tail(3)])
Survived Pclass Sex Age SibSp Parch Fare Embarked Title_Unusual Cabin_Known 0 0 3 0 22.0 1 0 7.2500 S 0 0 1 1 1 1 38.0 1 0 71.2833 C 0 1 2 1 3 1 26.0 0 0 7.9250 S 0 0 888 0 3 1 NaN 1 2 23.4500 S 0 0 889 1 1 0 26.0 0 0 30.0000 C 0 1 890 0 3 0 32.0 0 0 7.7500 Q 0 0
emb_dummies = pd.get_dummies(df['Embarked'], drop_first=True, prefix='Embarked')
df = pd.concat([df, emb_dummies], axis=1)
df.drop('Embarked', axis=1, inplace=True)
df['Age'] = df['Age'].fillna(int(df['Age'].mean()))
pd.concat([df.head(3),df.tail(3)])
Survived Pclass Sex Age SibSp Parch Fare Title_Unusual Cabin_Known Embarked_Q Embarked_S 0 0 3 0 22.0 1 0 7.2500 0 0 0 1 1 1 1 1 38.0 1 0 71.2833 0 1 0 0 2 1 3 1 26.0 0 0 7.9250 0 0 0 1 888 0 3 1 29.0 1 2 23.4500 0 0 0 1 889 1 1 0 26.0 0 0 30.0000 0 1 0 0 890 0 3 0 32.0 0 0 7.7500 0 0 1 0
Это все, что нужно сделать в отношении подготовки данных. Масштабирование/стандартизация не требуется, так как сам TPOT решит, необходим ли этот шаг.
Прежде чем обучить модель классификации, разделим набор данных на обучающие итестирующие части.
from sklearn.model_selection import train_test_split
X = df.drop('Survived', axis=1)y = df['Survived']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)y_train.shape, y_test.shape
((668,), (223,))
Переходим к моделям. Начнем с базовой модели — логистической регрессии. Мы будем тренировать его на обучающем наборе и оценить его на тестовом. Следующий
фрагмент кода обучает модель и выводит матрицу ошибок:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
m = LogisticRegression(random_state=42)
lm.fit(X_train, y_train)
lm_preds = lm.predict(X_test)
print(confusion_matrix(y_test, lm_preds))
[[111 23]
[ 23 66]]
Количество ложноположительных и ложноотрицательных результатов одинаково (23).
Если учитывать долю, то ложноотрицательных результатов
больше. Это значит, что модель с большей вероятностью скажет, что пассажир выжил,
даже если это не так. Для оценки качества модели используем F1 оценку. Значение
этого показателя находится в диапазоне от 0 до 1 (чем выше, тем лучше) и
представляет собой гармоническое среднее между точностью и полнотой. Вот как это
рассчитать с помощью Python
from sklearn.metrics import f1_score
print(f'F1 = {f1_score(y_test, lm_preds):.2f}')
F1 = 0.74
Значение 0,74 неплохо для базовой модели. Может ли TPOT превзойти его? Так же, как и раньше, мы ограничиваем обучение модели 10 минутами. Вместо точности мы будем оптимизировать оценку F1. Тем самым мы можем сравните оценки F1
автоматизированной модели с базовой. Следующий фрагмент кода обучает модель на
обучающем наборе: TPOT удалось обучить 4 поколения за 10 минут, а результат
немного увеличивается по мере обучения модели:
from tpot import TPOTClassifier
pipeline_optimizer = TPOTClassifier(
scoring='f1',
max_time_mins=10,
random_state=42,
verbosity=2)
pipeline_optimizer.fit(X_train, y_train)
Комментариев нет:
Отправить комментарий