F1-score в sklearn.metrics: оценка точности моделей машинного обучения
Пройдите тест, узнайте какой профессии подходите
Для кого эта статья:
- дата-сайентисты и аналитики данных
- студенты и специалисты, обучающиеся машинному обучению
- профессионалы, работающие с несбалансированными данными и моделями классификации
Выбор правильной метрики эффективности — тот рубеж, который отделяет провальные 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 с различными настройками и для различных типов задач классификации. 💻
Базовый синтаксис использования функции выглядит так:
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 в многоклассовых задачах:
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)
Для оценки вероятностных прогнозов, а не дискретных меток классов, необходимо сначала преобразовать их в бинарные или многоклассовые решения с использованием порога отсечения:
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:
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 для каждого класса:
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% наблюдений:
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:
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
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. Определение оптимального порога отсечения для бинарной классификации
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 с течением времени помогает обнаруживать дрейф модели и деградацию её качества:
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 для каждого класса отдельно, чтобы выявлять проблемные категории:
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 — это шаг от просто хороших моделей к решениям, способным эффективно работать в сложных, несбалансированных и динамически меняющихся условиях реального мира.