diff --git a/lab4/lab4.ipynb b/lab4/lab4.ipynb new file mode 100644 index 0000000..bea3dd4 --- /dev/null +++ b/lab4/lab4.ipynb @@ -0,0 +1,487 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Лабораторная работа 4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " 1. Выбрать бизнес-цели для набора данных

\n", + "\n", + "Классификация. Цель: определить, откликнется ли клиент на маркетинговую кампанию. Столбец целевой переменной - Response, 1 - откликнулся, 0 - нет. Признаки - Возраст, Уровень дохода. (Age, Income)
\n", + "\n", + "Регрессия. Цель: прогноз расходов клиента. Столбец целевой переменной: Total_Spending - общие расходы, будут считаться по всем расходам. Признаки такие же." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Определить достижимый уровень качества модели

\n", + "Классификация:
\n", + "Оценка метрики accuracy: ориентир 70-80% (с учетом ограниченных признаков).
\n", + "Регрессия:
\n", + "MSE (среднеквадратичная ошибка): минимизация, ориентир в зависимости от разброса целевой переменной.
\n", + "R^2 > 0.6" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3.Выбрать ориентир

\n", + "Классификация:
\n", + "DummyClassifier, предсказывающий самый частый класс, даст accuracy ~50-60%.
\n", + "Регрессия:
\n", + "Прогноз среднего значения целевой переменной." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_class_train: (1568, 2), y_class_train: (1568,)\n", + "X_reg_train: (1568, 2), y_reg_train: (1568,)\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "data = pd.read_csv(\"..//..//static//csv//marketing_campaign.csv\", sep=\"\\t\")\n", + "data2 = pd.read_csv(\"..//..//static//csv//marketing_campaign2.csv\", sep=\"\\t\")\n", + "\n", + "# Преобразуем данные для классификации (дата для отклика на кампанию)\n", + "data['Age'] = 2024 - data['Year_Birth'] \n", + "data = data[['Age', 'Income', 'Response']] \n", + "\n", + "X_class = data[['Age', 'Income']]\n", + "y_class = data['Response']\n", + "\n", + "# Преобразуем данные для регрессии (прогноз расходов)\n", + "data2['Age'] = 2024 - data2['Year_Birth'] \n", + "data2['Total_Spending'] = (data2['MntWines'] + data2['MntFruits'] + data2['MntMeatProducts'] +\n", + " data2['MntFishProducts'] + data2['MntSweetProducts'] + data2['MntGoldProds'])\n", + "data2 = data2[['Age', 'Income', 'Total_Spending']] \n", + "\n", + "# Разделение на признаки и целевую переменную для регрессии\n", + "X_reg = data2[['Age', 'Income']]\n", + "y_reg = data2['Total_Spending']\n", + "\n", + "# Масштабирование данных и преобразование обратно в DataFrame\n", + "scaler = StandardScaler()\n", + "X_class_scaled = pd.DataFrame(scaler.fit_transform(X_class), columns=X_class.columns)\n", + "X_reg_scaled = pd.DataFrame(scaler.fit_transform(X_reg), columns=X_reg.columns)\n", + "\n", + "# Разделение на тренировочные и тестовые выборки\n", + "X_train_class, X_test_class, y_train_class, y_test_class = train_test_split(X_class_scaled, y_class, test_size=0.3, random_state=42)\n", + "X_train_reg, X_test_reg, y_train_reg, y_test_reg = train_test_split(X_reg_scaled, y_reg, test_size=0.3, random_state=42)\n", + "\n", + "# Проверим, что все выглядит правильно\n", + "print(f\"X_class_train: {X_train_class.shape}, y_class_train: {y_train_class.shape}\")\n", + "print(f\"X_reg_train: {X_train_reg.shape}, y_reg_train: {y_train_reg.shape}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5-6. Выбрать не менее трёх моделей и пострить конвейер " + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Logistic Regression - Средняя точность модели: 0.8475 ± 0.0027\n", + "Random Forest - Средняя точность модели: 0.8267 ± 0.0090\n", + "SVM - Средняя точность модели: 0.8529 ± 0.0027\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn.model_selection import train_test_split, cross_val_score\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.svm import SVC\n", + "from sklearn.pipeline import Pipeline\n", + "\n", + "# Удаляем строки с пропущенными значениями\n", + "X_class_scaled = X_class_scaled.dropna()\n", + "y_class = y_class[X_class_scaled.index]\n", + "\n", + "models = [\n", + " ('Logistic Regression', LogisticRegression(max_iter=1000)),\n", + " ('Random Forest', RandomForestClassifier(n_estimators=100)),\n", + " ('SVM', SVC())\n", + "]\n", + "\n", + "# Создаем конвейер\n", + "imputer = SimpleImputer(strategy='mean') \n", + "scaler = StandardScaler()\n", + "\n", + "for name, model in models:\n", + " pipe = Pipeline([\n", + " ('imputer', imputer),\n", + " ('scaler', scaler),\n", + " ('classifier', model)\n", + " ])\n", + " \n", + " scores = cross_val_score(pipe, X_class_scaled, y_class, cv=5, scoring='accuracy')\n", + " print(f\"{name} - Средняя точность модели: {scores.mean():.4f} ± {scores.std():.4f}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Выбрал

\n", + "Imputer: Заполняет пропущенные значения средним (если они есть).
\n", + "Scaler: Масштабирует данные с помощью StandardScaler.
\n", + "Classifier: Используются три модели:
\n", + "LogisticRegression: Логистическая регрессия.
\n", + "RandomForestClassifier: Случайный лес.
\n", + "SVC: Метод опорных векторов (SVM)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "7. Реализовать настройку гиперпараметров

\n", + "\n", + "Делаем настройку гиперпараметров" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n", + "Logistic Regression - Лучшие гиперпараметры: {'classifier__C': 0.1, 'classifier__solver': 'lbfgs'}\n", + "Logistic Regression - Лучшая точность: 0.8484\n", + "--------------------------------------------------\n", + "Fitting 5 folds for each of 9 candidates, totalling 45 fits\n", + "Random Forest - Лучшие гиперпараметры: {'classifier__max_depth': 10, 'classifier__n_estimators': 100}\n", + "Random Forest - Лучшая точность: 0.8506\n", + "--------------------------------------------------\n", + "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n", + "SVM - Лучшие гиперпараметры: {'classifier__C': 1, 'classifier__kernel': 'rbf'}\n", + "SVM - Лучшая точность: 0.8529\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import GridSearchCV\n", + "\n", + "models = [\n", + " ('Logistic Regression', LogisticRegression(max_iter=1000), {'classifier__C': [0.1, 1, 10], 'classifier__solver': ['lbfgs', 'liblinear']}),\n", + " ('Random Forest', RandomForestClassifier(n_estimators=100), {'classifier__n_estimators': [50, 100, 200], 'classifier__max_depth': [10, 20, None]}),\n", + " ('SVM', SVC(), {'classifier__C': [0.1, 1, 10], 'classifier__kernel': ['linear', 'rbf']})\n", + "]\n", + "\n", + "for name, model, param_grid in models:\n", + " pipe = Pipeline([\n", + " ('imputer', imputer),\n", + " ('scaler', scaler),\n", + " ('classifier', model)\n", + " ])\n", + " \n", + " grid_search = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1, verbose=1)\n", + " grid_search.fit(X_class_scaled, y_class)\n", + "\n", + " print(f\"{name} - Лучшие гиперпараметры: {grid_search.best_params_}\")\n", + " print(f\"{name} - Лучшая точность: {grid_search.best_score_:.4f}\")\n", + " print(\"-\" * 50)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Проходим по моделям и настраиваем гиперпараметры с помощью GridSearchCV с помощью кросс-валидации.
\n", + "Параметры: cv=5: 5 фолдов для кросс-валидации.
\n", + "scoring='accuracy': Мы используем точность как метрику.
\n", + "n_jobs=-1: Используем все доступные процессоры для ускорения вычислений.
\n", + "verbose=1: Подробный вывод процесса." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "8. Обучить модели" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n", + "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n", + "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n" + ] + } + ], + "source": [ + "best_models = {} \n", + "for name, model, param_grid in models: \n", + " grid_search.fit(X_class_scaled, y_class)\n", + " best_models[name] = grid_search.best_estimator_ \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "9. Оценить качество моделей " + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score\n", + "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n", + "\n", + "# Оценка качества классификации\n", + "for name, model in best_models.items():\n", + " y_pred_class = model.predict(X_class_scaled) # Предсказание для классификации\n", + "\n", + " \n", + "# Оценка качества регрессии\n", + "for name, model in best_models.items():\n", + " y_pred_reg = model.predict(X_reg_scaled) # Предсказание для регрессии\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Вывод слишком длинный, приложу его тут

\n", + "Оценка качества для модели Logistic Regression:

\n", + "Accuracy: 0.8528880866425993
\n", + "Precision: 0.8181818181818182
\n", + "Recall: 0.02702702702702703
\n", + "F1-Score: 0.05232558139534884
\n", + "ROC AUC: Не поддерживается для этой модели
\n", + "
\n", + "
\n", + "Оценка качества для модели Random Forest:

\n", + "Accuracy: 0.8528880866425993
\n", + "Precision: 0.8181818181818182
\n", + "Recall: 0.02702702702702703
\n", + "F1-Score: 0.05232558139534884
\n", + "ROC AUC: Не поддерживается для этой модели
\n", + "
\n", + "
\n", + "Оценка качества для модели SVM:

\n", + "Accuracy: 0.8528880866425993
\n", + "Precision: 0.8181818181818182
\n", + "Recall: 0.02702702702702703
\n", + "F1-Score: 0.05232558139534884
\n", + "ROC AUC: Не поддерживается для этой модели
\n", + "
\n", + "
Задача регрессии:
\n", + "Оценка качества для модели Logistic Regression:

\n", + "MAE: 605.7982142857143
\n", + "MSE: 729533.7598214286
\n", + "RMSE: 854.1274845252485
\n", + "R²: -1.0122722045012051
\n", + "
\n", + "
\n", + "Оценка качества для модели Random Forest:

\n", + "MAE: 605.7982142857143
\n", + "MSE: 729533.7598214286
\n", + "RMSE: 854.1274845252485
\n", + "R²: -1.0122722045012051
\n", + "
\n", + "
\n", + "Оценка качества для модели SVM:

\n", + "MAE: 605.7982142857143
\n", + "MSE: 729533.7598214286
\n", + "RMSE: 854.1274845252485
\n", + "R²: -1.0122722045012051
\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Почему выбрал эти метирки:

Классификация (Отклик на предложение)
\n", + "Целевая переменная — бинарная (0 и 1), где 1 — откликнулся, а 0 — не откликнулся. Для классификации подходящими метриками являются:\n", + "

\n", + "Accuracy (Точность):

\n", + "Это доля правильно классифицированных объектов среди всех. \n", + "Подходит для оценки общей эффективности модели. Однако важно учитывать, что если классы несбалансированы, точность может быть обманчивой.

\n", + "Precision (Точность):\n", + "\n", + "Это доля истинных положительных случаев среди всех предсказанных положительных случаев.\n", + "Важна для задач, где важно минимизировать количество ложных срабатываний, например, когда модель ошибочно классифицирует клиента как откликнувшегося (True Positive).

\n", + "Recall (Полнота):

\n", + "\n", + "Это доля истинных положительных случаев среди всех истинных положительных случаев.\n", + "Важно для задач, где важно не пропустить откликнувшихся клиентов (False Negatives).

\n", + "F1-Score:

\n", + "\n", + "Это гармоническое среднее между точностью и полнотой.\n", + "Подходит для оценки моделей в случаях, когда важно иметь баланс между точностью и полнотой, особенно в ситуациях с несбалансированными классами.

\n", + "ROC AUC:

\n", + "Площадь под кривой ROC, которая отображает способность модели различать положительные и отрицательные классы.\n", + "Чем выше значение AUC, тем лучше модель справляется с разделением классов." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Регрессия (Прогноз расходов)

\n", + "Целевая переменная — это числовое значение (расходы клиента). Для задач регрессии используются другие метрики:

\n", + "\n", + "Mean Absolute Error (MAE):

\n", + "\n", + "Это средняя абсолютная ошибка предсказания.\n", + "Простой и интерпретируемый показатель, который описывает среднее отклонение предсказанных значений от фактических.

\n", + "Mean Squared Error (MSE):

\n", + "\n", + "Это средняя квадратичная ошибка.\n", + "Чувствителен к большим ошибкам, так как квадратичный штраф увеличивает вес больших отклонений, что полезно, если вы хотите минимизировать большие ошибки.

\n", + "Root Mean Squared Error (RMSE):

\n", + "\n", + "Это квадратный корень из MSE.\n", + "Подходит для задач, где важно учитывать большие ошибки, так как более чувствителен к выбросам.

\n", + "R-squared (R²):

\n", + "\n", + "Это коэффициент детерминации, который показывает, какая доля дисперсии целевой переменной объясняется моделью.\n", + "R² может быть полезен для оценки того, насколько хорошо модель объясняет вариацию целевой переменной, но не всегда подходит, если модель имеет много выбросов или некорректно подогнана.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "9. Оценить качество моделей" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluating model: Logistic Regression\n", + "Train Accuracy: 0.8501, Test Accuracy: 0.8631\n", + "Bias: 0.0045, Variance: 0.0104\n", + "Train Error (MSE): 732193.2283, Test Error (MSE): 723279.6414\n", + "Bias: 0.0045, Variance: 0.0104\n", + "Evaluating model: Random Forest\n", + "Train Accuracy: 0.8501, Test Accuracy: 0.8631\n", + "Bias: 0.0045, Variance: 0.0104\n", + "Train Error (MSE): 732193.2283, Test Error (MSE): 723279.6414\n", + "Bias: 0.0045, Variance: 0.0104\n", + "Evaluating model: SVM\n", + "Train Accuracy: 0.8501, Test Accuracy: 0.8631\n", + "Bias: 0.0045, Variance: 0.0104\n", + "Train Error (MSE): 732193.2283, Test Error (MSE): 723279.6414\n", + "Bias: 0.0045, Variance: 0.0104\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn.metrics import mean_squared_error, accuracy_score\n", + "\n", + "# Оценка смещения и дисперсии для классификации и регрессии\n", + "def evaluate_bias_variance(model, X_train, y_train, X_test, y_test, task='classification'):\n", + " # Прогнозы на обучающих и тестовых данных\n", + " y_train_pred = model.predict(X_train)\n", + " y_test_pred = model.predict(X_test)\n", + "\n", + " if task == 'classification':\n", + " # Для классификации считаем точность\n", + " train_accuracy = accuracy_score(y_train, y_train_pred)\n", + " test_accuracy = accuracy_score(y_test, y_test_pred)\n", + " print(f\"Train Accuracy: {train_accuracy:.4f}, Test Accuracy: {test_accuracy:.4f}\")\n", + " elif task == 'regression':\n", + " # Для регрессии считаем среднеквадратичную ошибку (MSE)\n", + " train_error = mean_squared_error(y_train, y_train_pred)\n", + " test_error = mean_squared_error(y_test, y_test_pred)\n", + " print(f\"Train Error (MSE): {train_error:.4f}, Test Error (MSE): {test_error:.4f}\")\n", + "\n", + " # Для оценки смещения и дисперсии на тестовых данных\n", + " bias = np.mean(y_test_pred - y_train_pred[:len(y_test_pred)]) # Смещение: разница между тестом и обучением\n", + " variance = np.var(y_test_pred - y_train_pred[:len(y_test_pred)]) # Дисперсия: варьирование прогнозов\n", + "\n", + " print(f\"Bias: {bias:.4f}, Variance: {variance:.4f}\")\n", + "\n", + "# Оценим для каждой из моделей\n", + "for name, model in best_models.items():\n", + " print(f\"Evaluating model: {name}\")\n", + " # Для классификации\n", + " evaluate_bias_variance(model, X_train_class, y_train_class, X_test_class, y_test_class, task='classification') \n", + " # Для регрессии\n", + " evaluate_bias_variance(model, X_train_reg, y_train_reg, X_test_reg, y_test_reg, task='regression') \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aimenv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}