项目文件夹

文件
2026-07-13 13:33:03 +08:00

162 行
6.4 KiB
C++

#include <cmath>
#include "AttentionConverter.hpp"
#include "core/TensorUtils.hpp"
#include "backend/NeuropilotBackend.hpp"
namespace MNN {
/**
Q = _Reshape(Q, {batch, seqLength, kvNumHead,group, headDim});
Q = _Transpose(Q, {0, 2, 3, 1, 4});
K = _Reshape(K, {batch, seqLength, kvNumHead, 1, headDim});
K = _Transpose(K, {0, 2, 3, 1, 4});
auto scale = 1.0f / sqrtf(headDim);
K = K * _Scalar<float>(scale);
K.fix(VARP::CONSTANT);
auto QK = _MatMul(Q, K, false, true); // [batch, kvNumHead, group , seq_len, seq_len]
QK = QK + mask;
auto QKPast = _MatMul(Q, cache.pastK, false, true);
QKPast = QKPast + cache.pastMask;
QK = _Concat({QKPast, QK}, -1);
QK = _Softmax(QK, -1);
V = _Reshape(V, {batch, seqLength, kvNumHead, 1, headDim});
V = _Transpose(V, {0, 2, 3, 1, 4});
V.fix(VARP::CONSTANT);
auto totalV = _Concat({cache.pastV, V}, 3);
auto QKV = _MatMul(QK, totalV, false, false);
auto info = QKV->getInfo();
auto O = _Transpose(QKV, {0, 3, 1, 2, 4});
O = _Reshape(O, {batch, seqLength, -1});
O.fix(VARP::CONSTANT);
*/
ConvertTflite::CommandBuffer AttentionConverter::onExecute(const Op* op, const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, ConvertTflite* root) {
auto kvMaxSize = root->pBackend->getRuntime()->hint().kvcacheSizeLimit;
ConvertTflite::CommandBuffer res;
auto Q = inputs[0];
auto K = inputs[1];
auto V = inputs[2];
auto mask = inputs[3];
auto seqLength = Q->length(1);
auto numHead = Q->length(2);
auto headDim = Q->length(3);
auto kvNumHead = K->length(2);
auto batch = Q->length(0);
auto group = numHead / kvNumHead;
auto scale = 1.0f / sqrtf(headDim);
auto attn = op->main_as_AttentionParam();
bool needState = false;
if (nullptr != attn && attn->kv_cache()) {
needState = true;
}
MNN_ASSERT(batch == 1);
if (1 != batch) {
MNN_ERROR("Don't support batch > 1 for mtk npu attention\n");
return res;
}
// Q = _Reshape(Q, {seqLength, kvNumHead,group, headDim});
// Q = _Transpose(Q, {1, 2, 0, 3});
// K = _Reshape(K, {batch, seqLength, kvNumHead, 1, headDim});
// K = _Transpose(K, {1, 2, 0, 3});
Q = root->makeReshape(res, Q, {seqLength, kvNumHead,group, headDim});
Q = root->makeTranspose(res, Q, {1, 2, 0, 3});
Q = root->makeReshape(res, Q, {1, kvNumHead * group, seqLength, headDim});
K = root->makeReshape(res, K, {seqLength, kvNumHead,1, headDim});
K = root->makeTranspose(res, K, {2, 1, 0, 3}); // {1, kvNumHead, seqLength, headDim}
Tensor* pastK = nullptr;
Tensor* pastV = nullptr;
Tensor* stateMask = nullptr;
if (needState) {
stateMask = root->pBackend->getStateMask(kvMaxSize);
// Create pk, pv
std::shared_ptr<Tensor> pastKWrap(Tensor::createDevice<float>({1, kvNumHead, kvMaxSize, headDim}));
pastK = pastKWrap.get();
root->pBackend->insertExtraInput(pastKWrap.get());
res.extraConst.emplace_back(pastKWrap);
std::shared_ptr<Tensor> pastVWrap(Tensor::createDevice<float>({1, kvNumHead, kvMaxSize, headDim}));
pastV = pastVWrap.get();
root->pBackend->insertExtraInput(pastVWrap.get());
res.extraConst.emplace_back(pastVWrap);
}
// K = K * scale
{
std::shared_ptr<Tensor> scaleTensor(Tensor::create<float>({}));
TensorUtils::getDescribe(scaleTensor.get())->usage = Tensor::InsideDescribe::Usage::CONSTANT;
scaleTensor->host<float>()[0] = scale;
res.extraConst.emplace_back(scaleTensor);
K = root->makeBinary(res, K, scaleTensor.get(), tflite::BuiltinOperator_MUL);
if (needState) {
root->pBackend->insertExtraOutput(K);
}
}
// auto QK = _MatMul(Q, K, false, true); // [batch, kvNumHead, group , seq_len, seq_len]
Tensor* QK = nullptr;
{
// Tile firstly
if (1 != group) {
K = root->makeTile(res, K, {1, 1, group, 1});
K = root->makeReshape(res, K, {1, kvNumHead * group, seqLength, headDim});
}
// Matmul
std::shared_ptr<Tensor> qktensor(Tensor::createDevice<float>({1, kvNumHead * group, seqLength, seqLength}));
root->makeMatMul(res, Q, K, false, true, qktensor.get());
QK = qktensor.get();
res.extraConst.emplace_back(qktensor);
}
// QK = QK + mask;
if (seqLength != 1) {
// For decode don't need mask, if add mask will cause crash for neuropilot
QK = root->makeBinary(res, QK, mask, tflite::BuiltinOperator_ADD);
}
if (needState) {
// auto QKPast = _MatMul(Q, pastK, false, true);
// Tile firstly
if (1 != group) {
pastK = root->makeTile(res, pastK, {1, 1, group, 1});
pastK = root->makeReshape(res, pastK, {1, kvNumHead * group, kvMaxSize, headDim});
}
// Matmul
std::shared_ptr<Tensor> qktensor(Tensor::createDevice<float>({1, kvNumHead * group, seqLength, kvMaxSize}));
root->makeMatMul(res, Q, pastK, false, true, qktensor.get());
res.extraConst.emplace_back(qktensor);
auto QKPastMask = root->makeBinary(res, qktensor.get(), stateMask, tflite::BuiltinOperator_ADD);
QK = root->makeConcat(res, {QKPastMask, QK}, 3);
}
// QK = _Softmax(QK, -1);
{
QK = root->makeSoftmax(res, QK);
}
// V = _Reshape(V, {seqLength, kvNumHead, 1, headDim});
// V = _Transpose(V, {1, 2, 0, 3});
V = root->makeReshape(res, V, {seqLength, kvNumHead,1, headDim});
V = root->makeTranspose(res, V, {2, 1, 0, 3}); // 1, kvNumHead, seqLength, headDim
if (needState) {
root->pBackend->insertExtraOutput(V);
V = root->makeConcat(res, {pastV, V}, 2);
}
// auto QKV = _MatMul(QK, V, false, false);
Tensor* QKV = nullptr;
{
// Tile firstly
if (1 != group) {
V = root->makeTile(res, V, {1, 1, group, 1});
V = root->makeReshape(res, V, {1, kvNumHead * group, seqLength + kvMaxSize, headDim});
}
// Matmul
std::shared_ptr<Tensor> qkvtensor(Tensor::createDevice<float>({1, kvNumHead * group, seqLength, headDim}));
root->makeMatMul(res, QK, V, false, false, qkvtensor.get());
QKV = qkvtensor.get();
res.extraConst.emplace_back(qkvtensor);
}
// auto O = _Transpose(QKV, {0, 3, 1, 2, 4});
// O = _Reshape(O, {batch, seqLength, -1});
auto O = root->makeTranspose(res, QKV, {0, 2, 1, 3});
root->makeReshape(res, O, {1, seqLength, -1}, outputs[0]);
return res;
}
};