Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab3_3352_ГарееваКР.docx
Скачиваний:
0
Добавлен:
24.01.2026
Размер:
60.95 Mб
Скачать

Метод дерева решений для классификации прочности бетона

Программа:

print("МЕТОД ДЕРЕВА РЕШЕНИЙ ДЛЯ КЛАССИФИКАЦИИ ПРОЧНОСТИ БЕТОНА")

max_depth_range = range(1, 21)

train_scores_tree = []

test_scores_tree = []

print("Поиск оптимальной глубины дерева...")

for depth in max_depth_range:

tree = DecisionTreeClassifier(max_depth=depth, random_state=42)

tree.fit(X_train_scaled, y_train)

train_score = tree.score(X_train_scaled, y_train)

test_score = tree.score(X_test_scaled, y_test)

train_scores_tree.append(train_score)

test_scores_tree.append(test_score)

if depth % 5 == 0:

print(f" Глубина={depth:2d}: Train accuracy={train_score:.4f}, Test accuracy={test_score:.4f}")

plt.figure(figsize=(14, 5))

plt.subplot(1, 2, 1)

plt.plot(max_depth_range, train_scores_tree, label='Обучающая выборка', marker='o', linewidth=2, markersize=6, color='#FF6B6B')

plt.plot(max_depth_range, test_scores_tree, label='Тестовая выборка', marker='s', linewidth=2, markersize=6, color='#4ECDC4')

plt.xlabel('Максимальная глубина дерева', fontsize=12)

plt.ylabel('Accuracy', fontsize=12)

plt.title('Зависимость точности от глубины дерева', fontsize=14, fontweight='bold')

plt.legend()

plt.grid(True, alpha=0.3)

plt.xticks(max_depth_range[::2])

optimal_depth = max_depth_range[np.argmax(test_scores_tree)]

optimal_score = test_scores_tree[np.argmax(test_scores_tree)]

plt.axvline(x=optimal_depth, color='red', linestyle='--', alpha=0.7)

plt.scatter(optimal_depth, optimal_score, color='red', s=100, zorder=5)

plt.text(optimal_depth+0.5, optimal_score-0.05, f'Глубина={optimal_depth}\nAcc={optimal_score:.3f}',

fontweight='bold', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))

plt.subplot(1, 2, 2)

gap_tree = [train - test for train, test in zip(train_scores_tree, test_scores_tree)]

plt.plot(max_depth_range, gap_tree, marker='o', linewidth=2, color='#45B7D1')

plt.fill_between(max_depth_range, gap_tree, alpha=0.3, color='#45B7D1')

plt.xlabel('Максимальная глубина дерева', fontsize=12)

plt.ylabel('Разность (Train - Test)', fontsize=12)

plt.title('Переобучение дерева решений', fontsize=14, fontweight='bold')

plt.grid(True, alpha=0.3)

plt.xticks(max_depth_range[::2])

plt.axhline(y=0, color='black', linestyle='-', alpha=0.3)

plt.tight_layout()

plt.show()

print(f"\nРЕЗУЛЬТАТЫ ПОИСКА ОПТИМАЛЬНОЙ ГЛУБИНЫ:")

print(f"Оптимальная глубина дерева: {optimal_depth}")

print(f"Максимальная accuracy на тестовой выборке: {optimal_score:.4f}")

print(f"\nПри глубине={optimal_depth}:")

print(f" Обучающая выборка: {train_scores_tree[optimal_depth-1]:.4f}")

print(f" Тестовая выборка: {test_scores_tree[optimal_depth-1]:.4f}")

print(f" Разница (переобучение): {train_scores_tree[optimal_depth-1] - test_scores_tree[optimal_depth-1]:.4f}")

print(f"\nОБУЧЕНИЕ ДЕРЕВА РЕШЕНИЙ С ГЛУБИНОЙ={optimal_depth}:")

