chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:19:01 +08:00
commit 3b90d1192f
2172 changed files with 594509 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
"""
---
title: Sampling Techniques for Language Models
summary: >
A set of PyTorch implementations/tutorials of sampling techniques for language models.
---
# Sampling Techniques for Language Models
* [Greedy Sampling](greedy.html)
* [Temperature Sampling](temperature.html)
* [Top-k Sampling](top_k.html)
* [Nucleus Sampling](nucleus.html)
Here's an [experiment](experiment.html) that uses these sampling techniques.
"""
import torch
class Sampler:
"""
### Sampler base class
"""
def __call__(self, logits: torch.Tensor) -> torch.Tensor:
"""
### Sample from logits
:param logits: are the logits of the distribution of shape `[..., n_tokens]`
"""
raise NotImplementedError()
+111
View File
@@ -0,0 +1,111 @@
"""
---
title: Trying out Sampling Techniques for Language Models
summary: >
We try out different sampling techniques for language models on HuggingFace's GPT2 model.
---
# Trying out Sampling Techniques for Language Models
* [Greedy Sampling](greedy.html)
* [Temperature Sampling](temperature.html)
* [Top-k Sampling](top_k.html)
* [Nucleus Sampling](nucleus.html)
This experiment uses the above sampling techniques, on HuggingFace's GPT2 model.
"""
import torch
from labml import monit, logger, lab
from labml.logger import Text
from labml_nn.sampling import Sampler
from labml_nn.sampling.greedy import GreedySampler
from labml_nn.sampling.nucleus import NucleusSampler
from labml_nn.sampling.temperature import TemperatureSampler
from labml_nn.sampling.top_k import TopKSampler
from transformers import GPT2Tokenizer, GPT2LMHeadModel
@torch.no_grad()
def sample(model: GPT2LMHeadModel, tokenizer: GPT2Tokenizer, sampler: Sampler,
n_samples: int, n_tokens: int, seq_len: int, prompt: str):
"""
## Sample from model
:param model: is the model to sample from
:param tokenizer: is the tokenizer to use
:param sampler: is the sampler to use
:param n_samples: is the number of samples to generate
:param n_tokens: is the number of tokens to generate
:param seq_len: is the maximum sequence length for the model
:param prompt: is the starting prompt
"""
# Tokenize the `prompt` and make `n_samples` copies of it
data = torch.tile(torch.tensor(tokenizer.encode(prompt))[None, :], (n_samples, 1))
# Collect output for printing
logs = [[(prompt, Text.meta)] for _ in range(n_samples)]
# Sample `n_tokens`
for i in monit.iterate('Sample', n_tokens):
# Truncate the data to the maximum sequence length
data = data[-seq_len:]
# Get the model output. The 'logits' has shape `[batch_size, seq_len, n_tokens]`
logits = model(data)[0]
# Get the `logits` of the last token
logits = logits[:, -1]
# Sample from the `logits`
res = sampler(logits)
# Add the sampled token to the data
data = torch.cat([data, res[:, None]], dim=1)
# Decode and add the sampled token for logging
for j in range(n_samples):
logs[j] += [('' + tokenizer.decode(res[j]), Text.value)]
# Print the sampled outputs
for j in range(n_samples):
logger.log(logs[j])
def main():
"""
### Try different sampling techniques
"""
# Load the model and tokenizer
with monit.section('Load tokenizer/model'):
tokenizer = GPT2Tokenizer.from_pretrained('gpt2', cache_dir=lab.get_data_path() / 'cache')
model = GPT2LMHeadModel.from_pretrained('gpt2', cache_dir=lab.get_data_path() / 'cache')
# Set the model to eval mode
model.eval()
# Prompts to use for sampling
prompt = 'I saw an interesting dream last night. '
# [Greedy Sampling](greedy.html)
with monit.section('greedy'):
sample(model, tokenizer, GreedySampler(), 4, 32, 128, prompt)
# [Temperature Sampling](temperature.html)
with monit.section('temperature=1.'):
sample(model, tokenizer, TemperatureSampler(1.), 4, 32, 128, prompt)
with monit.section('temperature=.1'):
sample(model, tokenizer, TemperatureSampler(.1), 4, 32, 128, prompt)
with monit.section('temperature=10.'):
sample(model, tokenizer, TemperatureSampler(10.), 4, 32, 128, prompt)
# [Top-k Sampling](top_k.html)
with monit.section('top_k=5'):
sample(model, tokenizer, TopKSampler(2, TemperatureSampler(1.)), 4, 32, 128, prompt)
# [Nucleus Sampling](nucleus.html)
with monit.section('nucleus p=.95'):
sample(model, tokenizer, NucleusSampler(0.95, TemperatureSampler(1.)), 4, 32, 128, prompt)
with monit.section('nucleus p=.1'):
sample(model, tokenizer, NucleusSampler(0.1, TemperatureSampler(1.)), 4, 32, 128, prompt)
#
if __name__ == '__main__':
main()
+82
View File
@@ -0,0 +1,82 @@
from typing import Tuple
import torch
from labml import experiment, monit
from labml import logger
from labml.logger import Text
from labml_nn.helpers.datasets import TextDataset
from labml_nn.sampling import Sampler
from labml_nn.sampling.greedy import GreedySampler
from labml_nn.sampling.nucleus import NucleusSampler
from labml_nn.sampling.temperature import TemperatureSampler
from labml_nn.sampling.top_k import TopKSampler
from labml_nn.transformers.basic.autoregressive_experiment import Configs, AutoregressiveTransformer
def get_model_dataset(run_uuid: str) -> Tuple[AutoregressiveTransformer, TextDataset]:
experiment.evaluate()
conf = Configs()
experiment.configs(conf, experiment.load_configs(run_uuid))
experiment.load(run_uuid)
experiment.add_pytorch_models({'model': conf.model})
experiment.start()
return conf.model, conf.text
def sample(model, ds, sampler: Sampler, n_samples: int, n_tokens: int, seq_len: int, prompt: str):
with torch.no_grad():
data = torch.tile(ds.text_to_i(prompt)[:, None], (1, n_samples))
# Collect output for printing
logs = [[(prompt, Text.meta)] for _ in range(n_samples)]
# Sample 25 tokens
for i in monit.iterate('Sample', n_tokens):
# Tokenize the prompt
data = data[-seq_len:]
# Get the model output
logits, *_ = model(data)
logits = logits[-1]
# Get the model prediction (greedy)
res = sampler(logits)
data = torch.cat([data, res[None, :]], dim=0)
# Add the prediction for logging
for j in range(n_samples):
logs[j] += [('' + ds.itos[res[j]], Text.value)]
# Print the sampled output
for j in range(n_samples):
logger.log(logs[j])
def main():
model, ds = get_model_dataset('074d4004cc6b11ecad7a0242ac1c0002')
model.eval()
with monit.section('greedy'):
sample(model, ds, GreedySampler(), 4, 32, 128, 'It is')
with monit.section('temperature=1.'):
sample(model, ds, TemperatureSampler(1.), 4, 32, 128, 'It is')
with monit.section('temperature=.1'):
sample(model, ds, TemperatureSampler(.1), 4, 32, 128, 'It is')
with monit.section('temperature=10.'):
sample(model, ds, TemperatureSampler(10.), 4, 32, 128, 'It is')
with monit.section('top_k=5'):
sample(model, ds, TopKSampler(2, TemperatureSampler(1.)), 4, 32, 128, 'It is')
with monit.section('nucles p=.95'):
sample(model, ds, NucleusSampler(0.95, TemperatureSampler(1.)), 4, 32, 128, 'It is')
with monit.section('nucles p=.95'):
sample(model, ds, NucleusSampler(0.1, TemperatureSampler(1.)), 4, 32, 128, 'It is')
if __name__ == '__main__':
main()
+24
View File
@@ -0,0 +1,24 @@
"""
---
title: Greedy Sampling
summary: A PyTorch implementation of greedy sampling from language models.
---
# Greedy Sampling
Here we sample the most likely token from the distribution of logits.
Here's an [experiment](experiment.html) that uses these sampling techniques.
"""
import torch
from labml_nn.sampling import Sampler
class GreedySampler(Sampler):
def __call__(self, logits: torch.Tensor):
"""
Sample the most likely token from the distribution of logits
"""
return logits.argmax(dim=-1)
+78
View File
@@ -0,0 +1,78 @@
"""
---
title: Nucleus Sampling
summary: A PyTorch implementation of nucleus sampling from language models.
---
# Nucleus Sampling
This is an implementation of nucleus sampling, introduced in the paper
[The Curious Case of Neural Text Degeneration](https://arxiv.org/abs/1904.09751).
The paper discusses the problems with other sampling methods such as Beam Search,
[Pure sampling](temperature.html), [Temperature sampling](temperature.html), and
[Top-k sampling](top_k.html). The paper introduces the idea of nucleus sampling,
which practically performs better than other sampling methods for text generation.
Nucleus sampling first picks a subset of the vocabulary $V^{(p)} \subset V$,
where $V^{(p)}$ is smallest set of tokens such that
$$\sum_{x_i \in V^{(p)}} P(x_i | x_{1:i-1}) \ge p$$
That is, we pick the highest probable tokens until the sum of their probabilities is less that $p$.
Then we sample from the selected tokens.
Here's an [experiment](experiment.html) that uses these sampling techniques.
"""
import torch
from torch import nn
from labml_nn.sampling import Sampler
class NucleusSampler(Sampler):
"""
## Nucleus Sampler
"""
def __init__(self, p: float, sampler: Sampler):
"""
:param p: is the sum of probabilities of tokens to pick $p$
:param sampler: is the sampler to use for the selected tokens
"""
self.p = p
self.sampler = sampler
# Softmax to compute $P(x_i | x_{1:i-1})$ from the logits
self.softmax = nn.Softmax(dim=-1)
def __call__(self, logits: torch.Tensor):
"""
Sample from logits with Nucleus Sampling
"""
# Get probabilities $P(x_i | x_{1:i-1})$
probs = self.softmax(logits)
# Sort probabilities in descending order
sorted_probs, indices = torch.sort(probs, dim=-1, descending=True)
# Get the cumulative sum of probabilities in the sorted order
cum_sum_probs = torch.cumsum(sorted_probs, dim=-1)
# Find the cumulative sums less than $p$.
nucleus = cum_sum_probs < self.p
# Prepend ones so that we add one token after the minimum number
# of tokens with cumulative probability less that $p$.
nucleus = torch.cat([nucleus.new_ones(nucleus.shape[:-1] + (1,)), nucleus[..., :-1]], dim=-1)
# Get log probabilities and mask out the non-nucleus
sorted_log_probs = torch.log(sorted_probs)
sorted_log_probs[~nucleus] = float('-inf')
# Sample from the sampler
sampled_sorted_indexes = self.sampler(sorted_log_probs)
# Get the actual indexes
res = indices.gather(-1, sampled_sorted_indexes.unsqueeze(-1))
#
return res.squeeze(-1)
+44
View File
@@ -0,0 +1,44 @@
"""
---
title: Sampling from Language Models with Temperature
summary: A PyTorch implementation of sampling from language models with temperature.
---
# Sampling from Language Models with Temperature
Here we sample from the following probability distribution where $V$ is the vocabulary,
$u_{1:|V|}$ are the logits of the distribution and T is the temperature:
$$P(x_i=V_l | x_{1:i-1}) = \frac{\exp(\frac{u_l}{T})}{\sum_j \exp(\frac{u_j}{T})}$$
$T = 1$ is normal random sampling.
Here's an [experiment](experiment.html) that uses these sampling techniques.
"""
import torch
from torch.distributions import Categorical
from labml_nn.sampling import Sampler
class TemperatureSampler(Sampler):
"""
## Sampler with Temperature
"""
def __init__(self, temperature: float = 1.0):
"""
:param temperature: is the temperature to sample with
"""
self.temperature = temperature
def __call__(self, logits: torch.Tensor):
"""
Sample from logits
"""
# Create a categorical distribution with temperature adjusted logits
dist = Categorical(logits=logits / self.temperature)
# Sample
return dist.sample()
+48
View File
@@ -0,0 +1,48 @@
"""
---
title: Top-k Sampling
summary: A PyTorch implementation of top-k sampling from language models.
---
# Top-k Sampling
Here we first pick the top-k tokens from the distribution of logits, and then
sample from them.
Here's an [experiment](experiment.html) that uses these sampling techniques.
"""
import torch
from labml_nn.sampling import Sampler
class TopKSampler(Sampler):
"""
## Top-k Sampler
"""
def __init__(self, k: int, sampler: Sampler):
"""
:param k: is the number of tokens to pick
:param sampler: is the sampler to use for the top-k tokens
`sampler` can be any sampler that takes a logits tensor as input and returns a token tensor;
e.g. [`TemperatureSampler'](temperature.html).
"""
self.k = k
self.sampler = sampler
def __call__(self, logits: torch.Tensor):
"""
Sample from logits
"""
# New logits filled with $-\infty$; i.e. zero probability
zeros = logits.new_ones(logits.shape) * float('-inf')
# Pick the largest $k$ logits and their indices
values, indices = torch.topk(logits, self.k, dim=-1)
# Set the values of the top-k selected indices to actual logits.
# Logits of other tokens remain $-\infty$
zeros.scatter_(-1, indices, values)
# Sample from the top-k logits with the specified sampler.
return self.sampler(zeros)