WelfordFinalize
产品支持情况
功能说明
Welford计算是一种在线计算均值和方差的方法。一方面,它可以在不存储所有样本的情况下,逐步计算所有样本的均值和方差,更适合处理海量数据;另一方面,它只需要对数据进行一次遍历,能减少访存次数,提高计算性能。本接口为Welford算法的后处理。
LayerNorm算法中Reduce轴较大的场景,可以通过切分Reduce轴,联合使用本接口与WelfordUpdate,能够实现等效计算LayerNorm。根据Reduce轴切分后是否有尾块,本接口分为如下两种计算公式:
-
不带尾块/不带counts参数场景:

-
方差系数未修正场景:

-
方差系数修正场景:

其中,Mean为均值输出,Var为方差输出。
Meani代表输入的第i个均值,Vari代表输入的第i个方差。Ab代表Reduce轴切分后一次计算的大小,Rn代表Reduce轴按Ab拆分的次数,
代表未修正的方差系数rRec,
代表修正方差系数rRecWithCorrection。 -
-
带尾块/带counts参数场景:

-
方差系数未修正场景:

-
方差系数修正场景:

除上述参数含义外,countsi代表Meani对应的系数,R代表未切分的原始Reduce轴长度,
代表未修正的方差系数rRec,
代表修正系数rRecWithCorrection。 -
函数原型
-
通过sharedTmpBuffer入参传入临时空间
-
不带counts参数场景
template <bool isReuseSource = false, const WelfordFinalizeConfig& config = WFFINALIZE_DEFAULT_CFG> __aicore__ inline void WelfordFinalize(const LocalTensor<float>& outputMean, const LocalTensor<float>& outputVariance, const LocalTensor<float>& inputMean, const LocalTensor<float>& inputVariance, const LocalTensor<uint8_t>& sharedTmpBuffer, WelfordFinalizePara& para) -
带counts参数场景
template <bool isReuseSource = false, const WelfordFinalizeConfig& config = WFFINALIZE_DEFAULT_CFG> __aicore__ inline void WelfordFinalize(const LocalTensor<float>& outputMean, const LocalTensor<float>& outputVariance, const LocalTensor<float>& inputMean, const LocalTensor<float>& inputVariance, const LocalTensor<int32_t>& counts, const LocalTensor<uint8_t>& sharedTmpBuffer, WelfordFinalizePara& para)
-
-
接口框架申请临时空间
-
不带counts参数场景
template <bool isReuseSource = false, const WelfordFinalizeConfig& config = WFFINALIZE_DEFAULT_CFG> __aicore__ inline void WelfordFinalize(const LocalTensor<float>& outputMean, const LocalTensor<float>& outputVariance, const LocalTensor<float>& inputMean, const LocalTensor<float>& inputVariance, WelfordFinalizePara& para) -
带counts参数场景
template <bool isReuseSource = false, const WelfordFinalizeConfig& config = WFFINALIZE_DEFAULT_CFG> __aicore__ inline void WelfordFinalize(const LocalTensor<float>& outputMean, const LocalTensor<float>& outputVariance, const LocalTensor<float>& inputMean, const LocalTensor<float>& inputVariance, const LocalTensor<int32_t>& counts, WelfordFinalizePara& para)
-
由于该接口的内部实现中涉及复杂的计算,需要额外的临时空间来存储计算过程中的中间变量。临时空间支持接口框架申请和开发者通过sharedTmpBuffer入参传入两种方式。
-
接口框架申请临时空间,开发者无需申请,但是需要预留临时空间的大小。
-
通过sharedTmpBuffer入参传入,使用该tensor作为临时空间进行处理,接口框架不再申请。该方式开发者可以自行管理sharedTmpBuffer内存空间,并在接口调用完成后,复用该部分内存,内存不会反复申请释放,灵活性较高,内存利用率也较高。
接口框架申请的方式,开发者需要预留临时空间;通过sharedTmpBuffer传入的情况,开发者需要为tensor申请空间。临时空间大小BufferSize的获取方式如下:通过WelfordFinalize Tiling中提供的GetWelfordFinalizeMaxMinTmpSize接口获取所需最大和最小临时空间大小,最小空间可以保证功能正确,最大空间用于提升性能。
参数说明
表 1 模板参数说明
结构体模板参数,用于配置相关信息,WelfordFinalizeConfig类型,具体定义如下: struct WelfordFinalizeConfig {
bool isCorrection = false;
}
constexpr WelfordFinalizeConfig WFFINALIZE_DEFAULT_CFG = { false };
|
表 2 接口参数说明
均值目的操作数,数据类型为float。输出的均值为1个数,需要sizeof(float)大小的空间进行保存,根据存储单元的对齐要求,开发者实际需要为outputMean分配32字节对齐的内存空间。 类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 |
||
方差目的操作数,数据类型为float。输出的方差为1个数,需要sizeof(float)大小的空间进行保存,根据存储单元的对齐要求,开发者实际需要为outputVariance分配32字节对齐的内存空间。 类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 |
||
均值源操作数,数据类型为float。shape为[abLength]。 类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 |
||
方差源操作数,数据类型为float。shape为[abLength]。 类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 |
||
源操作数,数据类型为int32_t。shape为[abLength]。 类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 |
||
|
类型为LocalTensor,支持的TPosition为VECIN/VECCALC/VECOUT。 临时空间大小BufferSize的获取方式请参考WelfordFinalize Tiling。 |
||
计算所需的参数信息。WelfordFinalizePara类型,定义如下。 struct WelfordFinalizePara {
uint32_t rnLength;
uint32_t abLength;
uint32_t headCount;
uint32_t headCountLength;
uint32_t tailCount;
uint32_t tailCountLength;
float abRec;
float rRec;
float rRecWithCorrection;
};
|
返回值说明
无
约束说明
- 接口参数para.abLength的取值必须为32/sizeof(float)的整数倍。
- 接口参数para.headCountLength与para.tailCountLength的和必须等于参数para.abLength。
- 接口处理逻辑以参数para中设置的具体参数值为准,不依赖源操作数的shape信息。
- 接口参数para.tailCount为0时,禁止配置para.tailCountLength为非0值。
- 不支持源操作数与目的操作数地址重叠。
- 不支持sharedTmpBuffer与源操作数和目的操作数地址重叠。
调用示例
pipe.InitBuffer(sharedTmpBuffer, stackBufferSize);
AscendC::LocalTensor<uint8_t> tmpLocalTensor = sharedTmpBuffer.Get<uint8_t>();
struct AscendC::WelfordFinalizePara para = {rnLength, abLength, head, headLength, tail, tailLength, abRec, rRec};
AscendC::WelfordFinalize<false>(meanLocal, varianceLocal, inputMeanLocal, inputVarianceLocal, inputCountsLocal, tmpLocalTensor, para);
#include "kernel_operator.h"
template <typename dataType, bool isCounts = false> class KernelWelfordFinalize {
public:
__aicore__ inline KernelWelfordFinalize() {}
__aicore__ inline void Init(GM_ADDR inputX_gm, GM_ADDR inputmean_gm, GM_ADDR inputvar_gm, GM_ADDR outputMean_gm,
GM_ADDR outputVariance_gm, uint32_t rnLength, uint32_t abLength, uint32_t rLength, uint32_t head,
uint32_t headLength, uint32_t tail, uint32_t tailLength, float rRecWithCorrection)
{
this->rnLength = rnLength;
this->abLength = abLength;
this->head = head;
this->headLength = headLength;
this->tail = tail;
this->tailLength = tailLength;
totalLength = rnLength * abLength;
this->rLength = rLength;
this->abRec = 1 / (float)abLength;
this->rRec = 1 / (float)rLength;
this->rRecWithCorrection = rRecWithCorrection;
this->outLength = (rnLength + 8 - 1) / 8 * 8;
inputX_global.SetGlobalBuffer(reinterpret_cast<__gm__ dataType *>(inputX_gm), totalLength);
inputmean_global.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(inputmean_gm), totalLength);
inputvar_global.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(inputvar_gm), totalLength);
outputMean_global.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(outputMean_gm), totalLength);
outputVariance_global.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(outputVariance_gm), totalLength);
pipe.InitBuffer(inQueueX, 1, sizeof(dataType) * totalLength);
pipe.InitBuffer(inQueueMean, 1, sizeof(float) * totalLength);
pipe.InitBuffer(inQueueVar, 1, sizeof(float) * totalLength);
pipe.InitBuffer(outQueueMean, 1, sizeof(float) * outLength);
pipe.InitBuffer(outQueueVariance, 1, sizeof(float) * outLength);
}
__aicore__ inline void Process()
{
CopyIn();
Compute();
CopyOut();
}
private:
__aicore__ inline void CopyIn()
{
AscendC::LocalTensor<dataType> inputXLocal = inQueueX.AllocTensor<dataType>();
AscendC::LocalTensor<float> inmeanLocal = inQueueMean.AllocTensor<float>();
AscendC::LocalTensor<float> invarLocal = inQueueVar.AllocTensor<float>();
AscendC::DataCopy(inputXLocal, inputX_global, totalLength);
AscendC::DataCopy(inmeanLocal, inputmean_global, totalLength);
AscendC::DataCopy(invarLocal, inputvar_global, totalLength);
inQueueX.EnQue(inputXLocal);
inQueueMean.EnQue(inmeanLocal);
inQueueVar.EnQue(invarLocal);
}
__aicore__ inline void Compute()
{
AscendC::LocalTensor<dataType> inputXLocal = inQueueX.DeQue<dataType>();
AscendC::LocalTensor<float> inmeanLocal = inQueueMean.DeQue<float>();
AscendC::LocalTensor<float> invarLocal = inQueueVar.DeQue<float>();
AscendC::LocalTensor<float> meanLocal = outQueueMean.AllocTensor<float>();
AscendC::LocalTensor<float> varianceLocal = outQueueVariance.AllocTensor<float>();
AscendC::Duplicate(meanLocal, float(0), outLength);
AscendC::Duplicate(varianceLocal, float(0), outLength);
static constexpr AscendC::WelfordFinalizeConfig CONFIG = {false};
AscendC::WelfordFinalizePara para = { rnLength, abLength, head, headLength, tail, tailLength, abRec, rRec, rRecWithCorrection};
if constexpr (isCounts) {
AscendC::WelfordFinalize<false, CONFIG>(meanLocal, varianceLocal, inmeanLocal, invarLocal, inputXLocal, para);
} else {
AscendC::WelfordFinalize<false, CONFIG>(meanLocal, varianceLocal, inmeanLocal, invarLocal, para);
}
outQueueMean.EnQue<float>(meanLocal);
outQueueVariance.EnQue<float>(varianceLocal);
inQueueX.FreeTensor(inputXLocal);
inQueueMean.FreeTensor(inmeanLocal);
inQueueVar.FreeTensor(invarLocal);
}
__aicore__ inline void CopyOut()
{
AscendC::LocalTensor<float> meanLocal = outQueueMean.DeQue<float>();
AscendC::LocalTensor<float> varianceLocal = outQueueVariance.DeQue<float>();
AscendC::DataCopy(outputMean_global, meanLocal, outLength);
AscendC::DataCopy(outputVariance_global, varianceLocal, outLength);
outQueueMean.FreeTensor(meanLocal);
outQueueVariance.FreeTensor(varianceLocal);
}
private:
AscendC::GlobalTensor<dataType> inputX_global;
AscendC::GlobalTensor<float> inputmean_global;
AscendC::GlobalTensor<float> inputvar_global;
AscendC::GlobalTensor<float> outputMean_global;
AscendC::GlobalTensor<float> outputVariance_global;
AscendC::TPipe pipe;
AscendC::TQue<AscendC::TPosition::VECIN, 1> inQueueX;
AscendC::TQue<AscendC::TPosition::VECIN, 1> inQueueMean;
AscendC::TQue<AscendC::TPosition::VECIN, 1> inQueueVar;
AscendC::TQue<AscendC::TPosition::VECOUT, 1> outQueueMean;
AscendC::TQue<AscendC::TPosition::VECOUT, 1> outQueueVariance;
uint32_t rnLength;
uint32_t abLength;
uint32_t rLength;
uint32_t head;
uint32_t headLength;
uint32_t tail;
uint32_t tailLength;
uint32_t totalLength;
uint32_t outLength;
float abRec;
float rRec;
float rRecWithCorrection;
};