385 KiB
Лабораторная работа №4¶
Определение бизнес-целей для решения задач регрессии и классификации¶
Вариант задания: Набор данных о ценах на акции Starbucks.
Бизнес-цели:
Регрессия: Предсказание цены закрытия акции (Close) на основе исторических данных.
Классификация: Определение направления изменения цены (повышение или понижение) на следующий день, что можно выразить в бинарной метке (например, 1 — цена повысилась, 0 — снизилась). Метка будет рассчитываться как разница между Close сегодняшнего и завтрашнего дня.
Столбцы датасета и их пояснение:
Date - Дата, на которую относятся данные. Эта характеристика указывает конкретный день, в который происходила торговля акциями Starbucks.
Open - Цена открытия. Стоимость акций Starbucks в начале торгового дня. Это важный показатель, который показывает, по какой цене начались торги в конкретный день, и часто используется для сравнения с ценой закрытия для определения дневного тренда.
High - Максимальная цена за день. Наибольшая цена, достигнутая акциями Starbucks в течение торгового дня. Эта характеристика указывает, какой была самая высокая стоимость акций за день.
Low - Минимальная цена за день. Наименьшая цена, по которой торговались акции Starbucks в течение дня.
Close - Цена закрытия. Стоимость акций Starbucks в конце торгового дня. Цена закрытия — один из основных показателей, используемых для анализа акций, так как она отображает итоговую стоимость акций за день и часто используется для расчета дневных изменений и трендов на длительных временных периодах.
Adj Close - Скорректированная цена закрытия. Цена закрытия, скорректированная с учетом всех корпоративных действий.
Volume - Объем торгов. Количество акций Starbucks, проданных и купленных в течение дня.
Подготовим рабочее окружение для анализа и построения моделей, а так же проверим на пустые значения
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.tree import DecisionTreeRegressor, DecisionTreeClassifier
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from sklearn.metrics import mean_squared_error, f1_score, accuracy_score, roc_auc_score, confusion_matrix, classification_report
df = pd.read_csv("..//static//csv//StarbucksDataset.csv")
print(df.head())
print(df.columns)
display(df.head(15))
print(df.isnull().sum())
Выберем три модели для задач регрессии и классификации¶
Сделаем выбор подходящих моделей для решения задач классификации и регрессии на основе анализа данных и целей.
Для регрессии выберем:
- LinearRegression
- DecisionTreeRegressor
- GradientBoostingRegressor
Для классификации выберем:
- LogisticRegression
- RandomForestClassifier
- GradientBoostingClassifier
Разбиение на выборки и создание ориентира для задач регрессии¶
Мы будем использовать подход к задаче регрессии, где целевой переменной будет выступать цена закрытия акции, а другие характеристики выбраны в качестве признаков.
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# Определяем признаки и целевой признак для задачи регрессии
features = ['Date', 'Open', 'High', 'Low', 'Adj Close', 'Volume']
target = 'Close' # Целевая переменная
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2, random_state=42)
print("Размер обучающей выборки:", X_train.shape)
print("Размер тестовой выборки:", X_test.shape)
baseline_predictions = [y_train.mean()] * len(y_test)
print('Baseline MAE:', mean_absolute_error(y_test, baseline_predictions))
print('Baseline MSE:', mean_squared_error(y_test, baseline_predictions))
print('Baseline R²:', r2_score(y_test, baseline_predictions))
Построение конвейера и обучение моделей для задач регрессии¶
Построим конвейер где проведем обучение моделей, а так же переделаем характеристику 'Date' под числовые данные.
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
# Извлечение признаков из даты
df['Year'] = df['Date'].dt.year
df['Month'] = df['Date'].dt.month
df['Day'] = df['Date'].dt.day
categorical_features = []
numeric_features = ['Year', 'Month', 'Day', 'Open', 'High', 'Low', 'Adj Close', 'Volume']
target = 'Close'
features = numeric_features + categorical_features
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2, random_state=42)
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numeric_features),
('cat', OneHotEncoder(), categorical_features)],
remainder='passthrough')
pipeline_linear_regression = Pipeline(steps=[
('preprocessor', preprocessor),
('regressor', LinearRegression())
])
pipeline_decision_tree = Pipeline(steps=[
('preprocessor', preprocessor),
('regressor', DecisionTreeRegressor(random_state=42))
])
pipeline_gradient_boosting = Pipeline(steps=[
('preprocessor', preprocessor),
('regressor', GradientBoostingRegressor(random_state=42))
])
pipelines = [
('Linear Regression', pipeline_linear_regression),
('Decision Tree', pipeline_decision_tree),
('Gradient Boosting', pipeline_gradient_boosting)
]
for name, pipeline in pipelines:
pipeline.fit(X_train, y_train)
print(f"Model: {name} trained.")
Оценка качества моделей для регрессии¶
Оценим качество моделей для решения задач регресси и обоснуем выбор метрик.
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
for name, pipeline in pipelines:
y_pred = pipeline.predict(X_test)
print(f"Model: {name}")
print('MAE:', mean_absolute_error(y_test, y_pred))
print('MSE:', mean_squared_error(y_test, y_pred))
print('R²:', r2_score(y_test, y_pred))
print()
В качестве метрик для оценки качества регрессионных моделей выбраны:
MAE (Mean Absolute Error) — средняя абсолютная ошибка. Она измеряет среднюю величину отклонений предсказанных значений от фактических, что позволяет понять, насколько в среднем модель ошибается. MAE удобна для интерпретации, так как измеряется в тех же единицах, что и целевая переменная.
MSE (Mean Squared Error) — среднеквадратичная ошибка, которая учитывает квадраты ошибок, что увеличивает вес больших ошибок по сравнению с MAE. Это полезно, когда нам нужно сильнее штрафовать крупные отклонения.
R² (коэффициент детерминации) — доля объясненной дисперсии, которая показывает, насколько хорошо модель объясняет изменчивость целевой переменной. Значение R² близкое к 1 указывает на высокую точность модели, а отрицательные значения — на низкое качество, когда модель хуже, чем простое усреднение.
Анализ метрик для моделей:
Линейная регрессия: MAE и MSE близки к нулю, а R² почти 1 (0.9999), что указывает на высокое качество предсказаний и низкое смещение. Это значит, что линейная регрессия хорошо подходит для данной задачи, объясняя почти всю дисперсию данных.
Дерево решений: MAE и MSE немного выше, чем у линейной регрессии, а R² всё ещё очень высок (0.9998). Хотя дерево решений немного уступает линейной модели в точности, оно способно находить нелинейные зависимости, которые могут улучшить результат в более сложных сценариях.
Градиентный бустинг: MAE и MSE также несколько выше, чем у линейной регрессии, но R² (0.9998) остаётся на высоком уровне. Градиентный бустинг обычно справляется лучше в задачах с более сложными нелинейными зависимостями. В данном случае его результаты аналогичны дереву решений, но не превосходят линейную регрессию.
Вывод
Поскольку R² для всех моделей близок к 1, каждая модель справляется с задачей на высоком уровне. Тем не менее, линейная регрессия имеет наименьшие значения MAE и MSE, что указывает на её лучшее соответствие данным.
Разбиение на выборки и создание ориентира для задач классификации¶
Мы будем использовать подход к задаче регрессии, где целевой переменной будет выступать цена закрытия акции, а другие характеристики выбраны в качестве признаков.
import pandas as pd
from sklearn.model_selection import train_test_split
df = pd.read_csv("..//static//csv//StarbucksDataset.csv")
# Создание целевой переменной для классификации направления изменения цены
# Если цена закрытия следующего дня выше текущего дня — 1 (повышение), иначе — 0 (снижение)
df['Price_Up'] = (df['Close'].shift(-1) > df['Close']).astype(int)
features = ['Open', 'High', 'Low', 'Volume']
target = 'Price_Up'
# Удаление последней строки, так как для неё нет значения следующего дня
df = df.dropna()
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2, random_state=42)
print("Размер обучающей выборки:", X_train.shape)
print("Размер тестовой выборки:", X_test.shape)
Построение конвейера и обучение моделей для задач классификации¶
Построим конвейер где проведем обучение моделей, а так же создадим отдельную переменную 'Price_Up' для точного подсчета направления изменения цены (повышение или понижение) на следующий день.
import pandas as pd
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from scipy.stats import uniform, randint
features = ['Open', 'High', 'Low', 'Volume']
target = 'Price_Up'
X = df[features]
y = df[target]
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)
def evaluate_model(model, X_test, y_test):
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred_proba)
print(f"Accuracy: {accuracy:.4f}")
print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1-Score: {f1:.4f}")
print(f"ROC-AUC: {roc_auc:.4f}")
return {'accuracy': accuracy, 'precision': precision, 'recall': recall, 'f1': f1, 'roc_auc': roc_auc}
# Логистическая регрессия
logreg_pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', LogisticRegression(max_iter=1000, random_state=42))
])
logreg_param_dist = {
'classifier__C': uniform(loc=0, scale=4),
'classifier__penalty': ['l1', 'l2'],
'classifier__solver': ['liblinear', 'saga']
}
logreg_random_search = RandomizedSearchCV(logreg_pipeline, param_distributions=logreg_param_dist, n_iter=50, cv=5, random_state=42, n_jobs=-1)
logreg_random_search.fit(X_train, y_train)
logreg_best_model = logreg_random_search.best_estimator_
logreg_results = evaluate_model(logreg_best_model, X_test, y_test)
# Случайный лес
rf_pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', RandomForestClassifier(random_state=42))
])
rf_param_dist = {
'classifier__n_estimators': randint(100, 1000),
'classifier__max_depth': [None] + list(randint(10, 100).rvs(10)),
'classifier__min_samples_split': randint(2, 20),
'classifier__min_samples_leaf': randint(1, 20),
'classifier__bootstrap': [True, False]
}
rf_random_search = RandomizedSearchCV(rf_pipeline, param_distributions=rf_param_dist, n_iter=50, cv=5, random_state=42, n_jobs=-1)
rf_random_search.fit(X_train, y_train)
rf_best_model = rf_random_search.best_estimator_
rf_results = evaluate_model(rf_best_model, X_test, y_test)
# Градиентный бустинг
gb_pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', GradientBoostingClassifier(random_state=42))
])
gb_param_dist = {
'classifier__n_estimators': randint(100, 1000),
'classifier__learning_rate': uniform(0.01, 0.5),
'classifier__max_depth': [None] + list(randint(10, 100).rvs(10)),
'classifier__min_samples_split': randint(2, 20),
'classifier__min_samples_leaf': randint(1, 20),
'classifier__subsample': uniform(0.5, 0.5)
}
gb_random_search = RandomizedSearchCV(gb_pipeline, param_distributions=gb_param_dist, n_iter=50, cv=5, random_state=42, n_jobs=-1)
gb_random_search.fit(X_train, y_train)
gb_best_model = gb_random_search.best_estimator_
gb_results = evaluate_model(gb_best_model, X_test, y_test)
print("\nРезультаты моделей:")
print("\nLogistic Regression:")
for metric, value in logreg_results.items():
print(f"{metric.capitalize()}: {value:.4f}")
print("\nRandom Forest:")
for metric, value in rf_results.items():
print(f"{metric.capitalize()}: {value:.4f}")
print("\nGradient Boosting:")
for metric, value in gb_results.items():
print(f"{metric.capitalize()}: {value:.4f}")
Оценка качества моделей для классификации¶
Оценим качество моделей для решения задач классификации и обоснуем выбор метрик.
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, roc_curve, auc, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
sns.set(style="whitegrid")
def plot_confusion_matrix(y_true, y_pred, title):
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(6, 5))
ax = sns.heatmap(cm, annot=True, fmt='d', cmap='coolwarm', cbar=True, annot_kws={"size": 14})
plt.title(title, fontsize=16)
plt.xlabel('Предсказанные значения', fontsize=12)
plt.ylabel('Истинные значения', fontsize=12)
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
cbar = ax.collections[0].colorbar
cbar.set_label('Count', rotation=270, labelpad=20, fontsize=12)
plt.show()
def plot_roc_curve(y_true, y_pred_proba, title):
fpr, tpr, _ = roc_curve(y_true, y_pred_proba)
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='#FF6347', lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--', lw=1.5, label='Random Guess')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Показатель ложных положительных результатов', fontsize=12)
plt.ylabel('Показатель истинных положительных результатов', fontsize=12)
plt.title(title, fontsize=16)
plt.legend(loc="lower right", fontsize=10)
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()
def evaluate_and_plot_model(model, X_test, y_test, model_name):
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, pos_label=1)
recall = recall_score(y_test, y_pred, pos_label=1)
f1 = f1_score(y_test, y_pred, pos_label=1)
roc_auc = roc_auc_score(y_test, y_pred_proba)
print(f"\n{model_name} Metrics:")
print(f"Accuracy: {accuracy:.4f}")
print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1-Score: {f1:.4f}")
print(f"ROC-AUC: {roc_auc:.4f}")
plot_confusion_matrix(y_test, y_pred, f'Confusion Matrix for {model_name}')
plot_roc_curve(y_test, y_pred_proba, f'ROC Curve for {model_name}')
evaluate_and_plot_model(logreg_best_model, X_test, y_test, 'Logistic Regression')
evaluate_and_plot_model(rf_best_model, X_test, y_test, 'Random Forest')
evaluate_and_plot_model(gb_best_model, X_test, y_test, 'Gradient Boosting')
Вывод по результатам задач классификации
Результаты обучения моделей для задачи классификации направления изменения цены показали, что качество прогнозирования остаётся на уровне случайного угадывания.
Анализ метрик для моделей:
Логистическая регрессия: Данная модель показала точность (Accuracy) 0.5043 и F1-меру 0.4162. Значения Precision (0.5017) и Recall (0.3556) также указывают на трудности модели с корректной классификацией. ROC-AUC на уровне 0.5056 близок к случайному значению (0.5), что говорит о слабой предсказательной способности.
Случайный лес: Случайный лес продемонстрировал лучшие результаты по сравнению с логистической регрессией: точность 0.5302 и F1-меру 0.5252. Метрика ROC-AUC составила 0.5463, что также превышает уровень случайного угадывания, но не является достаточным показателем качества.
Градиентный бустинг: Градиентный бустинг показал схожие результаты со случайным лесом: точность 0.5302, F1-меру 0.5258, и ROC-AUC на уровне 0.5434. Данные значения говорят о том, что, несмотря на сложности задачи, эта модель на данный момент является наилучшей из предложенных.