Skip to content

Commit 2653002

Browse files
Gumbel-Softmax sampling. (huggingface#2894)
* Gumbel-Softmax sampling. * Add a sampling test. * Share the gumbel-softmax bits.
1 parent a52b76a commit 2653002

5 files changed

Lines changed: 54 additions & 1 deletion

File tree

candle-examples/examples/helium/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl TextGeneration {
4646
Sampling::ArgMax
4747
} else {
4848
match (top_k, top_p) {
49-
(None, None) => Sampling::All { temperature },
49+
(None, None) => Sampling::GumbelSoftmax { temperature },
5050
(Some(k), None) => Sampling::TopK { k, temperature },
5151
(None, Some(p)) => Sampling::TopP { p, temperature },
5252
(Some(k), Some(p)) => Sampling::TopKThenTopP { k, p, temperature },

candle-nn/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub mod ops;
3131
pub mod optim;
3232
pub mod rnn;
3333
pub mod rotary_emb;
34+
pub mod sampling;
3435
pub mod sequential;
3536
pub mod var_builder;
3637
pub mod var_map;

candle-nn/src/sampling.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use candle::{Result, Tensor};
2+
3+
/// Sample according to the Gumbel-Softmax distribution.
4+
pub fn gumbel_softmax<D: candle::shape::Dim>(
5+
logits: &Tensor,
6+
temperature: f64,
7+
dim: D,
8+
) -> Result<Tensor> {
9+
if temperature <= 0.0 {
10+
logits.argmax(dim)
11+
} else if temperature == 1.0 {
12+
let minus_g = logits.rand_like(1e-7, 0.999)?.log()?.neg()?.log()?;
13+
let sampled = (logits - minus_g)?.argmax(dim)?;
14+
Ok(sampled)
15+
} else {
16+
let minus_g = logits.rand_like(1e-7, 0.999)?.log()?.neg()?.log()?;
17+
let sampled = (logits + minus_g * (-temperature))?.argmax(dim)?;
18+
Ok(sampled)
19+
}
20+
}

candle-transformers/src/generation/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub enum Sampling {
1313
TopK { k: usize, temperature: f64 },
1414
TopP { p: f64, temperature: f64 },
1515
TopKThenTopP { k: usize, p: f64, temperature: f64 },
16+
// Note that the rng is not used for the Gumbel-Softmax sampling.
17+
GumbelSoftmax { temperature: f64 },
1618
}
1719

1820
pub struct LogitsProcessor {
@@ -49,6 +51,11 @@ impl LogitsProcessor {
4951
Ok(next_token)
5052
}
5153

54+
fn sample_gumbel_softmax(&mut self, logits: &Tensor, temperature: f64) -> Result<u32> {
55+
let sampled = candle_nn::sampling::gumbel_softmax(logits, temperature, candle::D::Minus1)?;
56+
sampled.to_vec0::<u32>()
57+
}
58+
5259
fn sample_multinomial(&mut self, prs: &Vec<f32>) -> Result<u32> {
5360
let distr = rand::distr::weighted::WeightedIndex::new(prs).map_err(Error::wrap)?;
5461
let next_token = distr.sample(&mut self.rng) as u32;
@@ -127,6 +134,9 @@ impl LogitsProcessor {
127134

128135
let next_token = match &self.sampling {
129136
Sampling::ArgMax => self.sample_argmax(logits)?,
137+
Sampling::GumbelSoftmax { temperature } => {
138+
self.sample_gumbel_softmax(&logits, *temperature)?
139+
}
130140
Sampling::All { temperature } => {
131141
let prs = prs(*temperature)?;
132142
self.sample_multinomial(&prs)?

candle-transformers/tests/generation_tests.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,25 @@ fn sample_with_top_k() -> Result<()> {
5454
assert_eq!(token, 2);
5555
Ok(())
5656
}
57+
58+
#[test]
59+
fn sample_gumbel() -> Result<()> {
60+
let mut logits_process = LogitsProcessor::from_sampling(
61+
42,
62+
candle_transformers::generation::Sampling::GumbelSoftmax { temperature: 1.0 },
63+
);
64+
let logits = Tensor::new(&[-1.0, 0.0, 0.2, 1.0], &Device::Cpu)?;
65+
let sm = candle_nn::ops::softmax(&logits, 0)?.to_vec1::<f64>()?;
66+
let mut counts = vec![0f64; 4];
67+
let samples = 100000;
68+
for _ in 0..samples {
69+
let token = logits_process.sample(&logits)?;
70+
counts[token as usize] += 1f64 / samples as f64;
71+
}
72+
for i in 0..4 {
73+
if (counts[i] - sm[i]).abs() > 0.05 {
74+
panic!("pr mismatch {counts:?} {sm:?}");
75+
}
76+
}
77+
Ok(())
78+
}

0 commit comments

Comments
 (0)