rust_bert/models/t5/
attention.rs

1// Copyright 2018 Mesh TensorFlow authors, T5 Authors and HuggingFace Inc. team.
2// Copyright 2020 Guillaume Becquin
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//     http://www.apache.org/licenses/LICENSE-2.0
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use crate::common::dropout::Dropout;
14use crate::t5::layer_norm::T5LayerNorm;
15use crate::t5::T5Config;
16use std::borrow::Borrow;
17use tch::nn::LinearConfig;
18use tch::{nn, Device, Kind, Tensor};
19
20#[derive(Debug)]
21/// # Cache for T5 attention layers
22/// Stores the cached value of key, value and key to avoid recalculation (e.g. at each generation step)
23pub struct LayerState {
24    /// Cached keys
25    pub prev_key: Tensor,
26    /// Cached values
27    pub prev_value: Tensor,
28}
29
30impl Clone for LayerState {
31    fn clone(&self) -> Self {
32        LayerState {
33            prev_key: self.prev_key.copy(),
34            prev_value: self.prev_value.copy(),
35        }
36    }
37}
38
39impl LayerState {
40    pub(crate) fn reorder_cache(&mut self, new_indices: &Tensor) {
41        self.prev_key = self.prev_key.index_select(0, new_indices);
42        self.prev_value = self.prev_value.index_select(0, new_indices);
43    }
44}
45
46pub fn get_relative_position_bucket(
47    relative_position: &Tensor,
48    bidirectional: bool,
49    num_buckets: i64,
50    max_distance: i64,
51) -> Tensor {
52    let n = -relative_position;
53    let mut num_buckets = num_buckets;
54    let mut ret = n.zeros_like();
55    let n = if bidirectional {
56        num_buckets /= 2;
57        ret += n.lt(0).to_kind(Kind::Int64) * num_buckets;
58        n.abs()
59    } else {
60        n.max_other(&n.zeros_like())
61    };
62
63    let max_exact = num_buckets / 2;
64    let is_small = n.lt(max_exact);
65
66    let value_if_large: Tensor = ((n.to_kind(Kind::Float) / max_exact as f64).log2()
67        / (max_distance as f64 / max_exact as f64).log2()
68        * (num_buckets - max_exact) as f64)
69        .to_kind(Kind::Int64)
70        + max_exact;
71
72    let value_if_large = value_if_large.min_other(&value_if_large.full_like(num_buckets - 1));
73    ret += n.where_self(&is_small, &value_if_large);
74    ret
75}
76
77#[derive(Debug)]
78pub struct T5Attention {
79    is_decoder: bool,
80    is_bidirectional: bool,
81    has_relative_attention_bias: bool,
82    relative_attention_num_buckets: i64,
83    relative_attention_max_distance: i64,
84    d_kv: i64,
85    n_heads: i64,
86    dropout: Dropout,
87    inner_dim: i64,
88    output_attentions: bool,
89    store_cache: bool,
90    query: nn::Linear,
91    key: nn::Linear,
92    value: nn::Linear,
93    output: nn::Linear,
94    relative_attention_bias: Option<nn::Embedding>,
95}
96
97impl T5Attention {
98    pub fn new<'p, P>(
99        p: P,
100        config: &T5Config,
101        is_decoder: bool,
102        is_bidirectional: bool,
103        store_cache: bool,
104        output_attentions: bool,
105        has_relative_attention_bias: bool,
106    ) -> T5Attention
107    where
108        P: Borrow<nn::Path<'p>>,
109    {
110        let p = p.borrow();
111
112        let linear_config = LinearConfig {
113            bias: false,
114            ..Default::default()
115        };
116
117        let inner_dim = config.num_heads * config.d_kv;
118        let key = nn::linear(p / "k", config.d_model, inner_dim, linear_config);
119        let value = nn::linear(p / "v", config.d_model, inner_dim, linear_config);
120        let query = nn::linear(p / "q", config.d_model, inner_dim, linear_config);
121        let output = nn::linear(p / "o", inner_dim, config.d_model, linear_config);
122
123        let dropout = Dropout::new(config.dropout_rate);
124        let relative_attention_bias = if has_relative_attention_bias {
125            Some(nn::embedding(
126                p / "relative_attention_bias",
127                config.relative_attention_num_buckets,
128                config.num_heads,
129                Default::default(),
130            ))
131        } else {
132            None
133        };
134
135        T5Attention {
136            is_decoder,
137            is_bidirectional,
138            has_relative_attention_bias,
139            relative_attention_num_buckets: config.relative_attention_num_buckets,
140            relative_attention_max_distance: config.relative_attention_max_distance.unwrap_or(128),
141            d_kv: config.d_kv,
142            n_heads: config.num_heads,
143            dropout,
144            inner_dim,
145            output_attentions,
146            store_cache,
147            query,
148            key,
149            value,
150            output,
151            relative_attention_bias,
152        }
153    }
154
155    fn unshape(&self, x: Tensor, bs: i64) -> Tensor {
156        x.transpose(1, 2)
157            .contiguous()
158            .view((bs, -1, self.inner_dim))
159    }
160
161    fn shape(&self, x: Tensor, bs: i64) -> Tensor {
162        x.view((bs, -1, self.n_heads, self.d_kv)).transpose(1, 2)
163    }
164
165    pub fn forward_t(
166        &self,
167        hidden_states: &Tensor,
168        key_value_states: Option<&Tensor>,
169        position_bias: Option<&Tensor>,
170        attention_mask: Option<&Tensor>,
171        mut layer_state: Option<LayerState>,
172        query_length: Option<i64>,
173        train: bool,
174    ) -> (Tensor, Option<Tensor>, Option<Tensor>, Option<LayerState>) {
175        let input_size = hidden_states.size();
176        let (bs, seq_length) = (input_size[0], input_size[1]);
177
178        let real_seq_length = if layer_state.is_some() {
179            match query_length {
180                Some(value) => value,
181                None => seq_length + layer_state.as_ref().unwrap().prev_key.size()[2],
182            }
183        } else {
184            seq_length
185        };
186
187        let key_length = match key_value_states {
188            Some(value) => value.size()[1],
189            None => real_seq_length,
190        };
191
192        let q: Tensor = self.shape(hidden_states.as_ref().apply(&self.query), bs);
193
194        let (mut k, mut v) = if let Some(key_value_states_value) = key_value_states {
195            (
196                self.shape(key_value_states_value.apply(&self.key), bs),
197                self.shape(key_value_states_value.apply(&self.value), bs),
198            )
199        } else {
200            (
201                self.shape(hidden_states.apply(&self.key), bs),
202                self.shape(hidden_states.apply(&self.value), bs),
203            )
204        };
205
206        if layer_state.is_some() {
207            let layer_state = layer_state.as_ref().unwrap();
208            if key_value_states.is_none() {
209                k = Tensor::cat(&[&layer_state.prev_key, &k], 2);
210                v = Tensor::cat(&[&layer_state.prev_value, &v], 2);
211            } else {
212                k = layer_state.prev_key.copy();
213                v = layer_state.prev_value.copy();
214            }
215        };
216
217        layer_state = if self.is_decoder & self.store_cache {
218            Some(LayerState {
219                prev_key: k.copy(),
220                prev_value: v.copy(),
221            })
222        } else {
223            None
224        };
225
226        let mut scores = Tensor::einsum("bnqd,bnkd->bnqk", &[q, k], None::<i64>);
227
228        let calculated_position_bias = if position_bias.is_none() {
229            let mut temp_value = if self.has_relative_attention_bias {
230                self.compute_bias(real_seq_length, key_length, hidden_states.device())
231            } else {
232                Tensor::zeros(
233                    [1, self.n_heads, real_seq_length, key_length],
234                    (scores.kind(), scores.device()),
235                )
236            };
237            if layer_state.is_some() {
238                let length = temp_value.size()[2];
239                temp_value = temp_value.slice(2, length - seq_length, length, 1);
240            };
241            if let Some(attention_mask) = attention_mask {
242                temp_value = temp_value + attention_mask
243            };
244            Some(temp_value)
245        } else {
246            None
247        };
248
249        let position_bias = if let Some(position_bias) = position_bias {
250            position_bias
251        } else {
252            calculated_position_bias.as_ref().unwrap()
253        };
254
255        scores += position_bias;
256
257        let attention_weights = scores
258            .softmax(-1, scores.kind())
259            .apply_t(&self.dropout, train);
260        let context = self
261            .unshape(attention_weights.matmul(&v), bs)
262            .apply(&self.output);
263
264        let attention_weights = if self.output_attentions {
265            Some(attention_weights)
266        } else {
267            None
268        };
269
270        let position_bias = if self.has_relative_attention_bias {
271            calculated_position_bias
272        } else {
273            None
274        };
275
276        (context, attention_weights, position_bias, layer_state)
277    }
278
279    fn compute_bias(&self, q_len: i64, k_len: i64, device: Device) -> Tensor {
280        let context_position = Tensor::arange(q_len, (Kind::Int64, device)).unsqueeze(1);
281        let memory_position = Tensor::arange(k_len, (Kind::Int64, device)).unsqueeze(0);
282        let relative_position = memory_position - context_position;
283
284        let rp_bucket = get_relative_position_bucket(
285            &relative_position,
286            self.is_bidirectional,
287            self.relative_attention_num_buckets,
288            self.relative_attention_max_distance,
289        );
290        rp_bucket
291            .apply(self.relative_attention_bias.as_ref().unwrap())
292            .permute([2, 0, 1])
293            .unsqueeze(0)
294    }
295}
296
297pub struct T5LayerSelfAttention {
298    self_attention: T5Attention,
299    layer_norm: T5LayerNorm,
300    dropout: Dropout,
301}
302
303impl T5LayerSelfAttention {
304    pub fn new<'p, P>(
305        p: P,
306        config: &T5Config,
307        has_relative_attention_bias: bool,
308        is_decoder: bool,
309        store_cache: bool,
310        output_attentions: bool,
311    ) -> T5LayerSelfAttention
312    where
313        P: Borrow<nn::Path<'p>>,
314    {
315        let p = p.borrow();
316
317        let self_attention = T5Attention::new(
318            p / "SelfAttention",
319            config,
320            is_decoder,
321            !is_decoder,
322            store_cache,
323            output_attentions,
324            has_relative_attention_bias,
325        );
326
327        let layer_norm =
328            T5LayerNorm::new(p / "layer_norm", config.d_model, config.layer_norm_epsilon);
329        let dropout = Dropout::new(config.dropout_rate);
330
331        T5LayerSelfAttention {
332            self_attention,
333            layer_norm,
334            dropout,
335        }
336    }
337
338    pub fn forward_t(
339        &self,
340        hidden_states: &Tensor,
341        position_bias: Option<&Tensor>,
342        attention_mask: Option<&Tensor>,
343        layer_state: Option<LayerState>,
344        train: bool,
345    ) -> (Tensor, Option<Tensor>, Option<Tensor>, Option<LayerState>) {
346        let norm_x = hidden_states.apply(&self.layer_norm);
347        let (y, attention_weights, position_bias, layer_state) = self.self_attention.forward_t(
348            &norm_x,
349            None,
350            position_bias,
351            attention_mask,
352            layer_state,
353            None,
354            train,
355        );
356
357        let output = hidden_states + y.apply_t(&self.dropout, train);
358
359        (output, attention_weights, position_bias, layer_state)
360    }
361}
362
363pub struct T5LayerCrossAttention {
364    encoder_decoder_attention: T5Attention,
365    layer_norm: T5LayerNorm,
366    dropout: Dropout,
367}
368
369impl T5LayerCrossAttention {
370    pub fn new<'p, P>(
371        p: P,
372        config: &T5Config,
373        has_relative_attention_bias: bool,
374        is_decoder: bool,
375        store_cache: bool,
376        output_attentions: bool,
377    ) -> T5LayerCrossAttention
378    where
379        P: Borrow<nn::Path<'p>>,
380    {
381        let p = p.borrow();
382
383        let encoder_decoder_attention = T5Attention::new(
384            p / "EncDecAttention",
385            config,
386            is_decoder,
387            true,
388            store_cache,
389            output_attentions,
390            has_relative_attention_bias,
391        );
392
393        let layer_norm =
394            T5LayerNorm::new(p / "layer_norm", config.d_model, config.layer_norm_epsilon);
395        let dropout = Dropout::new(config.dropout_rate);
396
397        T5LayerCrossAttention {
398            encoder_decoder_attention,
399            layer_norm,
400            dropout,
401        }
402    }
403
404    pub fn forward_t(
405        &self,
406        hidden_states: &Tensor,
407        kv: Option<&Tensor>,
408        position_bias: Option<&Tensor>,
409        attention_mask: Option<&Tensor>,
410        layer_state: Option<LayerState>,
411        query_length: Option<i64>,
412        train: bool,
413    ) -> (Tensor, Option<Tensor>, Option<Tensor>, Option<LayerState>) {
414        let norm_x = hidden_states.apply(&self.layer_norm);
415
416        let (y, attention_weights, position_bias, layer_state) =
417            self.encoder_decoder_attention.forward_t(
418                &norm_x,
419                kv,
420                position_bias,
421                attention_mask,
422                layer_state,
423                query_length,
424                train,
425            );
426
427        let output = hidden_states + y.apply_t(&self.dropout, train);
428
429        (output, attention_weights, position_bias, layer_state)
430    }
431}