Compute perplexity for arbitrarily long *text* using a sliding‑window approach. Parameters ---------- text : str The input string (any length). model_name : str, optional HF Hub id of the model to use, by default "meta-llama/Llama-2-7b-hf". stride : int, optional
(
text: str,
model_name: str = "meta-llama/Llama-2-7b-hf",
stride: int = 512,
)
| 550 | return math.exp(-sum(valid) / len(valid)) |
| 551 | |
| 552 | def get_ppl( |
| 553 | text: str, |
| 554 | model_name: str = "meta-llama/Llama-2-7b-hf", |
| 555 | stride: int = 512, |
| 556 | ) -> float: |
| 557 | """Compute perplexity for arbitrarily long *text* using a sliding‑window approach. |
| 558 | |
| 559 | Parameters |
| 560 | ---------- |
| 561 | text : str |
| 562 | The input string (any length). |
| 563 | model_name : str, optional |
| 564 | HF Hub id of the model to use, by default "meta-llama/Llama-2-7b-hf". |
| 565 | stride : int, optional |
| 566 | Overlap between successive windows. 512 tends to work well for most |
| 567 | Transformer LMs with a 2 k context. Increase it for higher accuracy at |
| 568 | the cost of more compute. |
| 569 | |
| 570 | Returns |
| 571 | ------- |
| 572 | float |
| 573 | Per‑token perplexity under the given model. |
| 574 | """ |
| 575 | # Load tokenizer / model once per call (cache makes subsequent calls cheap) |
| 576 | tokenizer = AutoTokenizer.from_pretrained(model_name) |
| 577 | model = AutoModelForCausalLM.from_pretrained( |
| 578 | model_name, |
| 579 | torch_dtype=torch.float16, |
| 580 | device_map="auto", # place on GPU if available |
| 581 | ) |
| 582 | model.eval() |
| 583 | |
| 584 | # Encode the whole string in one shot |
| 585 | encodings = tokenizer(text, return_tensors="pt") |
| 586 | input_ids = encodings.input_ids[0] |
| 587 | |
| 588 | # Model context length (e.g. 2048 for Llama‑2) |
| 589 | max_len = model.config.max_position_embeddings |
| 590 | |
| 591 | # --- Short input: fits in a single window -------------------------------- |
| 592 | if input_ids.size(0) <= max_len: |
| 593 | with torch.no_grad(): |
| 594 | out = model(input_ids.unsqueeze(0).to(model.device), labels=input_ids.unsqueeze(0).to(model.device)) |
| 595 | return torch.exp(out.loss).item() |
| 596 | |
| 597 | # --- Long input: sliding window with overlap ----------------------------- |
| 598 | nlls = [] # negative‑log‑likelihoods (already multiplied by #tokens scored) |
| 599 | for i in range(0, input_ids.size(0), stride): |
| 600 | begin_loc = max(i + stride - max_len, 0) |
| 601 | end_loc = min(i + stride, input_ids.size(0)) |
| 602 | trg_len = end_loc - i # tokens we actually score in this window |
| 603 | |
| 604 | ids_chunk = input_ids[begin_loc:end_loc] |
| 605 | labels = ids_chunk.clone() |
| 606 | labels[:-trg_len] = -100 # mask out purely‑context tokens |
| 607 | |
| 608 | with torch.no_grad(): |
| 609 | out = model(ids_chunk.unsqueeze(0).to(model.device), labels=labels.unsqueeze(0).to(model.device)) |
no outgoing calls
no test coverage detected