# Навчання

Вбудована модель працює одразу. Модель, навчена на вашій власній пошті, працює краще, бо вивчає, як виглядає ваш ham (бажані листи): ваші розсилки, стиль ваших колег, мови, якими ви отримуєте пошту.


## Навчання моделі

Вкажіть `train` теки зі спамом і ham:

```sh
spamscanner train --spam ~/Maildir/.Junk --ham ~/Maildir/cur --ham ~/Maildir/.Archive --out my-model.json
```

Джерелами можуть бути:

* файли **mbox**, зокрема стиснені gzip (`.mbox.gz`),
* каталог **Maildir** (читаються його теки `cur` і `new`, `tmp` пропускається),
* **тека** з файлами `.eml`, яка читається рекурсивно,
* **набір даних**: файл CSV або JSON Lines зі стовпцем тексту й стовпцем мітки (`--dataset`). Стовпці з назвами `text`, `message`, `body`, `email` або `content` і `label`, `category`, `class`, `spam` або `is_spam` знаходяться автоматично; інакше використовуйте `--text-column` і `--label-column`. Розпізнаються мітки на кшталт `spam`, `1`, `phishing` і `ham`, `0`, `not_spam`, `legitimate`.

Дублікати листів ураховуються один раз. Щоб розвивати вбудовану модель, а не починати з порожньої, додайте `--merge`.

Використання моделі:

```sh
spamscanner scan message.eml --model my-model.json
SPAMSCANNER_MODEL=/var/lib/spamscanner/model.json spamscanner milter
```

```js
const scanner = new SpamScanner({classifier: '/var/lib/spamscanner/model.json'});
```

Скільки пошти достатньо: кілька сотень листів кожного типу дають корисну модель, кілька тисяч — добру. Тримайте обидва типи приблизно збалансованими й додавайте до ham пошту, яку не треба фільтрувати (скидання паролів, рахунки від ваших постачальників).


## Вимірювання

Залиште частину пошти поза навчанням і вимірюйте на ній:

```sh
spamscanner eval --model my-model.json --spam held-out/spam --ham held-out/ham
```

Вбудована модель на SMS-повідомленнях 21 мовою, яких вона ніколи не бачила, здебільшого мовами, які вона ледве знає:

```text
Messages: 13028 spam, 92406 ham
Precision: 89.72%   Recall: 11.18%   F1: 19.89%
False positives: 167 (0.18% of ham)   False negatives: 132
Unsure: 91247 (86.54%)
```

Точність (precision) показує, яка частка того, що модель називає спамом, справді є спамом; повнота (recall) — яку частку спаму вона виявляє. Невизначені листи тут зараховуються як пропущений спам, хоча під час перевірки їх ще можуть виявити інші перевірки та мовна модель. Стежити варто за хибнопозитивними результатами: ham, позначеним як спам. У наведеному запуску модель здебільшого не впевнена щодо цих листів, а не помиляється, і саме такої поведінки очікують для мов, у яких вона бачила мало пошти.

`--json` видає ті самі числа для скриптів.


## Навчання за скаргами

Коли користувачі переміщують пошту в теку Junk або з неї, навчайте модель по одному листу:

```sh
spamscanner learn spam message.eml --model /var/lib/spamscanner/model.json
spamscanner learn ham message.eml --model /var/lib/spamscanner/model.json
```

