- •Распределение классов прочности
- •Визуализация распределения классов прочности бетона
- •Анализ сбалансированных классов
- •Визуализация взаимосвязей между признаками
- •Предобработка данных
- •Разделение данных и масштабирование
- •Визуализация распределения признаков:
- •Метод kNn для классификации прочности бетона
- •Метод дерева решений для классификации прочности бетона
- •Обучение и оценка дерева решений для классификации бетона
- •Обучение и оценка дерева решений
- •Визуализация дерева решений
- •Кривые для оценки качества классификации бетона
- •Визуализация roc-кривых для классификации прочности бетона
Кривые для оценки качества классификации бетона
Программа:
print("\nROC-КРИВЫЕ ДЛЯ ОЦЕНКИ КАЧЕСТВА КЛАССИФИКАЦИИ БЕТОНА")
if 'y_pred_proba_knn' not in locals() and 'y_pred_proba_knn' not in globals():
print("ОШИБКА: Вероятности предсказаний kNN не найдены")
print("Убедитесь, что модель kNN была обучена с predict_proba")
elif 'y_pred_proba_tree' not in locals() and 'y_pred_proba_tree' not in globals():
print("ОШИБКА: Вероятности предсказаний дерева не найдены")
print("Убедитесь, что модель дерева была обучена с predict_proba")
else:
print("✓ Вероятности предсказаний найдены для обеих моделей")
classes = np.unique(y_test)
y_test_bin = label_binarize(y_test, classes=classes)
n_classes = y_test_bin.shape[1]
print(f"\nПАРАМЕТРЫ АНАЛИЗА:")
print(f" Количество классов: {n_classes}")
print(f" Классы: {classes}")
print(f" Размер тестовой выборки: {len(y_test)}")
print("\nВЫЧИСЛЕНИЕ ROC-КРИВЫХ:")
fpr_knn = dict()
tpr_knn = dict()
roc_auc_knn = dict()
for i in range(n_classes):
fpr_knn[i], tpr_knn[i], _ = roc_curve(y_test_bin[:, i], y_pred_proba_knn[:, i])
roc_auc_knn[i] = auc(fpr_knn[i], tpr_knn[i])
print(f" kNN - Класс {i} (AUC = {roc_auc_knn[i]:.4f})")
fpr_tree = dict()
tpr_tree = dict()
roc_auc_tree = dict()
for i in range(n_classes):
fpr_tree[i], tpr_tree[i], _ = roc_curve(y_test_bin[:, i], y_pred_proba_tree[:, i])
roc_auc_tree[i] = auc(fpr_tree[i], tpr_tree[i])
print(f" Дерево - Класс {i} (AUC = {roc_auc_tree[i]:.4f})")
class_names = [str(cls) for cls in le_strength.classes_]
print("\nВИЗУАЛИЗАЦИЯ ROC-КРИВЫХ:")
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
ax1 = axes[0]
for i, color in zip(range(n_classes), colors):
ax1.plot(fpr_knn[i], tpr_knn[i], color=color, lw=2.5, alpha=0.8,
label=f'{class_names[i]} (AUC = {roc_auc_knn[i]:.2f})')
ax1.plot([0, 1], [0, 1], 'k--', lw=2, alpha=0.6)
ax1.set_xlim([0.0, 1.0])
ax1.set_ylim([0.0, 1.05])
ax1.set_xlabel('False Positive Rate (Ложноположительная доля)', fontsize=11)
ax1.set_ylabel('True Positive Rate (Истинноположительная доля)', fontsize=11)
ax1.set_title('ROC-кривые - kNN\n(Метод k-ближайших соседей)',
fontsize=13, fontweight='bold', pad=15)
ax1.legend(loc="lower right", fontsize=10)
ax1.grid(True, alpha=0.3)
mean_auc_knn = np.mean(list(roc_auc_knn.values()))
ax1.text(0.6, 0.1, f'Средний AUC: {mean_auc_knn:.3f}',
transform=ax1.transAxes, fontsize=11,
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
ax2 = axes[1]
for i, color in zip(range(n_classes), colors):
ax2.plot(fpr_tree[i], tpr_tree[i], color=color, lw=2.5, alpha=0.8,
label=f'{class_names[i]} (AUC = {roc_auc_tree[i]:.2f})')
ax2.plot([0, 1], [0, 1], 'k--', lw=2, alpha=0.6)
ax2.set_xlim([0.0, 1.0])
ax2.set_ylim([0.0, 1.05])
ax2.set_xlabel('False Positive Rate (Ложноположительная доля)', fontsize=11)
ax2.set_ylabel('True Positive Rate (Истинноположительная доля)', fontsize=11)
ax2.set_title('ROC-кривые - Дерево решений\n(Метод дерева решений)',
fontsize=13, fontweight='bold', pad=15)
ax2.legend(loc="lower right", fontsize=10)
ax2.grid(True, alpha=0.3)
# Добавляем информацию о качестве
mean_auc_tree = np.mean(list(roc_auc_tree.values()))
ax2.text(0.6, 0.1, f'Средний AUC: {mean_auc_tree:.3f}',
transform=ax2.transAxes, fontsize=11,
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.suptitle('СРАВНЕНИЕ ROC-КРИВЫХ ДЛЯ КЛАССИФИКАЦИИ ПРОЧНОСТИ БЕТОНА',
fontsize=15, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()
print("\nСРАВНИТЕЛЬНЫЙ АНАЛИЗ AUC:")
print("=" * 40)
print(f"\n{'Класс':<15} {'kNN AUC':<12} {'Дерево AUC':<12} {'Разница':<10}")
print("-" * 55)
for i, cls_name in enumerate(class_names):
knn_auc = roc_auc_knn[i]
tree_auc = roc_auc_tree[i]
diff = knn_auc - tree_auc
diff_str = f"{diff:+.3f}"
if knn_auc > tree_auc:
best = "✓ kNN"
elif tree_auc > knn_auc:
best = "✓ Дерево"
else:
best = "≡ Одинаково"
print(f"{cls_name:<15} {knn_auc:<12.3f} {tree_auc:<12.3f} {diff_str:<10} {best}")
print("-" * 55)
print(f"{'Средний AUC':<15} {mean_auc_knn:<12.3f} {mean_auc_tree:<12.3f} "
f"{mean_auc_knn - mean_auc_tree:+.3f}")
print(f"\nИНТЕРПРЕТАЦИЯ AUC:")
def interpret_auc(auc_value):
if auc_value >= 0.9:
return "Отличное качество"
elif auc_value >= 0.8:
return "Хорошее качество"
elif auc_value >= 0.7:
return "Удовлетворительное качество"
elif auc_value >= 0.6:
return "Плохое качество"
else:
return "Очень плохое качество (не лучше случайного)"
print(f"kNN (средний AUC = {mean_auc_knn:.3f}): {interpret_auc(mean_auc_knn)}")
print(f"Дерево (средний AUC = {mean_auc_tree:.3f}): {interpret_auc(mean_auc_tree)}")
print(f"\nДОПОЛНИТЕЛЬНЫЙ АНАЛИЗ:")
y_test_bin_flat = y_test_bin.ravel()
y_pred_proba_knn_flat = y_pred_proba_knn.ravel()
y_pred_proba_tree_flat = y_pred_proba_tree.ravel()
fpr_knn_micro, tpr_knn_micro, _ = roc_curve(y_test_bin_flat, y_pred_proba_knn_flat)
roc_auc_knn_micro = auc(fpr_knn_micro, tpr_knn_micro)
fpr_tree_micro, tpr_tree_micro, _ = roc_curve(y_test_bin_flat, y_pred_proba_tree_flat)
roc_auc_tree_micro = auc(fpr_tree_micro, tpr_tree_micro)
print(f"Микро-усредненный AUC:")
print(f" kNN: {roc_auc_knn_micro:.4f}")
print(f" Дерево: {roc_auc_tree_micro:.4f}")
fig, ax = plt.subplots(figsize=(10, 8))
ax.plot(fpr_knn_micro, tpr_knn_micro,
label=f'kNN микро-усредненный (AUC = {roc_auc_knn_micro:.2f})',
color='blue', lw=3, alpha=0.7)
ax.plot(fpr_tree_micro, tpr_tree_micro,
label=f'Дерево микро-усредненный (AUC = {roc_auc_tree_micro:.2f})',
color='green', lw=3, alpha=0.7)
for i, color in zip(range(n_classes), colors):
ax.plot(fpr_knn[i], tpr_knn[i], color=color, lw=1, alpha=0.3, linestyle='--')
ax.plot(fpr_tree[i], tpr_tree[i], color=color, lw=1, alpha=0.3, linestyle='--')
ax.plot([0, 1], [0, 1], 'k--', lw=2, alpha=0.6, label='Случайный классификатор')
ax.set_xlim([0.0, 1.0])
ax.set_ylim([0.0, 1.05])
ax.set_xlabel('False Positive Rate', fontsize=12)
ax.set_ylabel('True Positive Rate', fontsize=12)
ax.set_title('Микро-усредненные ROC-кривые с индивидуальными кривыми классов',
fontsize=14, fontweight='bold', pad=15)
ax.legend(loc="lower right", fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\nВЫВОДЫ И РЕКОМЕНДАЦИИ ДЛЯ БЕТОНА:")
print("=" * 40)
if mean_auc_knn > mean_auc_tree:
print("kNN показывает лучшее качество по ROC-кривым")
print(" Модель лучше различает классы прочности бетона")
print(" Рекомендуется использовать kNN для точной классификации")
elif mean_auc_tree > mean_auc_knn:
print("Дерево решений показывает лучшее качество по ROC-кривым")
print(" Модель лучше разделяет классы по пороговым значениям")
print(" Рекомендуется использовать дерево для интерпретируемости")
else:
print("Обе модели показывают схожее качество по ROC-кривым")
print(" Выбор модели зависит от конкретной задачи")
print(f"\nПРАКТИЧЕСКОЕ ПРИМЕНЕНИЕ:")
print("-" * 30)
print("• AUC > 0.9: Модель отлично подходит для контроля качества бетона")
print("• AUC 0.8-0.9: Модель можно использовать в производстве")
print("• AUC 0.7-0.8: Требуется дополнительная настройка или данные")
print("• AUC < 0.7: Модель не рекомендуется для практического применения")
print(f"\n" + "=" * 70)
print("АНАЛИЗ ROC-КРИВЫХ ЗАВЕРШЕН!")
print("=" * 70)
Результат:
ROC-КРИВЫЕ ДЛЯ ОЦЕНКИ КАЧЕСТВА КЛАССИФИКАЦИИ БЕТОНА
Вероятности предсказаний найдены для обеих моделей
ПАРАМЕТРЫ АНАЛИЗА:
Количество классов: 3
Классы: [0 1 2]
Размер тестовой выборки: 309
ВЫЧИСЛЕНИЕ ROC-КРИВЫХ:
kNN - Класс 0 (AUC = 0.8570)
kNN - Класс 1 (AUC = 0.8343)
kNN - Класс 2 (AUC = 0.7490)
Дерево - Класс 0 (AUC = 0.9006)
ROC-анализ независимо подтверждает вывод, сделанный ранее по F1-score. Класс "Средняя" прочность является наиболее сложным для дискриминации, вероятно, из-за перекрытия его характеристик с соседними классами. Однако дерево справляется с этой задачей заметно лучше (AUC выше на 0.04).
