|
Сквозной проект по машинному обучению
ОП.10 Машинное обучение
Практическая работа №8
Тема: Сквозной проект по машинному обучению
Цель работы: Применить все полученные навыки машинного обучения для полного цикла анализа данных: от загрузки и очистки до построения, настройки и оценки модели.
Время выполнения: 2 академических часа
Инструменты: Python + Jupyter Notebook, sklearn, pandas, matplotlib, seaborn
ЗАДАНИЕ ДЛЯ СТУДЕНТА
Этап 1. Загрузка и первичный анализ данных (15 минут)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report, confusion_matrix, ConfusionMatrixDisplay
import warnings
warnings.filterwarnings('ignore')
# Загрузка данных о пассажирах Титаника
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
df = pd.read_csv(url)
print("Размер датасета:", df.shape)
print("Первые 5 строк:")
print(df.head())
print("\nИнформация о данных:")
print(df.info())
Вопрос: Какие признаки есть в датасете? Какие из них числовые, какие категориальные? Есть ли пропуски?
Этап 2. Очистка и предобработка данных (20 минут)
# Создаём копию данных
df_clean = df.copy()
# 1. Удаляем ненужные признаки
df_clean = df_clean.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)
print("После удаления ненужных признаков:", df_clean.shape)
# 2. Обработка пропусков
print("\nПропуски до обработки:")
print(df_clean.isnull().sum())
# Заполняем Age медианой
df_clean['Age'] = df_clean['Age'].fillna(df_clean['Age'].median())
# Заполняем Embarked модой
df_clean['Embarked'] = df_clean['Embarked'].fillna(df_clean['Embarked'].mode()[0])
# 3. Преобразование категориальных признаков
le_sex = LabelEncoder()
df_clean['Sex'] = le_sex.fit_transform(df_clean['Sex']) # male=1, female=0
le_embarked = LabelEncoder()
df_clean['Embarked'] = le_embarked.fit_transform(df_clean['Embarked'])
print("\nПропуски после обработки:")
print(df_clean.isnull().sum())
print("\nПервые 5 строк после предобработки:")
print(df_clean.head())
Вопрос: Какие шаги предобработки вы выполнили? Почему выбрали именно такие методы заполнения пропусков?
Этап 3. Исследовательский анализ данных (15 минут)
# Определяем целевую переменную и признаки
target = 'Survived'
features = [col for col in df_clean.columns if col != target]
X = df_clean[features]
y = df_clean[target]
print("Признаки:", features)
print(f"Распределение классов:\n{y.value_counts()}")
print(f"Доля выживших: {y.mean():.2%}")
# Визуализация влияния признаков
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
# 1. Выживаемость по полу
sex_survival = df_clean.groupby('Sex')[target].mean()
axes[0].bar(['Женщины', 'Мужчины'], sex_survival.values, color=['pink', 'lightblue'])
axes[0].set_title('Выживаемость по полу')
axes[0].set_ylabel('Доля выживших')
# 2. Выживаемость по классу
class_survival = df_clean.groupby('Pclass')[target].mean()
axes[1].bar(class_survival.index, class_survival.values, color='skyblue')
axes[1].set_title('Выживаемость по классу билета')
axes[1].set_xlabel('Класс')
axes[1].set_ylabel('Доля выживших')
# 3. Распределение возраста выживших и погибших
axes[2].hist(df_clean[df_clean[target]==0]['Age'], bins=20, alpha=0.7, label='Погибшие')
axes[2].hist(df_clean[df_clean[target]==1]['Age'], bins=20, alpha=0.7, label='Выжившие')
axes[2].set_title('Распределение возраста')
axes[2].set_xlabel('Возраст')
axes[2].legend()
# 4. Цена билета выживших и погибших
axes[3].boxplot([df_clean[df_clean[target]==0]['Fare'], df_clean[df_clean[target]==1]['Fare']],
labels=['Погибшие', 'Выжившие'])
axes[3].set_title('Цена билета')
axes[3].set_ylabel('Цена')
# 5. Тепловая карта корреляции
numeric_cols = df_clean.select_dtypes(include=[np.number]).columns
corr = df_clean[numeric_cols].corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f', ax=axes[4])
axes[4].set_title('Корреляция признаков')
# 6. Количество пассажиров по портам
embarked_counts = df_clean['Embarked'].value_counts()
axes[5].pie(embarked_counts.values, labels=embarked_counts.index, autopct='%1.1f%%')
axes[5].set_title('Распределение по портам посадки')
plt.tight_layout()
plt.show()
Вопрос: Какие признаки сильнее всего коррелируют с выживаемостью? Какие выводы можно сделать из графиков?
Этап 4. Нормализация и разделение данных (10 минут)
# Нормализация числовых признаков
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Разделение на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.25, random_state=42, stratify=y
)
print(f"Обучающая выборка: {X_train.shape}")
print(f"Тестовая выборка: {X_test.shape}")
print(f"Распределение в обучении: {np.bincount(y_train)}")
print(f"Распределение в тесте: {np.bincount(y_test)}")
Вопрос: Зачем нужна нормализация? Почему использовали стратифицированное разбиение?
Этап 5. Обучение и сравнение моделей (20 минут)
# Список моделей для сравнения
models = {
'Logistic Regression': LogisticRegression(max_iter=1000, random_state=42),
'Decision Tree': DecisionTreeClassifier(random_state=42),
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'SVM': SVC(kernel='rbf', random_state=42)
}
# Обучение и оценка моделей
results = []
for name, model in models.items():
# Обучение
model.fit(X_train, y_train)
# Предсказания
y_pred = model.predict(X_test)
# Метрики
acc = accuracy_score(y_test, y_pred)
prec = precision_score(y_test, y_pred)
rec = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
# Кросс-валидация
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
results.append({
'Model': name,
'Accuracy': acc,
'Precision': prec,
'Recall': rec,
'F1-score': f1,
'CV_mean': cv_scores.mean(),
'CV_std': cv_scores.std()
})
# Сводная таблица результатов
results_df = pd.DataFrame(results).round(4)
print("Сравнение моделей:")
print(results_df)
# Визуализация результатов
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# График 1: Сравнение метрик
x = np.arange(len(results_df['Model']))
width = 0.2
axes[0].bar(x - width, results_df['Accuracy'], width, label='Accuracy')
axes[0].bar(x, results_df['Precision'], width, label='Precision')
axes[0].bar(x + width, results_df['Recall'], width, label='Recall')
axes[0].set_xticks(x)
axes[0].set_xticklabels(results_df['Model'], rotation=45, ha='right')
axes[0].set_ylabel('Значение метрики')
axes[0].set_title('Сравнение метрик качества')
axes[0].legend()
axes[0].set_ylim(0, 1)
# График 2: Кросс-валидация
axes[1].bar(x, results_df['CV_mean'], yerr=results_df['CV_std'], capsize=5)
axes[1].set_xticks(x)
axes[1].set_xticklabels(results_df['Model'], rotation=45, ha='right')
axes[1].set_ylabel('Средняя точность (CV)')
axes[1].set_title('Результаты 5-кратной кросс-валидации')
axes[1].set_ylim(0, 1)
plt.tight_layout()
plt.show()
# Определяем лучшую модель
best_model_name = results_df.loc[results_df['Accuracy'].idxmax(), 'Model']
print(f"Лучшая модель: {best_model_name}")
Вопрос: Какая модель показала лучший результат? Какая модель наиболее стабильна (маленький CV_std)?
Этап 6. Настройка гиперпараметров лучшей модели (15 минут)
# Выбираем лучшую модель и настраиваем её
if best_model_name == 'Random Forest':
model = RandomForestClassifier(random_state=42)
param_grid = {
'n_estimators': [50, 100, 150],
'max_depth': [5, 10, None],
'min_samples_split': [2, 5, 10]
}
elif best_model_name == 'SVM':
model = SVC(random_state=42)
param_grid = {
'C': [0.1, 1, 10],
'gamma': [0.01, 0.1, 1],
'kernel': ['rbf']
}
elif best_model_name == 'Logistic Regression':
model = LogisticRegression(max_iter=1000, random_state=42)
param_grid = {
'C': [0.01, 0.1, 1, 10],
'penalty': ['l2']
}
else: # Decision Tree
model = DecisionTreeClassifier(random_state=42)
param_grid = {
'max_depth': [3, 5, 7, 10],
'min_samples_split': [2, 5, 10],
'criterion': ['gini', 'entropy']
}
# GridSearchCV
grid_search = GridSearchCV(model, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X_train, y_train)
print(f"Лучшие параметры для {best_model_name}:")
print(grid_search.best_params_)
print(f"Лучшее CV качество: {grid_search.best_score_:.4f}")
# Оценка на тестовой выборке
best_model = grid_search.best_estimator_
y_pred_best = best_model.predict(X_test)
print(f"Accuracy на тесте: {accuracy_score(y_test, y_pred_best):.4f}")
print(f"F1-score на тесте: {f1_score(y_test, y_pred_best):.4f}")
# Детальный отчёт
print("\nClassification Report:")
print(classification_report(y_test, y_pred_best, target_names=['Погиб', 'Выжил']))
# Матрица ошибок
plt.figure(figsize=(6, 5))
ConfusionMatrixDisplay.from_predictions(y_test, y_pred_best, cmap='Blues',
display_labels=['Погиб', 'Выжил'])
plt.title(f'Матрица ошибок ({best_model_name})')
plt.show()
Вопрос: Насколько улучшилось качество после подбора гиперпараметров? Какие параметры оказались оптимальными?
Этап 7. Важность признаков (10 минут)
# Анализ важности признаков (для моделей, поддерживающих feature_importances_)
if hasattr(best_model, 'feature_importances_'):
feature_importance = pd.DataFrame({
'feature': features,
'importance': best_model.feature_importances_
}).sort_values('importance', ascending=False)
print("Важность признаков:")
print(feature_importance)
plt.figure(figsize=(10, 6))
plt.barh(feature_importance['feature'], feature_importance['importance'], color='skyblue')
plt.xlabel('Важность')
plt.title('Важность признаков в лучшей модели')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
else:
print(f"Модель {best_model_name} не поддерживает прямую оценку важности признаков.")
print("Анализируем коэффициенты (для линейных моделей):")
if hasattr(best_model, 'coef_'):
coefs = pd.DataFrame({
'feature': features,
'coefficient': best_model.coef_[0]
}).sort_values('coefficient', ascending=False)
print(coefs)
Вопрос: Какой признак оказался самым важным для предсказания выживаемости? Совпадает ли это с вашим ожиданием?
Этап 8. Итоговые выводы по проекту (10 минут)
Напишите итоговый отчёт, включив в него:
1. Краткое описание датасета и его особенностей
2. Какие шаги предобработки были выполнены и почему
3. Сравнение моделей (какая модель лучше и почему)
4. Результаты настройки гиперпараметров
5. Какие признаки оказались наиболее важными для предсказания
6. Практические рекомендации: как можно улучшить модель
КРИТЕРИИ ОЦЕНКИ
| Критерий | Макс. балл |
|---|
| Загрузка и первичный анализ данных | 1 |
| Очистка и предобработка данных | 2 | | Исследовательский анализ и визуализация | 2 | | Сравнение моделей и выбор лучшей | 2 | | Настройка гиперпараметров | 1 | | Анализ важности признаков и итоговые выводы | 2 | | Итого | 10 |
Автор: УМК СПО
Лицензия: Бесплатное использование с указанием источника
|
|
|