635 KiB
Начало лабораторной¶
Выгрузка данных из csv файла в датафрейм
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier, GradientBoostingRegressor
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix, mean_squared_error, r2_score, mean_absolute_error, accuracy_score, precision_score, recall_score, f1_score, ConfusionMatrixDisplay
df = pd.read_csv(".//static//csv//ds_salaries.csv")
print("Первые 5 строк датасета:")
print(df.head())
print("\nИнформация о датасете:")
print(df.info())
Бизнес цели:
Предсказание зарплаты в долларах (salary_in_usd) на основе характеристик сотрудника и компании.
Предсказание уровня опыта (experience_level) сотрудника.
Разделение данных на обучающую и тестовую выборки
X = df.drop(columns=["salary_in_usd"])
y = df["salary_in_usd"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Для классификации: предсказание experience_level
y_clf = df["experience_level"]
X_train_clf, X_test_clf, y_train_clf, y_test_clf = train_test_split(X, y_clf, test_size=0.2, random_state=42)
Предбоработка данных
numeric_features = ["work_year", "salary", "remote_ratio"]
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
categorical_features = ["experience_level", "employment_type", "job_title", "company_size"]
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
Модели для регрессии
models_reg = {
"Linear Regression": LinearRegression(),
"Random Forest": RandomForestRegressor(random_state=42),
"Gradient Boosting": GradientBoostingRegressor(random_state=42)
}
# Конвейер для регрессии
pipelines_reg = {}
for name, model in models_reg.items():
pipelines_reg[name] = Pipeline(steps=[
('preprocessor', preprocessor),
('regressor', model)
])
Демонстрация работы конвейера
preprocessing_result = pipelines_reg["Random Forest"].named_steps['preprocessor'].fit_transform(X_train)
print(preprocessing_result.shape)
print(preprocessing_result[:5])
# Преобразуем разреженную матрицу в плотную форму
preprocessing_result_dense = preprocessing_result.toarray()
# Получаем имена признаков после предобработки
columns_reg = pipelines_reg["Random Forest"].named_steps['preprocessor'].get_feature_names_out()
# Если количество столбцов в данных совпадает с количеством в названиях
if preprocessing_result_dense.shape[1] == len(columns_reg):
preprocessed_df = pd.DataFrame(
preprocessing_result_dense,
columns=columns_reg
)
else:
# В случае, если количество столбцов не совпадает, выводим предупреждение
print(f"Количество столбцов в данных ({preprocessing_result_dense.shape[1]}) не совпадает с количеством в названиях ({len(columns_reg)})")
# Можно применить нужное количество столбцов из названий
preprocessed_df = pd.DataFrame(
preprocessing_result_dense,
columns=columns_reg[:preprocessing_result_dense.shape[1]]
)
# Выводим DataFrame с предобработанными данными
preprocessed_df.head()
Модели для классификации
models_clf = {
"Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
"Random Forest": RandomForestClassifier(random_state=42),
"KNN": KNeighborsClassifier()
}
# Конвейер для классификации
pipelines_clf = {}
for name, model in models_clf.items():
pipelines_clf[name] = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', model)
])
Демонстрация работы конвейера
preprocessing_result = pipelines_clf["Random Forest"].named_steps['preprocessor'].fit_transform(X_train)
print("Shape of preprocessed data:", preprocessing_result.shape)
print("First 5 rows of preprocessed data:", preprocessing_result[:5])
# Преобразуем разреженную матрицу в плотную
preprocessing_result_dense = preprocessing_result.toarray()
# Получаем имена признаков после предобработки
columns_clf = pipelines_clf["Random Forest"].named_steps['preprocessor'].get_feature_names_out()
# Если количество столбцов в данных совпадает с количеством в названиях
if preprocessing_result_dense.shape[1] == len(columns_clf):
preprocessed_df = pd.DataFrame(
preprocessing_result_dense,
columns=columns_clf
)
else:
# В случае, если количество столбцов не совпадает, выводим предупреждение
print(f"Количество столбцов в данных ({preprocessing_result_dense.shape[1]}) не совпадает с количеством в названиях ({len(columns_clf)})")
# Можно применить нужное количество столбцов из названий
preprocessed_df = pd.DataFrame(
preprocessing_result_dense,
columns=columns_clf[:preprocessing_result_dense.shape[1]]
)
# Выводим DataFrame с предобработанными данными
preprocessed_df.head()
Сводная таблица оценок качества для использованных моделей классификации
# Подсчитываем количество строк и столбцов для подграфиков
n_models = len(pipelines_clf)
n_cols = 2
n_rows = (n_models // n_cols) + (n_models % n_cols > 0) # Подсчитываем количество строк для подграфиков
fig, ax = plt.subplots(n_rows, n_cols, figsize=(12, 5 * n_rows), sharex=False, sharey=False)
for index, (name, pipeline) in enumerate(pipelines_clf.items()):
# Обучаем модель
pipeline.fit(X_train_clf, y_train_clf)
# Получаем предсказания
y_pred_clf = pipeline.predict(X_test_clf)
# Строим матрицу ошибок
c_matrix = confusion_matrix(y_test_clf, y_pred_clf)
# Отображаем матрицу ошибок
row = index // n_cols # Строка для текущего подграфика
col = index % n_cols # Столбец для текущего подграфика
disp = ConfusionMatrixDisplay(confusion_matrix=c_matrix, display_labels=np.unique(y_clf)).plot(ax=ax[row, col])
disp.ax_.set_title(name)
# Отключаем пустые подграфики, если они есть
if n_models % n_cols != 0:
for i in range(n_models, n_rows * n_cols):
fig.delaxes(ax.flatten()[i])
plt.subplots_adjust(top=1, bottom=0, hspace=0.4, wspace=0.1)
plt.show()
В ходе анализа работы моделей машинного обучения на задаче классификации с использованием матриц ошибок, можно отметить следующие ключевые моменты:
Logistic Regression и Random Forest показали хорошие результаты в классификации.
K-Nearest Neighbors (KNN) также демонстрирует удовлетворительные результаты. Количество правильных классификаций значительно превышает ошибки. Однако модель ошибается в классификации между классами "EN", "EX", "MI" и "SE", что проявляется в нескольких ложных отрицательных и ложных положительных результатах. Эти ошибки малозначительны.
Пример использования обученной модели для предсказания
Нстройка гиперпараметров для регрессии
param_grid_rf = {
'regressor__n_estimators': [100, 200],
'regressor__max_depth': [10, 20]
}
param_grid_gb = {
'regressor__n_estimators': [100, 200],
'regressor__learning_rate': [0.01, 0.1]
}
# Настройка для Random Forest
grid_search_rf = GridSearchCV(pipelines_reg["Random Forest"], param_grid_rf, cv=3, scoring='neg_mean_squared_error', n_jobs=-1)
grid_search_rf.fit(X_train, y_train)
# Настройка для Gradient Boosting
grid_search_gb = GridSearchCV(pipelines_reg["Gradient Boosting"], param_grid_gb, cv=3, scoring='neg_mean_squared_error', n_jobs=-1)
grid_search_gb.fit(X_train, y_train)
Настрайка гиперпараметров для классификации
param_grid_knn = {
'classifier__n_neighbors': [3, 5, 7]
}
# Настройка для KNN
grid_search_knn = GridSearchCV(pipelines_clf["KNN"], param_grid_knn, cv=3, scoring='accuracy', n_jobs=-1)
grid_search_knn.fit(X_train_clf, y_train_clf)
Оценка качества моделей для регрессии
print("Оценка качества моделей для регрессии:")
for name, pipeline in pipelines_reg.items():
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(f"{name}:")
print(f"MSE: {mean_squared_error(y_test, y_pred)}")
print(f"R^2: {r2_score(y_test, y_pred)}")
print(f"MAE: {mean_absolute_error(y_test, y_pred)}")
print()
Оценка качества моделей для классификации
print("Оценка качества моделей для классификации:")
for name, pipeline in pipelines_clf.items():
pipeline.fit(X_train_clf, y_train_clf)
y_pred = pipeline.predict(X_test_clf)
print(f"{name}:")
print(f"Accuracy: {accuracy_score(y_test_clf, y_pred)}")
print(f"Precision: {precision_score(y_test_clf, y_pred, average='weighted')}")
print(f"Recall: {recall_score(y_test_clf, y_pred, average='weighted')}")
print(f"F1-Score: {f1_score(y_test_clf, y_pred, average='weighted')}")
print()
Оценка смещения и дисперсии
print("Оценка смещения и дисперсии для регрессии:")
for name, pipeline in pipelines_reg.items():
scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring='neg_mean_squared_error')
print(f"{name}: MSE = {-scores.mean()} (+/- {scores.std()})")
print("\nОценка смещения и дисперсии для классификации:")
for name, pipeline in pipelines_clf.items():
scores = cross_val_score(pipeline, X_train_clf, y_train_clf, cv=5, scoring='accuracy')
print(f"{name}: Accuracy = {scores.mean()} (+/- {scores.std()})")
Сравнение моделей со старыми гиперпараметрами и новыми
# Лучшая модель для Random Forest
best_rf_model = grid_search_rf.best_estimator_
best_rf_params = grid_search_rf.best_params_
best_rf_mse = -grid_search_rf.best_score_ # Меняем знак, так как MSE был отрицательным
# Лучшая модель для Gradient Boosting
best_gb_model = grid_search_gb.best_estimator_
best_gb_params = grid_search_gb.best_params_
best_gb_mse = -grid_search_gb.best_score_ # Меняем знак, так как MSE был отрицательным
# Лучшая модель для KNN
best_knn_model = grid_search_knn.best_estimator_
best_knn_params = grid_search_knn.best_params_
best_knn_accuracy = grid_search_knn.best_score_
# Прогнозирование для тестовой выборки (с новыми гиперпараметрами)
y_pred_rf = best_rf_model.predict(X_test)
y_pred_gb = best_gb_model.predict(X_test)
y_pred_knn = best_knn_model.predict(X_test_clf)
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
]
)
# Старый RandomForest
old_rf_model = Pipeline([
('preprocessor', preprocessor),
('regressor', RandomForestRegressor(n_estimators=50, max_depth=5)) # Старые гиперпараметры
])
old_rf_model.fit(X_train, y_train)
# Старый Gradient Boosting
old_gb_model = Pipeline([
('preprocessor', preprocessor),
('regressor', GradientBoostingRegressor(n_estimators=50, learning_rate=0.1)) # Старые гиперпараметры
])
old_gb_model.fit(X_train, y_train)
# Старый KNN
old_knn_model = Pipeline([
('preprocessor', preprocessor),
('classifier', KNeighborsClassifier(n_neighbors=5)) # Старые гиперпараметры
])
old_knn_model.fit(X_train_clf, y_train_clf)
# Прогнозирование для старых моделей
y_oldpred_rf = old_rf_model.predict(X_test)
y_oldpred_gb = old_gb_model.predict(X_test)
y_oldpred_knn = old_knn_model.predict(X_test_clf)
# Прогнозирование для старых параметров (нужно использовать старые модели)
y_oldpred_rf = old_rf_model.predict(X_test) # Старое обучение для Random Forest
y_oldpred_gb = old_gb_model.predict(X_test) # Старое обучение для Gradient Boosting
y_oldpred_knn = old_knn_model.predict(X_test_clf) # Старое обучение для KNN
# Метрики для сравнения
# MSE для Random Forest
mse_rf_new = mean_squared_error(y_test, y_pred_rf)
mse_rf_old = mean_squared_error(y_test, y_oldpred_rf)
# MSE для Gradient Boosting
mse_gb_new = mean_squared_error(y_test, y_pred_gb)
mse_gb_old = mean_squared_error(y_test, y_oldpred_gb)
# Точность для KNN
accuracy_knn_new = accuracy_score(y_test_clf, y_pred_knn)
accuracy_knn_old = accuracy_score(y_test_clf, y_oldpred_knn)
# Вывод результатов
print(f"Лучшие параметры для Random Forest: {best_rf_params}")
print(f"Лучший MSE для Random Forest: {best_rf_mse}")
print(f"MSE для Random Forest с новыми параметрами: {mse_rf_new}")
print(f"MSE для Random Forest со старыми параметрами: {mse_rf_old}")
print()
print(f"Лучшие параметры для Gradient Boosting: {best_gb_params}")
print(f"Лучший MSE для Gradient Boosting: {best_gb_mse}")
print(f"MSE для Gradient Boosting с новыми параметрами: {mse_gb_new}")
print(f"MSE для Gradient Boosting со старыми параметрами: {mse_gb_old}")
print()
print(f"Лучшие параметры для KNN: {best_knn_params}")
print(f"Точность для KNN с новыми параметрами: {accuracy_knn_new}")
print(f"Точность для KNN со старыми параметрами: {accuracy_knn_old}")
Визуализация результатов
# График для регрессии
plt.figure(figsize=(10, 6))
plt.scatter(y_test, pipelines_reg["Gradient Boosting"].predict(X_test), alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)
plt.xlabel('Реальные значения')
plt.ylabel('Предсказанные значения')
plt.title('Реальные vs Предсказанные значения зарплаты (Gradient Boosting)')
plt.show()
plt.figure(figsize=(10, 5))
# График для Random Forest
plt.figure(figsize=(10, 5))
plt.scatter(range(len(y_test)), y_test, label="Актуальные значения", color="black", alpha=0.5, s=10)
plt.scatter(range(len(y_test)), y_pred_rf, label="Предсказанные (Random Forest, новые параметры)", color="blue", alpha=0.5, s=10)
plt.scatter(range(len(y_test)), y_oldpred_rf, label="Предсказанные (Random Forest, старые параметры)", color="red", alpha=0.5, s=10)
plt.xlabel("Выборка")
plt.ylabel("Значения")
plt.legend()
plt.title("Random Forest: Актуальные vs Предсказанные значения")
plt.show()
# График для Gradient Boosting
plt.figure(figsize=(10, 5))
plt.scatter(range(len(y_test)), y_test, label="Актуальные значения", color="black", alpha=0.5, s=10)
plt.scatter(range(len(y_test)), y_pred_gb, label="Предсказанные (Gradient Boosting, новые параметры)", color="green", alpha=0.5, s=10)
plt.scatter(range(len(y_test)), y_oldpred_gb, label="Предсказанные (Gradient Boosting, старые параметры)", color="orange", alpha=0.5, s=10)
plt.xlabel("Выборка")
plt.ylabel("Значения")
plt.legend()
plt.title("Gradient Boosting: Актуальные vs Предсказанные значения")
plt.show()
# График для KNN
plt.figure(figsize=(10, 5))
plt.scatter(range(len(y_test_clf)), y_test_clf, label="Актуальные значения", color="black", alpha=0.5, s=10)
plt.scatter(range(len(y_test_clf)), y_pred_knn, label="Предсказанные (KNN, новые параметры)", color="purple", alpha=0.5, s=10)
plt.scatter(range(len(y_test_clf)), y_oldpred_knn, label="Предсказанные (KNN, старые параметры)", color="yellow", alpha=0.5, s=10)
plt.xlabel("Выборка")
plt.ylabel("Значения")
plt.legend()
plt.title("KNN: Актуальные vs Предсказанные значения")
plt.show()