Equimo 0.3.5__tar.gz → 0.4.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {equimo-0.3.5/src/Equimo.egg-info → equimo-0.4.0a2}/PKG-INFO +3 -1
  2. {equimo-0.3.5 → equimo-0.4.0a2}/README.md +2 -0
  3. {equimo-0.3.5 → equimo-0.4.0a2}/pyproject.toml +1 -1
  4. {equimo-0.3.5 → equimo-0.4.0a2/src/Equimo.egg-info}/PKG-INFO +3 -1
  5. {equimo-0.3.5 → equimo-0.4.0a2}/src/Equimo.egg-info/SOURCES.txt +2 -0
  6. equimo-0.4.0a2/src/equimo/__init__.py +1 -0
  7. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/attention.py +484 -1
  8. equimo-0.4.0a2/src/equimo/layers/convolution.py +871 -0
  9. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/patch.py +3 -3
  10. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/__init__.py +6 -0
  11. equimo-0.4.0a2/src/equimo/models/lowformer.py +387 -0
  12. equimo-0.4.0a2/src/equimo/models/reduceformer.py +329 -0
  13. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/utils.py +51 -4
  14. {equimo-0.3.5 → equimo-0.4.0a2}/tests/test_models.py +24 -0
  15. equimo-0.3.5/src/equimo/__init__.py +0 -1
  16. equimo-0.3.5/src/equimo/layers/convolution.py +0 -329
  17. {equimo-0.3.5 → equimo-0.4.0a2}/LICENSE.md +0 -0
  18. {equimo-0.3.5 → equimo-0.4.0a2}/setup.cfg +0 -0
  19. {equimo-0.3.5 → equimo-0.4.0a2}/src/Equimo.egg-info/dependency_links.txt +0 -0
  20. {equimo-0.3.5 → equimo-0.4.0a2}/src/Equimo.egg-info/requires.txt +0 -0
  21. {equimo-0.3.5 → equimo-0.4.0a2}/src/Equimo.egg-info/top_level.txt +0 -0
  22. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/conversion/__init__.py +0 -0
  23. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/conversion/utils.py +0 -0
  24. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/experimental/__init__.py +0 -0
  25. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/experimental/text.py +0 -0
  26. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/io.py +0 -0
  27. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/__init__.py +0 -0
  28. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/activation.py +0 -0
  29. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/downsample.py +0 -0
  30. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/dropout.py +0 -0
  31. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/ffn.py +0 -0
  32. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/generic.py +0 -0
  33. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/mamba.py +0 -0
  34. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/norm.py +0 -0
  35. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/posemb.py +0 -0
  36. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/sharing.py +0 -0
  37. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/layers/squeeze_excite.py +0 -0
  38. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/emamodel.py +0 -0
  39. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/fastervit.py +0 -0
  40. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/mlla.py +0 -0
  41. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/partialformer.py +0 -0
  42. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/shvit.py +0 -0
  43. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/vit.py +0 -0
  44. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/models/vssd.py +0 -0
  45. {equimo-0.3.5 → equimo-0.4.0a2}/src/equimo/ops/scan.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Equimo