tree_model = DecisionTreeClassifier(max_depth=optimal_depth, random_state=42)

tree_model.fit(X_train_scaled, y_train)

y_pred_tree = tree_model.predict(X_test_scaled)

y_pred_proba_tree = tree_model.predict_proba(X_test_scaled)

accuracy_tree = accuracy_score(y_test, y_pred_tree)

precision_tree = precision_score(y_test, y_pred_tree, average='weighted')

recall_tree = recall_score(y_test, y_pred_tree, average='weighted')

f1_tree = f1_score(y_test, y_pred_tree, average='weighted')

print("\nМЕТРИКИ КАЧЕСТВА:")

print(f"Accuracy: {accuracy_tree:.4f}")

print(f"Precision: {precision_tree:.4f}")

print(f"Recall: {recall_tree:.4f}")

print(f"F1-Score: {f1_tree:.4f}")

print("\nДЕТАЛЬНЫЙ ОТЧЕТ ПО КЛАССАМ:")

print(classification_report(y_test, y_pred_tree,

target_names=[str(cls) for cls in le_strength.classes_]))

print("\nМАТРИЦА ОШИБОК:")

cm_tree = confusion_matrix(y_test, y_pred_tree)

cm_df_tree = pd.DataFrame(cm_tree,

index=[f'Истинный: {cls}' for cls in le_strength.classes_],

columns=[f'Предсказанный: {cls}' for cls in le_strength.classes_])

plt.figure(figsize=(8, 6))

sns.heatmap(cm_df_tree, annot=True, fmt='d', cmap='Greens', cbar=True,

square=True, linewidths=0.5, linecolor='gray')

plt.title(f'Матрица ошибок дерева решений (глубина={optimal_depth})',

fontsize=14, fontweight='bold')

plt.ylabel('Истинные классы', fontsize=12)

plt.xlabel('Предсказанные классы', fontsize=12)

plt.tight_layout()

plt.show()

if optimal_depth <= 5:

print("\nВИЗУАЛИЗАЦИЯ СТРУКТУРЫ ДЕРЕВА РЕШЕНИЙ:")

from sklearn.tree import plot_tree

plt.figure(figsize=(15, 8))

plot_tree(tree_model,

feature_names=features,

class_names=[str(cls) for cls in le_strength.classes_],

filled=True,

rounded=True,

fontsize=10,

max_depth=3) # Показываем только первые 3 уровня для читаемости

plt.title(f'Структура дерева решений (первые 3 уровня)', fontsize=14, fontweight='bold')

plt.tight_layout()

plt.show()

else:

print(f"\nДерево слишком глубокое (глубина={optimal_depth}) для визуализации всей структуры")

print("\nВАЖНОСТЬ ПРИЗНАКОВ В ДЕРЕВЕ РЕШЕНИЙ:")

feature_importances = tree_model.feature_importances_

importance_df = pd.DataFrame({

'Признак': features,

'Важность': feature_importances

}).sort_values('Важность', ascending=False)

print(importance_df.round(4).to_string(index=False))

plt.figure(figsize=(10, 6))

bars = plt.barh(range(len(importance_df)),

importance_df['Важность'],

color='#4ECDC4', alpha=0.7)

plt.yticks(range(len(importance_df)), importance_df['Признак'])

plt.xlabel('Важность признака', fontsize=12)

plt.ylabel('Признак', fontsize=12)

plt.title(f'Важность признаков в дереве решений (глубина={optimal_depth})',

fontsize=14, fontweight='bold')

plt.gca().invert_yaxis()

for i, (bar, importance) in enumerate(zip(bars, importance_df['Важность'])):

plt.text(importance + 0.01, bar.get_y() + bar.get_height()/2,

f'{importance:.3f}', va='center', fontweight='bold')

plt.grid(True, alpha=0.3, axis='x')

plt.tight_layout()

plt.show()

