rishanthrajendhran commited on
Commit
4607b19
·
verified ·
1 Parent(s): cc9ecee

Card: strict '<' flag rule, format-assignment wording, vLLM section; thresholds.json flag rule

Browse files
Files changed (2) hide show
  1. README.md +40 -2
  2. thresholds.json +1 -1
README.md CHANGED
@@ -88,9 +88,46 @@ print(p_human(text))
88
 
89
  Build the prompt string exactly as above rather than through the chat template.
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  ### Thresholds
92
 
93
- ProseLens flags a document as AI-written when P(human) is at or below a cut. Each cut is set so
94
  that a given share of human documents is flagged (the false-positive rate, FPR), measured on the 80,000 human
95
  documents in IdeaLens-1M's `calibration` split (10,000 per format). The paper's operating point is the global
96
  cut at 1% FPR.
@@ -100,7 +137,8 @@ cut at 1% FPR.
100
  | Global cut | 0.07082 | 0.30469 | 0.60539 | 0.92502 | 0.99889 |
101
 
102
  Per-format cuts give each format its own operating point. They need the document's format, which the paper
103
- assigns with WebOrganizer's FormatClassifier (FormatClassifier-NoURL when there is no URL). Each is the
 
104
  format's own quantile, shrunk toward the global cut with weight n / (n + 2500); at 0.1% FPR 10,000 documents
105
  per format are too few, so there is no per-format cut. A document outside these eight formats has no
106
  per-format cut; do not fall back to the global cut for it.
 
88
 
89
  Build the prompt string exactly as above rather than through the chat template.
90
 
91
+ ### Score with vLLM
92
+
93
+ For many inputs, vLLM is about 15 times faster than the code above and fits much longer inputs on one 80 GB GPU.
94
+ With vLLM 0.21 (install `xgrammar==0.2.1`; later releases require transformers < 5), reusing `SYSTEM`, `SUFFIX`,
95
+ `HUMAN` and `AI` from above:
96
+
97
+ ```python
98
+ import math, os
99
+ os.environ.setdefault("VLLM_USE_FLASHINFER_SAMPLER", "0") # FlashInfer kernels compile CUDA code and need nvcc
100
+ os.environ.setdefault("VLLM_USE_FLASHINFER_MOE_FP16", "0")
101
+ os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0") # H100 warmup crashes when DeepGEMM is not installed
102
+ from transformers import AutoTokenizer
103
+ from vllm import LLM, SamplingParams
104
+
105
+ tok = AutoTokenizer.from_pretrained("rishanthrajendhran/ProseLens")
106
+ llm = LLM(model="rishanthrajendhran/ProseLens", dtype="bfloat16", max_num_seqs=256, enable_prefix_caching=False,
107
+ max_logprobs=20, enable_flashinfer_autotune=False, seed=0)
108
+ sp = SamplingParams(max_tokens=1, temperature=0.0, logprobs=20)
109
+
110
+ def p_human_batch(texts):
111
+ prompts = [{"prompt_token_ids": tok.encode(f"<|im_start|>system\n{SYSTEM}<|im_end|>\n<|im_start|>user\n{x}{SUFFIX}",
112
+ add_special_tokens=False)} for x in texts]
113
+ out = []
114
+ for r in llm.generate(prompts, sp, use_tqdm=False):
115
+ lp = r.outputs[0].logprobs[0] # the top 20 next-token log-probabilities
116
+ out.append(1 / (1 + math.exp(lp[AI].logprob - lp[HUMAN].logprob)))
117
+ return out
118
+
119
+ # when done: without this, vLLM 0.21 keeps a script running after its last line
120
+ llm.llm_engine.engine_core.shutdown()
121
+ ```
122
+
123
+ `max_num_seqs=256` keeps every running sequence's Mamba state in memory; vLLM's H100 default (1,024) does not fit
124
+ beside the weights. If `human` or `ai` is missing from the top 20 (rare), score the prompt followed by each label
125
+ token with `SamplingParams(max_tokens=1, prompt_logprobs=0)` and read the last prompt log-probability of each.
126
+ Scores agree with the training-time scores to about 0.001 in P(human) on average; A100 and H100 GPUs differ by as much.
127
+
128
  ### Thresholds
129
 
130
+ ProseLens flags a document as AI-written when P(human) is below a cut. Each cut is set so
131
  that a given share of human documents is flagged (the false-positive rate, FPR), measured on the 80,000 human
132
  documents in IdeaLens-1M's `calibration` split (10,000 per format). The paper's operating point is the global
133
  cut at 1% FPR.
 
137
  | Global cut | 0.07082 | 0.30469 | 0.60539 | 0.92502 | 0.99889 |
138
 
139
  Per-format cuts give each format its own operating point. They need the document's format, which the paper
140
+ assigns with WebOrganizer's annotation prompt run on Gemini 3.7 Flash; the calibration documents use the formats
141
+ recorded in IdeaLens-1M. Each is the
142
  format's own quantile, shrunk toward the global cut with weight n / (n + 2500); at 0.1% FPR 10,000 documents
143
  per format are too few, so there is no per-format cut. A document outside these eight formats has no
144
  per-format cut; do not fall back to the global cut for it.
thresholds.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "model": "rishanthrajendhran/ProseLens",
3
  "input": "document",
4
- "flag_rule": "flag the document as AI when P(human) <= cut",
5
  "calibration": {
6
  "data": "IdeaLens-1M, calibration split (human documents only)",
7
  "scores_from": "the training checkpoint, scored on Tinker",
 
1
  {
2
  "model": "rishanthrajendhran/ProseLens",
3
  "input": "document",
4
+ "flag_rule": "flag the document as AI when P(human) < cut",
5
  "calibration": {
6
  "data": "IdeaLens-1M, calibration split (human documents only)",
7
  "scores_from": "the training checkpoint, scored on Tinker",