Machine Learning Academy · Урок

Оценка и вывод: от логитов к предсказанным меткам

Вы вычислите softmax для логитов, преобразуете предсказанные индексы классов в метки и оцените дообученную модель по точности и F1 на отложенном тестовом наборе.

Урок 4 из 413 шагов

«Оценка и вывод: от логитов к предсказанным меткам» — бесплатный урок Machine Learning Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Machine Learning Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Machine Learning Academy содержит 4 уроков всего.

Что такое логиты

Результат последнего линейного слоя модели классификации называется логитами: это необработанные ненормализованные оценки для каждого класса. Для задачи с 2 классами (отрицательный/положительный) вектор логитов может выглядеть как [-1.2, 3.5], что означает, что модель сильно склоняется к классу 1 (положительному). Логит может быть любым вещественным числом; это не вероятность. Нужен дополнительный шаг (softmax или sigmoid), чтобы преобразовать логиты в понятные значения вероятностей, сумма которых равна 1.

import torch
import torch.nn.functional as F

# Example logits for 2-class problem (batch of 3)
logits = torch.tensor([[-1.2, 3.5], [2.1, -0.3], [0.4, 0.6]])
print('Logits:\n', logits)

# Convert to probabilities with softmax
probs = F.softmax(logits, dim=1)
print('Probabilities:\n', probs)
# Each row sums to 1
print('Row sums:', probs.sum(dim=1))

Softmax: преобразование логитов в вероятности

Функция softmax преобразует вектор логитов с вещественными значениями в распределение вероятностей. Для класса i: softmax(z_i) = exp(z_i) / sum(exp(z_j)). Экспонента усиливает различия: разница логитов, равная 2, приводит к тому, что один класс становится примерно в 7 раз более вероятным, чем другой. Softmax используется для многоклассовой классификации, где правильным является ровно один класс.

import torch
import torch.nn.functional as F
import numpy as np

logits = torch.tensor([1.0, 3.0, 0.5])  # 3 classes
probs = F.softmax(logits, dim=0)
print('Class probabilities:', probs.numpy().round(4))
# [0.0900, 0.6652, 0.2449] -- class 1 is most likely
print('Sum:', probs.sum().item())  # 1.0

# argmax gives the predicted class index
pred_class = torch.argmax(probs).item()
print('Predicted class:', pred_class)  # 1

argmax: извлечение предсказанных меток

Когда у нас есть векторы логитов для пакета примеров, torch.argmax(logits, dim=1) возвращает индекс класса с наибольшей оценкой для каждого примера. Это и есть предсказанная метка класса. Для BERT, дообученного с параметром num_labels=2, результатом будет 0 (отрицательный класс) или 1 (положительный класс). Перед argmax не нужно применять softmax, поскольку softmax монотонна: наибольший логит всегда преобразуется в наибольшую вероятность.

import torch

# Logits from model output for a batch of 4 examples
logits = torch.tensor([
    [-2.1,  3.4],  # strongly positive
    [ 1.8, -0.5],  # negative
    [ 0.1,  0.2],  # uncertain, leans positive
    [-3.0, -1.0]   # both negative, less-negative wins
])

predictions = torch.argmax(logits, dim=1)
print('Predictions:', predictions.tolist())  # [1, 0, 1, 1]

labels = {'0': 'negative', '1': 'positive'}
for pred in predictions.tolist():
    print(labels[str(pred)])

Декодирование предсказанных индексов в названия классов

Модель возвращает целочисленные индексы. В рабочей системе следует хранить соответствие между индексом и понятной человеку меткой. Сохраните его в виде списка или словаря и обращайтесь к нему по предсказанному целому числу. Для BERT, дообученного на IMDB, соответствие выглядит просто: {0: 'NEGATIVE', 1: 'POSITIVE'}. Для многоклассовых задач, например классификации тем, соответствие может содержать 20 и более записей.

import torch
from transformers import BertForSequenceClassification, BertTokenizer

# Assume model and tokenizer are already loaded and fine-tuned
id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}

def predict(text, model, tokenizer, device):
    model.eval()
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    inputs = {k: v.to(device) for k, v in inputs.items()}
    with torch.no_grad():
        logits = model(**inputs).logits
    pred_id = torch.argmax(logits, dim=1).item()
    return id2label[pred_id]

# result = predict('This film was absolutely wonderful!', model, tokenizer, device)
# print(result)  # POSITIVE

Оценки уверенности на основе вероятностей

