tensor-ee 0.1.0__py3-none-any.whl
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.
- tensor_ee/__init__.py +28 -0
- tensor_ee/_swin_parameters.py +304 -0
- tensor_ee/blocks/__init__.py +0 -0
- tensor_ee/blocks/raster/__init__.py +36 -0
- tensor_ee/blocks/raster/activations.py +183 -0
- tensor_ee/blocks/raster/conv.py +183 -0
- tensor_ee/blocks/raster/merge.py +66 -0
- tensor_ee/blocks/raster/outputs.py +211 -0
- tensor_ee/blocks/raster/pooling.py +182 -0
- tensor_ee/blocks/raster/resample.py +64 -0
- tensor_ee/blocks/window/__init__.py +44 -0
- tensor_ee/blocks/window/activations.py +54 -0
- tensor_ee/blocks/window/attention.py +366 -0
- tensor_ee/blocks/window/conv.py +323 -0
- tensor_ee/blocks/window/linear.py +129 -0
- tensor_ee/blocks/window/merge.py +67 -0
- tensor_ee/blocks/window/normalization.py +197 -0
- tensor_ee/blocks/window/patch.py +362 -0
- tensor_ee/blocks/window/pooling.py +275 -0
- tensor_ee/blocks/window/resample.py +286 -0
- tensor_ee/blocks/window/segformer.py +718 -0
- tensor_ee/blocks/window/swin.py +602 -0
- tensor_ee/blocks/window/swin_upernet_config.py +361 -0
- tensor_ee/blocks/window/upernet.py +1016 -0
- tensor_ee/comparison.py +213 -0
- tensor_ee/compatibility.py +180 -0
- tensor_ee/compiler.py +356 -0
- tensor_ee/converter/__init__.py +72 -0
- tensor_ee/converter/build.py +120 -0
- tensor_ee/converter/convert.py +388 -0
- tensor_ee/converter/export.py +846 -0
- tensor_ee/converter/graph.py +417 -0
- tensor_ee/converter/operations.py +790 -0
- tensor_ee/converter/plan.py +675 -0
- tensor_ee/converter/recognizers.py +1273 -0
- tensor_ee/converter/request.py +146 -0
- tensor_ee/converter/run.py +950 -0
- tensor_ee/core/__init__.py +8 -0
- tensor_ee/core/errors.py +25 -0
- tensor_ee/core/grid.py +323 -0
- tensor_ee/core/metadata.py +398 -0
- tensor_ee/core/ops.py +336 -0
- tensor_ee/core/precision.py +133 -0
- tensor_ee/core/tensor.py +129 -0
- tensor_ee/examples/__init__.py +1 -0
- tensor_ee/examples/nonwater.py +251 -0
- tensor_ee/layouts/__init__.py +17 -0
- tensor_ee/layouts/boundaries.py +103 -0
- tensor_ee/layouts/raster.py +117 -0
- tensor_ee/layouts/shift.py +226 -0
- tensor_ee/layouts/window.py +201 -0
- tensor_ee/models/__init__.py +16 -0
- tensor_ee/models/fixtures.py +119 -0
- tensor_ee/models/segformer_b0/__init__.py +9 -0
- tensor_ee/models/segformer_b0/configuration.py +209 -0
- tensor_ee/models/segformer_b0/decoder.py +130 -0
- tensor_ee/models/segformer_b0/encoder.py +90 -0
- tensor_ee/models/swin_t224/__init__.py +30 -0
- tensor_ee/models/swin_t224/configuration.py +103 -0
- tensor_ee/models/swin_t224/decoder.py +53 -0
- tensor_ee/models/swin_t224/encoder.py +50 -0
- tensor_ee/onnx_import.py +431 -0
- tensor_ee/outputs.py +16 -0
- tensor_ee/primitives.py +110 -0
- tensor_ee/receptive_field.py +257 -0
- tensor_ee/resnet_models.py +185 -0
- tensor_ee/segwater_inference.py +235 -0
- tensor_ee/segwater_preprocessing.py +182 -0
- tensor_ee/segwater_pytorch.py +249 -0
- tensor_ee/semantics.py +389 -0
- tensor_ee/smp.py +155 -0
- tensor_ee/swin.py +197 -0
- tensor_ee/swin_workflow.py +470 -0
- tensor_ee/tiling.py +287 -0
- tensor_ee/weight_assets.py +371 -0
- tensor_ee/weight_mapping.py +51 -0
- tensor_ee/weights/__init__.py +14 -0
- tensor_ee/weights/assets.py +160 -0
- tensor_ee/weights/constants.py +156 -0
- tensor_ee-0.1.0.dist-info/METADATA +172 -0
- tensor_ee-0.1.0.dist-info/RECORD +84 -0
- tensor_ee-0.1.0.dist-info/WHEEL +5 -0
- tensor_ee-0.1.0.dist-info/licenses/LICENSE +21 -0
- tensor_ee-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
"""SegFormer's composites in window layout: the channel norm, the overlapping patch
|
|
2
|
+
embedding, efficient attention with global keys and values, the Mix-FFN, the MiT block, and
|
|
3
|
+
the all-MLP decoder's pyramid projection and fused head.
|
|
4
|
+
|
|
5
|
+
Moved from ``tensor_ee/models/segformer_b0/encoder.py`` (``NormConstants``,
|
|
6
|
+
``norm_constants``, ``channel_norm``, ``OverlapConstants``, ``overlap_constants``,
|
|
7
|
+
``decimate``, ``overlap_embedding``, ``AttentionConstants``, ``attention_constants``,
|
|
8
|
+
``spatial_reduction``, ``shared_key_attention``, ``global_attention``, ``MixFFNConstants``,
|
|
9
|
+
``mix_ffn_constants``, ``mix_ffn``, and the MiT block ``BlockConstants``, ``block_constants``
|
|
10
|
+
and ``block``, renamed here ``MiTBlockConstants``, ``mit_block_constants`` and ``mit_block``,
|
|
11
|
+
with their helpers) and ``decoder.py`` (``project_pyramid``, and the fuse, BatchNorm, ReLU and
|
|
12
|
+
head sequence of ``decoder()``, now ``fuse_head``). The fixed SegFormer-B0 model keeps its
|
|
13
|
+
configuration and checks there and re-exports these objects under their former names; there
|
|
14
|
+
is one implementation. The decoder composites' constants builders, ``project_pyramid_constants``
|
|
15
|
+
and ``fuse_head_constants``, are shared with ``decoder_constants``, so that the model and the
|
|
16
|
+
block-graph builder (``tensor_ee/converter/``) share them.
|
|
17
|
+
|
|
18
|
+
Parameters keep SMP 0.5.0 state_dict names under a ``prefix``: NumPy float32 arrays of the
|
|
19
|
+
original shapes, or ``ServerArray``s, a convolution's as [out, in k k] in PyTorch's (c, ky, kx)
|
|
20
|
+
flatten order. Every spatial feature is W7: one [49, C] float32 array per 7 x 7 window
|
|
21
|
+
(``WindowTensor``, window 7) of a ``TokenGrid`` divisible by seven. A token is a pixel of
|
|
22
|
+
that grid. In PyTorch's terms (``mit_b0``, ``SegformerDecoder``)::
|
|
23
|
+
|
|
24
|
+
channel_norm LayerNorm over channels, eps 1e-5 (layer_norm) or 1e-6 (layer_norm2d)
|
|
25
|
+
overlap_embedding LN(Conv2d(x; k, stride s, padding k // 2)), (k, s) = (7, 4) or (3, 2)
|
|
26
|
+
spatial_reduction LN(Conv2d(x; r, stride r)) for r > 1; x itself for r = 1
|
|
27
|
+
shared_key_attention per head h of width d: softmax(q_h k_h^T * d^-0.5) v_h, concatenated
|
|
28
|
+
global_attention proj(shared_key_attention(q(x), kv(spatial_reduction(x))))
|
|
29
|
+
mix_ffn fc2(GELU(DWConv3x3(fc1(x)))), hidden width 4 C
|
|
30
|
+
mit_block x1 = x + attn(LN1(x)); out = x1 + mlp(LN2(x1))
|
|
31
|
+
project_pyramid cat(up(Linear(C4)), up(Linear(C3)), up(Linear(C2)), Linear(C1))
|
|
32
|
+
fuse_head up4(Conv1x1_head(ReLU(BN(Conv1x1_fuse(x)))))
|
|
33
|
+
|
|
34
|
+
The overlap embedding convolves at stride 1 on the fine grid (k 7: a ring-3 halo, im2col and
|
|
35
|
+
``linear``; k 3: ``conv3x3_window``), then ``decimate`` samples phase (0, 0): pixel (i, j) of
|
|
36
|
+
the result is pixel (s i, s j) of the stride-1 map, exactly PyTorch's strided output. The
|
|
37
|
+
reduction's grid must be exactly one 7 x 7 window (a 7 r x 7 r chip): its 49 keys and values
|
|
38
|
+
are gathered once (``gather``) and shared by every query window, so the attention is global
|
|
39
|
+
without a per-window key or value. The pyramid's resizes are bilinear with
|
|
40
|
+
``align_corners=False`` (factors 8, 4, 2; C1 unchanged), the head's is four-times bilinear
|
|
41
|
+
with ``align_corners=True``.
|
|
42
|
+
|
|
43
|
+
**Policy** ``composite``: the norms, projections, convolutions, GELU, BatchNorm, residual
|
|
44
|
+
additions and resizes run in ``double_round_once``, the attention core in ``round_each_op``
|
|
45
|
+
with SMP's post-QK scale (``score_scale``), ReLU and concatenation without arithmetic.
|
|
46
|
+
``decimate`` has no arithmetic (``none``).
|
|
47
|
+
|
|
48
|
+
**Locality.** The global attention, its shared-key core and the MiT block read the whole chip
|
|
49
|
+
(``EXTENT``), and tiling with halos cannot reproduce them (chip-edge dependent); the
|
|
50
|
+
reduction makes the whole chip one window (``EXTENT``). The head's ``align_corners=True``
|
|
51
|
+
weights depend on the map's size (they are not shift-invariant), so ``fuse_head`` is
|
|
52
|
+
chip-edge dependent too, though it reads only a ring of one window.
|
|
53
|
+
|
|
54
|
+
**Rows.** Constants are built ahead for a layout (``LAYOUT``, ``WindowLayout(7)``: 49 rows),
|
|
55
|
+
pre-repeated to its rows because Earth Engine arrays do not broadcast, and the blocks refuse
|
|
56
|
+
constants built for another row count (client-side).
|
|
57
|
+
|
|
58
|
+
**Refused** (``reject``): a feature that is not W7 on a grid divisible by seven; norm epsilons
|
|
59
|
+
other than 1e-5 and 1e-6; overlap kernel and stride other than 7/4 and 3/2; decimation
|
|
60
|
+
factors other than 2 and 4; heads that do not divide the width; reduction ratios other than 1,
|
|
61
|
+
2, 4 and 8; a reduced map that is not exactly one 7 x 7 window; a pyramid that is not four
|
|
62
|
+
levels at C1, C1/2, C1/4 and C1/8; head constants of other widths than ``channels`` and
|
|
63
|
+
``classes``.
|
|
64
|
+
|
|
65
|
+
**Validated.** Live only inside the fixed SegFormer-B0 (``mit_b0``) at 224 x 224, two bands
|
|
66
|
+
and two classes: widths 32, 64, 160, 256, heads 1, 2, 5, 8, reduction ratios 8, 4, 2, 1,
|
|
67
|
+
window 7 and decoder width 256, by staged agreement on one seeded chip and state under the
|
|
68
|
+
1e-4 absolute target (docs/evidence/segformer_b0-fullmodel-live.json), and as small
|
|
69
|
+
compositions (docs/evidence/segformer_b0-live.json). Other settings the builders accept,
|
|
70
|
+
such as the small fixture widths, are not validated.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
from __future__ import annotations
|
|
74
|
+
|
|
75
|
+
from dataclasses import dataclass
|
|
76
|
+
|
|
77
|
+
import numpy as np
|
|
78
|
+
|
|
79
|
+
from ...core.errors import reject
|
|
80
|
+
from ...core.grid import TokenGrid, to_feature_grid, to_token_grid
|
|
81
|
+
from ...core.metadata import COMPOSITE, NO_ARITHMETIC, BlockMetadata, Locality, Validated, validate_metadata
|
|
82
|
+
from ...core.ops import ValueOps
|
|
83
|
+
from ...core.precision import DOUBLE_ROUND_ONCE, ROUND_EACH_OP
|
|
84
|
+
from ...core.tensor import WindowTensor, band_names
|
|
85
|
+
from ...layouts.raster import stride_sample_phase
|
|
86
|
+
from ...layouts.window import from_dense, halo, im2col, to_dense, to_windows
|
|
87
|
+
from ...weights.constants import ServerArray, WindowLayout, row_constant
|
|
88
|
+
from .activations import gelu, relu
|
|
89
|
+
from .attention import naive_attention
|
|
90
|
+
from .conv import METADATA as CONV_METADATA
|
|
91
|
+
from .conv import Conv3x3Constants, DepthwiseConv3x3Constants, conv3x3_window, depthwise_conv3x3_window
|
|
92
|
+
from .linear import METADATA as LINEAR_METADATA
|
|
93
|
+
from .linear import LinearConstants, conv1x1, conv1x1_constants, linear, no_constants
|
|
94
|
+
from .merge import add, concat
|
|
95
|
+
from .normalization import METADATA as NORM_METADATA
|
|
96
|
+
from .normalization import BatchNormConstants, batch_norm, layer_norm, layer_norm2d
|
|
97
|
+
from .patch import stride_columns
|
|
98
|
+
from .pooling import gather
|
|
99
|
+
from .resample import METADATA as RESIZE_METADATA
|
|
100
|
+
from .resample import window_resize
|
|
101
|
+
|
|
102
|
+
LAYOUT = WindowLayout(7)
|
|
103
|
+
"""The layout every SegFormer composite runs in: window 7, 49 rows per constant."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _server_shape(value, name):
|
|
107
|
+
if not isinstance(value.shape, (tuple, list)):
|
|
108
|
+
reject(f"{name}: ServerArray shape must be a tuple or list of dimensions")
|
|
109
|
+
if any(isinstance(d, (bool, np.bool_)) or not isinstance(d, (int, np.integer)) or d < 1
|
|
110
|
+
for d in value.shape):
|
|
111
|
+
reject(f"{name}: ServerArray dimensions must be positive non-bool integers")
|
|
112
|
+
return tuple(int(d) for d in value.shape)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _parameter(parameters, name, shape):
|
|
116
|
+
value = parameters[name]
|
|
117
|
+
if isinstance(value, ServerArray):
|
|
118
|
+
expected = (shape[0], int(np.prod(shape[1:]))) if len(shape) == 4 else shape
|
|
119
|
+
if _server_shape(value, name) != expected:
|
|
120
|
+
reject(f"{name}: ServerArray shape {value.shape}, expected {expected}")
|
|
121
|
+
elif (not isinstance(value, np.ndarray) or value.shape != shape or
|
|
122
|
+
value.dtype != np.float32 or not np.isfinite(value).all()):
|
|
123
|
+
reject(f"{name}: expected finite float32 NumPy shape {shape}")
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _linear_constants(ops, p, name, cin, cout, layout):
|
|
128
|
+
return LinearConstants(ops, _parameter(p, f"{name}.weight", (cout, cin)),
|
|
129
|
+
_parameter(p, f"{name}.bias", (cout,)), layout)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _conv_columns(ops, weight, cin, cout, kernel, *, tap_major):
|
|
133
|
+
"""OIHW -> Linear [out,columns], moving coefficients without arithmetic.
|
|
134
|
+
|
|
135
|
+
halo/im2col has (ky,kx,c) columns; stride_columns has (c,ky,kx).
|
|
136
|
+
Server convolution weights arrive flattened in the latter PyTorch order.
|
|
137
|
+
"""
|
|
138
|
+
if isinstance(weight, ServerArray):
|
|
139
|
+
if not tap_major:
|
|
140
|
+
return weight
|
|
141
|
+
arr = ValueOps(ops.ee)
|
|
142
|
+
w = arr.reshape(weight.array, [cout, cin, kernel * kernel])
|
|
143
|
+
w = arr.reshape(arr.transpose(w, 1, 2), [cout, kernel * kernel * cin])
|
|
144
|
+
return ServerArray(w, (cout, kernel * kernel * cin))
|
|
145
|
+
if tap_major:
|
|
146
|
+
return weight.transpose(0, 2, 3, 1).reshape(cout, kernel * kernel * cin)
|
|
147
|
+
return weight.reshape(cout, kernel * kernel * cin)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _check_feature(x, *, channels=None, grid=None):
|
|
151
|
+
if not isinstance(x, WindowTensor) or x.window != 7 or getattr(x, "layout", "window") != "window":
|
|
152
|
+
reject("SegFormer compositions require W7 spatial features")
|
|
153
|
+
if not isinstance(x.grid, TokenGrid) or x.grid.height % 7 or x.grid.width % 7:
|
|
154
|
+
reject("features require a TokenGrid divisible by seven")
|
|
155
|
+
if channels is not None and x.channels != channels:
|
|
156
|
+
reject(f"feature has {x.channels} channels; expected {channels}")
|
|
157
|
+
if grid is not None and x.grid != grid:
|
|
158
|
+
reject(f"feature grid {x.grid} differs from expected {grid}")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Channel norm
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
@dataclass
|
|
165
|
+
class NormConstants:
|
|
166
|
+
gamma: object
|
|
167
|
+
beta: object
|
|
168
|
+
channels: int
|
|
169
|
+
eps: float
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def norm_constants(ops, parameters, prefix, channels, *, eps, layout=LAYOUT):
|
|
173
|
+
"""Affine channel constants pre-repeated to the spatial layout's rows."""
|
|
174
|
+
if eps not in (1e-5, 1e-6):
|
|
175
|
+
reject("SegFormer norms use epsilon 1e-5 or 1e-6")
|
|
176
|
+
return NormConstants(row_constant(ops, _parameter(parameters, f"{prefix}.weight", (channels,)), layout),
|
|
177
|
+
row_constant(ops, _parameter(parameters, f"{prefix}.bias", (channels,)), layout),
|
|
178
|
+
channels, eps)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def channel_norm(ops, x, c: NormConstants):
|
|
182
|
+
"""LayerNorm over the channels of a W7 feature: ``layer_norm`` at eps 1e-5 (the
|
|
183
|
+
embedding and reduction norms), ``layer_norm2d`` at eps 1e-6 (the block and stage norms)."""
|
|
184
|
+
_check_feature(x, channels=c.channels)
|
|
185
|
+
if c.eps == 1e-5:
|
|
186
|
+
image = layer_norm(ops, x.image, c.gamma, c.beta, dim=c.channels,
|
|
187
|
+
layout=x, eps=1e-5, policy=DOUBLE_ROUND_ONCE)
|
|
188
|
+
elif c.eps == 1e-6:
|
|
189
|
+
image = layer_norm2d(ops, x.image, c.gamma, c.beta, dim=c.channels,
|
|
190
|
+
rows=x, eps=1e-6, policy=DOUBLE_ROUND_ONCE)
|
|
191
|
+
else:
|
|
192
|
+
reject("malformed SegFormer norm epsilon")
|
|
193
|
+
return x.like(image)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
# Overlapping patch embedding
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
@dataclass
|
|
200
|
+
class OverlapConstants:
|
|
201
|
+
projection: object
|
|
202
|
+
norm: NormConstants
|
|
203
|
+
cin: int
|
|
204
|
+
cout: int
|
|
205
|
+
kernel: int
|
|
206
|
+
stride: int
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def overlap_constants(ops, parameters, prefix, cin, cout, *, kernel, stride, layout=LAYOUT):
|
|
210
|
+
"""Biased overlapping embedding: supported k7/s4/p3 and k3/s2/p1 only.
|
|
211
|
+
|
|
212
|
+
This composition builder can use small fixture widths; the fixed model
|
|
213
|
+
constructor separately enforces every full-model state shape.
|
|
214
|
+
"""
|
|
215
|
+
if (any(isinstance(v, (bool, np.bool_)) or not isinstance(v, (int, np.integer)) for v in (kernel, stride))
|
|
216
|
+
or (kernel, stride) not in ((7, 4), (3, 2))):
|
|
217
|
+
reject("overlap embeddings support k7/s4/p3 or k3/s2/p1 only")
|
|
218
|
+
weight = _parameter(parameters, f"{prefix}.proj.weight", (cout, cin, kernel, kernel))
|
|
219
|
+
bias = _parameter(parameters, f"{prefix}.proj.bias", (cout,))
|
|
220
|
+
projection = (LinearConstants(ops, _conv_columns(ops, weight, cin, cout, kernel, tap_major=True), bias, layout)
|
|
221
|
+
if kernel == 7 else Conv3x3Constants(ops, weight, bias, layout))
|
|
222
|
+
return OverlapConstants(projection, norm_constants(ops, parameters, f"{prefix}.norm", cout,
|
|
223
|
+
eps=1e-5, layout=layout), cin, cout, kernel, stride)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def decimate(ee, x, factor):
|
|
227
|
+
"""Exact phase-(0,0) sample and explicit canonical relabel, with no arithmetic.
|
|
228
|
+
|
|
229
|
+
FeatureGrid supplies x0/y0 to stride_sample_phase; TokenGrid is never passed
|
|
230
|
+
directly to that sampler. Its pixel (i,j) is source (factor*i,factor*j).
|
|
231
|
+
"""
|
|
232
|
+
_check_feature(x)
|
|
233
|
+
if isinstance(factor, (bool, np.bool_)) or not isinstance(factor, (int, np.integer)) or factor not in (2, 4):
|
|
234
|
+
reject("SegFormer decimation factors are two and four")
|
|
235
|
+
destination = x.grid.coarse(factor)
|
|
236
|
+
if destination.height % 7 or destination.width % 7:
|
|
237
|
+
reject("decimated map must partition into complete W7 windows")
|
|
238
|
+
fine = to_feature_grid(x.grid)
|
|
239
|
+
coarse = fine.coarse(factor)
|
|
240
|
+
canonical = to_token_grid(coarse)
|
|
241
|
+
dense = to_dense(ee, x.grid, x.image, x.channels, window=7)
|
|
242
|
+
sampled = stride_sample_phase(ee, dense, fine, factor, a=0, b=0)
|
|
243
|
+
relabeled = sampled.changeProj(coarse.projection(ee), canonical.projection(ee))
|
|
244
|
+
image = from_dense(ee, canonical, relabeled, x.channels, window=7)
|
|
245
|
+
return WindowTensor(image, canonical, x.channels, 7)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def overlap_embedding(ops, x, c: OverlapConstants) -> dict:
|
|
249
|
+
"""The stride-1 convolution, ``decimate`` at the stride, then ``channel_norm`` (eps 1e-5)."""
|
|
250
|
+
_check_feature(x, channels=c.cin)
|
|
251
|
+
coarse = x.grid.coarse(7)
|
|
252
|
+
if c.kernel == 7 and c.stride == 4:
|
|
253
|
+
columns = im2col(ops.ee, halo(ops.ee, x.image, coarse, c.cin, window=7, ring=3),
|
|
254
|
+
c.cin, 7, window=7)
|
|
255
|
+
dense = linear(ops, columns, c.projection, layout=x, policy=DOUBLE_ROUND_ONCE)
|
|
256
|
+
elif c.kernel == 3 and c.stride == 2:
|
|
257
|
+
columns = None
|
|
258
|
+
dense = conv3x3_window(ops, x.image, c.projection, coarse, window=7, policy=DOUBLE_ROUND_ONCE)
|
|
259
|
+
else:
|
|
260
|
+
reject("malformed overlap constants")
|
|
261
|
+
dense = x.like(dense, c.cout)
|
|
262
|
+
projected = decimate(ops.ee, dense, c.stride)
|
|
263
|
+
return {"columns": columns, "dense_projection": dense, "projection": projected,
|
|
264
|
+
"out": channel_norm(ops, projected, c.norm)}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
# Efficient attention: spatial reduction and global keys and values
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
@dataclass
|
|
271
|
+
class AttentionConstants:
|
|
272
|
+
q: LinearConstants
|
|
273
|
+
kv: LinearConstants
|
|
274
|
+
projection: LinearConstants
|
|
275
|
+
sr: object
|
|
276
|
+
sr_norm: object
|
|
277
|
+
dim: int
|
|
278
|
+
heads: int
|
|
279
|
+
ratio: int
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def attention_constants(ops, parameters, prefix, dim, heads, ratio, *, layout=LAYOUT):
|
|
283
|
+
if (isinstance(heads, (bool, np.bool_)) or not isinstance(heads, (int, np.integer)) or heads < 1 or dim % heads or
|
|
284
|
+
isinstance(ratio, (bool, np.bool_)) or not isinstance(ratio, (int, np.integer)) or ratio not in (1, 2, 4, 8)):
|
|
285
|
+
reject("attention needs positive dividing heads and SR ratio 1/2/4/8")
|
|
286
|
+
sr, norm = None, None
|
|
287
|
+
if ratio > 1:
|
|
288
|
+
weight = _parameter(parameters, f"{prefix}.sr.weight", (dim, dim, ratio, ratio))
|
|
289
|
+
sr = LinearConstants(ops, _conv_columns(ops, weight, dim, dim, ratio, tap_major=False),
|
|
290
|
+
_parameter(parameters, f"{prefix}.sr.bias", (dim,)), layout)
|
|
291
|
+
norm = norm_constants(ops, parameters, f"{prefix}.norm", dim, eps=1e-5, layout=layout)
|
|
292
|
+
return AttentionConstants(_linear_constants(ops, parameters, f"{prefix}.q", dim, dim, layout),
|
|
293
|
+
_linear_constants(ops, parameters, f"{prefix}.kv", dim, 2 * dim, layout),
|
|
294
|
+
_linear_constants(ops, parameters, f"{prefix}.proj", dim, dim, layout),
|
|
295
|
+
sr, norm, dim, heads, ratio)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def spatial_reduction(ops, x, c: AttentionConstants) -> dict:
|
|
299
|
+
"""Learned unpadded full-channel convolution of the normalized query input."""
|
|
300
|
+
_check_feature(x, channels=c.dim)
|
|
301
|
+
reduced_grid = x.grid.coarse(c.ratio)
|
|
302
|
+
if (reduced_grid.height, reduced_grid.width) != (7, 7):
|
|
303
|
+
reject("global reduced keys must cover exactly one 7 x 7 window")
|
|
304
|
+
if c.ratio == 1:
|
|
305
|
+
if c.sr is not None or c.sr_norm is not None:
|
|
306
|
+
reject("ratio-one attention has no SR convolution or normalization")
|
|
307
|
+
return {"columns": None, "projection": x, "out": x}
|
|
308
|
+
dense = to_dense(ops.ee, x.grid, x.image, x.channels, window=7)
|
|
309
|
+
# Keep Python's ordinary EE serialization recursion limit meaningful. Each
|
|
310
|
+
# existing stride_columns call gathers a bounded channel group; concatenating
|
|
311
|
+
# its value-only rows preserves (c,ky,kx) order and changes no arithmetic.
|
|
312
|
+
names = band_names(x.channels)
|
|
313
|
+
group = max(1, 128 // (c.ratio * c.ratio))
|
|
314
|
+
parts = [stride_columns(ops.ee, dense, x.grid, patch=c.ratio, bands=names[first:first + group])
|
|
315
|
+
for first in range(0, x.channels, group)]
|
|
316
|
+
columns = ops.cat_cols(parts)
|
|
317
|
+
windows = to_windows(ops.ee, columns, reduced_grid, window=7, mask=True)
|
|
318
|
+
projection = linear(ops, windows, c.sr, layout=x, policy=DOUBLE_ROUND_ONCE)
|
|
319
|
+
feature = WindowTensor(projection, reduced_grid, x.channels, 7)
|
|
320
|
+
return {"columns": columns, "projection": feature, "out": channel_norm(ops, feature, c.sr_norm)}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def shared_key_attention(ops, q, kv, heads: int) -> dict:
|
|
324
|
+
"""Global V49 K/V shared dynamically by all W7 query windows.
|
|
325
|
+
|
|
326
|
+
q is W7 C channels; kv is the single reduced W7 window with 2C channels.
|
|
327
|
+
Each head computes [49,49] temporary scores, never a spatial tensor whose
|
|
328
|
+
channels or rows misrepresent the layout. Result images are pinned to q.
|
|
329
|
+
"""
|
|
330
|
+
_check_feature(q)
|
|
331
|
+
_check_feature(kv, channels=2 * q.channels)
|
|
332
|
+
if ((kv.grid.height, kv.grid.width) != (7, 7) or isinstance(heads, (bool, np.bool_)) or
|
|
333
|
+
not isinstance(heads, (int, np.integer)) or heads < 1 or q.channels % heads):
|
|
334
|
+
reject("shared keys need one W7 window and heads dividing the query width")
|
|
335
|
+
arr = ValueOps(ops.ee)
|
|
336
|
+
global_kv = arr.reshape(gather(ops.ee, kv.image, kv.grid.coarse(7)), [49, kv.channels])
|
|
337
|
+
dim = q.channels // heads
|
|
338
|
+
result, outputs = {"kv_global": global_kv}, []
|
|
339
|
+
for h in range(heads):
|
|
340
|
+
query = ops.slice_cols(q.image, h * dim, (h + 1) * dim)
|
|
341
|
+
key = ops.wrap(arr.slice_cols(global_kv, h * dim, (h + 1) * dim))
|
|
342
|
+
value = ops.wrap(arr.slice_cols(global_kv, q.channels + h * dim, q.channels + (h + 1) * dim))
|
|
343
|
+
key = key.reproject(q.grid.coarse(7).projection(ops.ee))
|
|
344
|
+
value = value.reproject(q.grid.coarse(7).projection(ops.ee))
|
|
345
|
+
head = naive_attention(ops, query, key, value, tokens=49, dim=dim,
|
|
346
|
+
score_scale=dim ** -0.5, policy=ROUND_EACH_OP)
|
|
347
|
+
result.update({f"h{h}_{name}": tensor for name, tensor in head.items()})
|
|
348
|
+
outputs.append(head["output"])
|
|
349
|
+
combined = ops.cat_cols(outputs).reproject(q.grid.coarse(7).projection(ops.ee))
|
|
350
|
+
result["out"] = q.like(combined)
|
|
351
|
+
return result
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def global_attention(ops, x, c: AttentionConstants) -> dict:
|
|
355
|
+
"""SMP's efficient self-attention: the query projection, ``spatial_reduction``, the key and
|
|
356
|
+
value projection, ``shared_key_attention`` and the output projection."""
|
|
357
|
+
_check_feature(x, channels=c.dim)
|
|
358
|
+
q = x.like(linear(ops, x.image, c.q, layout=x, policy=DOUBLE_ROUND_ONCE))
|
|
359
|
+
reduction = spatial_reduction(ops, x, c)
|
|
360
|
+
reduced = reduction["out"]
|
|
361
|
+
kv = reduced.like(linear(ops, reduced.image, c.kv, layout=reduced, policy=DOUBLE_ROUND_ONCE), 2 * c.dim)
|
|
362
|
+
result = shared_key_attention(ops, q, kv, c.heads)
|
|
363
|
+
combined = result["out"]
|
|
364
|
+
projected = x.like(linear(ops, combined.image, c.projection, layout=x, policy=DOUBLE_ROUND_ONCE))
|
|
365
|
+
result.update({"q": q, "sr_projection": reduction["projection"], "sr_norm": reduced,
|
|
366
|
+
"kv": kv, "concat": combined, "out": projected})
|
|
367
|
+
return result
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# ---------------------------------------------------------------------------
|
|
371
|
+
# Mix-FFN and the MiT block
|
|
372
|
+
# ---------------------------------------------------------------------------
|
|
373
|
+
@dataclass
|
|
374
|
+
class MixFFNConstants:
|
|
375
|
+
fc1: LinearConstants
|
|
376
|
+
depthwise: DepthwiseConv3x3Constants
|
|
377
|
+
fc2: LinearConstants
|
|
378
|
+
dim: int
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def mix_ffn_constants(ops, parameters, prefix, dim, *, layout=LAYOUT):
|
|
382
|
+
width = 4 * dim
|
|
383
|
+
dw = f"{prefix}.dwconv.dwconv"
|
|
384
|
+
depthwise = DepthwiseConv3x3Constants(ops, _parameter(parameters, f"{dw}.weight", (width, 1, 3, 3)),
|
|
385
|
+
_parameter(parameters, f"{dw}.bias", (width,)), rows=layout,
|
|
386
|
+
policy=DOUBLE_ROUND_ONCE)
|
|
387
|
+
return MixFFNConstants(_linear_constants(ops, parameters, f"{prefix}.fc1", dim, width, layout),
|
|
388
|
+
depthwise, _linear_constants(ops, parameters, f"{prefix}.fc2", width, dim, layout), dim)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def mix_ffn(ops, x, c: MixFFNConstants) -> dict:
|
|
392
|
+
"""fc1, the depthwise 3 x 3 convolution (zero padded at the chip edge), exact GELU, fc2."""
|
|
393
|
+
_check_feature(x, channels=c.dim)
|
|
394
|
+
first = x.like(linear(ops, x.image, c.fc1, layout=x, policy=DOUBLE_ROUND_ONCE), 4 * c.dim)
|
|
395
|
+
depthwise = first.like(depthwise_conv3x3_window(ops, first.image, c.depthwise, first.grid.coarse(7),
|
|
396
|
+
window=7, policy=DOUBLE_ROUND_ONCE))
|
|
397
|
+
activated = depthwise.like(gelu(ops, depthwise.image, policy=DOUBLE_ROUND_ONCE))
|
|
398
|
+
out = x.like(linear(ops, activated.image, c.fc2, layout=activated, policy=DOUBLE_ROUND_ONCE))
|
|
399
|
+
return {"fc1": first, "depthwise": depthwise, "gelu": activated, "out": out}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@dataclass
|
|
403
|
+
class MiTBlockConstants:
|
|
404
|
+
norm1: NormConstants
|
|
405
|
+
attention: AttentionConstants
|
|
406
|
+
norm2: NormConstants
|
|
407
|
+
mlp: MixFFNConstants
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def mit_block_constants(ops, parameters, prefix, dim, heads, ratio):
|
|
411
|
+
return MiTBlockConstants(norm_constants(ops, parameters, f"{prefix}.norm1", dim, eps=1e-6),
|
|
412
|
+
attention_constants(ops, parameters, f"{prefix}.attn", dim, heads, ratio),
|
|
413
|
+
norm_constants(ops, parameters, f"{prefix}.norm2", dim, eps=1e-6),
|
|
414
|
+
mix_ffn_constants(ops, parameters, f"{prefix}.mlp", dim))
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def mit_block(ops, x, c: MiTBlockConstants) -> dict:
|
|
418
|
+
"""One MiT block: ``channel_norm``, ``global_attention`` and a residual addition, then
|
|
419
|
+
``channel_norm``, ``mix_ffn`` and a residual addition."""
|
|
420
|
+
norm1 = channel_norm(ops, x, c.norm1)
|
|
421
|
+
attention = global_attention(ops, norm1, c.attention)
|
|
422
|
+
first = add(x, attention["out"], policy=DOUBLE_ROUND_ONCE)
|
|
423
|
+
norm2 = channel_norm(ops, first, c.norm2)
|
|
424
|
+
mlp = mix_ffn(ops, norm2, c.mlp)
|
|
425
|
+
return {"norm1": norm1, "attention": attention, "first_residual": first,
|
|
426
|
+
"norm2": norm2, "mlp": mlp, "out": add(first, mlp["out"], policy=DOUBLE_ROUND_ONCE)}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
# ---------------------------------------------------------------------------
|
|
430
|
+
# All-MLP decoder
|
|
431
|
+
# ---------------------------------------------------------------------------
|
|
432
|
+
def project_pyramid_constants(ops, parameters, prefixes, in_channels, channels: int, *, layout=LAYOUT) -> tuple:
|
|
433
|
+
"""``project_pyramid``'s four biased projections, deepest first as it takes them: projection
|
|
434
|
+
``i`` is the [``channels``, ``in_channels[i]``] ``weight`` and the ``bias`` under ``prefixes[i]``
|
|
435
|
+
(SMP: ``decoder.mlp_stage.{i}.linear``, C4 first)."""
|
|
436
|
+
if isinstance(prefixes, str) or len(prefixes) != 4 or len(in_channels) != 4:
|
|
437
|
+
reject("SegFormer pyramid requires four levels and four projections")
|
|
438
|
+
return tuple(_linear_constants(ops, parameters, prefix, cin, channels, layout)
|
|
439
|
+
for prefix, cin in zip(prefixes, in_channels))
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def project_pyramid(ops, features, projections) -> dict:
|
|
443
|
+
"""Project/resize/concat four W7 levels; projections are deepest-first.
|
|
444
|
+
|
|
445
|
+
Small fixture widths are permitted here; decoder() separately enforces the
|
|
446
|
+
fixed full-model shape contract. Equal-size interpolation is an identity.
|
|
447
|
+
"""
|
|
448
|
+
if len(features) != 4 or len(projections) != 4:
|
|
449
|
+
reject("SegFormer pyramid requires four levels and four projections")
|
|
450
|
+
target = features[0].grid
|
|
451
|
+
result, parts = {}, []
|
|
452
|
+
for i, (feature, projection) in enumerate(zip(reversed(features), projections)):
|
|
453
|
+
_check_feature(feature, channels=projection.in_features)
|
|
454
|
+
factor = 2 ** (3 - i)
|
|
455
|
+
if feature.grid.fine(factor) != target:
|
|
456
|
+
reject("pyramid feature grids must be C1, C1/2, C1/4, C1/8")
|
|
457
|
+
image = linear(ops, feature.image, projection, layout=feature, policy=DOUBLE_ROUND_ONCE)
|
|
458
|
+
projected = feature.like(image, projection.out_features)
|
|
459
|
+
result[f"C{4 - i}_projected"] = projected
|
|
460
|
+
if factor > 1:
|
|
461
|
+
image, fine = window_resize(ops.ee, image, feature.grid, projected.channels, factor,
|
|
462
|
+
align_corners=False, window=7, policy=DOUBLE_ROUND_ONCE)
|
|
463
|
+
else:
|
|
464
|
+
fine = feature.grid
|
|
465
|
+
resized = WindowTensor(image, fine, projected.channels, 7)
|
|
466
|
+
result[f"C{4 - i}_to_C1"] = resized
|
|
467
|
+
parts.append(resized)
|
|
468
|
+
result["out"] = concat(parts)
|
|
469
|
+
return result
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def fuse_head_constants(ops, parameters, prefixes, in_channels: int, channels: int, classes: int, *,
|
|
473
|
+
eps: float, layout=LAYOUT) -> tuple:
|
|
474
|
+
"""``fuse_head``'s constants ``(fusion, norm, head)`` from the three layers ``prefixes`` names
|
|
475
|
+
(SMP: ``decoder.fuse_stage.0``, ``decoder.fuse_stage.1``, ``segmentation_head.0``): the bias-free
|
|
476
|
+
fuse ``weight`` [``channels``, ``in_channels``, 1, 1]; the BatchNorm's ``weight``, ``bias``,
|
|
477
|
+
``running_mean`` and ``running_var`` with ``eps``; the head ``weight`` [``classes``, ``channels``,
|
|
478
|
+
1, 1] and its ``bias``."""
|
|
479
|
+
if isinstance(prefixes, str) or len(prefixes) != 3:
|
|
480
|
+
reject("the fused head reads three layers: the fusion, the BatchNorm and the head")
|
|
481
|
+
fusion, norm, head = prefixes
|
|
482
|
+
return (conv1x1_constants(ops, _parameter(parameters, f"{fusion}.weight", (channels, in_channels, 1, 1)),
|
|
483
|
+
None, layout),
|
|
484
|
+
BatchNormConstants(ops, *(_parameter(parameters, f"{norm}.{name}", (channels,))
|
|
485
|
+
for name in ("weight", "bias", "running_mean", "running_var")),
|
|
486
|
+
rows=layout, eps=eps),
|
|
487
|
+
conv1x1_constants(ops, _parameter(parameters, f"{head}.weight", (classes, channels, 1, 1)),
|
|
488
|
+
_parameter(parameters, f"{head}.bias", (classes,)), layout))
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def fuse_head(ops, x, fusion, norm, head, *, channels: int, classes: int) -> dict:
|
|
492
|
+
"""The fused pyramid ``x`` -> W7 logits four times finer: the bias-free fuse ``conv1x1``
|
|
493
|
+
(``fusion``, to ``channels``), inference ``batch_norm`` (``norm``), ``relu``, the biased head
|
|
494
|
+
``conv1x1`` (``head``, to ``classes``) and four-times ``window_resize`` with
|
|
495
|
+
``align_corners=True``. Graph construction only; constants of other widths are refused."""
|
|
496
|
+
if fusion.out_features != channels or head.out_features != classes:
|
|
497
|
+
reject(f"fuse and head constants must have {channels} and {classes} outputs")
|
|
498
|
+
fused = x.like(conv1x1(ops, x.image, fusion, layout=x, policy=DOUBLE_ROUND_ONCE), channels)
|
|
499
|
+
normalized = fused.like(batch_norm(ops, fused.image, norm, layout=fused, policy=DOUBLE_ROUND_ONCE))
|
|
500
|
+
activated = normalized.like(relu(ops, normalized.image))
|
|
501
|
+
logits = activated.like(conv1x1(ops, activated.image, head, layout=activated, policy=DOUBLE_ROUND_ONCE),
|
|
502
|
+
classes)
|
|
503
|
+
image, grid = window_resize(ops.ee, logits.image, logits.grid, classes, 4, align_corners=True,
|
|
504
|
+
window=7, policy=DOUBLE_ROUND_ONCE)
|
|
505
|
+
return {"fusion": fused, "batch_norm": normalized, "relu": activated, "head": logits,
|
|
506
|
+
"out": WindowTensor(image, grid, classes, 7)}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
# ---------------------------------------------------------------------------
|
|
510
|
+
# Cost functions (contract section 9): sums of the parts' METADATA cost functions
|
|
511
|
+
# ---------------------------------------------------------------------------
|
|
512
|
+
_PARTS = {**LINEAR_METADATA, **NORM_METADATA, **CONV_METADATA, **RESIZE_METADATA}
|
|
513
|
+
"""The parts' entries by block name: a composite's cost is the sum of its parts' costs."""
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def channel_norm_values(channels: int, *, layout=LAYOUT) -> int:
|
|
517
|
+
"""gamma and beta [rows, C] (``layer_norm`` and ``layer_norm2d`` count alike)."""
|
|
518
|
+
return _PARTS["layer_norm"].embedded_constants(channels, layout=layout)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def channel_norm_arrays() -> int:
|
|
522
|
+
"""Server arrays read: gamma and beta."""
|
|
523
|
+
return _PARTS["layer_norm"].asset_arrays()
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def overlap_embedding_values(cin: int, cout: int, *, kernel: int, layout=LAYOUT) -> int:
|
|
527
|
+
"""The k x k projection with its bias rows (``linear`` on k k Cin columns at k 7,
|
|
528
|
+
``conv3x3_window`` at k 3), and the norm."""
|
|
529
|
+
if kernel not in (7, 3):
|
|
530
|
+
reject("overlap embeddings support k7/s4/p3 or k3/s2/p1 only")
|
|
531
|
+
projection = (_PARTS["linear"].embedded_constants(kernel * kernel * cin, cout, bias=True, layout=layout)
|
|
532
|
+
if kernel == 7 else
|
|
533
|
+
_PARTS["conv3x3_window"].embedded_constants(cin, cout, bias=True, layout=layout))
|
|
534
|
+
return projection + channel_norm_values(cout, layout=layout)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def overlap_embedding_arrays() -> int:
|
|
538
|
+
"""Server arrays read: the projection's weight and bias, and the norm's gamma and beta."""
|
|
539
|
+
return _PARTS["linear"].asset_arrays(bias=True) + channel_norm_arrays()
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def spatial_reduction_values(dim: int, ratio: int, *, layout=LAYOUT) -> int:
|
|
543
|
+
"""The r x r reduction as a ``linear`` on r r C columns with its bias rows, and its norm;
|
|
544
|
+
nothing at ratio 1."""
|
|
545
|
+
if ratio == 1:
|
|
546
|
+
return 0
|
|
547
|
+
return (_PARTS["linear"].embedded_constants(ratio * ratio * dim, dim, bias=True, layout=layout)
|
|
548
|
+
+ channel_norm_values(dim, layout=layout))
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def spatial_reduction_arrays(*, ratio: int) -> int:
|
|
552
|
+
"""Server arrays read: the reduction's weight and bias and its norm's; none at ratio 1."""
|
|
553
|
+
return 0 if ratio == 1 else _PARTS["linear"].asset_arrays(bias=True) + channel_norm_arrays()
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def global_attention_values(dim: int, ratio: int, *, layout=LAYOUT) -> int:
|
|
557
|
+
"""The q [C, C], kv [C, 2C] and output [C, C] projections with their bias rows, and the
|
|
558
|
+
spatial reduction (``shared_key_attention`` embeds nothing; the heads cost nothing)."""
|
|
559
|
+
linear_values = _PARTS["linear"].embedded_constants
|
|
560
|
+
return (linear_values(dim, dim, layout=layout) + linear_values(dim, 2 * dim, layout=layout)
|
|
561
|
+
+ linear_values(dim, dim, layout=layout) + spatial_reduction_values(dim, ratio, layout=layout))
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def global_attention_arrays(*, ratio: int) -> int:
|
|
565
|
+
"""Server arrays read: the three projections' weights and biases, and the reduction's."""
|
|
566
|
+
return 3 * _PARTS["linear"].asset_arrays(bias=True) + spatial_reduction_arrays(ratio=ratio)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def mix_ffn_values(dim: int, *, layout=LAYOUT) -> int:
|
|
570
|
+
"""fc1 [C, 4C], the biased depthwise convolution of width 4C, and fc2 [4C, C]."""
|
|
571
|
+
linear_values = _PARTS["linear"].embedded_constants
|
|
572
|
+
return (linear_values(dim, 4 * dim, layout=layout)
|
|
573
|
+
+ _PARTS["depthwise_conv3x3_window"].embedded_constants(4 * dim, bias=True, layout=layout)
|
|
574
|
+
+ linear_values(4 * dim, dim, layout=layout))
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def mix_ffn_arrays() -> int:
|
|
578
|
+
"""Server arrays read: fc1, the depthwise convolution and fc2, each a weight and a bias."""
|
|
579
|
+
return (2 * _PARTS["linear"].asset_arrays(bias=True)
|
|
580
|
+
+ _PARTS["depthwise_conv3x3_window"].asset_arrays(bias=True))
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def mit_block_values(dim: int, ratio: int, *, layout=LAYOUT) -> int:
|
|
584
|
+
"""Two channel norms, the global attention and the Mix-FFN (the additions embed nothing)."""
|
|
585
|
+
return (2 * channel_norm_values(dim, layout=layout) + global_attention_values(dim, ratio, layout=layout)
|
|
586
|
+
+ mix_ffn_values(dim, layout=layout))
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def mit_block_arrays(*, ratio: int) -> int:
|
|
590
|
+
"""Server arrays read: two norms, the global attention and the Mix-FFN."""
|
|
591
|
+
return 2 * channel_norm_arrays() + global_attention_arrays(ratio=ratio) + mix_ffn_arrays()
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def project_pyramid_values(in_channels, channels: int, height: int, width: int, *, layout=LAYOUT) -> int:
|
|
595
|
+
"""Four projections, ``in_channels`` (the C1, C2, C3, C4 widths) to ``channels``, with their
|
|
596
|
+
bias rows, and the ``align_corners=False`` resize tables of C2, C3 and C4 (height / 2^k by
|
|
597
|
+
width / 2^k tokens, factor 2^k) onto C1's ``height`` x ``width`` tokens."""
|
|
598
|
+
if len(in_channels) != 4:
|
|
599
|
+
reject("SegFormer pyramid requires four levels and four projections")
|
|
600
|
+
projections = sum(_PARTS["linear"].embedded_constants(c, channels, layout=layout) for c in in_channels)
|
|
601
|
+
return projections + sum(_PARTS["window_resize"].embedded_constants(height // 2 ** k, width // 2 ** k, 2 ** k,
|
|
602
|
+
align_corners=False, window=7)
|
|
603
|
+
for k in (1, 2, 3))
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def project_pyramid_arrays() -> int:
|
|
607
|
+
"""Server arrays read: the four projections' weights and biases (resize tables are inline)."""
|
|
608
|
+
return 4 * _PARTS["linear"].asset_arrays(bias=True)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def fuse_head_values(in_channels: int, channels: int, classes: int, height: int, width: int, *,
|
|
612
|
+
layout=LAYOUT) -> int:
|
|
613
|
+
"""The bias-free fuse [in, channels], the BatchNorm rows, the biased head [channels, classes]
|
|
614
|
+
and the four-times ``align_corners=True`` resize table of a ``height`` x ``width`` token map."""
|
|
615
|
+
return (_PARTS["conv1x1"].embedded_constants(in_channels, channels, bias=False, layout=layout)
|
|
616
|
+
+ _PARTS["batch_norm"].embedded_constants(channels, layout=layout)
|
|
617
|
+
+ _PARTS["conv1x1"].embedded_constants(channels, classes, bias=True, layout=layout)
|
|
618
|
+
+ _PARTS["window_resize"].embedded_constants(height, width, 4, align_corners=True, window=7))
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def fuse_head_arrays() -> int:
|
|
622
|
+
"""Server arrays read: the fuse weight, the four BatchNorm vectors, the head weight and bias."""
|
|
623
|
+
return (_PARTS["conv1x1"].asset_arrays(bias=False) + _PARTS["batch_norm"].asset_arrays()
|
|
624
|
+
+ _PARTS["conv1x1"].asset_arrays(bias=True))
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
# ---------------------------------------------------------------------------
|
|
628
|
+
# Metadata
|
|
629
|
+
# ---------------------------------------------------------------------------
|
|
630
|
+
# Validated live only inside the fixed SegFormer-B0: the whole model's staged agreement, and
|
|
631
|
+
# small compositions (overlap, reduction, global keys, norms, Mix-FFN, pyramid, head).
|
|
632
|
+
_STATUS = "Fixed — SegFormer-B0 (mit_b0) at 224 × 224: "
|
|
633
|
+
_MODEL = "widths 32, 64, 160, 256; heads 1, 2, 5, 8; reduction ratios 8, 4, 2, 1; window 7"
|
|
634
|
+
_EVIDENCE = ("docs/evidence/segformer_b0-fullmodel-live.json", "docs/evidence/segformer_b0-live.json")
|
|
635
|
+
|
|
636
|
+
# Validated settings: SegFormer-B0's, named by the constants builders' parameters (the
|
|
637
|
+
# block graph's settings, tensor_ee/converter/operations.py) and joined per stage.
|
|
638
|
+
_WIDTHS = (32, 64, 160, 256)
|
|
639
|
+
_STAGES = tuple({"dim": (dim,), "heads": (heads,), "ratio": (ratio,), "window": (7,)}
|
|
640
|
+
for dim, heads, ratio in zip(_WIDTHS, (1, 2, 5, 8), (8, 4, 2, 1)))
|
|
641
|
+
_EMBEDDINGS = tuple({"cin": (cin,), "cout": (cout,), "kernel": (kernel,), "stride": (stride,), "window": (7,)}
|
|
642
|
+
for cin, cout, kernel, stride in ((2, 32, 7, 4), (32, 64, 3, 2), (64, 160, 3, 2), (160, 256, 3, 2)))
|
|
643
|
+
|
|
644
|
+
METADATA = validate_metadata({
|
|
645
|
+
"channel_norm": BlockMetadata(
|
|
646
|
+
name="channel_norm", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
|
|
647
|
+
policy=COMPOSITE, embedded_constants=channel_norm_values, asset_arrays=channel_norm_arrays,
|
|
648
|
+
status=_STATUS + "widths 32, 64, 160, 256; eps 1e-5, 1e-6; window 7",
|
|
649
|
+
evidence=_EVIDENCE + ("parts: layer_norm (eps 1e-5) or layer_norm2d (eps 1e-6), double_round_once",),
|
|
650
|
+
validated=Validated(({"channels": _WIDTHS, "eps": (1e-5, 1e-6), "window": (7,)},), sources=(norm_constants,))),
|
|
651
|
+
"decimate": BlockMetadata(
|
|
652
|
+
# A stride-f sample reads within each output token's f x f footprint: k x k, radius
|
|
653
|
+
# f // 2 at the largest factor (4), as stride_columns counts its patch.
|
|
654
|
+
name="decimate", layout_in="W", layout_out="W", locality=Locality.KERNEL, radius=2,
|
|
655
|
+
policy=NO_ARITHMETIC, embedded_constants=no_constants, asset_arrays=no_constants,
|
|
656
|
+
status=_STATUS + "factor 4 at width 32, factor 2 at widths 64, 160, 256; window 7",
|
|
657
|
+
evidence=_EVIDENCE,
|
|
658
|
+
# decimate moves values and takes no width: the widths say where the model ran each factor.
|
|
659
|
+
validated=Validated(({"factor": (2, 4), "window": (7,)},))),
|
|
660
|
+
"overlap_embedding": BlockMetadata(
|
|
661
|
+
name="overlap_embedding", layout_in="W", layout_out="W", locality=Locality.KERNEL, radius=3,
|
|
662
|
+
policy=COMPOSITE, embedded_constants=overlap_embedding_values, asset_arrays=overlap_embedding_arrays,
|
|
663
|
+
status=_STATUS + "k7/s4 2 → 32; k3/s2 32 → 64, 64 → 160, 160 → 256; window 7",
|
|
664
|
+
evidence=_EVIDENCE + ("parts: linear or conv3x3_window and layer_norm (double_round_once), decimate (none)",),
|
|
665
|
+
validated=Validated(_EMBEDDINGS, sources=(overlap_constants,))),
|
|
666
|
+
"spatial_reduction": BlockMetadata(
|
|
667
|
+
name="spatial_reduction", layout_in="W", layout_out="W", locality=Locality.EXTENT,
|
|
668
|
+
policy=COMPOSITE, embedded_constants=spatial_reduction_values, asset_arrays=spatial_reduction_arrays,
|
|
669
|
+
status=_STATUS + "widths 32, 64, 160, 256 at reduction ratios 8, 4, 2, 1; window 7",
|
|
670
|
+
evidence=_EVIDENCE + ("parts: stride_columns (none), linear and layer_norm (double_round_once)",),
|
|
671
|
+
validated=Validated(tuple({name: stage[name] for name in ("dim", "ratio", "window")} for stage in _STAGES),
|
|
672
|
+
sources=(attention_constants,))),
|
|
673
|
+
"shared_key_attention": BlockMetadata(
|
|
674
|
+
name="shared_key_attention", layout_in="W", layout_out="W", locality=Locality.EXTENT,
|
|
675
|
+
policy=COMPOSITE, embedded_constants=no_constants, asset_arrays=no_constants,
|
|
676
|
+
chip_edge_dependent=True,
|
|
677
|
+
status=_STATUS + "widths 32, 64, 160, 256; heads 1, 2, 5, 8; one 7 × 7 key window",
|
|
678
|
+
evidence=_EVIDENCE + ("parts: gather (none), naive_attention (round_each_op)",),
|
|
679
|
+
validated=Validated(tuple({name: stage[name] for name in ("dim", "heads", "window")} for stage in _STAGES),
|
|
680
|
+
sources=(attention_constants,))),
|
|
681
|
+
"global_attention": BlockMetadata(
|
|
682
|
+
name="global_attention", layout_in="W", layout_out="W", locality=Locality.EXTENT,
|
|
683
|
+
policy=COMPOSITE, embedded_constants=global_attention_values, asset_arrays=global_attention_arrays,
|
|
684
|
+
chip_edge_dependent=True, status=_STATUS + _MODEL,
|
|
685
|
+
evidence=_EVIDENCE + ("parts: linear (double_round_once), spatial_reduction, shared_key_attention",),
|
|
686
|
+
validated=Validated(_STAGES, sources=(attention_constants,))),
|
|
687
|
+
"mix_ffn": BlockMetadata(
|
|
688
|
+
name="mix_ffn", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=1,
|
|
689
|
+
policy=COMPOSITE, embedded_constants=mix_ffn_values, asset_arrays=mix_ffn_arrays,
|
|
690
|
+
status=_STATUS + "widths 32, 64, 160, 256, hidden 4 × width; window 7",
|
|
691
|
+
evidence=_EVIDENCE + ("parts: linear, depthwise_conv3x3_window, gelu (double_round_once)",),
|
|
692
|
+
validated=Validated(({"dim": _WIDTHS, "window": (7,)},), sources=(mix_ffn_constants,))),
|
|
693
|
+
"mit_block": BlockMetadata(
|
|
694
|
+
name="mit_block", layout_in="W", layout_out="W", locality=Locality.EXTENT,
|
|
695
|
+
policy=COMPOSITE, embedded_constants=mit_block_values, asset_arrays=mit_block_arrays,
|
|
696
|
+
chip_edge_dependent=True, status=_STATUS + _MODEL,
|
|
697
|
+
evidence=_EVIDENCE + ("parts: channel_norm, global_attention, mix_ffn, add (double_round_once)",),
|
|
698
|
+
validated=Validated(_STAGES, sources=(mit_block_constants,))),
|
|
699
|
+
"project_pyramid": BlockMetadata(
|
|
700
|
+
name="project_pyramid", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=1,
|
|
701
|
+
policy=COMPOSITE, embedded_constants=project_pyramid_values, asset_arrays=project_pyramid_arrays,
|
|
702
|
+
status=_STATUS + "widths 32, 64, 160, 256 to 256 on C1 56 × 56; window 7",
|
|
703
|
+
evidence=_EVIDENCE + ("parts: linear and window_resize (double_round_once), concat (none)",),
|
|
704
|
+
# in_channels deepest first, as project_pyramid_constants takes them; height x width is C1's.
|
|
705
|
+
validated=Validated(({"in_channels": (_WIDTHS[::-1],), "channels": (256,), "height": (56,), "width": (56,),
|
|
706
|
+
"window": (7,)},), sources=(project_pyramid_constants,))),
|
|
707
|
+
"fuse_head": BlockMetadata(
|
|
708
|
+
# Chip-edge dependent: the four-times resize's align_corners=True weights depend on the
|
|
709
|
+
# map's size and are not shift-invariant, so tiling with halos cannot reproduce them.
|
|
710
|
+
name="fuse_head", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=1,
|
|
711
|
+
policy=COMPOSITE, embedded_constants=fuse_head_values, asset_arrays=fuse_head_arrays,
|
|
712
|
+
chip_edge_dependent=True,
|
|
713
|
+
status=_STATUS + "1024 → 256 channels → 2 classes, 56 × 56 to 224 × 224; window 7",
|
|
714
|
+
evidence=_EVIDENCE + ("parts: conv1x1, batch_norm and window_resize (double_round_once), relu (none)",),
|
|
715
|
+
# The BatchNorm's eps 1e-5 never varied; height x width is the input's (C1).
|
|
716
|
+
validated=Validated(({"in_channels": (1024,), "channels": (256,), "classes": (2,), "eps": (1e-5,),
|
|
717
|
+
"height": (56,), "width": (56,), "window": (7,)},), sources=(fuse_head_constants,))),
|
|
718
|
+
})
|