ferritin_plms/esmfold2/layers/
lm_encoder.rs1use candle_core::{D, Result, Tensor};
22use candle_nn::{self as nn, LayerNorm, LayerNormConfig, Module, VarBuilder};
23
24struct LMAttention {
27 layernorm_qkv: nn::Sequential,
28 out_proj: nn::Linear,
29 q_ln: LayerNorm,
30 k_ln: LayerNorm,
31 n_heads: usize,
32 d_head: usize,
33}
34
35impl LMAttention {
36 fn load(vb: VarBuilder, d_model: usize, n_heads: usize) -> Result<Self> {
37 let d_head = d_model / n_heads;
38 let norm = nn::layer_norm(
39 d_model,
40 LayerNormConfig::from(1e-5),
41 vb.pp("layernorm_qkv.0"),
42 )?;
43 let qkv = nn::linear_no_bias(d_model, d_model * 3, vb.pp("layernorm_qkv.1"))?;
44 let layernorm_qkv = nn::seq().add(norm).add(qkv);
45 let out_proj = nn::linear_no_bias(d_model, d_model, vb.pp("out_proj"))?;
46
47 let q_ln = LayerNorm::new_no_bias(vb.pp("q_ln").get((d_model,), "weight")?, 1e-5);
48 let k_ln = LayerNorm::new_no_bias(vb.pp("k_ln").get((d_model,), "weight")?, 1e-5);
49
50 Ok(Self {
51 layernorm_qkv,
52 out_proj,
53 q_ln,
54 k_ln,
55 n_heads,
56 d_head,
57 })
58 }
59
60 fn forward(&self, x: &Tensor) -> Result<Tensor> {
61 let (b, l, _) = x.dims3()?;
62
63 let qkv = self.layernorm_qkv.forward(x)?;
65 let chunks = qkv.chunk(3, D::Minus1)?;
66 let (q, k, v) = (&chunks[0], &chunks[1], &chunks[2]);
67
68 let q = self.q_ln.forward(q)?;
70 let k = self.k_ln.forward(k)?;
71
72 let reshape_heads = |t: &Tensor| -> Result<Tensor> {
74 t.reshape((b, l, self.n_heads, self.d_head))?
75 .transpose(1, 2)?
76 .reshape((b * self.n_heads, l, self.d_head))
77 };
78 let q = reshape_heads(&q)?;
79 let k = reshape_heads(&k)?;
80 let v = reshape_heads(v)?;
81
82 let scale = (self.d_head as f64).sqrt();
84 let scores = (q.matmul(&k.transpose(D::Minus2, D::Minus1)?)? / scale)?;
85 let weights = nn::ops::softmax(&scores, D::Minus1)?;
86 let out = weights.matmul(&v)?; let out = out
90 .reshape((b, self.n_heads, l, self.d_head))?
91 .transpose(1, 2)?
92 .reshape((b, l, self.n_heads * self.d_head))?;
93
94 self.out_proj.forward(&out)
95 }
96}
97
98struct LMFfn {
101 norm: LayerNorm,
102 gate_up_proj: nn::Linear,
103 down_proj: nn::Linear,
104}
105
106impl LMFfn {
107 fn load(vb: VarBuilder, d_model: usize) -> Result<Self> {
108 let hidden = ((8.0 / 3.0 * d_model as f64 + 255.0) / 256.0).floor() as usize * 256;
110 Ok(Self {
111 norm: nn::layer_norm(d_model, LayerNormConfig::from(1e-5), vb.pp("0"))?,
112 gate_up_proj: nn::linear_no_bias(d_model, hidden * 2, vb.pp("1"))?,
113 down_proj: nn::linear_no_bias(hidden, d_model, vb.pp("3"))?,
114 })
115 }
116
117 fn forward(&self, x: &Tensor) -> Result<Tensor> {
118 let x = self.norm.forward(x)?;
119 let gate_up = self.gate_up_proj.forward(&x)?;
120 let chunks = gate_up.chunk(2, D::Minus1)?;
121 let hidden = (chunks[0].silu()? * &chunks[1])?;
122 self.down_proj.forward(&hidden)
123 }
124}
125
126struct LMBlock {
129 attn: LMAttention,
130 ffn: LMFfn,
131}
132
133impl LMBlock {
134 fn load(vb: VarBuilder, d_model: usize, n_heads: usize) -> Result<Self> {
135 Ok(Self {
136 attn: LMAttention::load(vb.pp("attn"), d_model, n_heads)?,
137 ffn: LMFfn::load(vb.pp("ffn"), d_model)?,
138 })
139 }
140
141 fn forward(&self, x: &Tensor) -> Result<Tensor> {
142 let x = (x + &self.attn.forward(x)?)?;
143 &x + &self.ffn.forward(&x)?
144 }
145}
146
147pub struct LMEncoder {
152 blocks: Vec<LMBlock>,
153 norm: LayerNorm,
154 proj: nn::Linear,
155}
156
157impl LMEncoder {
158 pub fn load(vb: VarBuilder, d_in: usize, d_out: usize, n_layers: usize) -> Result<Self> {
162 let n_heads = d_in / 64; let blocks = (0..n_layers)
164 .map(|i| LMBlock::load(vb.pp(format!("blocks.{i}")), d_in, n_heads))
165 .collect::<Result<Vec<_>>>()?;
166 let norm = nn::layer_norm(d_in, LayerNormConfig::from(1e-5), vb.pp("norm"))?;
167 let proj = nn::linear_no_bias(d_in, d_out, vb.pp("proj"))?;
168 Ok(Self { blocks, norm, proj })
169 }
170
171 pub fn forward(&self, x: &Tensor) -> Result<Tensor> {
176 let mut x = x.clone();
177 for block in &self.blocks {
178 x = block.forward(&x)?;
179 }
180 self.proj.forward(&self.norm.forward(&x)?)
181 }
182}
183
184#[cfg(test)]
187mod tests {
188 use super::*;
189 use candle_core::{DType, Device, Tensor};
190
191 #[test]
192 fn test_lm_encoder_output_shape() {
193 let device = Device::Cpu;
194 let vb = VarBuilder::zeros(DType::F32, &device);
195 let encoder = LMEncoder::load(vb, 2560, 384, 4).unwrap();
196 let x = Tensor::zeros(&[1, 16, 2560], DType::F32, &device).unwrap();
197 let out = encoder.forward(&x).unwrap();
198 assert_eq!(out.dims(), &[1, 16, 384]);
199 }
200
201 #[test]
202 fn test_lm_encoder_batch_invariant_shape() {
203 let device = Device::Cpu;
204 let vb = VarBuilder::zeros(DType::F32, &device);
205 let encoder = LMEncoder::load(vb, 2560, 384, 4).unwrap();
206 let x = Tensor::zeros(&[2, 8, 2560], DType::F32, &device).unwrap();
207 let out = encoder.forward(&x).unwrap();
208 assert_eq!(out.dims(), &[2, 8, 384]);
209 }
210
211 #[test]
212 fn test_lm_ffn_hidden_dim() {
213 let expected = ((8.0_f64 / 3.0 * 2560.0 + 255.0) / 256.0).floor() as usize * 256;
215 assert_eq!(expected, 6912);
216 }
217}