3
- Version: 0.3.5
3
+ Version: 0.4.0a2
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -76,6 +76,8 @@ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architect
76
76
  | PartialFormer | [Efficient Vision Transformers with Partial Attention](https://eccv.ecva.net/virtual/2024/poster/1877) | 2024 | ✅ |
77
77
  | SHViT | [SHViT: Single-Head Vision Transformer with Memory Efficient Macro Design](https://arxiv.org/abs/2401.16456) | 2024 | ✅ |
78
78
  | VSSD | [VSSD: Vision Mamba with Non-Causal State Space Duality](https://arxiv.org/abs/2407.18559) | 2024 | ✅ |
79
+ | ReduceFormer | [ReduceFormer: Attention with Tensor Reduction by Summation](https://arxiv.org/abs/2406.07488) | 2024 | ✅ |
80
+ | LowFormer | [LowFormer: Hardware Efficient Design for Convolutional Transformer Backbones](https://arxiv.org/abs/2409.03460) | 2024 | ✅ |
79
81
 
80
82
  \*: Only contains the [Linear Angular Attention](https://github.com/clementpoiret/Equimo/blob/f8fcc79e45ca65e9deb1d970c4286c0b8562f9c2/equimo/layers/attention.py#L1407) module. It is straight forward to build a ViT around it, but may require an additional `__call__` kwarg to control the `sparse_reg` bool.
81
83
 
@@ -40,6 +40,8 @@ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architect
40
40
  | PartialFormer | [Efficient Vision Transformers with Partial Attention](https://eccv.ecva.net/virtual/2024/poster/1877) | 2024 | ✅ |
41
41
  | SHViT | [SHViT: Single-Head Vision Transformer with Memory Efficient Macro Design](https://arxiv.org/abs/2401.16456) | 2024 | ✅ |
42
42
  | VSSD | [VSSD: Vision Mamba with Non-Causal State Space Duality](https://arxiv.org/abs/2407.18559) | 2024 | ✅ |
43
+ | ReduceFormer | [ReduceFormer: Attention with Tensor Reduction by Summation](https://arxiv.org/abs/2406.07488) | 2024 | ✅ |
44
+ | LowFormer | [LowFormer: Hardware Efficient Design for Convolutional Transformer Backbones](https://arxiv.org/abs/2409.03460) | 2024 | ✅ |
43
45
 
44
46
  \*: Only contains the [Linear Angular Attention](https://github.com/clementpoiret/Equimo/blob/f8fcc79e45ca65e9deb1d970c4286c0b8562f9c2/equimo/layers/attention.py#L1407) module. It is straight forward to build a ViT around it, but may require an additional `__call__` kwarg to control the `sparse_reg` bool.
45
47
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Equimo"
3
- version = "0.3.5"
3
+ version = "0.4.0-alpha.2"
4
4
  description = "Implementation of popular vision models in Jax"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Equimo
3
- Version: 0.3.5
3
+ Version: 0.4.0a2
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -76,6 +76,8 @@ Beyond normal ViT (e.g., dinov2 or siglip), equimo proposes other SotA architect
76
76
  | PartialFormer | [Efficient Vision Transformers with Partial Attention](https://eccv.ecva.net/virtual/2024/poster/1877) | 2024 | ✅ |
77
77
  | SHViT | [SHViT: Single-Head Vision Transformer with Memory Efficient Macro Design](https://arxiv.org/abs/2401.16456) | 2024 | ✅ |
78
78
  | VSSD | [VSSD: Vision Mamba with Non-Causal State Space Duality](https://arxiv.org/abs/2407.18559) | 2024 | ✅ |
79
+ | ReduceFormer | [ReduceFormer: Attention with Tensor Reduction by Summation](https://arxiv.org/abs/2406.07488) | 2024 | ✅ |
80
+ | LowFormer | [LowFormer: Hardware Efficient Design for Convolutional Transformer Backbones](https://arxiv.org/abs/2409.03460) | 2024 | ✅ |
79
81
 
80
82
  \*: Only contains the [Linear Angular Attention](https://github.com/clementpoiret/Equimo/blob/f8fcc79e45ca65e9deb1d970c4286c0b8562f9c2/equimo/layers/attention.py#L1407) module. It is straight forward to build a ViT around it, but may require an additional `__call__` kwarg to control the `sparse_reg` bool.
81
83
 
@@ -30,8 +30,10 @@ src/equimo/layers/squeeze_excite.py
30
30
  src/equimo/models/__init__.py
31
31
  src/equimo/models/emamodel.py
32
32
  src/equimo/models/fastervit.py
33
+ src/equimo/models/lowformer.py
33
34
  src/equimo/models/mlla.py
34
35
  src/equimo/models/partialformer.py
36
+ src/equimo/models/reduceformer.py
35
37
  src/equimo/models/shvit.py
36
38
  src/equimo/models/vit.py
37
39
  src/equimo/models/vssd.py
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0-alpha.2"
@@ -1,4 +1,5 @@
1
- from typing import Callable, List, Optional, Tuple
1
+ from typing import Callable, List, Literal, Optional, Sequence, Tuple
2
+
2
3
  import equinox as eqx
3
4
  import jax
4
5
  import jax.numpy as jnp
@@ -6,6 +7,7 @@ import jax.random as jr
6
7
  from einops import rearrange, reduce
7
8
  from jaxtyping import Array, Float, PRNGKeyArray
8
9
 
10
+ from equimo.layers.convolution import ConvBlock, SingleConvBlock, MBConv
9
11
  from equimo.layers.dropout import DropPathAdd
10
12
  from equimo.layers.ffn import Mlp
11
13
  from equimo.layers.mamba import Mamba2Mixer
@@ -1565,6 +1567,487 @@ class LinearAngularAttention(eqx.Module):
1565
1567
  return x
1566
1568
 
1567
1569
 
1570
+ class RFAttention(eqx.Module):
1571
+ """Attention with Tensor Reduction by Summation[1].
1572
+
1573
+ A ReLU Linear Attention mechanism replacing matmuls with global
1574
+ summation and element-wise multiplications.
1575
+
1576
+ Attributes:
1577
+ dim: Total dimension of the input/output
1578
+ num_heads: Number of attention heads
1579
+ head_dim: Dimension of each attention head (dim // num_heads)
1580
+
1581
+ References:
1582
+ [1]. Yang, J., An, L., & Park, S. I. (2024). ReduceFormer: Attention
1583
+ with Tensor Reduction by Summation (No. arXiv:2406.07488). arXiv.
1584
+ https://doi.org/10.48550/arXiv.2406.07488
1585
+ """
1586
+
1587
+ total_dim: int = eqx.field(static=True)
1588
+ kernel_func: Callable = eqx.field(static=True)
1589
+ eps: float = eqx.field(static=True)
1590
+
1591
+ qkv: eqx.nn.Conv2d
1592
+ aggreg: list[eqx.nn.Conv2d]
1593
+ proj: SingleConvBlock
1594
+
1595
+ def __init__(
1596
+ self,
1597
+ in_channels: int,
1598
+ out_channels: int,
1599
+ *,
1600
+ key: PRNGKeyArray,
1601
+ num_heads: int | None = None,
1602
+ head_dim: int = 8,
1603
+ heads_ratio: float = 1.0,
1604
+ scales: Sequence[int] = (5,),
1605
+ use_bias: bool = False,
1606
+ kernel_func: Callable = jax.nn.relu,
1607
+ # TODO: Benchmark against LN, RMSN, NsLN
1608
+ norm_layer: eqx.Module = eqx.nn.GroupNorm,
1609
+ norm_kwargs: dict = {},
1610
+ eps: float = 1e-15,
1611
+ **kwargs,
1612
+ ):
1613
+ key_qkv, key_aggreg, key_proj = jr.split(key, 3)
1614
+
1615
+ self.kernel_func = kernel_func
1616
+ self.eps = eps
1617
+ num_heads = num_heads or int(in_channels // head_dim * heads_ratio)
1618
+ total_dim = num_heads * head_dim
1619
+ self.total_dim = total_dim * (1 + len(scales))
1620
+
1621
+ self.qkv = eqx.nn.Conv2d(
1622
+ in_channels=in_channels,
1623
+ out_channels=3 * total_dim,
1624
+ kernel_size=1,
1625
+ padding="SAME",
1626
+ use_bias=use_bias,
1627
+ key=key_qkv,
1628
+ )
1629
+ self.aggreg = [
1630
+ eqx.nn.Conv2d(
1631
+ in_channels=3 * total_dim,
1632
+ out_channels=3 * total_dim,
1633
+ kernel_size=scale,
1634
+ padding="SAME",
1635
+ groups=3 * total_dim,
1636
+ key=key_aggreg,
1637
+ use_bias=use_bias,
1638
+ )
1639
+ for scale in scales
1640
+ ]
1641
+ # TODO: test different normalizations
1642
+ self.proj = SingleConvBlock(
1643
+ in_channels=self.total_dim,
1644
+ out_channels=out_channels,
1645
+ kernel_size=1,
1646
+ use_bias=use_bias,
1647
+ norm_layer=norm_layer,
1648
+ norm_kwargs=norm_kwargs,
1649
+ key=key_proj,
1650
+ )
1651
+
1652
+ def __call__(
1653
+ self,
1654
+ x: Float[Array, "seqlen height width"],
1655
+ key: PRNGKeyArray,
1656
+ inference: Optional[bool] = None,
1657
+ ) -> Float[Array, "seqlen height width"]:
1658
+ qkv_base = self.qkv(x)
1659
+
1660
+ aggregated_qkvs = [op(qkv_base) for op in self.aggreg]
1661
+ all_qkvs = [qkv_base] + aggregated_qkvs
1662
+
1663
+ rearranged_qkvs = [
1664
+ rearrange(qkv, "(n d) h w -> n d h w", n=3) for qkv in all_qkvs
1665
+ ]
1666
+ multiscale_qkv = jnp.concatenate(rearranged_qkvs, axis=1)
1667
+
1668
+ q, k, v = multiscale_qkv
1669
+
1670
+ q = self.kernel_func(q)
1671
+ k = self.kernel_func(k)
1672
+
1673
+ sum_k = jnp.sum(k, axis=(-1, -2), keepdims=True)
1674
+ sum_v = jnp.sum(v, axis=(-1, -2), keepdims=True)
1675
+ sum_kv = jnp.sum(k * sum_v, axis=(-1, -2), keepdims=True)
1676
+ sum_q = jnp.sum(q, axis=0, keepdims=True)
1677
+
1678
+ out = (q * sum_kv) / (sum_q * sum_k + self.eps)
1679
+ out = self.proj(out)
1680
+
1681
+ return out
1682
+
1683
+
1684
+ class RFAttentionBlock(eqx.Module):
1685
+ context_module: RFAttention
1686
+ local_module: MBConv
1687
+
1688
+ def __init__(
1689
+ self,
1690
+ in_channels: int,
1691
+ *,
1692
+ key,
1693
+ head_dim: int = 32,
1694
+ heads_ratio: float = 1.0,
1695
+ scales: Sequence[int] = (5,),
1696
+ rfattn_norm_layer: eqx.Module = eqx.nn.GroupNorm,
1697
+ norm_kwargs: dict = {},
1698
+ expand_ratio: float = 4.0,
1699
+ mbconv_norm_layers: tuple = (None, None, eqx.nn.GroupNorm),
1700
+ mbconv_act_layers: tuple = (jax.nn.hard_swish, jax.nn.hard_swish, None),
1701
+ fuse_mbconv: bool = False,
1702
+ **kwargs,
1703
+ ):
1704
+ key_context, key_local = jr.split(key, 2)
1705
+
1706
+ self.context_module = RFAttention(
1707
+ in_channels=in_channels,
1708
+ out_channels=in_channels,
1709
+ head_dim=head_dim,
1710
+ heads_ratio=heads_ratio,
1711
+ scales=scales,
1712
+ norm_layer=rfattn_norm_layer,
1713
+ norm_kwargs=norm_kwargs,
1714
+ key=key_context,
1715
+ )
1716
+ self.local_module = MBConv(
1717
+ in_channels=in_channels,
1718
+ out_channels=in_channels,
1719
+ expand_ratio=expand_ratio,
1720
+ norm_layers=mbconv_norm_layers,
1721
+ act_layers=mbconv_act_layers,
1722
+ use_bias=(True, True, False),
1723
+ fuse=fuse_mbconv,
1724
+ key=key_local,
1725
+ )
1726
+
1727
+ def __call__(
1728
+ self,
1729
+ x: Float[Array, "dim height width"],
1730
+ key: PRNGKeyArray,
1731
+ inference: Optional[bool] = None,
1732
+ ):
1733
+ key_context, key_local = jr.split(key, 2)
1734
+
1735
+ x += self.context_module(x, inference=inference, key=key_context)
1736
+ x += self.local_module(x, inference=inference, key=key_local)
1737
+
1738
+ return x
1739
+
1740
+
1741
+ class ConvAttention(eqx.Module):
1742
+ """Lightweight ConvAttention from LowFormer."""
1743
+
1744
+ num_heads: int = eqx.field(static=True)
1745
+ head_dim: int = eqx.field(static=True)
1746
+ attention_type: Literal["softmax", "sigmoid"] = eqx.field(static=True)
1747
+
1748
+ qkv: eqx.nn.Sequential
1749
+ out_proj: eqx.nn.Conv2d | eqx.nn.Identity
1750
+ upsample: eqx.nn.ConvTranspose2d
1751
+
1752
+ def __init__(
1753
+ self,
1754
+ in_channels: int,
1755
+ *,
1756
+ key: PRNGKeyArray,
1757
+ att_kernel: int = 7,
1758
+ att_stride: int = 4,
1759
+ fuse: bool = True,
1760
+ attention_type: Literal["softmax", "sigmoid"] = "softmax",
1761
+ norm_layer: eqx.Module | None = eqx.nn.GroupNorm,
1762
+ norm_kwargs: dict = {},
1763
+ **kwargs,
1764
+ ):
1765
+ key_qkv1, key_qkv2, key_oproj, key_upsampling = jr.split(key, 4)
1766
+
1767
+ self.attention_type = attention_type
1768
+ self.num_heads = int(max(1, in_channels // 30))
1769
+ self.head_dim = in_channels // self.num_heads
1770
+ total_dim = int(self.head_dim * self.num_heads * 3)
1771
+
1772
+ self.qkv = eqx.nn.Sequential(
1773
+ [
1774
+ SingleConvBlock(
1775
+ in_channels=in_channels,
1776
+ out_channels=in_channels,
1777
+ kernel_size=att_kernel,
1778
+ stride=att_stride,
1779
+ padding=att_kernel // 2,
1780
+ groups=in_channels,
1781
+ use_bias=False,
1782
+ norm_layer=norm_layer,
1783
+ act_layer=None,
1784
+ key=key_qkv1,
1785
+ ),
1786
+ eqx.nn.Conv2d(
1787
+ in_channels=in_channels,
1788
+ out_channels=total_dim,
1789
+ kernel_size=1,
1790
+ stride=1,
1791
+ padding=0,
1792
+ use_bias=False,
1793
+ key=key_qkv2,
1794
+ ),
1795
+ ]
1796
+ )
1797
+
1798
+ if fuse:
1799
+ self.out_proj = eqx.nn.Identity()
1800
+ self.upsample = eqx.nn.ConvTranspose2d(
1801
+ in_channels=self.head_dim * self.num_heads,
1802
+ out_channels=in_channels,
1803
+ kernel_size=3 if att_stride == 1 else (att_stride * 2),
1804
+ stride=att_stride,
1805
+ padding=1 if att_stride == 1 else (att_stride // 2),
1806
+ key=key_upsampling,
1807
+ )
1808
+ else:
1809
+ self.out_proj = eqx.nn.Conv2d(
1810
+ self.head_dim * self.num_heads,
1811
+ in_channels,
1812
+ kernel_size=1,
1813
+ stride=1,
1814
+ padding=0,
1815
+ key=key_oproj,
1816
+ )
1817
+ self.upsample = eqx.nn.ConvTranspose2d(
1818
+ in_channels=in_channels,
1819
+ out_channels=in_channels,
1820
+ kernel_size=3 if att_stride == 1 else (att_stride * 2),
1821
+ stride=att_stride,
1822
+ padding=1 if att_stride == 1 else (att_stride // 2),
1823
+ groups=in_channels,
1824
+ key=key_upsampling,
1825
+ )
1826
+
1827
+ def __call__(
1828
+ self,
1829
+ x: Float[Array, "seqlen height width"],
1830
+ key: PRNGKeyArray,
1831
+ inference: Optional[bool] = None,
1832
+ ) -> Float[Array, "seqlen height width"]:
1833
+ q, k, v = rearrange(
1834
+ self.qkv(x),
1835
+ "(n h d) s1 s2 -> n h (s1 s2) d",
1836
+ n=3,
1837
+ h=self.num_heads,
1838
+ d=self.head_dim,
1839
+ )
1840
+
1841
+ _, s, _ = q.shape
1842
+ h = w = int(s**0.5)
1843
+
1844
+ attn_logits = q @ rearrange(k, "h s d -> h d s")
1845
+ attn_logits /= self.head_dim**0.5
1846
+
1847
+ if self.attention_type == "softmax":
1848
+ attn = jax.nn.softmax(attn_logits, axis=-1)
1849
+ elif self.attention_type == "sigmoid":
1850
+ attn = jax.nn.sigmoid(attn_logits)
1851
+
1852
+ v = attn @ v
1853
+
1854
+ out = self.out_proj(rearrange(v, "h (s1 s2) d -> (h d) s1 s2", s1=h, s2=w))
1855
+ out = self.upsample(out)
1856
+
1857
+ return out
1858
+
1859
+
1860
+ class ConvAttentionBlock(eqx.Module):
1861
+ prenorm: eqx.Module
1862
+ postnorm: eqx.Module
1863
+ norm: eqx.Module
1864
+ attn: eqx.Module
1865
+ mlp: eqx.Module
1866
+ drop_path1: DropPathAdd
1867
+ drop_path2: DropPathAdd
1868
+
1869
+ def __init__(
1870
+ self,
1871
+ in_channels: int,
1872
+ *,
1873
+ key: PRNGKeyArray,
1874
+ mlp_ratio: float = 4.0,
1875
+ att_stride: int = 1,
1876
+ attention_type: Literal["softmax", "sigmoid"] = "softmax",
1877
+ fuse: bool = True, # TODO: verify
1878
+ drop_path: float | List[float] = 0.0,
1879
+ act_layer: Callable = jax.nn.gelu, # TODO: try hardswish
1880
+ norm_layer: eqx.Module = eqx.nn.GroupNorm,
1881
+ norm_max_group: int = 32,
1882
+ post_attention_norm: bool = False,
1883
+ eps: float = 1e-5,
1884
+ **kwargs,
1885
+ ):
1886
+ key_attn, key_mlp = jr.split(key, 2)
1887
+
1888
+ if isinstance(drop_path, list):
1889
+ if len(drop_path) != 2:
1890
+ raise AssertionError(
1891
+ f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
1892
+ )
1893
+ dr1, dr2 = drop_path
1894
+ dr1 = float(dr1)
1895
+ dr2 = float(dr2)
1896
+ else:
1897
+ dr1 = dr2 = float(drop_path)
1898
+
1899
+ num_groups = nearest_power_of_2_divisor(in_channels, norm_max_group)
1900
+ self.prenorm = norm_layer(num_groups, in_channels, eps=eps)
1901
+ self.postnorm = (
1902
+ norm_layer(num_groups, in_channels, eps=eps)
1903
+ if post_attention_norm
1904
+ else eqx.nn.Identity()
1905
+ )
1906
+ self.norm = norm_layer(num_groups, in_channels, eps=eps)
1907
+
1908
+ self.attn = ConvAttention(
1909
+ in_channels=in_channels,
1910
+ att_kernel=5 if att_stride > 1 else 3,
1911
+ att_stride=att_stride,
1912
+ fuse=fuse,
1913
+ attention_type=attention_type,
1914
+ key=key_attn,
1915
+ )
1916
+
1917
+ self.mlp = ConvBlock(
1918
+ dim=in_channels,
1919
+ hidden_dim=int(in_channels * mlp_ratio),
1920
+ kernel_size=1,
1921
+ stride=1,
1922
+ padding=0,
1923
+ drop_path=dr1,
1924
+ act_layer=act_layer,
1925
+ key=key_mlp,
1926
+ )
1927
+
1928
+ self.drop_path1 = DropPathAdd(dr1)
1929
+ self.drop_path2 = DropPathAdd(dr2)
1930
+
1931
+ def __call__(
1932
+ self,
1933
+ x: Float[Array, "seqlen dim"],
1934
+ key: PRNGKeyArray,
1935
+ inference: Optional[bool] = None,
1936
+ ) -> Float[Array, "seqlen dim"]:
1937
+ key_attn, key_mlp, key_dr1, key_dr2 = jr.split(key, 4)
1938
+
1939
+ x = self.drop_path1(
1940
+ x,
1941
+ self.postnorm(
1942
+ self.attn(
1943
+ self.prenorm(x),
1944
+ inference=inference,
1945
+ key=key_attn,
1946
+ )
1947
+ ),
1948
+ inference=inference,
1949
+ key=key_dr1,
1950
+ )
1951
+ x = self.drop_path2(
1952
+ x,
1953
+ self.mlp(
1954
+ self.norm(x),
1955
+ inference=inference,
1956
+ key=key_mlp,
1957
+ ),
1958
+ inference=inference,
1959
+ key=key_dr2,
1960
+ )
1961
+
1962
+ return x
1963
+
1964
+
1965
+ class LowFormerBlock(eqx.Module):
1966
+ context_module: ConvAttentionBlock
1967
+ local_module: MBConv
1968
+ drop_path1: DropPathAdd
1969
+ drop_path2: DropPathAdd
1970
+
1971
+ def __init__(
1972
+ self,
1973
+ in_channels: int,
1974
+ *,
1975
+ key,
1976
+ mlp_ratio: float = 4.0,
1977
+ att_stride: int = 1,
1978
+ attention_type: Literal["softmax", "sigmoid"] = "softmax",
1979
+ fuse_conv: bool = True,
1980
+ drop_path: float | List[float] = 0.0,
1981
+ act_layer: Callable = jax.nn.hard_swish,
1982
+ norm_layer: eqx.Module = eqx.nn.GroupNorm,
1983
+ expand_ratio: float = 4.0,
1984
+ mbconv_norm_layers: tuple = (None, None, eqx.nn.GroupNorm),
1985
+ mbconv_act_layers: tuple = (jax.nn.hard_swish, jax.nn.hard_swish, None),
1986
+ fuse_mbconv: bool = False,
1987
+ **kwargs,
1988
+ ):
1989
+ key_context, key_local = jr.split(key, 2)
1990
+
1991
+ if isinstance(drop_path, list):
1992
+ if len(drop_path) != 2:
1993
+ raise AssertionError(
1994
+ f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
1995
+ )
1996
+ dr1, dr2 = drop_path
1997
+ dr1 = float(dr1)
1998
+ dr2 = float(dr2)
1999
+ else:
2000
+ dr1 = dr2 = float(drop_path)
2001
+
2002
+ self.context_module = ConvAttentionBlock(
2003
+ in_channels=in_channels,
2004
+ mlp_ratio=mlp_ratio,
2005
+ att_stride=att_stride,
2006
+ attention_type=attention_type,
2007
+ fuse=fuse_conv,
2008
+ drop_path=drop_path,
2009
+ act_layer=act_layer,
2010
+ norm_layer=norm_layer,
2011
+ key=key_context,
2012
+ )
2013
+ self.local_module = MBConv(
2014
+ in_channels=in_channels,
2015
+ out_channels=in_channels,
2016
+ expand_ratio=expand_ratio,
2017
+ norm_layers=mbconv_norm_layers,
2018
+ act_layers=mbconv_act_layers,
2019
+ use_bias=(True, True, False),
2020
+ fuse=fuse_mbconv,
2021
+ key=key_local,
2022
+ )
2023
+
2024
+ self.drop_path1 = DropPathAdd(dr1)
2025
+ self.drop_path2 = DropPathAdd(dr2)
2026
+
2027
+ def __call__(
2028
+ self,
2029
+ x: Float[Array, "dim height width"],
2030
+ key: PRNGKeyArray,
2031
+ inference: Optional[bool] = None,
2032
+ ):
2033
+ key_context, key_local, key_dr1, key_dr2 = jr.split(key, 4)
2034
+
2035
+ x = self.drop_path1(
2036
+ x,
2037
+ self.context_module(x, inference=inference, key=key_context),
2038
+ inference=inference,
2039
+ key=key_dr1,
2040
+ )
2041
+ x = self.drop_path2(
2042
+ x,
2043
+ self.local_module(x, inference=inference, key=key_local),
2044
+ inference=inference,
2045
+ key=key_dr2,
2046
+ )
2047
+
2048
+ return x
2049
+
2050
+
1568
2051
  def get_attention(module: str | eqx.Module) -> eqx.Module:
1569
2052
  """Get an `eqx.Module` from its common name.
1570
2053