F1-score в sklearn.metrics: оценка точности моделей машинного обучения

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • дата-сайентисты и аналитики данных
  • студенты и специалисты, обучающиеся машинному обучению
  • профессионалы, работающие с несбалансированными данными и моделями классификации

Выбор правильной метрики эффективности — тот рубеж, который отделяет провальные ML-проекты от успешных. F1-score выступает золотым стандартом оценки моделей классификации, особенно в условиях несбалансированных данных. Когда ни точность, ни полнота по отдельности не дают полной картины, F1-score объединяет их в единый показатель, позволяющий принимать взвешенные решения. Научиться грамотно оперировать этой метрикой с помощью библиотеки sklearn — незаменимый навык для каждого дата-сайентиста, который стремится создавать модели, работающие в реальных условиях. 📊🔍

Стремитесь стать экспертом в оценке моделей машинного обучения? Курс «Аналитик данных» с нуля от Skypro даст вам глубокое понимание не только F1-score, но и всего спектра метрик оценки моделей. Вы научитесь оптимизировать алгоритмы под конкретные бизнес-задачи, интерпретировать результаты и принимать обоснованные решения на основе данных. Более 87% выпускников уже применяют эти навыки в реальных проектах!

Основы F1-score и его место в оценке моделей ML

F1-score — гармоническое среднее между precision (точностью) и recall (полнотой), представляющее собой сбалансированную метрику для оценки моделей классификации. В отличие от простой точности (accuracy), F1-score учитывает как ложноположительные, так и ложноотрицательные результаты, что делает её особенно ценной для несбалансированных наборов данных. 🎯

Значение F1-score находится в диапазоне от 0 до 1, где:

  • 0 — наихудший возможный результат
  • 1 — идеальная модель с безупречной точностью и полнотой

В экосистеме метрик машинного обучения F1-score занимает особое место благодаря своей способности предоставлять сбалансированную оценку даже в сложных случаях.

Метрика Преимущества Недостатки Оптимальное применение
Accuracy Интуитивно понятна Обманчива для несбалансированных данных Сбалансированные классы
Precision Минимизирует ложноположительные Игнорирует ложноотрицательные Когда FP критичны (спам-фильтры)
Recall Минимизирует ложноотрицательные Игнорирует ложноположительные Когда FN критичны (медицина)
F1-score Баланс между precision и recall Не учитывает истинно отрицательные Несбалансированные данные

F1-score становится незаменимой в сценариях, где цена ошибки важна в обоих направлениях. Например, в медицинской диагностике, где одинаково опасно как пропустить заболевание (ложноотрицательный результат), так и поставить ложный диагноз (ложноположительный результат).

Алексей Петров, ведущий data scientist проекта диагностики онкозаболеваний В нашем проекте по автоматическому анализу медицинских снимков мы столкнулись с фундаментальной проблемой. Наша модель показывала впечатляющую точность 98%, но это была ловушка: в датасете было только 2% положительных случаев. По сути, алгоритм просто предсказывал "нет заболевания" для всех пациентов!

Переход на F1-score полностью изменил картину. Оказалось, что наша "успешная" модель имела F1-score всего 0.15. Это заставило нас пересмотреть архитектуру и баланс выборки. После оптимизации под F1-score мы достигли значения 0.87, что действительно отражало способность модели выявлять редкие, но критически важные случаи заболевания. В медицинском контексте это означало спасенные жизни, а не просто красивую цифру в отчете.

При выборе метрики оценки необходимо также учитывать специфику домена и последствия различных типов ошибок. В некоторых случаях F1-score может быть даже оптимизирован как целевая функция при обучении модели, а не только использоваться как метрика оценки постфактум.

Пошаговый план для смены профессии

Математический аппарат F1-score: precision и recall

Для глубокого понимания F1-score необходимо разобраться в его составляющих: precision (точность) и recall (полнота). Эти метрики основаны на четырех ключевых показателях матрицы ошибок: TP (True Positive), TN (True Negative), FP (False Positive) и FN (False Negative). 📐

Precision (точность) — доля объектов, действительно принадлежащих к положительному классу, среди всех объектов, которые модель отнесла к положительному классу:

Precision = TP / (TP + FP)

Recall (полнота) — доля объектов положительного класса, которые модель правильно идентифицировала среди всех объектов положительного класса в тестовой выборке:

Recall = TP / (TP + FN)

F1-score объединяет precision и recall через формулу гармонического среднего:

F1 = 2 * (Precision * Recall) / (Precision + Recall)

Гармоническое среднее используется вместо арифметического, поскольку оно "наказывает" крайние значения. Если одна из метрик близка к нулю, F1-score также будет стремиться к нулю, несмотря на высокое значение другой метрики.

Рассмотрим практический пример расчета F1-score:

Модель TP FP FN TN Precision Recall F1-score
Модель A 90 10 20 880 0.90 0.82 0.86
Модель B 95 40 15 850 0.70 0.86 0.77
Модель C 70 5 40 885 0.93 0.64 0.76

Как видно из таблицы, модель A показывает наилучший F1-score (0.86), несмотря на то, что по отдельным метрикам precision и recall она может уступать другим моделям. Модель C имеет высочайшую точность (0.93), но страдает от недостаточной полноты (0.64), что существенно снижает её F1-score.

При работе с многоклассовой классификацией F1-score вычисляется для каждого класса отдельно, а затем может быть агрегирован различными способами:

  • Макро-усреднение (macro): равное весовое усреднение по всем классам
  • Взвешенное усреднение (weighted): учитывает количество экземпляров каждого класса
  • Микро-усреднение (micro): агрегирует TP, FP и FN для всех классов, а затем вычисляет общий F1-score

Выбор конкретного метода агрегации зависит от специфики задачи и распределения классов в данных. 🔄

Реализация F1-score в sklearn.metrics: параметры и опции

Библиотека scikit-learn предоставляет удобный и гибкий функционал для расчета F1-score через модуль sklearn.metrics. Основная функция для этого — f1_score(), которая позволяет вычислять F1-score с различными настройками и для различных типов задач классификации. 💻

Базовый синтаксис использования функции выглядит так:

Python
Скопировать код
from sklearn.metrics import f1_score

y_true = [0, 1, 0, 1, 1, 0, 1] # Истинные метки
y_pred = [0, 0, 0, 1, 0, 0, 1] # Прогнозы модели

f1 = f1_score(y_true, y_pred)
print(f"F1-score: {f1:.4f}") # Выведет F1-score: 0.5000

Функция f1_score() принимает множество параметров, позволяющих настроить расчет под конкретные нужды:

  • y_true: Истинные метки классов
  • y_pred: Прогнозированные метки классов
  • labels: Список меток, которые необходимо учитывать при вычислении
  • pos_label: Метка положительного класса для бинарной классификации
  • average: Способ усреднения для многоклассовой классификации
  • sample_weight: Веса для каждого образца
  • zero_division: Значение, возвращаемое при делении на ноль

Особое внимание следует уделить параметру average, который определяет стратегию агрегации F1-score в многоклассовых задачах:

Python
Скопировать код
from sklearn.metrics import f1_score

y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]

# Различные способы усреднения
f1_macro = f1_score(y_true, y_pred, average='macro')
f1_micro = f1_score(y_true, y_pred, average='micro')
f1_weighted = f1_score(y_true, y_pred, average='weighted')
f1_none = f1_score(y_true, y_pred, average=None)

print(f"Macro F1: {f1_macro:.4f}")
print(f"Micro F1: {f1_micro:.4f}")
print(f"Weighted F1: {f1_weighted:.4f}")
print(f"F1 for each class:", f1_none)

Для оценки вероятностных прогнозов, а не дискретных меток классов, необходимо сначала преобразовать их в бинарные или многоклассовые решения с использованием порога отсечения:

Python
Скопировать код
from sklearn.metrics import f1_score
import numpy as np

# Вероятностные прогнозы для бинарной классификации
y_true = [0, 1, 1, 0, 1]
y_scores = [0\.1, 0.4, 0.7, 0.3, 0.9]

# Преобразуем в бинарные решения с порогом 0.5
y_pred = np.array(y_scores) >= 0.5

f1 = f1_score(y_true, y_pred)
print(f"F1-score with threshold 0.5: {f1:.4f}")

Scikit-learn также предоставляет функции для поиска оптимального порога отсечения для максимизации F1-score:

Python
Скопировать код
from sklearn.metrics import precision_recall_curve
import numpy as np

