Metadata-Version: 2.5
Name: typingnorm
Version: 0.1.0
Summary: 人類打字的常模，以及據此判斷輸入是否為真人的判準。每條判準的誤判率都在獨立資料集上量過。
Project-URL: Homepage, https://github.com/gba3124/typingnorm
Project-URL: Source, https://github.com/gba3124/typingnorm
Project-URL: Method, https://github.com/gba3124/typingnorm/blob/main/METHOD.md
Author: MangoCore LLC
License-Expression: MIT
License-File: LICENSE
Keywords: bot-detection,human-verification,keystroke-dynamics,liveness,typing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Security
Requires-Python: >=3.9
Provides-Extra: desktop
Requires-Dist: pynput>=1.7; extra == 'desktop'
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# typingnorm

人類打字的常模，以及據此判斷一段輸入是否為真人的判準。

每條判準的誤判率都在一份獨立資料集上量過，數字寫在程式碼裡，方法寫在
[METHOD.md](METHOD.md)，擬合與驗證的腳本都在這個 repo 裡，你可以自己重跑。

```python
from typingnorm import check

report = check(events)      # events: [(毫秒, 'D'|'U', 實體鍵碼, key), ...]

report.verdict              # 'human-consistent' | 'synthetic-signals' | 'insufficient-data'
report.flags                # ['zero_rollover']
report.measures             # {'wpm': 61.2, 'rollover_pct': 0.0, 'dwell_cv': 0.31, ...}
report.false_positive_budget  # 0.0031，觸發判準當中最高的誤判率
```

## 判準

只有三條，而且都是刻意選的。

| 判準 | 內容 | 在 2,245 位真人上的誤判率 |
|---|---|---|
| `same_key_overlap` | 同一個實體鍵在放開前又被按下 | 0.00% |
| `constant_dwell` | 每個鍵按住的時間幾乎完全一樣 | 0.00% |
| `zero_rollover` | 完全沒有按鍵重疊（僅 35 WPM 以上套用） | 0.31% |

選擇的原則是**只用不受打字速度影響的判準**。打字快與慢的人，重疊率、間隔、按住時間
全都差很多，任何「離人類平均多遠」的判準都會系統性誤判速度極端的人，而那恰好是最
不該被誤傷的族群：密碼管理器、語音輸入、切換式存取、螢幕小鍵盤的使用者在時序上都
長得像機器人。

被排除在外的例子：換手比值（左右手交替是否比同手快）看起來是很好的特徵，實測卻在
慢速端反轉，用它當硬判準會誤判 15.46% 的真人。詳見 METHOD.md 第 7.2 節。

## 這個方法擋得住什麼、擋不住什麼

擋得住不用心的自動化。擋不住刻意模仿真人時序的程式，包括本套件自己的模擬器。

所以 `check()` 回傳的是訊號不是判決，也刻意不提供 `is_human` 這種布林值：那會誘導
呼叫端拿它當閘門。請把它當成風險分數的其中一個輸入，不要當成身分驗證。

樣本不足時會回 `insufficient-data` 而不是猜。少於 150 次擊鍵的統計不可靠，登入表單
那種二三十個按鍵的長度做不了任何判斷。

## 速度指標

同一份原始資料可以用不同國家的慣用指標計分，因為分數不儲存，只在顯示時現算。

```python
from typingnorm import score, load_session

s = load_session("session.json")
score(s)                    # 依語言自動選當地慣用指標
score(s, "kdph_ssc")        # 換成印度公職考試的單位
```

| 指標 | 單位 | 出處 |
|---|---|---|
| `cpm_tqc` | 字/分 | TQC 中文輸入，專業級 80 |
| `cpm_jp` | 字/分 | 全商速度部門，1 級 70 |
| `tasu` | 타수 | 워드프로세서 1급 300，按字母鍵計數 |
| `kdph_ssc` | KDPH | SSC 公職考試，10,500 = 35 WPM |
| `wpm_net_5` | WPM | 英文慣例，五字元一詞 |
| `wpm_4` | คำ/นาที | 泰文慣例，四字元一詞 |
| `kpm` | 打鍵/分 | 跨語言比較用的共同分母 |

標為 `authoritative=False` 的指標沒有官方標準，介面上不應出現「合格」「專業級」
這類字眼。

## 模擬器

`typingnorm.simulate` 從同一份常模抽樣，產生帶時間戳的按鍵事件流。它存在的主要理由
是給偵測端當測試對手：本套件的測試就是拿它產生的真人樣本，驗證判準不會誤判。

```bash
python -m typingnorm.simulate --validate    # 輸出分佈與常模對帳
python -m typingnorm.simulate --selftest    # 修正邏輯與按鍵序列合法性
```

## 資料來源

常模擬合自 KeyRecs（99 位受試者、562,372 筆 digraph，CC BY 4.0，**允許商業使用**）：

> Dias, T., Vitorino, J., Maia, E., Sousa, O., & Praça, I. (2023). KeyRecs: A keystroke
> dynamics and typing pattern recognition dataset. *Data in Brief*, 50, 109509.

誤判率則在 Aalto 136M Keystrokes 的 2,245 位受試者上獨立量測。該資料集授權限研究與
非商業用途，**其導出參數未進入本套件**，僅用於驗證，且驗證結果不回頭調整參數。

## 授權

MIT。