Для реальных приложений часто недостаточно вернуть только предсказанную метку. Оценка уверенности (вероятность предсказанного класса) показывает, насколько модель уверена в своём решении. Предсказание POSITIVE с уверенностью 99% сильно отличается от предсказания с уверенностью 52%. Направление предсказаний с низкой уверенностью на проверку человеком — распространённая практика в рабочих системах, принимающих чувствительные решения.

import torch
import torch.nn.functional as F

def predict_with_confidence(text, model, tokenizer, device):
    model.eval()
    id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    inputs = {k: v.to(device) for k, v in inputs.items()}
    with torch.no_grad():
        logits = model(**inputs).logits
    probs = F.softmax(logits, dim=1).squeeze()
    pred_id = torch.argmax(probs).item()
    confidence = probs[pred_id].item()
    return id2label[pred_id], round(confidence, 4)

# label, conf = predict_with_confidence('Terrible movie.', model, tokenizer, device)
# print(label, conf)  # NEGATIVE 0.9732

Оценка с помощью точности и F1

После сбора прогнозов для всей тестовой выборки вычислите стандартные метрики с помощью scikit-learn. Точность — доля правильных прогнозов; она полезна, когда классы сбалансированы. Оценка F1 — гармоническое среднее полноты и точности; она важна, когда один класс (например, положительные отзывы) имеет большее значение или классы несбалансированы. classification_report выводит все метрики в одной понятной таблице.

from sklearn.metrics import classification_report, accuracy_score
import torch

all_preds, all_labels = [], []

model.eval()
with torch.no_grad():
    for batch in test_loader:
        outputs = model(
            input_ids=batch['input_ids'].to(device),
            attention_mask=batch['attention_mask'].to(device)
        )
        preds = torch.argmax(outputs.logits, dim=1).cpu().numpy()
        all_preds.extend(preds)
        all_labels.extend(batch['label'].numpy())

print('Accuracy:', accuracy_score(all_labels, all_preds))
print(classification_report(all_labels, all_preds,
      target_names=['NEGATIVE', 'POSITIVE']))

Матрица ошибок для BERT

Матрица ошибок показывает распределение прогнозов относительно истинных меток. При анализе тональности ячейки вне главной диагонали показывают ложноположительные результаты (предсказан POSITIVE, но на самом деле NEGATIVE) и ложноотрицательные результаты (предсказан NEGATIVE, но на самом деле POSITIVE). Визуализация с помощью тепловой карты seaborn выявляет систематические ошибки: не предсказывает ли модель один класс слишком часто? Не классифицируются ли неправильно одни и те же типы отзывов?

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

cm = confusion_matrix(all_labels, all_preds)

