| 24 | } |
| 25 | |
| 26 | void DFlashGeneration::load(Module::Config module_config) { |
| 27 | // Check if separate lm_head model exists |
| 28 | std::string lmheadPath = mLlm->mConfig->dflash_lmhead(); |
| 29 | // Guard against empty config value: base_dir + "" = directory path, which MNNFileExist would match |
| 30 | bool hasSeparateLmHead = |
| 31 | !lmheadPath.empty() && MNNFileExist(lmheadPath.c_str()) && !MNNDirExist(lmheadPath.c_str()); |
| 32 | |
| 33 | // Load dflash main module |
| 34 | std::vector<std::string> dflashInputNames{"noise_embedding", "context_hidden", "attention_mask", "q_position_ids", "k_position_ids"}; |
| 35 | std::vector<std::string> dflashOutputNames{hasSeparateLmHead ? "hidden_states" : "logits"}; |
| 36 | mDFlashModule.reset(Module::load( |
| 37 | dflashInputNames, dflashOutputNames, |
| 38 | mLlm->mConfig->dflash_model().c_str(), |
| 39 | mLlm->mRuntimeManager, &module_config)); |
| 40 | |
| 41 | // Load separate lm_head module if available (allows fp16 transformer + int4 lm_head) |
| 42 | if (hasSeparateLmHead) { |
| 43 | std::vector<std::string> lmInputNames{"hidden_states"}; |
| 44 | std::vector<std::string> lmOutputNames{"logits"}; |
| 45 | mLmHeadModule.reset(Module::load( |
| 46 | lmInputNames, lmOutputNames, |
| 47 | lmheadPath.c_str(), |
| 48 | mLlm->mRuntimeManager, &module_config)); |
| 49 | MNN_PRINT("DFlash: loaded separate lm_head from %s\n", lmheadPath.c_str()); |
| 50 | } |
| 51 | |
| 52 | // Load fc module with dedicated CPU runtime to ensure fp32 precision |
| 53 | // The fc layer has very high input dimension (num_layers * hidden_size), |
| 54 | // which can cause NaN in fp16 dot products during prefill |
| 55 | { |
| 56 | // Create a dedicated CPU runtime for FC to guarantee fp32 execution |
| 57 | ScheduleConfig fc_schedule; |
| 58 | fc_schedule.type = MNN_FORWARD_CPU; |
| 59 | fc_schedule.numThread = 4; |
| 60 | BackendConfig fc_backend_config; |
| 61 | fc_backend_config.precision = BackendConfig::Precision_High; |
| 62 | fc_schedule.backendConfig = &fc_backend_config; |
| 63 | mFcRuntimeManager.reset(Executor::RuntimeManager::createRuntimeManager(fc_schedule)); |
| 64 | mFcRuntimeManager->setHint(Interpreter::MEM_ALLOCATOR_TYPE, 0); |
| 65 | |
| 66 | Module::Config fc_config; |
| 67 | fc_config.shapeMutable = true; |
| 68 | fc_config.rearrange = true; |
| 69 | std::vector<std::string> fcInputNames{"target_hidden"}; |
| 70 | std::vector<std::string> fcOutputNames{"context_hidden"}; |
| 71 | mFcModule.reset(Module::load( |
| 72 | fcInputNames, fcOutputNames, |
| 73 | mLlm->mConfig->dflash_fc().c_str(), |
| 74 | mFcRuntimeManager, &fc_config)); |
| 75 | MNN_PRINT("DFlash: FC module loaded with dedicated CPU runtime (fp32)\n"); |
| 76 | } |
| 77 | |
| 78 | mHiddenStateIndex = mLlm->getOutputIndex("hidden_states"); |
| 79 | |
| 80 | // Disable thinking mode for better draft acceptance rate. |
| 81 | // Qwen3's chat template enables thinking by default, generating unpredictable |
| 82 | // <think>...</think> tokens that the draft model cannot predict well. |
| 83 | // Setting enable_thinking=false via jinja context skips the <think> prefix. |
nothing calls this directly
no test coverage detected