Refactor DeepSeek adapter around native vLLM lifecycle#151
Conversation
c891e66 to
8e2216f
Compare
jiangkuaixue123
left a comment
There was a problem hiding this comment.
Please remove the unnecessary harness artifacts from this PR.
8e2216f to
3db61ea
Compare
|
Nice work — the current structure is much simpler and cleaner than before. One idea for consideration: since the expert components currently use either I'm not sure yet whether this is technically feasible, especially given the backend-specific behavior, but if it works, it could be reused across other models and significantly reduce the amount of model-specific adaptation required. It may be worth doing a quick feasibility check first. |
Signed-off-by: zzh <jiaranran2@gmail.com>
3db61ea to
88fb906
Compare
DeepSeek V2 native lifecycle refactor
Background, objective, and scope
The previous DeepSeek adapter copied the ordinary Model and Decoder forward
paths and most of the native weight-loader loop. AFD role selection,
communication, model lifecycle, and backend-specific weight handling were
therefore coupled in one adapter. Changes to upstream
deepseek_v2.pyhad tobe merged manually, and native fixes such as new parameter mappings could
drift from the plugin.
Issue #143 changes that ownership boundary:
The refactor targets only this pinned stack:
It covers synchronous AFD, the existing Attention-side-gate path, async CAM
integration, and role-aware checkpoint filtering. It does not add
sequence-parallel MoE support, adapt another upstream version, redesign
connectors or workers, or remove existing process-wide runtime compatibility
patches. Native model registration also remains protected by the existing
AFD-prefixed alias mechanism introduced for Issue #141.
Architecture before and after
Before the refactor, the common adapter owned both AFD composition and ordinary
DeepSeek semantics. After the refactor, the adapter inherits native lifecycle
behavior and inserts AFD at explicit stage boundaries.
flowchart LR subgraph Before["Before: copied lifecycle"] BC["AFD CausalLM"] BM["Standalone AFD Model"] BD["AFD DecoderLayer"] BF1["Copied Model.forward"] BF2["Copied DecoderLayer.forward"] BL["Copied load_weights loop"] BC --> BM BC --> BL BM --> BF1 BM --> BD BD --> BF2 end subgraph After["After: native lifecycle"] AC["AFD CausalLM<br/>native subclass"] AM["AFD Model<br/>native subclass"] AD["AFD DecoderLayer<br/>native forward"] AP[".mlp stage proxy"] AL["Role filter<br/>native loader"] AA["Async CAM<br/>schedule helper"] AC --> AM AC --> AL AM --> AD AM --> AA AD --> AP endThe resulting responsibility boundary is:
.mlpstage proxies,send/yield/receive, checkpoint role filtering, Attention-side gate policy, and async CAM schedulingOnly
DeepseekV2Model.__init__andDeepseekV2DecoderLayer.__init__remain as pinned upstream copies becausevLLM 0.19.1 hard-codes layer construction. Their signatures match upstream,
AFD differences are marked with
PATCH START/END, and the construction unit testscheck the non-patch constructor digest.
Core changes and effects
Role-aware construction
Modules are now selected during construction instead of constructing a full
Attention/MoE stack and deleting the unused half.
.mlp.mlpRemoteFFNProxyDeepseekV2MLPRemoteFFNProxyDeepseekV2MoEDeepseekV2MLPMissingFFNStageGateOnlyRemoteMoEDeepseekV2MoEThe Attention worker constructs native Attention; the FFN worker uses
MissingAttentionStageand does not allocate a real Attention/KV module. TheDeepSeek V3.2 Indexer buffer is allocated only on the Attention role.
Attention-side gate parameters retain the native
model.layers.<idx>.mlp.gatepath. In that mode gate weights may be loaded onboth roles to keep the FFN MoE structure native, but routing executes only on
Attention.
This removes unused large modules and their parameter, cache, quantization,
and backend initialization side effects from the wrong role.
Synchronous forward
The synchronous path calls native
DeepseekV2Model.forward()and inheritsnative
DeepseekV2DecoderLayer.forward(). Communication occurs when the nativeDecoder calls
self.mlp(hidden_states).sequenceDiagram participant Native as Native Model/Decoder participant Proxy as AFD .mlp proxy participant Connector participant FFN as FFN worker Native->>Proxy: self.mlp(hidden_states) Proxy->>Connector: send_attn_output Proxy->>Proxy: optional DBO yield Connector->>FFN: hidden states and optional gate payload FFN->>FFN: native MLP or MoE FFN-->>Connector: FFN output Connector-->>Proxy: recv_ffn_output Proxy-->>Native: remote FFN result Native->>Native: continue native residual/layer flowAFD no longer copies the ordinary residual, RMSNorm, Attention, or layer-loop
logic.
RemoteFFNProxyis not aDeepseekV2MLP, so native Decoder code doesnot apply Dense FP16 output scaling on Attention. The FFN-side
compute_ffn_output()applies that scaling once for native Dense MLP and doesnot repeat native MoE scaling.
Role filtering and native weight loading
The old adapter performed role filtering and then reimplemented native
projection, expert, quantization, PP, and scale loading. The new adapter
answers only whether a checkpoint entry belongs to the current role:
flowchart LR CKPT["One-shot checkpoint iterator"] --> FILTER["AFD role filter"] FILTER --> LOADER["Native DeepSeek load_weights"] LOADER --> MAP["QKV / MLA / Indexer mappings"] LOADER --> MOE["Experts / shared experts / scales"] LOADER --> PP["PP missing parameters"] LOADER --> RESULT["Native loaded_params"]Common parameters remain visible to both roles. Attention parameters are
visible only to Attention. Dense and MoE ownership follows the construction
table above; in Attention-side-gate mode MoE gate parameters are visible to
both roles while expert/shared-expert parameters remain FFN-owned.
The filter is a generator, consumes the checkpoint iterator once, and returns
the exact native
loaded_paramsresult. Native vLLM remains responsible forMHA QKV packing, MLA fusion, Indexer loading, quantization scales, expert
mapping, EPLB/redundant experts, PP-missing parameters, and KV-scale remapping.
For vLLM-Ascend 0.19.1rc1,
NPUModelRunner.load_model()enables vLLM's fusedMoE/shared-expert probes before model construction when mix-placement is
active. The vLLM 0.19.1 native loader consequently performs widened
shared-expert splitting and appended-expert mapping. The adapter must not
duplicate that loader behavior. This describes the pinned design path;
mix-placement and W8A8 still require real NPU validation.
Async CAM integration
The async CAM wavefront, dual-ubatch ordering, and connector semantics remain
AFD-owned. What changed is how that schedule enters the Model lifecycle:
flowchart TD FORWARD["AFD Model.forward"] --> MODE{"Async CAM active?"} MODE -- "No" --> NATIVE["Native Model.forward"] MODE -- "Yes" --> ASYNC["NPU async CAM helper"] ASYNC --> SCHEDULE["Existing cross-layer / dual-ubatch schedule"] SCHEDULE --> STAGES["Native stage modules and AFD send/receive"]The Model override is a thin dispatcher. Synchronous execution never enters
the async Attention fragment. The NPU helper continues to own ubatch metadata,
cross-layer scheduling, PP first/last-rank handling, and ForwardContext
installation/restoration without forcing the synchronous path to maintain a
second Model/Decoder forward.
Validation status and remaining work
send/yield/receive, Dense scaling, PP boundaries, and ForwardContext restorationThe static, CPU, and GPU results establish the new ownership and synchronous
execution contracts. They do not prove NPU fused MoE, quantization, or real
async communication behavior. Issue #143 should not claim complete NPU
validation until the pending hardware paths have recorded evidence.