Перший `learn` створює файл із вбудованої моделі. Через HTTP те саме роблять `POST /learn/spam` і `/learn/ham` у [HTTP API](https://spamscanner.net/uk/docs/http-api/), а `spamc -L spam` працює із [сервером spamd](https://spamscanner.net/uk/docs/mail-servers/#a-drop-in-for-spamassassins-spamd) з `--allow-tell`. [IMAPSieve у Dovecot](https://spamscanner.net/uk/docs/mail-servers/#dovecot-junk-folder-and-learning) може викликати будь-який із них, коли лист переміщують.

З Node.js:

```js
await scanner.learn(raw, 'spam');
await scanner.unlearn(raw, 'spam'); // undo, before learning it as ham
scanner.saveModel('/var/lib/spamscanner/model.json');
```

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


## Вбудована модель

`model/classifier.json` збирається командою `npm run model:train` з таких відкритих наборів даних на Hugging Face, усі з вільними ліцензіями:

| Набір даних                                                                                                                                                                                                                                                                                                                | Ліцензія                       | Вміст                             |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | --------------------------------- |
| [FredZhang7/all-scam-spam](https://huggingface.co/datasets/FredZhang7/all-scam-spam)                                                                                                                                                                                                                                       | Apache-2.0                     | Повідомлення й листи 43 мовами    |
| [SetFit/enron_spam](https://huggingface.co/datasets/SetFit/enron_spam)                                                                                                                                                                                                                                                     | Відкритий дослідницький корпус | Корпус Enron-Spam                 |
| [alt-gnome/telegram-spam](https://huggingface.co/datasets/alt-gnome/telegram-spam)                                                                                                                                                                                                                                         | CC0-1.0                        | Російські повідомлення з Telegram |
| [tanaos/synthetic-spam-detection-dataset-german](https://huggingface.co/datasets/tanaos/synthetic-spam-detection-dataset-german), [-italian](https://huggingface.co/datasets/tanaos/synthetic-spam-detection-dataset-italian), [-spanish](https://huggingface.co/datasets/tanaos/synthetic-spam-detection-dataset-spanish) | MIT                            | Синтетичні повідомлення           |

Вона навчалася на 62 480 листах зі спамом і 76 489 листах ham. Скрипт відкладає кожен десятий лист, навчається на решті й вимірює лише класифікатор, без інших перевірок:

| Відкладений тест |  Листи | Точність | Повнота | Хибні спрацювання | Невизначені |
| ---------------- | -----: | -------: | ------: | ----------------: | ----------: |
| Англійська       |   6564 |  100,0 % |  97,0 % |             0,0 % |       2,4 % |
| Російська        |   1682 |  100,0 % |  97,4 % |             0,0 % |       2,2 % |
| Італійська       |   1389 |   98,1 % |  85,3 % |             1,8 % |      10,9 % |
| Німецька         |   1309 |   97,7 % |  76,1 % |             2,2 % |      20,7 % |
| Іспанська        |   1281 |   97,5 % |  82,5 % |             2,6 % |      16,8 % |
| Enron-Spam       |   2888 |  100,0 % |  93,1 % |             0,0 % |       4,5 % |
| all-scam-spam    |   4236 |  100,0 % |  88,8 % |             0,0 % |      11,2 % |
| Усі              | 13 840 |   99,2 % |  85,1 % |             0,5 % |      12,4 % |

Спамом тут вважається ймовірність за класифікатором 99 % або більше — точка, у якій сам класифікатор досягає порогу спаму. Під час перевірки спам, щодо якого він менш упевнений, усе одно отримує бали, а інші перевірки додають свої.

Результати для німецької, іспанської та італійської отримано із синтетичних наборів даних, які містять майже однакові листи з мітками і спаму, і ham: частина цієї помилки — у мітках, а не в моделі. Найкращий спосіб виправити це — пошта вашими власними мовами. Числа для кожної мови й набору даних містяться в `metadata.metrics` моделі.

### Більше мов

`npm run model:train -- --with multilingual-sms` додає [SMS Spam Multilingual Collection](https://huggingface.co/datasets/dbarbedillo/SMS_Spam_Multilingual_Collection_Dataset): SMS Spam Collection, машинно перекладену 21 мовою. Її не включено до вбудованої моделі, бо в її картці вказано ліцензію GPL; перевірте, чи це підходить для того, як ви поширюєте модель. Після навчання з нею результати на відкладених даних для мов, які вбудована модель ледве знає, були такими:

| Мова       | Листи | Точність | Повнота | Хибні спрацювання |
| ---------- | ----: | -------: | ------: | ----------------: |
| Китайська  |   430 |  100,0 % |  82,3 % |             0,0 % |
| Арабська   |   430 |  100,0 % |  84,6 % |             0,0 % |
| Корейська  |   412 |  100,0 % |  80,4 % |             0,0 % |
| Японська   |   486 |   96,0 % |  85,7 % |             0,5 % |
| Гінді      |   412 |  100,0 % |  63,9 % |             0,0 % |
| Французька |   480 |   98,6 % |  94,2 % |             0,6 % |
| Турецька   |   220 |  100,0 % |  73,1 % |             0,0 % |

### Перенавчання

```sh
npm run model:train                       # downloads the datasets into data/ the first time
npm run model:train -- --no-download      # reuse data/
npm run model:train -- --out /tmp/model.json --max-features 200000
```


## Файл моделі

Модель — це файл JSON: кількість вивчених листів зі спамом і з ham, а для кожної хешованої ознаки — скільки листів зі спамом і з ham її містили, у відсортованому вигляді й закодовано в base64. Він не містить ні слів, ні тексту листів. `--max-features` залишає лише найчастіші ознаки, а `--min-count` відкидає рідкісні, обмінюючи точність на розмір; вбудована модель зберігає 400 000 ознак приблизно в 6 МБ.

Моделі зі Spam Scanner 6 і раніших версій завантажити не можна: вони хешували інші ознаки. Навчіть нову на тій самій пошті.
