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

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

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

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

Выбор правильной метрики эффективности — тот рубеж, который отделяет провальные 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 может быть даже оптимизирован как целевая функция при обучении модели, а не только использоваться как метрика оценки постфактум.

Кинга Идем в IT: пошаговый план для смены профессии

Математический аппарат 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:

МодельTPFPFNTNPrecisionRecallF1-score
Модель A9010208800.900.820.86
Модель B9540158500.700.860.77
Модель C705408850.930.640.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 — это шаг от просто хороших моделей к решениям, способным эффективно работать в сложных, несбалансированных и динамически меняющихся условиях реального мира.