print("\nАНАЛИЗ ПОРОГОВЫХ ЗНАЧЕНИЙ ДЛЯ ВАЖНЫХ ПРИЗНАКОВ:")

top_features = importance_df.head(3)['Признак'].tolist()

print(f"Топ-3 важных признака: {top_features}")

for feature in top_features:

print(f"\n{feature}:")

feature_idx = features.index(feature)

tree = tree_model.tree_

threshold_values = []

for i in range(tree.node_count):

if tree.feature[i] == feature_idx:

threshold = tree.threshold[i]

threshold_values.append(threshold)

if threshold_values:

print(f" Количество порогов в дереве: {len(threshold_values)}")

print(f" Диапазон порогов: {min(threshold_values):.1f} - {max(threshold_values):.1f}")

print(f" Средний порог: {np.mean(threshold_values):.1f}")

print(f" Средние значения по классам:")

for cls_idx in range(len(le_strength.classes_)):

cls_name = le_strength.inverse_transform([cls_idx])[0]

cls_mask = (y_train == cls_idx)

if cls_mask.sum() > 0:

mean_value = X_train_scaled[cls_mask, feature_idx].mean()

print(f" '{cls_name}': {scaler.mean_[feature_idx] + mean_value*scaler.scale_[feature_idx]:.1f}")

else:

print(f" Этот признак не используется в дереве")

print("\nИССЛЕДОВАНИЕ ДРУГИХ ГИПЕРПАРАМЕТРОВ:")

min_samples_leaf_values = [1, 2, 5, 10, 20]

leaf_results = []

for min_samples in min_samples_leaf_values:

tree_leaf = DecisionTreeClassifier(max_depth=optimal_depth,

min_samples_leaf=min_samples,

random_state=42)

tree_leaf.fit(X_train_scaled, y_train)

leaf_score = tree_leaf.score(X_test_scaled, y_test)

leaf_results.append(leaf_score)

print(f" min_samples_leaf={min_samples:2d}: accuracy={leaf_score:.4f}")

best_min_samples = min_samples_leaf_values[np.argmax(leaf_results)]

print(f"\nЛучшее min_samples_leaf: {best_min_samples} (accuracy={max(leaf_results):.4f})")

print("АНАЛИЗ ДЕРЕВА РЕШЕНИЙ ЗАВЕРШЕН!")

Результат:

МЕТОД ДЕРЕВА РЕШЕНИЙ ДЛЯ КЛАССИФИКАЦИИ ПРОЧНОСТИ БЕТОНА

Поиск оптимальной глубины дерева...

Глубина= 5: Train accuracy=0.7850, Test accuracy=0.7411

Глубина=10: Train accuracy=0.9639, Test accuracy=0.7832

Глубина=15: Train accuracy=0.9945, Test accuracy=0.7994

Глубина=20: Train accuracy=0.9972, Test accuracy=0.7961

Промежуточный вывод:

Дерево решений демонстрирует меньший разрыв между обучающей и тестовой точностью (19.5% против 24.6% у kNN при k=1), что указывает на его лучшую способность выявлять истинные закономерности, а не запоминать данные.

Максимальная достигнутая тестовая accuracy для дерева (79.9% при глубине 15) превышает результат kNN на 4.8% (75.1% при k=1).

Рост тестовой accuracy дерева с увеличением глубины носит более устойчивый характер, выходя на "плато" после глубины 15, в то время как kNN уже при k=5 показывает стагнацию качества (~69%).

Для дерева решений найдена явная оптимальная глубина (~15), дальнейшее увеличение которой не улучшает качество на тестовых данных, что свидетельствует о разумном балансе между точностью и переобучением.

РЕЗУЛЬТАТЫ ПОИСКА ОПТИМАЛЬНОЙ ГЛУБИНЫ:

Оптимальная глубина дерева: 11

Максимальная accuracy на тестовой выборке: 0.8026

При глубине=11:

Обучающая выборка: 0.9750

