# การฝึกโมเดล

โมเดลที่มาพร้อมแพ็กเกจใช้งานได้ทันที แต่โมเดลที่ฝึกจากอีเมลของคุณเองจะทำงานได้ดีกว่า เพราะเรียนรู้ว่า 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/th/docs/http-api/) ได้ผลเหมือนกัน และ `spamc -L spam` ใช้ได้กับ[เซิร์ฟเวอร์ spamd](https://spamscanner.net/th/docs/mail-servers/#a-drop-in-for-spamassassins-spamd) เมื่อเปิด `--allow-tell` [IMAPSieve ของ Dovecot](https://spamscanner.net/th/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');
```

ข้อความที่ถูกรายงานว่าจัดประเภทผิด ควรถูก unlearn ออกจากประเภทที่ผิดก่อนจะเรียนรู้ในประเภทที่ถูก หากเคยเรียนรู้ไว้แล้ว


## โมเดลที่มาพร้อมแพ็กเกจ

`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 ข้อความและ ham 76,489 ข้อความ สคริปต์กันทุกข้อความที่สิบไว้ ฝึกด้วยส่วนที่เหลือ และวัดผลตัวจำแนกอย่างเดียว โดยไม่มีการตรวจอื่น:

| ชุดทดสอบที่กันไว้ | ข้อความ | Precision | Recall | ผลบวกลวง | ไม่แน่ใจ |
| ----------------- | ------: | --------: | -----: | -------: | -------: |
| ภาษาอังกฤษ        |   6,564 |    100.0% |  97.0% |     0.0% |     2.4% |
| ภาษารัสเซีย       |   1,682 |    100.0% |  97.4% |     0.0% |     2.2% |
| ภาษาอิตาลี        |   1,389 |     98.1% |  85.3% |     1.8% |    10.9% |
| ภาษาเยอรมัน       |   1,309 |     97.7% |  76.1% |     2.2% |    20.7% |
| ภาษาสเปน          |   1,281 |     97.5% |  82.5% |     2.6% |    16.8% |
| Enron-Spam        |   2,888 |    100.0% |  93.1% |     0.0% |     4.5% |
| all-scam-spam     |   4,236 |    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 ภาษา ชุดข้อมูลนี้ไม่ได้รวมไว้ในโมเดลที่มาพร้อมแพ็กเกจ เพราะ dataset card ระบุสัญญาอนุญาต GPL ตรวจสอบว่าเหมาะกับวิธีที่คุณแบ่งปันโมเดลหรือไม่ เมื่อฝึกรวมชุดข้อมูลนี้ ผลทดสอบที่กันไว้สำหรับภาษาที่โมเดลที่มาพร้อมแพ็กเกจแทบไม่รู้จักเป็นดังนี้:

| ภาษา         | ข้อความ | Precision | Recall | ผลบวกลวง |
| ------------ | ------: | --------: | -----: | -------: |
| ภาษาจีน      |     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 MB

โมเดลจาก Spam Scanner 6 และก่อนหน้าโหลดไม่ได้ เพราะแฮชคุณลักษณะต่างกัน ให้ฝึกโมเดลใหม่จากอีเมลชุดเดิม
