Optimize anchor-only graph transformer inference - #732
Conversation
| Anchor output of shape ``(batch, 1, model_dim)``. | ||
|
|
||
| """ | ||
| batch_size, seq_len, model_dim = x.shape |
There was a problem hiding this comment.
I'm a little wary that we have some copied over logic in this separate forward, changes in the original forward might not be captured. Can the anchor only be a condition in the existing forward?
There was a problem hiding this comment.
good point - we now have a shared path :)
|
|
||
| return x | ||
|
|
||
| def _zero_relation_parameter_dependency(self, reference: Tensor) -> Tensor: |
There was a problem hiding this comment.
Do we need this? Should be unrelated to this PR?
There was a problem hiding this comment.
_forward_anchor_only() filters relation edges whose query position is not the anchor. During training, if that filters every edge, the full-sequence path still includes the relation parameters in autograd and yields zero gradients, while the optimized path would mark them unused (grad=None). _zero_relation_parameter_dependency() adds a zero-valued dependency only in that case, preserving DDP unused-parameter handling and optimizer semantics without changing the forward value or gradients. It is required for training parity, not inference.
| final_encoder_layer = ( | ||
| cast(GraphTransformerEncoderLayer, encoder_layers[-1]) | ||
| if use_anchor_only_final_layer | ||
| else None | ||
| ) | ||
| for encoder_layer_module in encoder_layers: | ||
| encoder_layer = cast( | ||
| GraphTransformerEncoderLayer, | ||
| encoder_layer_module, | ||
| ) | ||
| if encoder_layer is final_encoder_layer: | ||
| break | ||
| x = encoder_layer( | ||
| x, | ||
| attn_bias=attn_bias, | ||
| pairwise_relation_indices=pairwise_relation_indices, | ||
| valid_mask=valid_mask, | ||
| ) | ||
|
|
||
| output_valid_mask = valid_mask | ||
| if final_encoder_layer is not None: | ||
| x = final_encoder_layer._forward_anchor_only( | ||
| x, | ||
| attn_bias=attn_bias, | ||
| pairwise_relation_indices=pairwise_relation_indices, | ||
| valid_mask=valid_mask, | ||
| ) | ||
| output_valid_mask = valid_mask[:, :1] |
There was a problem hiding this comment.
include some comments on what and why we do this
There was a problem hiding this comment.
Do these comments help make it clear?
| # anchor query/output and shorten the mask to match that one-token result. | ||
| output_valid_mask = valid_mask | ||
| if final_encoder_layer is not None: | ||
| x = final_encoder_layer._forward_anchor_only( |
There was a problem hiding this comment.
Direct method calls skip nn.Module.__call__, so for the final layer this silently bypasses forward hooks and per layer checkpoint wrappers.
Suggestion: have forward pass x.size(1) if query_seq_len is None else query_seq_len to _forward_query_prefix, and call the layer here as:
x = final_encoder_layer(
x,
attn_bias=attn_bias,
valid_mask=valid_mask,
pairwise_relation_indices=pairwise_relation_indices,
query_seq_len=1,
)
There was a problem hiding this comment.
Good callout, done :)
58aa3d2 to
3960125
Compare
Summary
At batch size 1,120 on a Tesla T4, this reduces median graph-transformer transform + encoder latency from 1,298.46 ms to 781.69 ms and raises throughput from 862.6 to 1,432.8 anchors/s (+66.1%).
mainanchor_onlyreadout discards every final-layer output except token zero. The specialized final-layer path keeps full-sequence keys, values, and source relation features, but computes query-side attention, relation-message targets, output projection, and feed-forward work only for the anchor.The encoder selection logic enables this path during evaluation with
anchor_onlyreadout. Relation-aware attention uses a rectangular anchor-query bias while preserving full-sequence keys.Relation-aware T4 measurements retain the same benefit:
Both cases use nonzero learned relation parameters and match the full path within
1.0431e-7maximum absolute error.Correctness
The production-count T4 fixture produced:
1.0431e-72.2403e-7The new real-tensor tests compare the specialized output with an independently evaluated full-sequence reference. They exercise:
Validation
tychecks pass.git diff --checkpasses.The benchmark graph topology, input features, and initial weights are synthetic. A checkpoint-backed L4 validation remains the next deployment gate.
Full diff: 322d351...6989558