Spaces:
Runtime error
Runtime error
Implement full CPU forward for MultiScaleDeformableAttention
Browse filesReplace NotImplementedError stub with the actual pure-Python CPU
implementation using F.grid_sample bilinear interpolation, matching
the upstream mmcv multi_scale_deformable_attn_pytorch logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- setup_mmcv_shim.py +100 -6
setup_mmcv_shim.py
CHANGED
|
@@ -170,35 +170,129 @@ class ConcatCell:
|
|
| 170 |
def __init__(self, *args, **kwargs): raise NotImplementedError
|
| 171 |
''')
|
| 172 |
|
| 173 |
-
# Create multi_scale_deform_attn
|
| 174 |
with open(os.path.join(ops_dir, 'multi_scale_deform_attn.py'), 'w') as f:
|
| 175 |
-
f.write('''import
|
|
|
|
| 176 |
import torch
|
| 177 |
import torch.nn as nn
|
|
|
|
|
|
|
| 178 |
from mmcv.cnn.bricks.registry import ATTENTION
|
| 179 |
from mmcv.runner import BaseModule
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
@ATTENTION.register_module()
|
| 182 |
class MultiScaleDeformableAttention(BaseModule):
|
| 183 |
def __init__(self, embed_dims=256, num_heads=8, num_levels=4, num_points=4,
|
| 184 |
im2col_step=64, dropout=0.1, batch_first=False, norm_cfg=None,
|
| 185 |
init_cfg=None, **kwargs):
|
| 186 |
super().__init__(init_cfg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
self.embed_dims = embed_dims
|
| 188 |
-
self.num_heads = num_heads
|
| 189 |
self.num_levels = num_levels
|
|
|
|
| 190 |
self.num_points = num_points
|
| 191 |
-
self.batch_first = batch_first
|
| 192 |
self.sampling_offsets = nn.Linear(embed_dims, num_heads * num_levels * num_points * 2)
|
| 193 |
self.attention_weights = nn.Linear(embed_dims, num_heads * num_levels * num_points)
|
| 194 |
self.value_proj = nn.Linear(embed_dims, embed_dims)
|
| 195 |
self.output_proj = nn.Linear(embed_dims, embed_dims)
|
| 196 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
def forward(self, query, key=None, value=None, identity=None,
|
| 199 |
query_pos=None, key_padding_mask=None, reference_points=None,
|
| 200 |
spatial_shapes=None, level_start_index=None, **kwargs):
|
| 201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
''')
|
| 203 |
|
| 204 |
print('mmcv.ops shim created successfully')
|
|
|
|
| 170 |
def __init__(self, *args, **kwargs): raise NotImplementedError
|
| 171 |
''')
|
| 172 |
|
| 173 |
+
# Create multi_scale_deform_attn with full CPU implementation
|
| 174 |
with open(os.path.join(ops_dir, 'multi_scale_deform_attn.py'), 'w') as f:
|
| 175 |
+
f.write('''import math
|
| 176 |
+
import warnings
|
| 177 |
import torch
|
| 178 |
import torch.nn as nn
|
| 179 |
+
import torch.nn.functional as F
|
| 180 |
+
from mmcv.cnn import constant_init, xavier_init
|
| 181 |
from mmcv.cnn.bricks.registry import ATTENTION
|
| 182 |
from mmcv.runner import BaseModule
|
| 183 |
|
| 184 |
+
|
| 185 |
+
def multi_scale_deformable_attn_pytorch(value, value_spatial_shapes,
|
| 186 |
+
sampling_locations, attention_weights):
|
| 187 |
+
"""CPU version of multi-scale deformable attention."""
|
| 188 |
+
bs, _, num_heads, embed_dims = value.shape
|
| 189 |
+
_, num_queries, num_heads, num_levels, num_points, _ = sampling_locations.shape
|
| 190 |
+
value_list = value.split([int(H_ * W_) for H_, W_ in value_spatial_shapes], dim=1)
|
| 191 |
+
sampling_grids = 2 * sampling_locations - 1
|
| 192 |
+
sampling_value_list = []
|
| 193 |
+
for level, (H_, W_) in enumerate(value_spatial_shapes):
|
| 194 |
+
value_l_ = value_list[level].flatten(2).transpose(1, 2).reshape(
|
| 195 |
+
bs * num_heads, embed_dims, int(H_), int(W_))
|
| 196 |
+
sampling_grid_l_ = sampling_grids[:, :, :, level].transpose(1, 2).flatten(0, 1)
|
| 197 |
+
sampling_value_l_ = F.grid_sample(
|
| 198 |
+
value_l_, sampling_grid_l_, mode=\'bilinear\', padding_mode=\'zeros\',
|
| 199 |
+
align_corners=False)
|
| 200 |
+
sampling_value_list.append(sampling_value_l_)
|
| 201 |
+
attention_weights = attention_weights.transpose(1, 2).reshape(
|
| 202 |
+
bs * num_heads, 1, num_queries, num_levels * num_points)
|
| 203 |
+
output = (torch.stack(sampling_value_list, dim=-2).flatten(-2) *
|
| 204 |
+
attention_weights).sum(-1).view(bs, num_heads * embed_dims, num_queries)
|
| 205 |
+
return output.transpose(1, 2).contiguous()
|
| 206 |
+
|
| 207 |
+
|
| 208 |
@ATTENTION.register_module()
|
| 209 |
class MultiScaleDeformableAttention(BaseModule):
|
| 210 |
def __init__(self, embed_dims=256, num_heads=8, num_levels=4, num_points=4,
|
| 211 |
im2col_step=64, dropout=0.1, batch_first=False, norm_cfg=None,
|
| 212 |
init_cfg=None, **kwargs):
|
| 213 |
super().__init__(init_cfg)
|
| 214 |
+
if embed_dims % num_heads != 0:
|
| 215 |
+
raise ValueError(f\'embed_dims must be divisible by num_heads, \'
|
| 216 |
+
f\'but got {embed_dims} and {num_heads}\')
|
| 217 |
+
self.norm_cfg = norm_cfg
|
| 218 |
+
self.dropout = nn.Dropout(dropout)
|
| 219 |
+
self.batch_first = batch_first
|
| 220 |
+
self.im2col_step = im2col_step
|
| 221 |
self.embed_dims = embed_dims
|
|
|
|
| 222 |
self.num_levels = num_levels
|
| 223 |
+
self.num_heads = num_heads
|
| 224 |
self.num_points = num_points
|
|
|
|
| 225 |
self.sampling_offsets = nn.Linear(embed_dims, num_heads * num_levels * num_points * 2)
|
| 226 |
self.attention_weights = nn.Linear(embed_dims, num_heads * num_levels * num_points)
|
| 227 |
self.value_proj = nn.Linear(embed_dims, embed_dims)
|
| 228 |
self.output_proj = nn.Linear(embed_dims, embed_dims)
|
| 229 |
+
self.init_weights()
|
| 230 |
+
|
| 231 |
+
def init_weights(self):
|
| 232 |
+
constant_init(self.sampling_offsets, 0.)
|
| 233 |
+
device = next(self.parameters()).device
|
| 234 |
+
thetas = torch.arange(self.num_heads, dtype=torch.float32, device=device) * (2.0 * math.pi / self.num_heads)
|
| 235 |
+
grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)
|
| 236 |
+
grid_init = (grid_init / grid_init.abs().max(-1, keepdim=True)[0]).view(
|
| 237 |
+
self.num_heads, 1, 1, 2).repeat(1, self.num_levels, self.num_points, 1)
|
| 238 |
+
for i in range(self.num_points):
|
| 239 |
+
grid_init[:, :, i, :] *= i + 1
|
| 240 |
+
self.sampling_offsets.bias.data = grid_init.view(-1)
|
| 241 |
+
constant_init(self.attention_weights, val=0., bias=0.)
|
| 242 |
+
xavier_init(self.value_proj, distribution=\'uniform\', bias=0.)
|
| 243 |
+
xavier_init(self.output_proj, distribution=\'uniform\', bias=0.)
|
| 244 |
+
self._is_init = True
|
| 245 |
|
| 246 |
def forward(self, query, key=None, value=None, identity=None,
|
| 247 |
query_pos=None, key_padding_mask=None, reference_points=None,
|
| 248 |
spatial_shapes=None, level_start_index=None, **kwargs):
|
| 249 |
+
if value is None:
|
| 250 |
+
value = query
|
| 251 |
+
if identity is None:
|
| 252 |
+
identity = query
|
| 253 |
+
if query_pos is not None:
|
| 254 |
+
query = query + query_pos
|
| 255 |
+
if not self.batch_first:
|
| 256 |
+
query = query.permute(1, 0, 2)
|
| 257 |
+
value = value.permute(1, 0, 2)
|
| 258 |
+
|
| 259 |
+
bs, num_query, _ = query.shape
|
| 260 |
+
bs, num_value, _ = value.shape
|
| 261 |
+
|
| 262 |
+
value = self.value_proj(value)
|
| 263 |
+
if key_padding_mask is not None:
|
| 264 |
+
value = value.masked_fill(key_padding_mask[..., None], 0.0)
|
| 265 |
+
value = value.view(bs, num_value, self.num_heads, -1)
|
| 266 |
+
|
| 267 |
+
sampling_offsets = self.sampling_offsets(query).view(
|
| 268 |
+
bs, num_query, self.num_heads, self.num_levels, self.num_points, 2)
|
| 269 |
+
attention_weights = self.attention_weights(query).view(
|
| 270 |
+
bs, num_query, self.num_heads, self.num_levels * self.num_points)
|
| 271 |
+
attention_weights = attention_weights.softmax(-1)
|
| 272 |
+
attention_weights = attention_weights.view(
|
| 273 |
+
bs, num_query, self.num_heads, self.num_levels, self.num_points)
|
| 274 |
+
|
| 275 |
+
if reference_points.shape[-1] == 2:
|
| 276 |
+
offset_normalizer = torch.stack(
|
| 277 |
+
[spatial_shapes[..., 1], spatial_shapes[..., 0]], -1)
|
| 278 |
+
sampling_locations = reference_points[:, :, None, :, None, :] \\
|
| 279 |
+
+ sampling_offsets / offset_normalizer[None, None, None, :, None, :]
|
| 280 |
+
elif reference_points.shape[-1] == 4:
|
| 281 |
+
sampling_locations = reference_points[:, :, None, :, None, :2] \\
|
| 282 |
+
+ sampling_offsets / self.num_points \\
|
| 283 |
+
* reference_points[:, :, None, :, None, 2:] * 0.5
|
| 284 |
+
else:
|
| 285 |
+
raise ValueError(f\'Last dim of reference_points must be 2 or 4, \'
|
| 286 |
+
f\'but get {reference_points.shape[-1]} instead.\')
|
| 287 |
+
|
| 288 |
+
output = multi_scale_deformable_attn_pytorch(
|
| 289 |
+
value, spatial_shapes, sampling_locations, attention_weights)
|
| 290 |
+
output = self.output_proj(output)
|
| 291 |
+
|
| 292 |
+
if not self.batch_first:
|
| 293 |
+
output = output.permute(1, 0, 2)
|
| 294 |
+
|
| 295 |
+
return self.dropout(output) + identity
|
| 296 |
''')
|
| 297 |
|
| 298 |
print('mmcv.ops shim created successfully')
|