stack-attention-pytorch 0.0.1__tar.gz → 0.0.2__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.
- {stack_attention_pytorch-0.0.1 → stack_attention_pytorch-0.0.2}/PKG-INFO +11 -2
- {stack_attention_pytorch-0.0.1 → stack_attention_pytorch-0.0.2}/README.md +9 -0
- {stack_attention_pytorch-0.0.1 → stack_attention_pytorch-0.0.2}/pyproject.toml +2 -2
- stack_attention_pytorch-0.0.2/stack_attention/__init__.py +2 -0
- stack_attention_pytorch-0.0.2/stack_attention/stack_trans_layer.py +199 -0
- stack_attention_pytorch-0.0.1/stack_attention/__init__.py +0 -0
- stack_attention_pytorch-0.0.1/stack_attention/stack_trans_layer.py +0 -0
- {stack_attention_pytorch-0.0.1 → stack_attention_pytorch-0.0.2}/.gitignore +0 -0
- {stack_attention_pytorch-0.0.1 → stack_attention_pytorch-0.0.2}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stack-attention-pytorch
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Implementation of Differentiable Stacks for augmenting Transformers / Attention
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/stack-attention-pytorch/
|
|
6
6
|
Project-URL: Repository, https://github.com/lucidrains/stack-attention-pytorch
|
|
@@ -36,7 +36,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
36
36
|
Requires-Python: >=3.10
|
|
37
37
|
Requires-Dist: einops>=0.8.1
|
|
38
38
|
Requires-Dist: einx>=0.4.0
|
|
39
|
-
Requires-Dist: torch-einops-utils>=0.
|
|
39
|
+
Requires-Dist: torch-einops-utils>=0.1.27
|
|
40
40
|
Requires-Dist: torch>=2.5
|
|
41
41
|
Provides-Extra: examples
|
|
42
42
|
Provides-Extra: test
|
|
@@ -72,3 +72,12 @@ For following a line of research that augments attention with a differentiable s
|
|
|
72
72
|
url = {https://arxiv.org/abs/2507.15343},
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
|
+
|
|
76
|
+
```bibtex
|
|
77
|
+
@inproceedings{joulin2015inferring,
|
|
78
|
+
author = {Armand Joulin and Tom{\'a}{\v{s}} Mikolov},
|
|
79
|
+
title = {Inferring Algorithmic Patterns with Stack-Augmented Recurrent Nets},
|
|
80
|
+
booktitle = {Advances in Neural Information Processing Systems 28 (NIPS 2015)},
|
|
81
|
+
year = {2015}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
@@ -27,3 +27,12 @@ For following a line of research that augments attention with a differentiable s
|
|
|
27
27
|
url = {https://arxiv.org/abs/2507.15343},
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
|
+
|
|
31
|
+
```bibtex
|
|
32
|
+
@inproceedings{joulin2015inferring,
|
|
33
|
+
author = {Armand Joulin and Tom{\'a}{\v{s}} Mikolov},
|
|
34
|
+
title = {Inferring Algorithmic Patterns with Stack-Augmented Recurrent Nets},
|
|
35
|
+
booktitle = {Advances in Neural Information Processing Systems 28 (NIPS 2015)},
|
|
36
|
+
year = {2015}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stack-attention-pytorch"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.2"
|
|
4
4
|
description = "Implementation of Differentiable Stacks for augmenting Transformers / Attention"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Phil Wang", email = "lucidrains@gmail.com" }
|
|
@@ -27,7 +27,7 @@ dependencies = [
|
|
|
27
27
|
"einx>=0.4.0",
|
|
28
28
|
"einops>=0.8.1",
|
|
29
29
|
"torch>=2.5",
|
|
30
|
-
"torch-einops-utils>=0.
|
|
30
|
+
"torch-einops-utils>=0.1.27",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from functools import partial
|
|
3
|
+
|
|
4
|
+
import torch
|
|
5
|
+
from torch import cat, nn, Tensor
|
|
6
|
+
from torch.nn import Module, Linear, RMSNorm, Parameter
|
|
7
|
+
import torch.nn.functional as F
|
|
8
|
+
|
|
9
|
+
from torch_einops_utils import pack_with_inverse, pad_left_at_dim, pad_right_at_dim
|
|
10
|
+
|
|
11
|
+
import einx
|
|
12
|
+
from einops import einsum, rearrange, repeat
|
|
13
|
+
from einops.layers.torch import Rearrange
|
|
14
|
+
|
|
15
|
+
# ein equations
|
|
16
|
+
|
|
17
|
+
# b - batch seq
|
|
18
|
+
# h - heads or num stacks
|
|
19
|
+
# s - stack depth
|
|
20
|
+
# d - dim stack
|
|
21
|
+
# a - actions
|
|
22
|
+
|
|
23
|
+
# constants
|
|
24
|
+
|
|
25
|
+
LinearNoBias = partial(Linear, bias = False)
|
|
26
|
+
|
|
27
|
+
# helper functions
|
|
28
|
+
|
|
29
|
+
def exists(v):
|
|
30
|
+
return v is not None
|
|
31
|
+
|
|
32
|
+
def default(v, d):
|
|
33
|
+
return v if exists(v) else d
|
|
34
|
+
|
|
35
|
+
# gumbel
|
|
36
|
+
|
|
37
|
+
def log(t, eps = 1e-20):
|
|
38
|
+
return t.clamp_min(eps).log()
|
|
39
|
+
|
|
40
|
+
def gumbel_noise_like(t):
|
|
41
|
+
return -log(-log(torch.rand_like(t)))
|
|
42
|
+
|
|
43
|
+
def straight_through(src, tgt):
|
|
44
|
+
return src + (tgt - src).detach()
|
|
45
|
+
|
|
46
|
+
def mask_value(t):
|
|
47
|
+
return -torch.finfo(t.dtype).max
|
|
48
|
+
|
|
49
|
+
# classes
|
|
50
|
+
|
|
51
|
+
class StackTransLayer(Module):
|
|
52
|
+
# Kechi Zhang et al. https://arxiv.org/abs/2507.15343
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
dim,
|
|
57
|
+
*,
|
|
58
|
+
num_stacks = 4, # heads
|
|
59
|
+
dim_stack = 16, # dim head
|
|
60
|
+
stack_size = 24,
|
|
61
|
+
prenorm = False
|
|
62
|
+
):
|
|
63
|
+
super().__init__()
|
|
64
|
+
|
|
65
|
+
self.norm = RMSNorm(dim) if prenorm else nn.Identity()
|
|
66
|
+
|
|
67
|
+
# action related
|
|
68
|
+
|
|
69
|
+
num_actions = 3 # (push, pop, noop)
|
|
70
|
+
self.to_action_logits = LinearNoBias(dim, num_stacks * num_actions)
|
|
71
|
+
self.split_action_logits = Rearrange('... (num_stacks num_actions) -> ... num_stacks num_actions', num_actions = num_actions)
|
|
72
|
+
self.num_actions = num_actions
|
|
73
|
+
|
|
74
|
+
# to hidden per stack for maybe push
|
|
75
|
+
|
|
76
|
+
dim_inner = num_stacks * dim_stack
|
|
77
|
+
self.to_stack_inputs = LinearNoBias(dim, dim_inner)
|
|
78
|
+
self.split_num_stacks = Rearrange('... (num_stacks dim_stack) -> ... num_stacks dim_stack', num_stacks = num_stacks)
|
|
79
|
+
|
|
80
|
+
self.num_stacks = num_stacks
|
|
81
|
+
self.stack_size = stack_size
|
|
82
|
+
self.dim_stack = dim_stack
|
|
83
|
+
|
|
84
|
+
# attention based read from stack
|
|
85
|
+
|
|
86
|
+
self.to_read_stack_attn = LinearNoBias(dim_stack, 1)
|
|
87
|
+
|
|
88
|
+
self.null_stack = Parameter(torch.randn(dim_stack) * 1e-2)
|
|
89
|
+
|
|
90
|
+
# combine
|
|
91
|
+
|
|
92
|
+
self.combine = LinearNoBias(dim_inner, dim)
|
|
93
|
+
|
|
94
|
+
def forward(
|
|
95
|
+
self,
|
|
96
|
+
tokens,
|
|
97
|
+
stack_states: tuple[Tensor, Tensor] | None = None,
|
|
98
|
+
action_temperature = 1.,
|
|
99
|
+
stochastic_action = False,
|
|
100
|
+
hard_action = False
|
|
101
|
+
):
|
|
102
|
+
assert action_temperature > 0.
|
|
103
|
+
|
|
104
|
+
orig, device = tokens, tokens.device
|
|
105
|
+
|
|
106
|
+
# maybe pre norm
|
|
107
|
+
|
|
108
|
+
tokens = self.norm(tokens)
|
|
109
|
+
|
|
110
|
+
tokens, inverse_pack = pack_with_inverse(tokens, '* d')
|
|
111
|
+
|
|
112
|
+
# dimensions
|
|
113
|
+
|
|
114
|
+
batch, num_stacks, stack_size, dim_stack = tokens.shape[0], self.num_stacks, self.stack_size, self.dim_stack
|
|
115
|
+
|
|
116
|
+
# project to stack inputs
|
|
117
|
+
|
|
118
|
+
stack_inputs = self.to_stack_inputs(tokens)
|
|
119
|
+
stack_inputs = self.split_num_stacks(stack_inputs) # (b h d)
|
|
120
|
+
|
|
121
|
+
# the actions per stack per token
|
|
122
|
+
|
|
123
|
+
action_logits = self.to_action_logits(tokens)
|
|
124
|
+
action_logits = self.split_action_logits(action_logits) # (b h 3)
|
|
125
|
+
action_logits = action_logits / action_temperature
|
|
126
|
+
|
|
127
|
+
if stochastic_action:
|
|
128
|
+
action_logits = action_logits + gumbel_noise_like(action_logits)
|
|
129
|
+
|
|
130
|
+
actions = action_logits.softmax(dim = -1)
|
|
131
|
+
|
|
132
|
+
if hard_action:
|
|
133
|
+
soft_actions = actions
|
|
134
|
+
hard_actions = F.one_hot(actions.argmax(dim = -1), self.num_actions)
|
|
135
|
+
actions = straight_through(soft_actions, hard_actions)
|
|
136
|
+
|
|
137
|
+
# handle initial stack state
|
|
138
|
+
# but the transformer could dynamically generate this initial state as well
|
|
139
|
+
|
|
140
|
+
if not exists(stack_states):
|
|
141
|
+
init_stack = torch.zeros((batch, num_stacks, stack_size, dim_stack), device = device)
|
|
142
|
+
init_stack_mask = torch.zeros((batch, num_stacks, stack_size), dtype = torch.bool, device = device)
|
|
143
|
+
stack_states = (init_stack, init_stack_mask)
|
|
144
|
+
|
|
145
|
+
# differentiable stack - eq 3 (applies to mask as well)
|
|
146
|
+
|
|
147
|
+
stack, stack_mask = stack_states
|
|
148
|
+
zeros = torch.zeros_like(stack_inputs)
|
|
149
|
+
|
|
150
|
+
stack_with_inputs, _ = pack_with_inverse((stack_inputs, stack, zeros), 'b h * d')
|
|
151
|
+
|
|
152
|
+
stack_mask_with_input = pad_left_at_dim(stack_mask, 1, value = True, dim = -1)
|
|
153
|
+
stack_mask_with_input = pad_right_at_dim(stack_mask_with_input, 1, value = False, dim = -1)
|
|
154
|
+
|
|
155
|
+
# superposition of all three possibilities, weighted summed by the action
|
|
156
|
+
|
|
157
|
+
pushed_stack = stack_with_inputs[..., :-2, :]
|
|
158
|
+
popped_stack = stack_with_inputs[..., 2:,:]
|
|
159
|
+
noop_stack = stack
|
|
160
|
+
|
|
161
|
+
stack_superpositions = torch.stack((pushed_stack, popped_stack, noop_stack), dim = -1)
|
|
162
|
+
|
|
163
|
+
pushed_mask = stack_mask_with_input[..., :-2]
|
|
164
|
+
popped_mask = stack_mask_with_input[..., 2:]
|
|
165
|
+
noop_mask = stack_mask
|
|
166
|
+
|
|
167
|
+
stack_mask_superpositions = torch.stack((pushed_mask, popped_mask, noop_mask), dim = -1)
|
|
168
|
+
|
|
169
|
+
next_stack = einsum(stack_superpositions, actions, 'b h s d a, b h a -> b h s d')
|
|
170
|
+
next_mask = einsum(stack_mask_superpositions.float(), actions.float(), 'b h s a, b h a -> b h s').bool()
|
|
171
|
+
|
|
172
|
+
next_stack_states = (next_stack, next_mask)
|
|
173
|
+
|
|
174
|
+
# read from the stacks, using their attention like method
|
|
175
|
+
# however, will do some adjustments as it does not look quite right
|
|
176
|
+
|
|
177
|
+
null_stack = repeat(self.null_stack, 'd -> b h 1 d', b = batch, h = num_stacks)
|
|
178
|
+
|
|
179
|
+
next_stack_with_null = cat((next_stack, null_stack), dim = -2)
|
|
180
|
+
next_mask_with_null = pad_right_at_dim(next_mask, 1, value = True)
|
|
181
|
+
|
|
182
|
+
read_stack_attn_logits = self.to_read_stack_attn(next_stack_with_null)
|
|
183
|
+
read_stack_attn_logits = rearrange(read_stack_attn_logits, '... 1 -> ...')
|
|
184
|
+
|
|
185
|
+
read_stack_attn_logits = read_stack_attn_logits.masked_fill(next_mask_with_null, mask_value(next_stack_with_null))
|
|
186
|
+
|
|
187
|
+
read_stack_attn = read_stack_attn_logits.softmax(dim = -1)
|
|
188
|
+
|
|
189
|
+
read_stack_out = einsum(next_stack_with_null, read_stack_attn, 'b h s d, b h s -> b h d')
|
|
190
|
+
|
|
191
|
+
# combine
|
|
192
|
+
|
|
193
|
+
out = rearrange(stack_inputs, 'b h d -> b (h d)')
|
|
194
|
+
|
|
195
|
+
out = self.combine(out)
|
|
196
|
+
|
|
197
|
+
out = inverse_pack(out)
|
|
198
|
+
|
|
199
|
+
return out, next_stack_states
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|