# Находим оптимальный порог для максимального F1-score
precision, recall, thresholds = precision_recall_curve(y_true, y_scores)
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-10)
optimal_threshold = thresholds[np.argmax(f1_scores)]
y_pred_optimal = np.array(y_scores) >= optimal_threshold

f1_optimal = f1_score(y_true, y_pred_optimal)
print(f"Optimal threshold: {optimal_threshold:.4f}")
print(f"F1-score with optimal threshold: {f1_optimal:.4f}")

Для комплексной оценки модели часто используется функция classification_report, которая выводит не только F1-score, но и precision, recall и support для каждого класса:

Python
Скопировать код
from sklearn.metrics import classification_report

report = classification_report(y_true, y_pred)
print(report)

Сергей Иванов, руководитель отдела аналитики финтех-проекта Мы разрабатывали модель для выявления мошеннических транзакций, где ложноположительные результаты стоили нам денег (заблокированные легитимные транзакции), а ложноотрицательные — репутации (пропущенные мошенники). Первоначально я оценивал модель по accuracy и был уверен, что мы достигли отличного результата — 99.3%.

Но когда мы запустили систему, начали поступать жалобы. Проблема оказалась в том, что мошеннические транзакции составляли всего 0.8% от общего числа. Переход к F1-score показал совсем другую картину — наша показательная модель имела F1-score всего 0.32! Особенно ценным оказался параметр pos_label в sklearn.metrics.f1_score, который позволил нам сосредоточиться именно на мошеннических транзакциях как на положительном классе.

Мы перенастроили гиперпараметры модели, оптимизируя именно F1-score, и достигли значения 0.78. После этого количество жалоб снизилось на 85%, а процент выявленных мошенников вырос на 64%. Это был поворотный момент в проекте и наглядный пример того, как выбор правильной метрики может кардинально влиять на результаты.

Особенности применения F1-score для несбалансированных данных

Несбалансированные данные — один из самых распространенных вызовов в машинном обучении, где F1-score демонстрирует своё преимущество над стандартной accuracy. Когда один класс представлен значительно чаще другого, метрики оценки могут давать искаженную картину эффективности модели. ⚖️

Рассмотрим ситуацию с сильно несбалансированным набором данных, где положительный класс составляет только 5% наблюдений:

Python
Скопировать код
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score, confusion_matrix
import numpy as np

# Создаем несбалансированный набор данных (95% – класс 0, 5% – класс 1)
X, y = make_classification(n_samples=1000, weights=[0\.95, 0.05], random_state=42)

# Разделяем на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Обучаем простую модель логистической регрессии
model = LogisticRegression()
model.fit(X_train, y_train)

# Делаем прогнозы
y_pred = model.predict(X_test)

# Оцениваем различными метриками
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)

print(f"Accuracy: {accuracy:.4f}")
print(f"F1-score: {f1:.4f}")
print("Confusion Matrix:")
print(conf_matrix)

В таких случаях модель часто "учится" просто предсказывать мажоритарный класс, что дает высокую accuracy, но полностью игнорирует миноритарный класс. F1-score позволяет обнаружить эту проблему, принимая низкие значения даже при высокой accuracy.

Для эффективного применения F1-score с несбалансированными данными полезны следующие стратегии:

  • Настройка порога принятия решения: Порог 0.5 может быть не оптимальным для несбалансированных данных
  • Взвешенный F1-score: Использование параметра sample_weight для придания большего веса миноритарному классу
  • Техники ребалансировки: Применение SMOTE, under-sampling, over-sampling или их комбинаций перед расчетом F1-score
  • Stratified cross-validation: Сохранение пропорций классов в складках кросс-валидации для более стабильных оценок F1-score

Важно понимать, что в некоторых случаях может потребоваться адаптация F1-score через параметры функции sklearn.metrics.f1_score:

Python
Скопировать код
from sklearn.metrics import f1_score
import numpy as np

# Предположим, у нас есть сильно несбалансированные данные
y_true = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) # 90% – класс 0, 10% – класс 1
y_pred = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # Модель предсказывает только класс 0

# Стандартный F1-score (для класса 1 по умолчанию)
f1_default = f1_score(y_true, y_pred)
print(f"Default F1-score (positive_class=1): {f1_default:.4f}")

# F1-score для класса 0 как положительного
f1_class0 = f1_score(y_true, y_pred, pos_label=0)
print(f"F1-score with pos_label=0: {f1_class0:.4f}")

