Download presentation/config.yaml from BuildITSystems/phishing-detector-encoder: direct link, hf CLI and curl.
- Browser
- Download file 12.6 kB
-
https://huggingface.co/BuildITSystems/phishing-detector-encoder/resolve/main/presentation/config.yaml
- Command line
-
hf download hf://BuildITSystems/phishing-detector-encoder/presentation/config.yaml
-
curl -L -o config.yaml https://huggingface.co/BuildITSystems/phishing-detector-encoder/resolve/main/presentation/config.yaml
12.6 kB
| # BITS Phishing Detector — all dials in one place. | |
| # Verified pins and the reasoning behind every number: docs/research/*.md (2026-10-07). | |
| seed: 42 | |
| paths: | |
| data_raw: data/raw | |
| data_processed: data/processed | |
| tokenizer: data/processed/tokenizer.json | |
| checkpoints: checkpoints | |
| reports: reports | |
| logs: logs | |
| app_dir: app | |
| # ---------------------------------------------------------------- model (Step 3) | |
| model: | |
| # "Large" BERT-style pre-LN encoder. 24L/1024/16/4096/32768/512 = 337,476,608 params with the | |
| # tied MLM head, 337,443,842 as the deployed classifier (+2.3% over the ~330M target). | |
| # Switch n_layers to 23 (324,880,384) only if ~330M must be a hard ceiling. | |
| n_layers: 24 | |
| d_model: 1024 | |
| n_heads: 16 # head_dim 64 -> Flash-SDPA eligible on sm_86 | |
| d_ff: 4096 | |
| vocab_size: 32768 # 2^15, tensor-core aligned | |
| max_seq_len: 512 | |
| dropout: 0.1 | |
| attention_dropout: 0.1 | |
| norm: pre_ln | |
| activation: gelu | |
| tie_mlm_head: true | |
| grad_checkpointing: false # B=16 fits without it; checkpointing costs ~1.33x compute | |
| # ---------------------------------------------------------------- tokenizer | |
| tokenizer: | |
| type: byte_level_bpe | |
| vocab_size: 32768 | |
| min_frequency: 2 | |
| special_tokens: ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] | |
| # Trained ONLY on the pretraining corpus + fine-tune train split. Never on val/test. | |
| train_on: [pretrain, finetune_train] | |
| # ---------------------------------------------------------------- data (Step 2 / data_prep) | |
| data: | |
| labelled_primary: puyang2025/seven-phishing-email-datasets # 203,017 rows; text/subject/label/dataset_name | |
| labelled_excluded: | |
| - it4lia/PhishingEmailCuratedDatasets_Cleaned # no body text (subjects + 53 numeric features only) | |
| - zefang-liu/phishing-email-dataset # 91.9% exact subset of the primary set, lossy text | |
| pretrain_corpus: corbt/enron-emails # 517,401 rows -> ~244k after exact dedup | |
| holdout_test_source: TREC-06 # never seen in tokenizer, pretraining, or fine-tuning | |
| second_choice_holdout: CEAS-08 # only after stripping mailing-list footers | |
| # Positive class = label 1 of the primary set ("phishing or spam / unwanted"). All label-1 rows | |
| # come from spam-track corpora with no spam-vs-phishing distinction; mapping spam->0 would leave | |
| # ~0 positives. PENDING Nathan's confirmation (see RUN_LOG.md entry 0). | |
| spam_policy: positive_class | |
| min_alnum_chars: 20 # labelled rows: min alphanumerics in subject+body | |
| pretrain_min_alnum_chars: 50 # corbt bodies shorter than this (after quote stripping) are dropped | |
| quote_strip_min_alnum: 200 # cut a quoted/forwarded tail only if this much text survives above it | |
| dedup: | |
| normalize: "NFKC; lowercase; keep [a-z0-9] only (common.normalize_text)" | |
| keys: [full_sha1, prefix300_sha1, subject_body_prefix300_sha1, subject_body_full_sha1] | |
| key_prefix_chars: 300 | |
| key_prefix_min_len: 80 | |
| near_dup_shingle_chars: 50 | |
| near_dup_stride: 25 | |
| near_dup_min_shared: 3 | |
| near_dup_window_chars: 1500 | |
| near_dup_shingle_cap: 2000 # shingles shared by more rows than this are boilerplate; ignored and counted | |
| finetune_val_fraction: 0.10 # grouped by near-dup cluster, stratified by source x label | |
| model_input: "subject + \"\\n\\n\" + body" # never sender/receiver (TREC headers are synthetic) | |
| synthetic: | |
| enabled: false # <= 2,000 rows, source=synthetic, training-only; results reported with/without | |
| max_rows: 2000 | |
| # Three-way labels (RUN_LOG entry 6; data_prep.py build3 / check3). label3: 0 legitimate / 1 phishing / 2 spam / | |
| # -1 unwanted-but-unsplit; label_set: legit / phish / spam / unwanted. The binary `label` column is unchanged. | |
| threeway: | |
| # Provenance of a binary-positive row -> label3. TREC-05/06/07 and CEAS-08 never separated spam from phishing | |
| # (-1: trains on P(phishing)+P(spam)); SpamAssassin / Enron-Spam / Ling label-1 rows are spam folders (2); | |
| # Nazario and Nigerian_Fraud are phishing corpora by construction (1). Every label-0 row is 0. | |
| positive_label3_by_source: | |
| Assassin: 2 | |
| Enron: 2 | |
| Ling: 2 | |
| TREC-05: -1 | |
| TREC-06: -1 | |
| TREC-07: -1 | |
| CEAS-08: -1 | |
| Nazario: 1 | |
| Nigerian_Fraud: 1 | |
| champa: # Champa et al. raw corpus mirror (CC-BY-4.0, byte-identical to Zenodo 8339691) | |
| repo: kudzaiprichard/aura-phishing-email-corpus | |
| files: [Nazario.csv, Nigerian_Fraud.csv] | |
| local_dir: data/raw/aura | |
| label3: 1 | |
| bigshp: # locuoco 300k set: research verdict DO_NOT_USE (docs/research/bigshp_research.md) | |
| enabled: false # set true only on a USE / USE_PARTIALLY verdict; the filter chain below then applies | |
| verdict: DO_NOT_USE | |
| repo: locuoco/the-biggest-spam-ham-phish-email-dataset-300000 | |
| file: df.csv | |
| local_dir: data/raw/bigshp | |
| drop_phish_class: true # its phish label is source-derived (87% spam-folder / spam-track mail) and stopword-stripped | |
| min_chars: 160 # SMS-like fragments | |
| min_english_fraction: 0.7 # label-2 rows: share of word tokens in our legitimate-mail vocabulary | |
| near_dup_token_key_len: 30 # within-file near-dup key: first 30 word tokens | |
| new_source_test_fraction: 0.15 # Nazario + Nigerian_Fraud: cluster-grouped slice -> test_holdout3 | |
| new_source_val_fraction: 0.10 # of the remainder -> finetune_val | |
| # A new near-dup cluster that links to BOTH train (or the MLM corpus) AND val is a 419 template the primary set | |
| # already holds on both sides; dropping it keeps train_vs_val_shingle_links at the binary build's 0 (costs ~424 | |
| # Nigerian_Fraud rows). false = pin such clusters to train (adds ~226 info-only train/val near-dup links). | |
| drop_new_clusters_linked_to_both_train_and_val: true | |
| holdout3_from_val: # clean-provenance legit / spam rows moved from finetune_val into test_holdout3 | |
| legit_sources: [CEAS-08, TREC-07, Assassin] | |
| spam_sources: [Assassin, Enron] | |
| legit_rows: 2000 | |
| spam_rows: 700 | |
| # exclusions: keys or >=3-shingle links into the pretraining corpus, clusters also in finetune_train, | |
| # clusters touched by a new row, clusters with an ineligible member (whole clusters move or none) | |
| display_names: {0: Legitimate, 1: Phishing, 2: Spam} | |
| binary_rollup_name: "Unwanted (phishing or spam)" | |
| # ---------------------------------------------------------------- VRAM policy | |
| vram: | |
| target_gb_low: 14 | |
| target_gb_high: 18 | |
| hard_cap_gb: 20 | |
| alloc_conf: "expandable_segments:True" | |
| log_peak_every_steps: 20 | |
| # ---------------------------------------------------------------- Phase 1: MLM pretraining | |
| pretrain: | |
| precision: bf16_autocast | |
| sequence_packing: true # no attn_mask -> Flash SDPA backend | |
| effective_batch_sequences: 256 # 256 x 512 = 131,072 tokens/step (BERT setting) | |
| micro_batch: auto # batch finder picks the largest candidate whose reserved peak is in the VRAM band | |
| micro_batch_start: 24 # Nathan's choice 2026-10-08 (17.65 GiB reserved, 25.2k tok/s); 256/24 -> accumulation 11 = effective 264 | |
| micro_batch_candidates: [16, 24, 12, 8] | |
| epochs: 12 # Nathan's choice 2026-10-08: ~14 h at 25k tok/s, ~9,550 steps; val MLM loss watched for a rise | |
| optimizer: adamw | |
| lr: 1.5e-4 | |
| betas: [0.9, 0.98] | |
| eps: 1.0e-6 | |
| weight_decay: 0.01 # excluded for biases and LayerNorm | |
| grad_clip: 1.0 | |
| warmup_steps: 1000 # cap; actual warmup = min(warmup_steps, warmup_fraction x total steps) | |
| warmup_fraction: 0.06 # 3 epochs (~2,460 steps) -> ~148 warmup steps; 6 epochs -> ~295 | |
| schedule: cosine_to_10pct | |
| mlm_probability: 0.15 | |
| mask_replace_probs: [0.8, 0.1, 0.1] # [MASK] / random token / unchanged | |
| loss_on_masked_only: true # gather masked positions before the MLM head | |
| checkpoint_every_steps: 500 | |
| log_every_steps: 20 | |
| eval_every_steps: 500 | |
| # ---------------------------------------------------------------- Phase 2: fine-tuning (the instructor's dials) | |
| finetune: | |
| # Three-way head (RUN_LOG entry 6): 0 Legitimate / 1 Phishing / 2 Spam. Lives here, NOT under model:, because the | |
| # live pretraining run refuses to resume if its model snapshot changes. 2 reproduces the binary baseline runs. | |
| # Rows with label_set "unwanted" (TREC-05/07, CEAS-08 positives) train on P(phishing)+P(spam) (partial-label NLL). | |
| num_labels: 3 | |
| epochs: 3 | |
| batch_size: 32 # examples per optimizer step (= micro_batch x accumulation) | |
| micro_batch: 16 # rows per forward/backward; measured ~11.8 GiB reserved at 16 x 512 | |
| eval_batch_size: 64 | |
| lr: 3.0e-5 | |
| weight_decay: 0.01 | |
| betas: [0.9, 0.98] | |
| eps: 1.0e-6 | |
| grad_clip: 1.0 | |
| warmup_fraction: 0.06 | |
| schedule: linear_decay | |
| eval_every_steps: 500 | |
| log_every_steps: 20 | |
| loss: cross_entropy # experiment set: cross_entropy | weighted_cross_entropy | focal (all over the partial-label NLL) | |
| class_weights: auto # inverse-frequency over fully-labelled rows when loss == weighted_cross_entropy; | |
| # "unwanted" rows get mean(w_phishing, w_spam) | |
| focal_gamma: 2.0 | |
| early_stopping: | |
| # val_f1_unwanted = F1 of the binary roll-up P(phishing)+P(spam) >= 0.5 (identical to the binary runs' val F1, | |
| # so results stay comparable); alternative: val_macro_f1_3way (macro-F1 over the three classes on val rows | |
| # with a full label). | |
| metric: val_f1_unwanted | |
| patience: 2 | |
| pooling: cls | |
| freeze_encoder_layers: 0 # experiment dial (instructor: "number of layers") | |
| dynamic_padding_multiple: 8 | |
| checkpoint_every_steps: 500 | |
| # ---------------------------------------------------------------- evaluation | |
| eval: | |
| metrics: [accuracy, precision, recall, f1, roc_auc, pr_auc, confusion_matrix, recall_at_fpr_1pct, recall_at_fpr_0_1pct] | |
| credential_lure_slice: true # secondary slice of the holdout matching credential-lure phrases | |
| latency_samples: 100 # avg ms/email, GPU and CPU, like the instructor's step 5 | |
| latency_max_len: 512 | |
| threshold: 0.5 # decision threshold; evaluate.py also reports the best-F1 threshold | |
| # Three-way evaluation (RUN_LOG entry 6). The binary metrics above are unchanged: they are computed on the | |
| # roll-up P(unwanted) = P(phishing) + P(spam) (= P(class 1) for a 2-class head), so results stay comparable | |
| # with the binary-era runs. Three-way metrics use only rows with label3 in {0,1,2}; -1/"unwanted" rows | |
| # (TREC-05 / TREC-07 / CEAS-08 positives) enter the roll-up only. | |
| test3_file: test_holdout3.parquet # clean-provenance three-way holdout written by data_prep (--split test3); read by evaluate.py | |
| # The three keys below RESTATE the label contract that is fixed in code (evaluate.LABEL_NAMES / UNWANTED_NAME / | |
| # DECISION_RULE, mirrored in export.py and the app). evaluate.py asserts at startup that they still match and | |
| # refuses to run otherwise, so a drift is loud; editing them here does not rename anything. | |
| label_names: {0: Legitimate, 1: Phishing, 2: Spam} | |
| unwanted_name: "Unwanted (phishing or spam)" | |
| threeway_decision_rule: rollup_then_argmax # unwanted iff P(unwanted) >= threshold, then argmax(P(phishing), P(spam)); argmax-of-3 reported alongside | |
| # documentation only: the sections every evaluated three-way block contains (not read by any script) | |
| metrics_threeway: [confusion_matrix_3x3, per_class_precision_recall_f1, macro_f1, phishing_vs_spam_on_true_unwanted, per_source_counts] | |
| top_k_confusions: 25 # read by evaluate.py: size of the phishing->spam / spam->phishing preview tables in error_analysis_test3.md | |
| model_card_metrics3: reports/metrics_test3.json # read by export.py: three-way table source (binary table: reports/metrics.json); both files must name the exported checkpoint | |
| # ---------------------------------------------------------------- Hugging Face (ask before any push) | |
| hf: | |
| owner: BuildITSystems | |
| model_repo: BuildITSystems/phishing-detector-encoder | |
| space_repo: BuildITSystems/phishing-detector-demo | |
| private: true | |
| space_sdk: gradio | |
| space_sdk_version: "6.29.1" | |
| space_python_version: "3.12.12" # ZeroGPU supports exactly 3.12.12 / 3.10.13 | |
| space_hardware: zero-a10g # ZeroGPU flavor string (backing GPU: RTX Pro 6000 Blackwell slice) | |
| space_torch: "2.13.0" # highest version on the ZeroGPU allow-list (2.14.x not listed) | |
| weights_format: safetensors_bf16 | |