plt.figure(figsize=(6, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=['NEG', 'POS'],
            yticklabels=['NEG', 'POS'])
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.title('BERT Confusion Matrix on IMDB Test Set')
plt.tight_layout()
plt.savefig('bert_confusion.png')

Пакетное получение прогнозов для повышения эффективности

Получение прогноза для одного примера за раз выполняется медленно. Обрабатывайте примеры пакетами, чтобы использовать преимущества параллельной работы GPU. Установите фиксированный batch_size в своём DataLoader, передавайте модели целые пакеты и собирайте результаты. На GPU пакетное получение прогнозов может быть в 50 раз быстрее обработки отдельных примеров. Используйте torch.no_grad() и перемещайте тензоры на нужное устройство для оптимальной производительности.

import torch
from torch.utils.data import DataLoader, TensorDataset
from transformers import BertTokenizer

def batch_predict(texts, model, tokenizer, device, batch_size=32):
    model.eval()
    all_predictions = []
    for i in range(0, len(texts), batch_size):
        batch_texts = texts[i:i+batch_size]
        enc = tokenizer(batch_texts, truncation=True, padding=True,
                        max_length=256, return_tensors='pt')
        enc = {k: v.to(device) for k, v in enc.items()}
        with torch.no_grad():
            logits = model(**enc).logits
        preds = torch.argmax(logits, dim=1).cpu().tolist()
        all_predictions.extend(preds)
    return all_predictions

Sigmoid для многометочной классификации

Если текст может одновременно относиться к нескольким классам (например, отзыв может быть одновременно «смешным» и «эмоциональным»), используйте sigmoid вместо softmax. Sigmoid применяется независимо к логиту каждого класса и выдаёт для каждого класса вероятность от 0 до 1, при этом сумма вероятностей не обязана быть равна 1. Примените порог (обычно 0,5) к каждой вероятности, чтобы получить двоичное предсказание для каждого класса. Установите problem_type='multi_label_classification' в BertForSequenceClassification.

import torch
import torch.nn.functional as F

# 5 classes, multi-label: a text can have multiple labels
logits = torch.tensor([[1.2, -0.5, 2.1, -1.8, 0.3]])
probs = torch.sigmoid(logits)
print('Per-class probabilities:', probs)

threshold = 0.5
predicted_labels = (probs > threshold).int()
print('Predicted labels (multi-hot):', predicted_labels)
# e.g., [1, 0, 1, 0, 0] -- classes 0 and 2 are predicted

Обработка неправильно классифицированных примеров

Всегда проверяйте неправильно классифицированные примеры вручную. Выведите примеры, в которых модель предсказала 0, а истинная метка равна 1 (ложноотрицательные результаты), и наоборот. К распространённым закономерностям относятся сарказм («О, какой великолепный фильм-катастрофа»), сдвиг предметной области (устаревшая лексика) и длинные отзывы, когда модель видит только первые 256 токенов. Понимание причин ошибок помогает планировать разработку признаков или сбор данных.

import numpy as np

all_preds = np.array(all_preds)
all_labels = np.array(all_labels)
texts = test_dataset['text'] if hasattr(test_dataset, '__getitem__') else []

false_negatives = np.where((all_preds == 0) & (all_labels == 1))[0]
false_positives = np.where((all_preds == 1) & (all_labels == 0))[0]

print('False negatives (predicted NEG, true POS):')
for idx in false_negatives[:3]:
    print(' -', str(texts[idx])[:120] if texts else idx)

print('False positives (predicted POS, true NEG):')
for idx in false_positives[:3]:
    print(' -', str(texts[idx])[:120] if texts else idx)

Экспорт прогнозов в CSV

В рабочих или конкурсных системах часто требуется экспортировать прогнозы в CSV-файл. Используйте pandas, чтобы создать DataFrame с исходным текстом, истинной меткой, предсказанной меткой и оценкой уверенности. Такой формат удобно передавать заинтересованным сторонам, проверять и использовать как входные данные для последующих конвейеров формирования отчётов. Всегда включайте в метаданные версию модели и время получения прогноза.

import pandas as pd
import torch.nn.functional as F
import torch

results = []
id2label = {0: 'NEGATIVE', 1: 'POSITIVE'}

model.eval()
for i, text in enumerate(sample_texts):
    inputs = tokenizer(text, return_tensors='pt',
                       truncation=True, max_length=256)
    with torch.no_grad():
        logits = model(**inputs).logits
    probs = F.softmax(logits, dim=1).squeeze()
    pred = torch.argmax(probs).item()
    results.append({
        'text': text[:80],
        'true_label': id2label[sample_labels[i]],
        'predicted_label': id2label[pred],
        'confidence': round(probs[pred].item(), 4)
    })

df = pd.DataFrame(results)
df.to_csv('bert_predictions.csv', index=False)
print(df.head())

Быстрая проверка

Проверьте своё понимание концепций машинного обучения на Python из этого урока.

Итоги урока

В этом уроке Вы узнали, что логиты — это необработанные оценки модели, которые преобразуются в вероятности с помощью softmax для многоклассовых задач или sigmoid для многометочных задач, argmax возвращает индекс предсказанного класса, который с помощью словаря id2label преобразуется в понятную человеку метку, а classification_report и матрица ошибок вместе дают полную картину качества модели, дополняя точность. Далее мы рассмотрим MLflow для отслеживания экспериментов, параметров и метрик в нескольких запусках обучения.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
30
Уроки
120

Часто задаваемые вопросы

Урок «Оценка и вывод: от логитов к предсказанным меткам» бесплатный?

Да — полный текст урока «Оценка и вывод: от логитов к предсказанным меткам» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Machine Learning Academy, подпишись на CoddyKit PRO. Курс Machine Learning Academy содержит 4 уроков всего.

Чему я научусь в уроке «Оценка и вывод: от логитов к предсказанным меткам»?

Вы вычислите softmax для логитов, преобразуете предсказанные индексы классов в метки и оцените дообученную модель по точности и F1 на отложенном тестовом наборе. Ты практикуешь Machine Learning Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Machine Learning Academy?

Предыдущий опыт не требуется. Machine Learning Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Оценка и вывод: от логитов к предсказанным меткам»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Machine Learning Academy?

Да. Каждый урок Machine Learning Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Архитектура Transformer: внимание, токены и контекст
  2. Токенизаторы Hugging Face: кодирование текста для BERT
  3. Дообучение BertForSequenceClassification
  4. Оценка и вывод: от логитов к предсказанным меткам
← Назад к Machine Learning Academy