# Добавляем веса для повышения значимости миноритарного класса
sample_weights = np.ones_like(y_true)
sample_weights[y_true == 1] = 9 # Придаем класcу 1 вес в 9 раз больший
f1_weighted = f1_score(y_true, y_pred, sample_weight=sample_weights)
print(f"F1-score with adjusted sample weights: {f1_weighted:.4f}")

При работе с многоклассовой классификацией и несбалансированными данными особенно важен выбор параметра average:

Параметр average Описание Преимущества Недостатки
None Возвращает F1 для каждого класса отдельно Детальный анализ по классам Сложно интерпретировать при большом количестве классов
'binary' Только для бинарной классификации Простая интерпретация Не применим к многоклассовым задачам
'micro' Агрегирует TP, FP, FN для всех классов Учитывает частоту классов Доминируют многочисленные классы
'macro' Среднее F1 для всех классов Равная значимость всех классов Может быть завышен при редких классах с низкими метриками
'weighted' Среднее F1, взвешенное по частоте класса Баланс между 'micro' и 'macro' Менее чувствителен к редким классам, чем 'macro'
'samples' Для многозначной классификации Учитывает каждый образец отдельно Применим только к специфическим задачам

Для задач с критической важностью редких классов часто рекомендуется использовать 'macro' averaging, чтобы обеспечить равную значимость всех классов независимо от их распространенности в данных.

Выбор правильной стратегии оценки моделей машинного обучения зависит от ваших карьерных целей и специализации. Тест на профориентацию от Skypro поможет определить, подходит ли вам роль специалиста по машинному обучению, data scientist или аналитика данных. Тест учитывает ваши технические навыки, аналитическое мышление и предпочтения в работе с данными, что особенно важно для профессий, где требуется понимание таких нюансов, как выбор и оптимизация метрик вроде F1-score. 📋✓

Практические сценарии использования sklearn.metrics.f1_score

Рассмотрим практические сценарии, в которых знание тонкостей применения F1-score и функций из sklearn.metrics может существенно повысить эффективность машинного обучения. 🔧

1. Оптимизация гиперпараметров модели по F1-score

Python
Скопировать код
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.metrics import make_scorer, f1_score

# Создаем набор данных
X, y = make_classification(n_samples=1000, n_features=20, 
class_sep=0.8, random_state=42)

# Создаем собственный скорер на основе F1-score
f1_scorer = make_scorer(f1_score)

# Определяем модель и параметры для поиска
rf = RandomForestClassifier(random_state=42)
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 5, 10, 15],
'min_samples_split': [2, 5, 10]
}

# Запускаем поиск по сетке с оптимизацией по F1-score
grid_search = GridSearchCV(
rf, param_grid, scoring=f1_scorer, cv=5, n_jobs=-1
)
grid_search.fit(X, y)

# Выводим лучшие параметры и результат
print("Лучшие параметры:", grid_search.best_params_)
print("Лучший F1-score:", grid_search.best_score_)

2. Определение оптимального порога отсечения для бинарной классификации

Python
Скопировать код
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve, f1_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)

# Обучаем модель логистической регрессии
model = LogisticRegression()
model.fit(X_train, y_train)
y_scores = model.predict_proba(X_test)[:, 1]

# Вычисляем precision и recall для разных порогов
precision, recall, thresholds = precision_recall_curve(y_test, y_scores)

# Вычисляем F1-score для каждого порога
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-10)

# Находим оптимальный порог
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx] if optimal_idx < len(thresholds) else 0

# Применяем оптимальный порог
y_pred_optimal = (y_scores >= optimal_threshold).astype(int)
f1_optimal = f1_score(y_test, y_pred_optimal)

# Для сравнения используем стандартный порог 0.5
y_pred_standard = (y_scores >= 0.5).astype(int)
f1_standard = f1_score(y_test, y_pred_standard)

print(f"Стандартный порог (0.5): F1-score = {f1_standard:.4f}")
print(f"Оптимальный порог ({optimal_threshold:.4f}): F1-score = {f1_optimal:.4f}")

# Визуализация зависимости F1-score от порога
plt.figure(figsize=(10, 6))
plt.plot(thresholds, f1_scores[:-1], 'b-', label='F1-score')
plt.axvline(x=optimal_threshold, color='r', linestyle='--', 
label=f'Optimal threshold: {optimal_threshold:.4f}')
plt.xlabel('Threshold')
plt.ylabel('F1-score')
plt.title('F1-score vs Threshold')
plt.legend()
plt.grid(True)
plt.show()