Тестовая выборка: 0.8026

Разница (переобучение): 0.1724

ОБУЧЕНИЕ ДЕРЕВА РЕШЕНИЙ С ГЛУБИНОЙ=11:

МЕТРИКИ КАЧЕСТВА:

Accuracy: 0.8026

Precision: 0.8011

Recall: 0.8026

F1-Score: 0.8015

ДЕТАЛЬНЫЙ ОТЧЕТ ПО КЛАССАМ:

precision recall f1-score support

Высокая 0.84 0.89 0.86 114

Низкая 0.83 0.81 0.82 88

Средняя 0.74 0.71 0.72 107

accuracy 0.80 309

macro avg 0.80 0.80 0.80 309

weighted avg 0.80 0.80 0.80 309

МАТРИЦА ОШИБОК:

Дерево слишком глубокое (глубина=11) для визуализации всей структуры

ВАЖНОСТЬ ПРИЗНАКОВ В ДЕРЕВЕ РЕШЕНИЙ:

Признак Важность

Cement 0.2542

Age (day) 0.2447

Fine Aggregate 0.1223

Coarse Aggregate 0.1133

Water 0.0904

Blast Furnace Slag 0.0873

Superplasticizer 0.0664

Fly Ash 0.0214

АНАЛИЗ ПОРОГОВЫХ ЗНАЧЕНИЙ ДЛЯ ВАЖНЫХ ПРИЗНАКОВ:

Топ-3 важных признака: ['Cement', 'Age (day)', 'Fine Aggregate']

Cement:

Количество порогов в дереве: 20

Диапазон порогов: -1.2 - 2.3

Средний порог: 0.3

Средние значения по классам:

'Высокая': 330.7

'Низкая': 228.4

'Средняя': 266.9

Age (day):

Количество порогов в дереве: 20

Диапазон порогов: -0.7 - 2.7

Средний порог: -0.1

Средние значения по классам:

'Высокая': 74.9

'Низкая': 14.3

'Средняя': 44.2

Fine Aggregate:

Количество порогов в дереве: 19

Диапазон порогов: -2.0 - 1.8

Средний порог: -0.0

Средние значения по классам:

'Высокая': 766.1

'Низкая': 795.1

'Средняя': 767.7

ИССЛЕДОВАНИЕ ДРУГИХ ГИПЕРПАРАМЕТРОВ:

min_samples_leaf= 1: accuracy=0.8026

min_samples_leaf= 2: accuracy=0.7702

min_samples_leaf= 5: accuracy=0.7929

min_samples_leaf=10: accuracy=0.7249

min_samples_leaf=20: accuracy=0.7217

Лучшее min_samples_leaf: 1 (accuracy=0.8026)

АНАЛИЗ ДЕРЕВА РЕШЕНИЙ ЗАВЕРШЕН!

Промежуточные выводы:

Ведущие факторы прочности: Дерево подтвердило фундаментальные принципы — содержание цемента (важность 25.4%) и возраст образца (важность 24.5%) являются определяющими для классификации. Среднее содержание цемента закономерно возрастает от низкомарочных (228 кг/м³) к высокомарочным бетонам (331 кг/м³).

Количественные пороговые значения: Преобразование правил дерева в инженерные единицы показывает осмысленные технологические границы. Например, частое правило разделения около 310 кг цемента/м³ соответствует практическому опыту разграничения средних и высоких марок бетона.

Важность длительного твердения: Обнаружено, что образцы высокой прочности имеют значительно больший средний возраст (75 дней), чем стандартные 28 суток. Это указывает на существенную роль длительного набора прочности, что согласуется с теорией гидратации цемента.

Чувствительность к регуляризации: Исследование гиперпараметра min_samples_leaf показало, что даже небольшое его увеличение (до 2) приводит к значительной потере точности (-3.2%), что свидетельствует о сложной структуре данных и необходимости тонкой настройки модели для сохранения эффективности.