# Кейс 1. Фильтр спама: вероятность ≠ решение

#### Цель кейса

Этот кейс показывает одну из самых частых ошибок при работе с вероятностями в машинном обучении – смешение оценки модели и решения системы. Мы разберем ситуацию, где модель возвращает вероятность того, что письмо является спамом, но окончательное действие (что делать с письмом) принимается отдельным слоем логики.

Ключевая идея кейса проста, но фундаментальна: модель не принимает решений – она выражает степень уверенности.

#### Сценарий

Представим почтовый сервис. Для каждого входящего письма модель машинного обучения оценивает вероятность того, что письмо является спамом. Однако бизнес-задача формулируется иначе:

* можно ли автоматически убрать письмо из входящих
* или лучше оставить его, даже если есть сомнения
* насколько критична ошибка, когда нормальное письмо попадает в спам

Модель отвечает только на один вопрос: *насколько я уверена, что это спам?* Все остальное – ответственность инженерной и бизнес-логики.

#### Данные и признаки

Для простоты используем два числовых признака:

* длина темы письма;
* количество ссылок в тексте.

Это искусственный пример, но он хорошо иллюстрирует механику вероятностей.

```php
use Rubix\ML\Classifiers\LogisticRegression;
use Rubix\ML\Datasets\Labeled;

$samples = [
    [3, 1],   // короткая тема, мало ссылок
    [15, 8],  // длинная тема, много ссылок
    [5, 0],   // средняя тем, нет ссылок
];

$labels = ['normal', 'spam', 'normal'];

$dataset = new Labeled($samples, $labels);
```

Здесь важно не качество данных, а сам факт: модель будет вынуждена обобщать и выражать уверенность, а не выдавать бинарный ответ. Численные значения вероятностей в этом примере не имеют практического смысла и приведены исключительно для иллюстрации логики.

#### Обучение модели

Для бинарной классификации с вероятностным выходом логистическая регрессия подходит идеально.

```php
$model = new LogisticRegression();
$model->train($dataset);
```

Логистическая регрессия по своей природе выдает вероятность принадлежности к целевому классу; для бинарной классификации вероятность второго класса получается как дополнение до 1. Это делает ее удобной для демонстрации идеи "уверенность ≠ (не равно) решение".

#### Предсказание вероятности

Пусть пришло новое письмо:

* тема достаточно длинная;
* ссылок много.

```php
// новое письмо
$sample = new Unlabeled([[12, 6]]);
$probabilities = $model->proba($sample)[0];
```

На выходе мы получаем распределение вероятностей, например:

```php
normal: 0.32
spam: 0.68
```

Это не приказ к действию. Это лишь выражение оценки вероятности в рамках текущей модели и текущих данных.

#### Принятие решения

Теперь появляется слой, который вообще не относится к машинному обучению напрямую – бизнес-логика.

```php
$threshold = 0.7;

if ($probabilities['spam'] >= $threshold) {
    echo 'Отправить в спам';
} else {
    echo 'Оставить во входящих';
}
```

Почему выбран порог 0.7, а не 0.5?

Потому что цена ошибок различна:

* ложноположительная ошибка (нормальное письмо попало в спам) часто критичнее
* ложоотрицательная (спам остался во входящих) обычно терпима

Порог – это компромисс между рисками, а не характеристика модели. Поэтому конкретное значение порога всегда подбирается эмпирически и может отличаться в зависимости от контекста и аудитории.

#### Почему это принципиально важно

Если заменить порог 0.7 на 0.5, модель не изменится вообще. Изменится только политика принятия решений. Это значит, что:

* вероятность – универсальный интерфейс между моделью и системой
* одна и та же модель может использоваться в разных сценариях с разными порогами
* A/B-тестирование часто может сводиться к подбору порога, а не переобучению модели

#### Типичная ошибка

Распространенная ошибка – воспринимать вероятность как "почти истину":

* 0.68 выглядит как "скорее всего спам"
* но это все еще 32% сомнений

Если система ведет себя так, будто модель всегда права, это почти гарантированно приведет к плохому пользовательскому опыту.

<details>

<summary>Кейс 1. Полный пример кода.</summary>

```php
use Rubix\ML\Classifiers\LogisticRegression;
use Rubix\ML\Datasets\Labeled;

$samples = [
    [3, 1],   // короткая тема, мало ссылок
    [15, 8],  // длинная тема, много ссылок
    [5, 0],   // средняя тем, нет ссылок
];

$labels = ['normal', 'spam', 'normal'];

$dataset = new Labeled($samples, $labels);

$model = new LogisticRegression();
$model->train($dataset);

// новое письмо
$sample = new Unlabeled([[12, 6]]);
$probabilities = $model->proba($sample)[0];

// Результат:
// normal: 0.32
// spam: 0.68

$threshold = 0.7;

if ($probabilities['spam'] >= $threshold) {
    echo 'Отправить в спам';
} else {
    echo 'Оставить во входящих';
}
```

</details>

#### Выводы для этого кейса

1. Модель машинного обучения возвращает оценку уверенности, а не решение.
2. Вероятность – это язык неопределенности, а не бинарный флаг.
3. Порог принятия решения определяется контекстом и ценой ошибки.
4. Разделение модели и бизнес-логики делает систему гибкой и управляемой.

Этот кейс – фундамент для всех последующих примеров, где вероятность используется как инструмент мышления, а не как "магическое число".

{% hint style="info" %}
Чтобы самостоятельно протестировать этот код, воспользуйтесь [онлайн-демонстрацией](https://aiwithphp.org/books/ai-for-php-developers/examples/part-3/probability-as-degree-of-confidence) для его запуска.
{% endhint %}