3. Мониторинг модели в продакшене с использованием F1-score по времени

Отслеживание изменений F1-score с течением времени помогает обнаруживать дрейф модели и деградацию её качества:

Python
Скопировать код
import pandas as pd
from sklearn.metrics import f1_score
import matplotlib.pyplot as plt
from datetime import datetime, timedelta

# Предположим, что у нас есть предсказания модели за несколько дней
# Имитируем данные для примера

# Генерируем даты
dates = [(datetime.now() – timedelta(days=i)).strftime('%Y-%m-%d') 
for i in range(30, 0, -1)]

# Симулируем результаты для каждого дня
results = []
for i in range(30):
# Имитируем некоторое снижение качества со временем
correct_ratio = 0.85 – (i / 200)
n_samples = 1000
y_true = np.random.randint(0, 2, n_samples)

# Генерируем предсказания с заданным соотношением правильных ответов
y_pred = np.copy(y_true)
flip_indices = np.random.choice(
n_samples, 
size=int(n_samples * (1-correct_ratio)), 
replace=False
)
y_pred[flip_indices] = 1 – y_pred[flip_indices]

f1 = f1_score(y_true, y_pred)
results.append({
'date': dates[i],
'f1_score': f1,
'samples': n_samples
})

# Создаем DataFrame из результатов
monitoring_df = pd.DataFrame(results)

# Визуализация изменения F1-score со временем
plt.figure(figsize=(12, 6))
plt.plot(monitoring_df['date'], monitoring_df['f1_score'], 'o-')
plt.axhline(y=0.8, color='r', linestyle='--', label='Threshold for retraining')
plt.xlabel('Date')
plt.ylabel('F1-score')
plt.title('F1-score Monitoring Over Time')
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()
plt.legend()
plt.show()

# Определяем, требуется ли переобучение модели
recent_f1 = monitoring_df.iloc[-7:]['f1_score'].mean()
if recent_f1 < 0.8:
print("Alert: Model retraining required. Average F1-score for the last week:", 
round(recent_f1, 4))
else:
print("Model performance is stable. Average F1-score for the last week:", 
round(recent_f1, 4))

4. Многоклассовая классификация и анализ F1-score по классам

Для задач с несколькими классами важно анализировать F1-score для каждого класса отдельно, чтобы выявлять проблемные категории:

Python
Скопировать код
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, f1_score
import pandas as pd

# Загружаем набор данных Iris
iris = load_iris()
X, y = iris.data, iris.target

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)

# Обучаем модель
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)

# Вычисляем F1-score для каждого класса
f1_per_class = f1_score(y_test, y_pred, average=None)
f1_macro = f1_score(y_test, y_pred, average='macro')
f1_weighted = f1_score(y_test, y_pred, average='weighted')

# Создаем DataFrame для визуализации результатов
class_names = iris.target_names
f1_df = pd.DataFrame({
'Class': class_names,
'F1-score': f1_per_class
})

print("F1-score по классам:")
print(f1_df)
print(f"\nMacro F1-score: {f1_macro:.4f}")
print(f"Weighted F1-score: {f1_weighted:.4f}")

# Выводим полный отчет о классификации
print("\nПолный отчет о классификации:")
print(classification_report(y_test, y_pred, target_names=class_names))

Эти практические примеры демонстрируют различные аспекты применения F1-score в реальных задачах машинного обучения. Глубокое понимание этой метрики и умение эффективно использовать функции из sklearn.metrics позволяют создавать более надежные и эффективные модели, особенно в сложных случаях с несбалансированными данными или при необходимости уделить особое внимание определенным классам. 🚀

F1-score — это не просто число в отчете, а мощный инструмент для принятия решений в машинном обучении. Оптимизируя модели по этой метрике, мы можем находить тонкий баланс между точностью и полнотой, критически важный в большинстве реальных задач. Библиотека sklearn.metrics предоставляет исчерпывающий набор инструментов для работы с F1-score, позволяя адаптировать методы оценки под конкретные потребности проекта. Овладение тонкостями применения F1-score — это шаг от просто хороших моделей к решениям, способным эффективно работать в сложных, несбалансированных и динамически меняющихся условиях реального мира.

Загрузка...