1use candle::{Module, Result, Tensor, D};
12use candle_nn::{conv2d, group_norm, Conv2d, Conv2dConfig, GroupNorm, VarBuilder};
13
14#[derive(Debug, Clone, serde::Deserialize)]
18pub struct VaeConfig {
19 #[serde(default = "default_in_channels")]
20 pub in_channels: usize,
21 #[serde(default = "default_out_channels")]
22 pub out_channels: usize,
23 #[serde(default = "default_latent_channels")]
24 pub latent_channels: usize,
25 #[serde(default = "default_block_out_channels")]
26 pub block_out_channels: Vec<usize>,
27 #[serde(default = "default_layers_per_block")]
28 pub layers_per_block: usize,
29 #[serde(default = "default_scaling_factor")]
30 pub scaling_factor: f64,
31 #[serde(default = "default_shift_factor")]
32 pub shift_factor: f64,
33 #[serde(default = "default_norm_num_groups")]
34 pub norm_num_groups: usize,
35}
36
37fn default_in_channels() -> usize {
38 3
39}
40fn default_out_channels() -> usize {
41 3
42}
43fn default_latent_channels() -> usize {
44 16
45}
46fn default_block_out_channels() -> Vec<usize> {
47 vec![128, 256, 512, 512]
48}
49fn default_layers_per_block() -> usize {
50 2
51}
52fn default_scaling_factor() -> f64 {
53 0.3611
54}
55fn default_shift_factor() -> f64 {
56 0.1159
57}
58fn default_norm_num_groups() -> usize {
59 32
60}
61
62impl Default for VaeConfig {
63 fn default() -> Self {
64 Self::z_image()
65 }
66}
67
68impl VaeConfig {
69 pub fn z_image() -> Self {
71 Self {
72 in_channels: 3,
73 out_channels: 3,
74 latent_channels: 16,
75 block_out_channels: vec![128, 256, 512, 512],
76 layers_per_block: 2,
77 scaling_factor: 0.3611,
78 shift_factor: 0.1159,
79 norm_num_groups: 32,
80 }
81 }
82}
83
84fn scaled_dot_product_attention(q: &Tensor, k: &Tensor, v: &Tensor) -> Result<Tensor> {
87 let dim = q.dim(D::Minus1)?;
88 let scale_factor = 1.0 / (dim as f64).sqrt();
89 let attn_weights = (q.matmul(&k.t()?)? * scale_factor)?;
90 candle_nn::ops::softmax_last_dim(&attn_weights)?.matmul(v)
91}
92
93#[derive(Debug, Clone)]
98struct Attention {
99 group_norm: GroupNorm,
100 to_q: candle_nn::Linear,
101 to_k: candle_nn::Linear,
102 to_v: candle_nn::Linear,
103 to_out: candle_nn::Linear,
104}
105
106impl Attention {
107 fn new(channels: usize, num_groups: usize, vb: VarBuilder) -> Result<Self> {
108 let group_norm = group_norm(num_groups, channels, 1e-6, vb.pp("group_norm"))?;
109 let to_q = candle_nn::linear(channels, channels, vb.pp("to_q"))?;
111 let to_k = candle_nn::linear(channels, channels, vb.pp("to_k"))?;
112 let to_v = candle_nn::linear(channels, channels, vb.pp("to_v"))?;
113 let to_out = candle_nn::linear(channels, channels, vb.pp("to_out").pp("0"))?;
114 Ok(Self {
115 group_norm,
116 to_q,
117 to_k,
118 to_v,
119 to_out,
120 })
121 }
122}
123
124impl Module for Attention {
125 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
126 let residual = xs;
127 let (b, c, h, w) = xs.dims4()?;
128
129 let xs = xs.apply(&self.group_norm)?;
131
132 let xs = xs.permute((0, 2, 3, 1))?.reshape((b * h * w, c))?;
134
135 let q = xs.apply(&self.to_q)?; let k = xs.apply(&self.to_k)?;
138 let v = xs.apply(&self.to_v)?;
139
140 let q = q.reshape((b, h * w, c))?.unsqueeze(1)?;
142 let k = k.reshape((b, h * w, c))?.unsqueeze(1)?;
143 let v = v.reshape((b, h * w, c))?.unsqueeze(1)?;
144
145 let xs = scaled_dot_product_attention(&q, &k, &v)?;
147
148 let xs = xs.squeeze(1)?.reshape((b * h * w, c))?;
150
151 let xs = xs.apply(&self.to_out)?;
153
154 let xs = xs.reshape((b, h, w, c))?.permute((0, 3, 1, 2))?;
156
157 xs + residual
159 }
160}
161
162#[derive(Debug, Clone)]
166struct ResnetBlock2D {
167 norm1: GroupNorm,
168 conv1: Conv2d,
169 norm2: GroupNorm,
170 conv2: Conv2d,
171 conv_shortcut: Option<Conv2d>,
172}
173
174impl ResnetBlock2D {
175 fn new(
176 in_channels: usize,
177 out_channels: usize,
178 num_groups: usize,
179 vb: VarBuilder,
180 ) -> Result<Self> {
181 let conv_cfg = Conv2dConfig {
182 padding: 1,
183 ..Default::default()
184 };
185
186 let norm1 = group_norm(num_groups, in_channels, 1e-6, vb.pp("norm1"))?;
187 let conv1 = conv2d(in_channels, out_channels, 3, conv_cfg, vb.pp("conv1"))?;
188 let norm2 = group_norm(num_groups, out_channels, 1e-6, vb.pp("norm2"))?;
189 let conv2 = conv2d(out_channels, out_channels, 3, conv_cfg, vb.pp("conv2"))?;
190
191 let conv_shortcut = if in_channels != out_channels {
192 Some(conv2d(
193 in_channels,
194 out_channels,
195 1,
196 Default::default(),
197 vb.pp("conv_shortcut"),
198 )?)
199 } else {
200 None
201 };
202
203 Ok(Self {
204 norm1,
205 conv1,
206 norm2,
207 conv2,
208 conv_shortcut,
209 })
210 }
211}
212
213impl Module for ResnetBlock2D {
214 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
215 let h = xs
216 .apply(&self.norm1)?
217 .apply(&candle_nn::Activation::Swish)?
218 .apply(&self.conv1)?
219 .apply(&self.norm2)?
220 .apply(&candle_nn::Activation::Swish)?
221 .apply(&self.conv2)?;
222
223 match &self.conv_shortcut {
224 Some(conv) => xs.apply(conv)? + h,
225 None => xs + h,
226 }
227 }
228}
229
230#[derive(Debug, Clone)]
233struct Downsample2D {
234 conv: Conv2d,
235}
236
237impl Downsample2D {
238 fn new(channels: usize, vb: VarBuilder) -> Result<Self> {
239 let conv_cfg = Conv2dConfig {
240 stride: 2,
241 padding: 0,
242 ..Default::default()
243 };
244 let conv = conv2d(channels, channels, 3, conv_cfg, vb.pp("conv"))?;
245 Ok(Self { conv })
246 }
247}
248
249impl Module for Downsample2D {
250 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
251 let xs = xs.pad_with_zeros(D::Minus1, 0, 1)?; let xs = xs.pad_with_zeros(D::Minus2, 0, 1)?; xs.apply(&self.conv)
255 }
256}
257
258#[derive(Debug, Clone)]
259struct DownEncoderBlock2D {
260 resnets: Vec<ResnetBlock2D>,
261 downsampler: Option<Downsample2D>,
262}
263
264impl DownEncoderBlock2D {
265 fn new(
266 in_channels: usize,
267 out_channels: usize,
268 num_layers: usize,
269 num_groups: usize,
270 add_downsample: bool,
271 vb: VarBuilder,
272 ) -> Result<Self> {
273 let mut resnets = Vec::with_capacity(num_layers);
274 let vb_resnets = vb.pp("resnets");
275
276 for i in 0..num_layers {
277 let in_c = if i == 0 { in_channels } else { out_channels };
278 resnets.push(ResnetBlock2D::new(
279 in_c,
280 out_channels,
281 num_groups,
282 vb_resnets.pp(i),
283 )?);
284 }
285
286 let downsampler = if add_downsample {
287 Some(Downsample2D::new(
288 out_channels,
289 vb.pp("downsamplers").pp("0"),
290 )?)
291 } else {
292 None
293 };
294
295 Ok(Self {
296 resnets,
297 downsampler,
298 })
299 }
300}
301
302impl Module for DownEncoderBlock2D {
303 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
304 let mut h = xs.clone();
305 for resnet in &self.resnets {
306 h = h.apply(resnet)?;
307 }
308 if let Some(ds) = &self.downsampler {
309 h = h.apply(ds)?;
310 }
311 Ok(h)
312 }
313}
314
315#[derive(Debug, Clone)]
318struct Upsample2D {
319 conv: Conv2d,
320}
321
322impl Upsample2D {
323 fn new(channels: usize, vb: VarBuilder) -> Result<Self> {
324 let conv_cfg = Conv2dConfig {
325 padding: 1,
326 ..Default::default()
327 };
328 let conv = conv2d(channels, channels, 3, conv_cfg, vb.pp("conv"))?;
329 Ok(Self { conv })
330 }
331}
332
333impl Module for Upsample2D {
334 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
335 let (_, _, h, w) = xs.dims4()?;
336 xs.upsample_nearest2d(h * 2, w * 2)?.apply(&self.conv)
337 }
338}
339
340#[derive(Debug, Clone)]
341struct UpDecoderBlock2D {
342 resnets: Vec<ResnetBlock2D>,
343 upsampler: Option<Upsample2D>,
344}
345
346impl UpDecoderBlock2D {
347 fn new(
348 in_channels: usize,
349 out_channels: usize,
350 num_layers: usize, num_groups: usize,
352 add_upsample: bool,
353 vb: VarBuilder,
354 ) -> Result<Self> {
355 let mut resnets = Vec::with_capacity(num_layers + 1);
356 let vb_resnets = vb.pp("resnets");
357
358 for i in 0..=num_layers {
359 let in_c = if i == 0 { in_channels } else { out_channels };
360 resnets.push(ResnetBlock2D::new(
361 in_c,
362 out_channels,
363 num_groups,
364 vb_resnets.pp(i),
365 )?);
366 }
367
368 let upsampler = if add_upsample {
369 Some(Upsample2D::new(out_channels, vb.pp("upsamplers").pp("0"))?)
370 } else {
371 None
372 };
373
374 Ok(Self { resnets, upsampler })
375 }
376}
377
378impl Module for UpDecoderBlock2D {
379 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
380 let mut h = xs.clone();
381 for resnet in &self.resnets {
382 h = h.apply(resnet)?;
383 }
384 if let Some(us) = &self.upsampler {
385 h = h.apply(us)?;
386 }
387 Ok(h)
388 }
389}
390
391#[derive(Debug, Clone)]
394struct UNetMidBlock2D {
395 resnet_0: ResnetBlock2D,
396 attention: Attention,
397 resnet_1: ResnetBlock2D,
398}
399
400impl UNetMidBlock2D {
401 fn new(channels: usize, num_groups: usize, vb: VarBuilder) -> Result<Self> {
402 let resnet_0 =
403 ResnetBlock2D::new(channels, channels, num_groups, vb.pp("resnets").pp("0"))?;
404 let attention = Attention::new(channels, num_groups, vb.pp("attentions").pp("0"))?;
405 let resnet_1 =
406 ResnetBlock2D::new(channels, channels, num_groups, vb.pp("resnets").pp("1"))?;
407 Ok(Self {
408 resnet_0,
409 attention,
410 resnet_1,
411 })
412 }
413}
414
415impl Module for UNetMidBlock2D {
416 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
417 xs.apply(&self.resnet_0)?
418 .apply(&self.attention)?
419 .apply(&self.resnet_1)
420 }
421}
422
423#[derive(Debug, Clone)]
427pub struct Encoder {
428 conv_in: Conv2d,
429 down_blocks: Vec<DownEncoderBlock2D>,
430 mid_block: UNetMidBlock2D,
431 conv_norm_out: GroupNorm,
432 conv_out: Conv2d,
433}
434
435impl Encoder {
436 pub fn new(cfg: &VaeConfig, vb: VarBuilder) -> Result<Self> {
437 let conv_cfg = Conv2dConfig {
438 padding: 1,
439 ..Default::default()
440 };
441 let conv_in = conv2d(
442 cfg.in_channels,
443 cfg.block_out_channels[0],
444 3,
445 conv_cfg,
446 vb.pp("conv_in"),
447 )?;
448
449 let mut down_blocks = Vec::with_capacity(cfg.block_out_channels.len());
450 let vb_down = vb.pp("down_blocks");
451
452 for (i, &out_channels) in cfg.block_out_channels.iter().enumerate() {
453 let in_channels = if i == 0 {
454 cfg.block_out_channels[0]
455 } else {
456 cfg.block_out_channels[i - 1]
457 };
458 let add_downsample = i < cfg.block_out_channels.len() - 1;
459 down_blocks.push(DownEncoderBlock2D::new(
460 in_channels,
461 out_channels,
462 cfg.layers_per_block,
463 cfg.norm_num_groups,
464 add_downsample,
465 vb_down.pp(i),
466 )?);
467 }
468
469 let mid_channels = *cfg.block_out_channels.last().unwrap();
470 let mid_block = UNetMidBlock2D::new(mid_channels, cfg.norm_num_groups, vb.pp("mid_block"))?;
471
472 let conv_norm_out = group_norm(
473 cfg.norm_num_groups,
474 mid_channels,
475 1e-6,
476 vb.pp("conv_norm_out"),
477 )?;
478 let conv_out = conv2d(
479 mid_channels,
480 2 * cfg.latent_channels,
481 3,
482 conv_cfg,
483 vb.pp("conv_out"),
484 )?;
485
486 Ok(Self {
487 conv_in,
488 down_blocks,
489 mid_block,
490 conv_norm_out,
491 conv_out,
492 })
493 }
494}
495
496impl Module for Encoder {
497 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
498 let mut h = xs.apply(&self.conv_in)?;
499 for block in &self.down_blocks {
500 h = h.apply(block)?;
501 }
502 h.apply(&self.mid_block)?
503 .apply(&self.conv_norm_out)?
504 .apply(&candle_nn::Activation::Swish)?
505 .apply(&self.conv_out)
506 }
507}
508
509#[derive(Debug, Clone)]
513pub struct Decoder {
514 conv_in: Conv2d,
515 mid_block: UNetMidBlock2D,
516 up_blocks: Vec<UpDecoderBlock2D>,
517 conv_norm_out: GroupNorm,
518 conv_out: Conv2d,
519}
520
521impl Decoder {
522 pub fn new(cfg: &VaeConfig, vb: VarBuilder) -> Result<Self> {
523 let conv_cfg = Conv2dConfig {
524 padding: 1,
525 ..Default::default()
526 };
527 let mid_channels = *cfg.block_out_channels.last().unwrap();
528
529 let conv_in = conv2d(
530 cfg.latent_channels,
531 mid_channels,
532 3,
533 conv_cfg,
534 vb.pp("conv_in"),
535 )?;
536 let mid_block = UNetMidBlock2D::new(mid_channels, cfg.norm_num_groups, vb.pp("mid_block"))?;
537
538 let reversed_channels: Vec<usize> = cfg.block_out_channels.iter().rev().cloned().collect();
540 let mut up_blocks = Vec::with_capacity(reversed_channels.len());
541 let vb_up = vb.pp("up_blocks");
542
543 for (i, &out_channels) in reversed_channels.iter().enumerate() {
544 let in_channels = if i == 0 {
545 mid_channels
546 } else {
547 reversed_channels[i - 1]
548 };
549 let add_upsample = i < reversed_channels.len() - 1;
550 up_blocks.push(UpDecoderBlock2D::new(
551 in_channels,
552 out_channels,
553 cfg.layers_per_block,
554 cfg.norm_num_groups,
555 add_upsample,
556 vb_up.pp(i),
557 )?);
558 }
559
560 let final_channels = *reversed_channels.last().unwrap();
561 let conv_norm_out = group_norm(
562 cfg.norm_num_groups,
563 final_channels,
564 1e-6,
565 vb.pp("conv_norm_out"),
566 )?;
567 let conv_out = conv2d(
568 final_channels,
569 cfg.out_channels,
570 3,
571 conv_cfg,
572 vb.pp("conv_out"),
573 )?;
574
575 Ok(Self {
576 conv_in,
577 mid_block,
578 up_blocks,
579 conv_norm_out,
580 conv_out,
581 })
582 }
583}
584
585impl Module for Decoder {
586 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
587 let mut h = xs.apply(&self.conv_in)?.apply(&self.mid_block)?;
588 for block in &self.up_blocks {
589 h = h.apply(block)?;
590 }
591 h.apply(&self.conv_norm_out)?
592 .apply(&candle_nn::Activation::Swish)?
593 .apply(&self.conv_out)
594 }
595}
596
597#[derive(Debug, Clone)]
601pub struct DiagonalGaussian {
602 sample: bool,
603}
604
605impl DiagonalGaussian {
606 pub fn new(sample: bool) -> Self {
607 Self { sample }
608 }
609}
610
611impl Module for DiagonalGaussian {
612 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
613 let chunks = xs.chunk(2, 1)?; let mean = &chunks[0];
615 let logvar = &chunks[1];
616
617 if self.sample {
618 let std = (logvar * 0.5)?.exp()?;
619 mean + (std * mean.randn_like(0., 1.)?)?
620 } else {
621 Ok(mean.clone())
622 }
623 }
624}
625
626#[derive(Debug, Clone)]
630pub struct AutoEncoderKL {
631 encoder: Encoder,
632 decoder: Decoder,
633 reg: DiagonalGaussian,
634 scale_factor: f64,
635 shift_factor: f64,
636}
637
638impl AutoEncoderKL {
639 pub fn new(cfg: &VaeConfig, vb: VarBuilder) -> Result<Self> {
640 let encoder = Encoder::new(cfg, vb.pp("encoder"))?;
641 let decoder = Decoder::new(cfg, vb.pp("decoder"))?;
642 let reg = DiagonalGaussian::new(true);
643
644 Ok(Self {
645 encoder,
646 decoder,
647 reg,
648 scale_factor: cfg.scaling_factor,
649 shift_factor: cfg.shift_factor,
650 })
651 }
652
653 pub fn encode(&self, xs: &Tensor) -> Result<Tensor> {
657 let z = xs.apply(&self.encoder)?.apply(&self.reg)?;
658 (z - self.shift_factor)? * self.scale_factor
659 }
660
661 pub fn decode(&self, xs: &Tensor) -> Result<Tensor> {
665 let xs = ((xs / self.scale_factor)? + self.shift_factor)?;
666 xs.apply(&self.decoder)
667 }
668
669 pub fn scale_factor(&self) -> f64 {
671 self.scale_factor
672 }
673
674 pub fn shift_factor(&self) -> f64 {
676 self.shift_factor
677 }
678}
679
680impl Module for AutoEncoderKL {
681 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
682 self.decode(&self.encode(xs)?)
683 }
684}