use burn::{
config::Config,
module::Module,
nn::{
Embedding, EmbeddingConfig, Linear, LinearConfig, RmsNorm, RmsNormConfig, RotaryEncoding,
SwiGlu, SwiGluConfig,
},
tensor::{Bool, Device, Int, Tensor, activation::softmax},
};
use crate::cache::AutoregressiveCache;
#[derive(Config, Debug)]
pub struct TransformerConfig {
pub vocab_size: usize,
pub n_layers: usize,
pub d_model: usize,
pub hidden_size: usize,
pub n_heads: usize,
pub n_kv_heads: usize,
#[config(default = "512")]
pub max_seq_len: usize,
#[config(default = "1e-5")]
pub norm_eps: f64,
}
impl TransformerConfig {
pub fn init(&self, device: &Device) -> Transformer {
let tok_embeddings = EmbeddingConfig::new(self.vocab_size, self.d_model).init(device);
let layers = (0..self.n_layers)
.map(|_| {
TransformerBlockConfig::new(
self.n_layers,
self.d_model,
self.hidden_size,
self.n_heads,
self.n_kv_heads,
self.norm_eps,
)
.init(device)
})
.collect::<Vec<_>>();
let norm = RmsNormConfig::new(self.d_model)
.with_epsilon(self.norm_eps)
.init(device);
let output = LinearConfig::new(self.d_model, self.vocab_size)
.with_bias(false)
.init(device);
Transformer {
tok_embeddings,
layers,
norm,
output,
}
}
}
#[derive(Module, Debug)]
pub struct Transformer {
tok_embeddings: Embedding,
layers: Vec<TransformerBlock>,
norm: RmsNorm,
output: Linear,
}
impl Transformer {
pub fn forward(
&self,
input: Tensor<2, Int>,
cache: &mut [KeyValueCache],
rope: &RotaryEncoding,
) -> Tensor<3> {
let mut h = self.tok_embeddings.forward(input);
for (layer, c) in self.layers.iter().zip(cache.iter_mut()) {
h = layer.forward(h, c, rope);
}
let h = self.norm.forward(h);
self.output.forward(h)
}
}
#[derive(Config, Debug)]
pub struct TransformerBlockConfig {
pub n_layers: usize,
pub d_model: usize,
pub hidden_size: usize,
pub n_heads: usize,
pub n_kv_heads: usize,
pub norm_eps: f64,
}
impl TransformerBlockConfig {
pub fn init(&self, device: &Device) -> TransformerBlock {
let attention =
MultiHeadAttentionConfig::new(self.d_model, self.n_heads, self.n_kv_heads).init(device);
let feed_forward = FeedForwardConfig::new(self.d_model, self.hidden_size).init(device);
let attention_norm = RmsNormConfig::new(self.d_model)
.with_epsilon(self.norm_eps)
.init(device);
let ffn_norm = RmsNormConfig::new(self.d_model)
.with_epsilon(self.norm_eps)
.init(device);
TransformerBlock {
attention,
feed_forward,
attention_norm,
ffn_norm,
}
}
}
#[derive(Module, Debug)]
pub struct TransformerBlock {
attention: MultiHeadAttention,
feed_forward: FeedForward,
attention_norm: RmsNorm,
ffn_norm: RmsNorm,
}
impl TransformerBlock {
pub fn forward(
&self,
input: Tensor<3>,
cache: &mut KeyValueCache,
rope: &RotaryEncoding,
) -> Tensor<3> {
let h = input.clone()
+ self
.attention
.forward(self.attention_norm.forward(input), cache, rope);
h.clone() + self.feed_forward.forward(self.ffn_norm.forward(h))
}
}
#[derive(Config, Debug)]
pub struct FeedForwardConfig {
pub d_model: usize,
pub hidden_size: usize,
}
impl FeedForwardConfig {
pub fn init(&self, device: &Device) -> FeedForward {
let swiglu = SwiGluConfig::new(self.d_model, self.hidden_size)
.with_bias(false)
.init(device);
let w2 = LinearConfig::new(self.hidden_size, self.d_model)
.with_bias(false)
.init(device);
FeedForward { swiglu, w2 }
}
}
#[derive(Module, Debug)]
pub struct FeedForward {
swiglu: SwiGlu,
w2: Linear,
}
impl FeedForward {
pub fn forward(&self, input: Tensor<3>) -> Tensor<3> {
self.w2.forward(self.swiglu.forward(input))
}
}
pub struct KeyValueCache {
key: AutoregressiveCache,
value: AutoregressiveCache,
}
impl KeyValueCache {
pub fn new(
max_batch_size: usize,
num_heads: usize,
max_seq_len: usize,
d_model: usize,
device: &Device,
) -> Self {
Self {
key: AutoregressiveCache::new(max_batch_size, num_heads, max_seq_len, d_model, device),
value: AutoregressiveCache::new(
max_batch_size,
num_heads,
max_seq_len,
d_model,
device,
),
}
}
pub fn forward(&mut self, key: Tensor<4>, value: Tensor<4>) -> (Tensor<4>, Tensor<4>) {
let k = self.key.forward(key);
let v = self.value.forward(value);
(k, v)
}
pub fn len(&self) -> usize {
self.key.len()
}
pub fn reset(&mut self) {
self.key.reset();
self.value.reset();
}
}
#[derive(Config, Debug)]
pub struct MultiHeadAttentionConfig {
pub d_model: usize,
pub n_heads: usize,
pub n_kv_heads: usize,
}
impl MultiHeadAttentionConfig {
pub fn init(&self, device: &Device) -> MultiHeadAttention {
let head_dim = self.d_model / self.n_heads;
let wq = LinearConfig::new(self.d_model, self.n_heads * head_dim)
.with_bias(false)
.init(device);
let wk = LinearConfig::new(self.d_model, self.n_kv_heads * head_dim)
.with_bias(false)
.init(device);
let wv = LinearConfig::new(self.d_model, self.n_kv_heads * head_dim)
.with_bias(false)
.init(device);
let wo = LinearConfig::new(self.n_heads * head_dim, self.d_model)
.with_bias(false)
.init(device);
MultiHeadAttention {
wq,
wk,
wv,
wo,
n_heads: self.n_heads,
n_kv_heads: self.n_kv_heads,
head_dim,
}
}
}
#[derive(Module, Debug)]
pub struct MultiHeadAttention {
wq: Linear,
wk: Linear,
wv: Linear,
wo: Linear,
n_heads: usize,
n_kv_heads: usize,
head_dim: usize,
}
impl MultiHeadAttention {
pub fn forward(
&self,
input: Tensor<3>,
cache: &mut KeyValueCache,
rope: &RotaryEncoding,
) -> Tensor<3> {
let device = input.device();
let [batch_size, seq_len, hidden_size] = input.dims();
let q = self.wq.forward(input.clone());
let k = self.wk.forward(input.clone());
let v = self.wv.forward(input);
let q = q
.reshape([batch_size, seq_len, self.n_heads, self.head_dim])
.swap_dims(1, 2);
let k = k
.reshape([batch_size, seq_len, self.n_kv_heads, self.head_dim])
.swap_dims(1, 2);
let v = v
.reshape([batch_size, seq_len, self.n_kv_heads, self.head_dim])
.swap_dims(1, 2);
let cache_seq_len = cache.len();
let q = rope.apply(q, cache_seq_len);
let k = rope.apply(k, cache_seq_len);
let (k, v) = cache.forward(k, v);
let k = self.repeat_kv(k);
let v = self.repeat_kv(v);
let mut scores = q
.matmul(k.swap_dims(2, 3))
.div_scalar((self.head_dim as f32).sqrt());
if seq_len > 1 {
let cache_seq_len = cache.len();
let mask = Tensor::<2, Bool>::tril_mask(
[seq_len, cache_seq_len],
(cache_seq_len - seq_len) as i64, &device,
);
scores = scores.mask_fill(mask.unsqueeze::<4>(), f32::NEG_INFINITY);
}
let scores = softmax(scores, 3);
let output = scores.matmul(v);
let output = output
.swap_dims(1, 2)
.reshape([batch_size, seq_len, hidden_size]);
self.wo.forward(output)
}
fn repeat_kv(&self, x: Tensor<4>) -> Tensor<4> {
let n_rep = self.n_heads / self.n_kv_heads;
if n_rep == 1 {
x
} else {
let [batch_size, num_kv_heads, seq_len, head_dim] = x.dims();
x.unsqueeze_dim::<5>(2)
.expand([batch_size, num_kv_heads, n_rep, seq_len, head_dim])
.reshape([batch_size, num_kv_heads * n_rep, seq_len, head_dim])
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use burn::tensor::{Device, TensorData};
#[test]
fn test_rms_norm() {
let device = Device::flex();
let rms = RmsNormConfig::new(4).with_epsilon(1e-5).init(&device);
let input = Tensor::<3>::from_floats(
[[
[0.0025997162, 0.0030002594, -0.006000519, 0.006000519],
[0.0010004044, 0.00080013275, 0.0015001297, -0.01600647],
]],
&device,
);
let output = rms.forward(input);
let expected = TensorData::from([[
[0.45996094, 0.5307617, -1.0615234, 1.0615234],
[0.11553955, 0.09240723, 0.17321777, -1.8486328],
]]);
output.into_data().assert_approx_eq(
&expected,
burn::tensor::Tolerance::<f32>::rel_abs(1e-3, 1e-3),
);
}
}