- •Распределение классов прочности
- •Визуализация распределения классов прочности бетона
- •Анализ сбалансированных классов
- •Визуализация взаимосвязей между признаками
- •Предобработка данных
- •Разделение данных и масштабирование
- •Визуализация распределения признаков:
- •Метод kNn для классификации прочности бетона
- •Метод дерева решений для классификации прочности бетона
- •Обучение и оценка дерева решений для классификации бетона
- •Обучение и оценка дерева решений
- •Визуализация дерева решений
- •Кривые для оценки качества классификации бетона
- •Визуализация roc-кривых для классификации прочности бетона
Метод дерева решений для классификации прочности бетона
Программа:
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%), что свидетельствует о сложной структуре данных и необходимости тонкой настройки модели для сохранения эффективности.
