namespace NsOptimizedTransducer {
using namespace AscendC;
constexpr int BUFFER_NUM = 2;
constexpr uint32_t ALIGN_ELEMENTS = 8;
#pragma region Class_Definition
class OptimizedTransducer {
public:
__aicore__ inline OptimizedTransducer(){};
__aicore__ inline void Init(
GM_ADDR logits, GM_ADDR targets, GM_ADDR logitLengths, GM_ADDR targetLengths, GM_ADDR loss, GM_ADDR grad,
GM_ADDR workspace, const OptimizedTransducerTilingData* tilingData);
__aicore__ inline void Process();
private:
__aicore__ inline void GetCurrentTU(int32_t sampleIdx, int32_t& T, int32_t& U);
template <HardEvent event>
__aicore__ inline void Sync(int8_t id = 0);
__aicore__ inline float ScalarLog(float x);
__aicore__ inline float ScalarExp(float x);
__aicore__ inline float ScalarAbs(float x);
__aicore__ inline void ComputeLogSoftmax(int32_t sampleIdx);
__aicore__ inline void ComputeAlpha(int32_t sampleIdx);
__aicore__ inline void ComputeBeta(int32_t sampleIdx);
__aicore__ inline void ComputeGrad(int32_t sampleIdx);
__aicore__ inline float LogAdd(float a, float b);
__aicore__ inline void ComputeLogSoftmaxSingleLargeVector(int64_t vectorIdx, uint32_t tileLen);
__aicore__ inline void ComputeLogSoftmaxForBatchVector(
int64_t startVectorIdx, int32_t vectorCount, uint32_t tileLen);
__aicore__ inline void ComputeGradForBatchVector(
int64_t startVectorIdx, int32_t vectorCount, uint32_t tileLen, int32_t sampleIdx);
__aicore__ inline void ComputeGradSingleLargeVector(
int64_t vectorIdx, uint32_t tileLen, float logGamma, float gradBlankPosterior, float gradLabelPosterior,
int32_t targetLabel);
__aicore__ inline int64_t GetPosOffsetLocal(int32_t t, int32_t u);
__aicore__ inline GlobalTensor<float>& GetLogProbGm();
__aicore__ inline int32_t GetTargetLabel(int32_t sample, int32_t targetIdx);
__aicore__ inline void LoadProbRow(
int32_t t, uint32_t uSizeAligned, LocalTensor<int32_t>& targetsLocal, LocalTensor<float>& probIn);
__aicore__ inline void StoreBetaRow(int32_t t, uint32_t uSizeAligned, LocalTensor<float>& betaRowSrc);
private:
GlobalTensor<float> logitsGm;
GlobalTensor<int32_t> targetsGm;
GlobalTensor<int32_t> logitLengthsGm;
GlobalTensor<int32_t> targetLengthsGm;
GlobalTensor<float> lossGm;
GlobalTensor<float> gradGm;
GlobalTensor<float> alphaGm;
GlobalTensor<float> betaGm;
int32_t blankIdx_ = 0;
float clamp_ = -1.0f;
bool fusedLogSoftmax_ = true;
int32_t vocabSize_ = 0;
uint32_t alignedVocabSize_ = 0;
int32_t sampleStart_ = 0;
int32_t sampleEnd_ = 0;
int32_t maxTargetLength_ = 0;
int32_t T_ = 0;
int32_t U_ = 0;
int64_t currentSampleOffset_ = 0;
int64_t ubSize_ = 0;
uint32_t scalarSize_ = 128;
LocalTensor<float> scalarLocal_;
float cachedLoss_ = 0.0f;
TPipe pipe_;
TQue<QuePosition::VECIN, BUFFER_NUM> inQueue_;
TQue<QuePosition::VECOUT, BUFFER_NUM> outQueue_;
TBuf<QuePosition::VECCALC> workBuf_;
uint32_t logSoftmaxTileLen_ = 0;
};
#pragma endregion
#pragma region Initialization
__aicore__ inline void OptimizedTransducer::Init(
GM_ADDR logits, GM_ADDR targets, GM_ADDR logitLengths, GM_ADDR targetLengths, GM_ADDR loss, GM_ADDR grad,
GM_ADDR workspace, const OptimizedTransducerTilingData* tilingData)
{
vocabSize_ = static_cast<int32_t>(tilingData->vocabSize);
alignedVocabSize_ = ((vocabSize_ + ALIGN_ELEMENTS - 1) / ALIGN_ELEMENTS) * ALIGN_ELEMENTS;
int32_t blockIdx = GetBlockIdx();
int32_t blockNum = tilingData->usedCoreNum;
int64_t blank = tilingData->blank;
ubSize_ = tilingData->ubSize;
blankIdx_ = (blank < 0) ? (vocabSize_ - 1) : static_cast<int32_t>(blank);
clamp_ = tilingData->clamp;
fusedLogSoftmax_ = tilingData->fusedLogSoftmax;
int32_t batchSize = static_cast<int32_t>(tilingData->batchSize);
maxTargetLength_ = static_cast<int32_t>(tilingData->maxTargetLength);
int32_t samplesPerBlock = batchSize / blockNum;
int32_t sampleRemainder = batchSize % blockNum;
sampleStart_ = blockIdx * samplesPerBlock + (blockIdx < sampleRemainder ? blockIdx : sampleRemainder);
sampleEnd_ = sampleStart_ + samplesPerBlock + (blockIdx < sampleRemainder ? 1 : 0);
int64_t totalElements = tilingData->totalPositions * vocabSize_;
logitsGm.SetGlobalBuffer((__gm__ float*)logits, totalElements);
gradGm.SetGlobalBuffer((__gm__ float*)grad, totalElements);
lossGm.SetGlobalBuffer((__gm__ float*)loss, batchSize);
targetsGm.SetGlobalBuffer((__gm__ int32_t*)targets, batchSize * maxTargetLength_);
logitLengthsGm.SetGlobalBuffer((__gm__ int32_t*)logitLengths, batchSize);
targetLengthsGm.SetGlobalBuffer((__gm__ int32_t*)targetLengths, batchSize);
int64_t totalPositions = tilingData->totalPositions;
alphaGm.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(workspace), totalPositions);
betaGm.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(workspace) + totalPositions, totalPositions);
for (int32_t s = 0; s < sampleStart_; ++s) {
int32_t T, U;
GetCurrentTU(s, T, U);
currentSampleOffset_ += static_cast<int64_t>(T) * U;
}
}
#pragma endregion
#pragma region LogSoftmax
__aicore__ inline void OptimizedTransducer::ComputeLogSoftmaxForBatchVector(
int64_t startVectorIdx, int32_t vectorCount, uint32_t tileLen)
{
int64_t gmOffset = (currentSampleOffset_ + startVectorIdx) * vocabSize_;
LocalTensor<float> inLocal = inQueue_.AllocTensor<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
int64_t srcOffset = gmOffset + v * vocabSize_;
uint32_t dstOffset = v * alignedVocabSize_;
DataCopy(inLocal[dstOffset], logitsGm[srcOffset], alignedVocabSize_);
}
inQueue_.EnQue(inLocal);
inLocal = inQueue_.DeQue<float>();
LocalTensor<float> outLocal = outQueue_.AllocTensor<float>();
LocalTensor<float> workLocal = workBuf_.Get<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
uint32_t vOffset = v * alignedVocabSize_;
LocalTensor<float> vInput = inLocal[vOffset];
LocalTensor<float> vOutput = outLocal[vOffset];
ReduceMax(scalarLocal_, vInput, workLocal, vocabSize_);
pipe_barrier(PIPE_V);
float vMax = scalarLocal_.GetValue(0);
Adds(vOutput, vInput, -vMax, vocabSize_);
Exp(vOutput, vOutput, vocabSize_);
ReduceSum(scalarLocal_, vOutput, workLocal, vocabSize_);
pipe_barrier(PIPE_V);
float vSum = scalarLocal_.GetValue(0);
float finalBias = -vMax - ScalarLog(vSum);
Adds(vOutput, vInput, finalBias, vocabSize_);
}
outQueue_.EnQue(outLocal);
inQueue_.FreeTensor(inLocal);
outLocal = outQueue_.DeQue<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
uint32_t srcOffset = v * alignedVocabSize_;
int64_t dstOffset = gmOffset + v * vocabSize_;
uint32_t copySize = vocabSize_ * sizeof(float);
DataCopyExtParams copyOutParams{1, copySize, 0, 0, 0};
DataCopyPad(gradGm[dstOffset], outLocal[srcOffset], copyOutParams);
}
outQueue_.FreeTensor(outLocal);
}
__aicore__ inline void OptimizedTransducer::ComputeLogSoftmaxSingleLargeVector(int64_t vectorIdx, uint32_t tileLen)
{
int64_t gmOffset = (currentSampleOffset_ + vectorIdx) * vocabSize_;
uint32_t loopCount = (vocabSize_ + tileLen - 1) / tileLen;
LocalTensor<float> workLocal = workBuf_.Get<float>();
float globalMax = -1.0e20f;
float globalSum = 0.0f;
for (uint32_t i = 0; i < loopCount; ++i) {
int32_t curOffset = i * tileLen;
int32_t curLen = (i == loopCount - 1) ? (vocabSize_ - curOffset) : tileLen;
uint32_t copySize = curLen * sizeof(float);
LocalTensor<float> inLocal = inQueue_.AllocTensor<float>();
DataCopyExtParams copyParams{1, copySize, 0, 0, 0};
DataCopyPadExtParams<float> padParams{true, 0, 0, -1.0e20f};
DataCopyPad(inLocal, logitsGm[gmOffset + curOffset], copyParams, padParams);
inQueue_.EnQue(inLocal);
inLocal = inQueue_.DeQue<float>();
LocalTensor<float> outLocal = outQueue_.AllocTensor<float>();
ReduceMax(scalarLocal_, inLocal, workLocal, curLen);
pipe_barrier(PIPE_V);
float localMax = scalarLocal_.GetValue(0);
Adds(outLocal, inLocal, -localMax, curLen);
Exp(outLocal, outLocal, curLen);
ReduceSum(scalarLocal_, outLocal, workLocal, curLen);
pipe_barrier(PIPE_V);
float localSum = scalarLocal_.GetValue(0);
if (localMax > globalMax) {
float correction = ScalarExp(globalMax - localMax);
globalSum = globalSum * correction + localSum;
globalMax = localMax;
} else {
float correction = ScalarExp(localMax - globalMax);
globalSum = globalSum + localSum * correction;
}
outQueue_.EnQue(outLocal);
inQueue_.FreeTensor(inLocal);
outLocal = outQueue_.DeQue<float>();
outQueue_.FreeTensor(outLocal);
}
float finalBias = -globalMax - ScalarLog(globalSum);
for (uint32_t i = 0; i < loopCount; ++i) {
int32_t curOffset = i * tileLen;
int32_t curLen = (i == loopCount - 1) ? (vocabSize_ - curOffset) : tileLen;
uint32_t copySize = curLen * sizeof(float);
LocalTensor<float> inLocal = inQueue_.AllocTensor<float>();
DataCopyExtParams copyParams{1, copySize, 0, 0, 0};
DataCopyPadExtParams<float> padParams{true, 0, 0, -1.0e20f};
DataCopyPad(inLocal, logitsGm[gmOffset + curOffset], copyParams, padParams);
inQueue_.EnQue(inLocal);
inLocal = inQueue_.DeQue<float>();
LocalTensor<float> outLocal = outQueue_.AllocTensor<float>();
Adds(outLocal, inLocal, finalBias, curLen);
outQueue_.EnQue(outLocal);
inQueue_.FreeTensor(inLocal);
outLocal = outQueue_.DeQue<float>();
DataCopyPad(gradGm[gmOffset + curOffset], outLocal, copyParams);
outQueue_.FreeTensor(outLocal);
}
}
__aicore__ inline void OptimizedTransducer::ComputeLogSoftmax(int32_t sampleIdx)
{
if (!fusedLogSoftmax_) {
return;
}
int64_t totalPos = static_cast<int64_t>(T_) * U_;
uint32_t bufferSize = (ubSize_ - scalarSize_) / 5 / 32 * 32;
uint32_t tileLen = bufferSize / sizeof(float);
pipe_.InitBuffer(inQueue_, BUFFER_NUM, bufferSize);
pipe_.InitBuffer(outQueue_, BUFFER_NUM, bufferSize);
pipe_.InitBuffer(workBuf_, bufferSize + scalarSize_);
scalarLocal_ = workBuf_.GetWithOffset<float>(scalarSize_ / sizeof(float), bufferSize);
if (alignedVocabSize_ < tileLen) {
uint32_t vectorsPerBatch = tileLen / alignedVocabSize_;
for (int64_t i = 0; i < totalPos; i += vectorsPerBatch) {
uint32_t currentBatchSize = (i + vectorsPerBatch > totalPos) ? (totalPos - i) : vectorsPerBatch;
ComputeLogSoftmaxForBatchVector(i, currentBatchSize, tileLen);
}
} else {
for (int64_t i = 0; i < totalPos; ++i) {
ComputeLogSoftmaxSingleLargeVector(i, tileLen);
}
}
pipe_.Reset();
}
#pragma endregion
#pragma region Helpers
__aicore__ inline void OptimizedTransducer::GetCurrentTU(int32_t sampleIdx, int32_t& T, int32_t& U)
{
T = logitLengthsGm.GetValue(sampleIdx);
U = targetLengthsGm.GetValue(sampleIdx) + 1;
Sync<HardEvent::MTE2_S>();
}
template <HardEvent event>
__aicore__ inline void OptimizedTransducer::Sync(int8_t id)
{
SetFlag<event>(id);
WaitFlag<event>(id);
}
__aicore__ inline int64_t OptimizedTransducer::GetPosOffsetLocal(int32_t t, int32_t u)
{
return (currentSampleOffset_ + t * U_ + u) * vocabSize_;
}
__aicore__ inline GlobalTensor<float>& OptimizedTransducer::GetLogProbGm()
{
return fusedLogSoftmax_ ? gradGm : logitsGm;
}
__aicore__ inline int32_t OptimizedTransducer::GetTargetLabel(int32_t sample, int32_t targetIdx)
{
return targetsGm.GetValue(sample * maxTargetLength_ + targetIdx);
Sync<HardEvent::MTE2_S>();
}
__aicore__ inline float OptimizedTransducer::ScalarAbs(float x)
{
return (x >= 0.0f) ? x : -x;
}
__aicore__ inline float OptimizedTransducer::ScalarLog(float x)
{
scalarLocal_.SetValue(0, x);
AscendC::Ln(scalarLocal_, scalarLocal_, 1);
return scalarLocal_.GetValue(0);
}
__aicore__ inline float OptimizedTransducer::ScalarExp(float x)
{
scalarLocal_.SetValue(0, x);
AscendC::Exp(scalarLocal_, scalarLocal_, 1);
return scalarLocal_.GetValue(0);
}
__aicore__ inline float OptimizedTransducer::LogAdd(float a, float b)
{
float maxVal = (a > b) ? a : b;
float diff = ScalarAbs(a - b);
if (diff > 50.0f) {
return maxVal;
}
float expNegDiff = ScalarExp(-diff);
float logTerm = ScalarLog(1.0f + expNegDiff);
return maxVal + logTerm;
}
__aicore__ inline void OptimizedTransducer::LoadProbRow(
int32_t t, uint32_t uSizeAligned, LocalTensor<int32_t>& targetsLocal, LocalTensor<float>& probIn)
{
GlobalTensor<float>& logProbGm = GetLogProbGm();
LocalTensor<float> logPBlankPart = probIn;
for (int32_t u = 0; u < U_; ++u) {
logPBlankPart.SetValue(u, logProbGm.GetValue(GetPosOffsetLocal(t, u) + blankIdx_));
}
LocalTensor<float> logPLabelPart = probIn[uSizeAligned];
for (int32_t u = 0; u < U_ - 1; ++u) {
int32_t label = targetsLocal.GetValue(u);
logPLabelPart.SetValue(u, logProbGm.GetValue(GetPosOffsetLocal(t, u) + label));
}
}
__aicore__ inline void OptimizedTransducer::StoreBetaRow(
int32_t t, uint32_t uSizeAligned, LocalTensor<float>& betaRowSrc)
{
LocalTensor<float> betaOut = outQueue_.AllocTensor<float>();
DataCopy(betaOut, betaRowSrc, uSizeAligned);
outQueue_.EnQue(betaOut);
betaOut = outQueue_.DeQue<float>();
DataCopyExtParams outCopyParams{1, static_cast<uint32_t>(U_ * sizeof(float)), 0, 0, 0};
DataCopyPad(betaGm[currentSampleOffset_ + t * U_], betaOut, outCopyParams);
outQueue_.FreeTensor(betaOut);
}
#pragma endregion
#pragma region Alpha
__aicore__ inline void OptimizedTransducer::ComputeAlpha(int32_t sampleIdx)
{
uint32_t uSizeAligned = (U_ + 7) / 8 * 8;
uint32_t rowBytes = uSizeAligned * sizeof(float);
uint32_t probRowBytes = 2 * rowBytes;
uint32_t workBytes = 4 * rowBytes + scalarSize_;
pipe_.InitBuffer(inQueue_, BUFFER_NUM, probRowBytes);
pipe_.InitBuffer(outQueue_, BUFFER_NUM, rowBytes);
pipe_.InitBuffer(workBuf_, workBytes);
scalarLocal_ = workBuf_.GetWithOffset<float>(scalarSize_ / sizeof(float), 4 * rowBytes);
LocalTensor<int32_t> targetsLocal = workBuf_.Get<int32_t>(rowBytes / sizeof(int32_t));
LocalTensor<float> alphaRow[2];
alphaRow[0] = workBuf_.GetWithOffset<float>(uSizeAligned, rowBytes);
alphaRow[1] = workBuf_.GetWithOffset<float>(uSizeAligned, 2 * rowBytes);
LocalTensor<float> logPBlankPrev = workBuf_.GetWithOffset<float>(uSizeAligned, 3 * rowBytes);
DataCopyExtParams targetCopyParams{1, static_cast<uint32_t>((U_ - 1) * sizeof(int32_t)), 0, 0, 0};
DataCopyPadExtParams<int32_t> targetPadParams{false, 0, 0, 0};
DataCopyPad(targetsLocal, targetsGm[sampleIdx * maxTargetLength_], targetCopyParams, targetPadParams);
Sync<HardEvent::MTE2_S>();
LocalTensor<float> probIn = inQueue_.AllocTensor<float>();
LoadProbRow(0, uSizeAligned, targetsLocal, probIn);
inQueue_.EnQue(probIn);
probIn = inQueue_.DeQue<float>();
LocalTensor<float> logPBlank = probIn;
LocalTensor<float> logPLabel = probIn[uSizeAligned];
int32_t currIdx = 0;
alphaRow[currIdx].SetValue(0, 0.0f);
for (int32_t u = 1; u < U_; ++u) {
alphaRow[currIdx].SetValue(u, alphaRow[currIdx].GetValue(u - 1) + logPLabel.GetValue(u - 1));
}
DataCopy(logPBlankPrev, logPBlank, uSizeAligned);
inQueue_.FreeTensor(probIn);
LocalTensor<float> alphaOut = outQueue_.AllocTensor<float>();
DataCopy(alphaOut, alphaRow[currIdx], uSizeAligned);
outQueue_.EnQue(alphaOut);
alphaOut = outQueue_.DeQue<float>();
DataCopyExtParams outCopyParams{1, static_cast<uint32_t>(U_ * sizeof(float)), 0, 0, 0};
DataCopyPad(alphaGm[currentSampleOffset_], alphaOut, outCopyParams);
outQueue_.FreeTensor(alphaOut);
for (int32_t t = 1; t < T_; ++t) {
int32_t nextIdx = 1 - currIdx;
LocalTensor<float> probIn = inQueue_.AllocTensor<float>();
LoadProbRow(t, uSizeAligned, targetsLocal, probIn);
inQueue_.EnQue(probIn);
probIn = inQueue_.DeQue<float>();
LocalTensor<float> logPBlankCurr = probIn;
LocalTensor<float> logPLabelCurr = probIn[uSizeAligned];
alphaRow[nextIdx].SetValue(0, alphaRow[currIdx].GetValue(0) + logPBlankPrev.GetValue(0));
for (int32_t u = 1; u < U_; ++u) {
float alphaFromT = alphaRow[currIdx].GetValue(u) + logPBlankPrev.GetValue(u);
float alphaFromU = alphaRow[nextIdx].GetValue(u - 1) + logPLabelCurr.GetValue(u - 1);
alphaRow[nextIdx].SetValue(u, LogAdd(alphaFromT, alphaFromU));
}
DataCopy(logPBlankPrev, logPBlankCurr, uSizeAligned);
pipe_barrier(PIPE_V);
inQueue_.FreeTensor(probIn);
alphaOut = outQueue_.AllocTensor<float>();
DataCopy(alphaOut, alphaRow[nextIdx], uSizeAligned);
outQueue_.EnQue(alphaOut);
alphaOut = outQueue_.DeQue<float>();
DataCopyPad(alphaGm[currentSampleOffset_ + t * U_], alphaOut, outCopyParams);
outQueue_.FreeTensor(alphaOut);
currIdx = nextIdx;
}
// 对比 alpha 和 beta 计算得到的 loss
// 从 alpha 计算: loss = -(alpha(T-1, U-1) + logPBlank(T-1, U-1))
GlobalTensor<float>& logProbGm = GetLogProbGm();
int64_t lastPosIdx = currentSampleOffset_ + (T_ - 1) * U_ + (U_ - 1);
float alphaLast = alphaGm.GetValue(lastPosIdx);
float logPBlankLast = logProbGm.GetValue(lastPosIdx * vocabSize_ + blankIdx_);
Sync<HardEvent::MTE2_S>();
float lossFromAlpha = -(alphaLast + logPBlankLast);
float lossFromBeta = cachedLoss_;
float lossError = ScalarAbs(lossFromAlpha - lossFromBeta);
float lossRelativeError = (lossFromBeta != 0.0f) ? (lossError / ScalarAbs(lossFromBeta)) : lossError;
// 打印 lossRelativeError
{
float val = lossRelativeError;
int sign = (val < 0.0f);
float absVal = sign ? -val : val;
int exp = 0;
if (absVal >= 1.0f) {
while (absVal >= 10.0f) { absVal /= 10.0f; exp++; }
} else if (absVal > 0.0f) {
while (absVal < 1.0f) { absVal *= 10.0f; exp--; }
}
int digits = (int)(absVal * 1000000.0f + 0.5f);
printf("lossRelErr: %s%d x10^%d\n", sign ? "-" : "", digits, exp - 6);
}
// 打印 lossError
{
float val = lossError;
int sign = (val < 0.0f);
float absVal = sign ? -val : val;
int exp = 0;
if (absVal >= 1.0f) {
while (absVal >= 10.0f) { absVal /= 10.0f; exp++; }
} else if (absVal > 0.0f) {
while (absVal < 1.0f) { absVal *= 10.0f; exp--; }
}
int digits = (int)(absVal * 1000000.0f + 0.5f);
printf("lossAbsErr: %s%d x10^%d\n", sign ? "-" : "", digits, exp - 6);
}
*/
pipe_.Reset();
}
#pragma endregion
#pragma region Beta
__aicore__ inline void OptimizedTransducer::ComputeBeta(int32_t sampleIdx)
{
uint32_t uSizeAligned = (U_ + 7) / 8 * 8;
uint32_t rowBytes = uSizeAligned * sizeof(float);
uint32_t probRowBytes = 2 * rowBytes;
uint32_t workBytes = 3 * rowBytes + scalarSize_;
pipe_.InitBuffer(inQueue_, BUFFER_NUM, probRowBytes);
pipe_.InitBuffer(outQueue_, BUFFER_NUM, rowBytes);
pipe_.InitBuffer(workBuf_, workBytes);
scalarLocal_ = workBuf_.GetWithOffset<float>(scalarSize_ / sizeof(float), 3 * rowBytes);
LocalTensor<int32_t> targetsLocal = workBuf_.Get<int32_t>(rowBytes / sizeof(int32_t));
LocalTensor<float> betaRow[2];
betaRow[0] = workBuf_.GetWithOffset<float>(uSizeAligned, rowBytes);
betaRow[1] = workBuf_.GetWithOffset<float>(uSizeAligned, 2 * rowBytes);
DataCopyExtParams targetCopyParams{1, static_cast<uint32_t>((U_ - 1) * sizeof(int32_t)), 0, 0, 0};
DataCopyPadExtParams<int32_t> targetPadParams{false, 0, 0, 0};
DataCopyPad(targetsLocal, targetsGm[sampleIdx * maxTargetLength_], targetCopyParams, targetPadParams);
Sync<HardEvent::MTE2_S>();
int32_t t_last = T_ - 1;
LocalTensor<float> probIn = inQueue_.AllocTensor<float>();
LoadProbRow(t_last, uSizeAligned, targetsLocal, probIn);
inQueue_.EnQue(probIn);
probIn = inQueue_.DeQue<float>();
LocalTensor<float> logPBlank = probIn;
LocalTensor<float> logPLabel = probIn[uSizeAligned];
int32_t currIdx = 0;
betaRow[currIdx].SetValue(U_ - 1, logPBlank.GetValue(U_ - 1));
for (int32_t u = U_ - 2; u >= 0; --u) {
betaRow[currIdx].SetValue(u, betaRow[currIdx].GetValue(u + 1) + logPLabel.GetValue(u));
}
inQueue_.FreeTensor(probIn);
StoreBetaRow(t_last, uSizeAligned, betaRow[currIdx]);
for (int32_t t = T_ - 2; t >= 0; --t) {
int32_t nextIdx = 1 - currIdx;
LocalTensor<float> probIn = inQueue_.AllocTensor<float>();
LoadProbRow(t, uSizeAligned, targetsLocal, probIn);
inQueue_.EnQue(probIn);
probIn = inQueue_.DeQue<float>();
LocalTensor<float> logPBlank = probIn;
LocalTensor<float> logPLabel = probIn[uSizeAligned];
betaRow[nextIdx].SetValue(U_ - 1, betaRow[currIdx].GetValue(U_ - 1) + logPBlank.GetValue(U_ - 1));
for (int32_t u = U_ - 2; u >= 0; --u) {
float betaToT = betaRow[currIdx].GetValue(u) + logPBlank.GetValue(u);
float betaToU = betaRow[nextIdx].GetValue(u + 1) + logPLabel.GetValue(u);
betaRow[nextIdx].SetValue(u, LogAdd(betaToT, betaToU));
}
inQueue_.FreeTensor(probIn);
StoreBetaRow(t, uSizeAligned, betaRow[nextIdx]);
currIdx = nextIdx;
}
cachedLoss_ = -betaRow[currIdx].GetValue(0);
scalarLocal_.SetValue(0, cachedLoss_);
pipe_barrier(PIPE_V);
DataCopyExtParams lossCopyParams{1, sizeof(float), 0, 0, 0};
DataCopyPad(lossGm[sampleIdx], scalarLocal_, lossCopyParams);
pipe_.Reset();
}
#pragma endregion
#pragma region Grad
__aicore__ inline void OptimizedTransducer::ComputeGradForBatchVector(
int64_t startVectorIdx, int32_t vectorCount, uint32_t tileLen, int32_t sampleIdx)
{
int64_t gmOffset = (currentSampleOffset_ + startVectorIdx) * vocabSize_;
GlobalTensor<float>& logProbGm = GetLogProbGm();
float loss = cachedLoss_;
bool lossInvalid = (loss > 1.0e20f || loss < -1.0e20f || (loss != loss));
LocalTensor<float> inLocal = inQueue_.AllocTensor<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
int64_t srcOffset = gmOffset + v * vocabSize_;
uint32_t dstOffset = v * alignedVocabSize_;
DataCopy(inLocal[dstOffset], logProbGm[srcOffset], alignedVocabSize_);
}
inQueue_.EnQue(inLocal);
inLocal = inQueue_.DeQue<float>();
LocalTensor<float> outLocal = outQueue_.AllocTensor<float>();
LocalTensor<float> workLocal = workBuf_.Get<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
int64_t vectorIdx = startVectorIdx + v;
int32_t t = static_cast<int32_t>(vectorIdx / U_);
int32_t u = static_cast<int32_t>(vectorIdx % U_);
uint32_t vOffset = v * alignedVocabSize_;
LocalTensor<float> vInput = inLocal[vOffset];
LocalTensor<float> vOutput = outLocal[vOffset];
if (lossInvalid) {
Duplicate(vOutput, 0.0f, vocabSize_);
continue;
}
int64_t posIdx = currentSampleOffset_ + vectorIdx;
float alpha = alphaGm.GetValue(posIdx);
float beta = betaGm.GetValue(posIdx);
float c = alpha + loss;
float logGamma = c + beta;
Adds(vOutput, vInput, logGamma, vocabSize_);
Exp(vOutput, vOutput, vocabSize_);
pipe_barrier(PIPE_V);
float logPBlank = vInput.GetValue(blankIdx_);
float gBlank = logPBlank + c;
float gradBlankPosterior = 0.0f;
if (t == T_ - 1 && u == U_ - 1) {
gradBlankPosterior = ScalarExp(gBlank);
} else if (t < T_ - 1) {
int64_t posNextT = currentSampleOffset_ + (t + 1) * U_ + u;
float betaNextT = betaGm.GetValue(posNextT);
gradBlankPosterior = ScalarExp(gBlank + betaNextT);
}
float currentGradBlank = vOutput.GetValue(blankIdx_);
vOutput.SetValue(blankIdx_, currentGradBlank - gradBlankPosterior);
int32_t targetLabel = (u < U_ - 1) ? GetTargetLabel(sampleIdx, u) : -1;
if (targetLabel >= 0 && targetLabel < vocabSize_) {
float logPLabel = vInput.GetValue(targetLabel);
float gLabel = logPLabel + c;
int64_t posNextU = currentSampleOffset_ + t * U_ + (u + 1);
float betaNextU = betaGm.GetValue(posNextU);
float gradLabelPosterior = ScalarExp(gLabel + betaNextU);
float currentGradLabel = vOutput.GetValue(targetLabel);
vOutput.SetValue(targetLabel, currentGradLabel - gradLabelPosterior);
}
if (clamp_ > 0.0f) {
Maxs(vOutput, vOutput, -clamp_, vocabSize_);
Mins(vOutput, vOutput, clamp_, vocabSize_);
}
}
outQueue_.EnQue(outLocal);
inQueue_.FreeTensor(inLocal);
outLocal = outQueue_.DeQue<float>();
for (int32_t v = 0; v < vectorCount; ++v) {
uint32_t srcOffset = v * alignedVocabSize_;
int64_t dstOffset = gmOffset + v * vocabSize_;
uint32_t copySize = vocabSize_ * sizeof(float);
DataCopyExtParams copyOutParams{1, copySize, 0, 0, 0};
DataCopyPad(gradGm[dstOffset], outLocal[srcOffset], copyOutParams);
}
outQueue_.FreeTensor(outLocal);
}
__aicore__ inline void OptimizedTransducer::ComputeGradSingleLargeVector(
int64_t vectorIdx, uint32_t tileLen, float logGamma, float gradBlankPosterior, float gradLabelPosterior,
int32_t targetLabel)
{
int64_t gmOffset = (currentSampleOffset_ + vectorIdx) * vocabSize_;
uint32_t loopCount = (vocabSize_ + tileLen - 1) / tileLen;
GlobalTensor<float>& logProbGm = GetLogProbGm();
for (uint32_t i = 0; i < loopCount; ++i) {
int32_t curOffset = i * tileLen;
int32_t curLen = (i == loopCount - 1) ? (vocabSize_ - curOffset) : tileLen;
uint32_t copySize = curLen * sizeof(float);
LocalTensor<float> inLocal = inQueue_.AllocTensor<float>();
DataCopyExtParams copyParams{1, copySize, 0, 0, 0};
DataCopyPadExtParams<float> padParams{true, 0, 0, 0.0f};
DataCopyPad(inLocal, logProbGm[gmOffset + curOffset], copyParams, padParams);
inQueue_.EnQue(inLocal);
inLocal = inQueue_.DeQue<float>();
LocalTensor<float> outLocal = outQueue_.AllocTensor<float>();
Adds(outLocal, inLocal, logGamma, curLen);
Exp(outLocal, outLocal, curLen);
pipe_barrier(PIPE_V);
int32_t startIdx = curOffset;
int32_t endIdx = curOffset + curLen;
if (blankIdx_ >= startIdx && blankIdx_ < endIdx) {
int32_t localIdx = blankIdx_ - startIdx;
float currentGradBlank = outLocal.GetValue(localIdx);
outLocal.SetValue(localIdx, currentGradBlank - gradBlankPosterior);
}
if (targetLabel >= 0 && targetLabel >= startIdx && targetLabel < endIdx) {
int32_t localIdx = targetLabel - startIdx;
float currentGradLabel = outLocal.GetValue(localIdx);
outLocal.SetValue(localIdx, currentGradLabel - gradLabelPosterior);
}
if (clamp_ > 0.0f) {
Maxs(outLocal, outLocal, -clamp_, curLen);
Mins(outLocal, outLocal, clamp_, curLen);
}
outQueue_.EnQue(outLocal);
inQueue_.FreeTensor(inLocal);
outLocal = outQueue_.DeQue<float>();
DataCopyPad(gradGm[gmOffset + curOffset], outLocal, copyParams);
outQueue_.FreeTensor(outLocal);
}
}
__aicore__ inline void OptimizedTransducer::ComputeGrad(int32_t sampleIdx)
{
int64_t totalPos = static_cast<int64_t>(T_) * U_;
uint32_t bufferSize = (ubSize_ - scalarSize_) / 5 / 32 * 32;
uint32_t tileLen = bufferSize / sizeof(float);
pipe_.InitBuffer(inQueue_, BUFFER_NUM, bufferSize);
pipe_.InitBuffer(outQueue_, BUFFER_NUM, bufferSize);
pipe_.InitBuffer(workBuf_, bufferSize + scalarSize_);
scalarLocal_ = workBuf_.GetWithOffset<float>(scalarSize_ / sizeof(float), bufferSize);
GlobalTensor<float>& logProbGm = GetLogProbGm();
float loss = cachedLoss_;
bool lossInvalid = (loss > 1.0e20f || loss < -1.0e20f || (loss != loss));
if (alignedVocabSize_ <= tileLen) {
uint32_t vectorsPerBatch = tileLen / alignedVocabSize_;
for (int64_t i = 0; i < totalPos; i += vectorsPerBatch) {
uint32_t currentBatchSize = (i + vectorsPerBatch > totalPos) ? (totalPos - i) : vectorsPerBatch;
ComputeGradForBatchVector(i, currentBatchSize, tileLen, sampleIdx);
}
} else {
for (int64_t i = 0; i < totalPos; ++i) {
int32_t t = static_cast<int32_t>(i / U_);
int32_t u = static_cast<int32_t>(i % U_);
int64_t posIdx = currentSampleOffset_ + i;
if (lossInvalid) {
ComputeGradSingleLargeVector(i, tileLen, -1.0e20f, 0.0f, 0.0f, -1);
continue;
}
float alpha = alphaGm.GetValue(posIdx);
float beta = betaGm.GetValue(posIdx);
Sync<HardEvent::MTE2_S>();
float c = alpha + loss;
float logGamma = c + beta;
int64_t offset = GetPosOffsetLocal(t, u);
float logPBlank = logProbGm.GetValue(offset + blankIdx_);
float gBlank = logPBlank + c;
float gradBlankPosterior = 0.0f;
if (t == T_ - 1 && u == U_ - 1) {
gradBlankPosterior = ScalarExp(gBlank);
} else if (t < T_ - 1) {
int64_t posNextT = currentSampleOffset_ + (t + 1) * U_ + u;
float betaNextT = betaGm.GetValue(posNextT);
Sync<HardEvent::MTE2_S>();
gradBlankPosterior = ScalarExp(gBlank + betaNextT);
}
int32_t targetLabel = (u < U_ - 1) ? GetTargetLabel(sampleIdx, u) : -1;
float gradLabelPosterior = 0.0f;
if (targetLabel >= 0 && targetLabel < vocabSize_) {
float logPLabel = logProbGm.GetValue(offset + targetLabel);
float gLabel = logPLabel + c;
int64_t posNextU = currentSampleOffset_ + t * U_ + (u + 1);
float betaNextU = betaGm.GetValue(posNextU);
gradLabelPosterior = ScalarExp(gLabel + betaNextU);
}
ComputeGradSingleLargeVector(i, tileLen, logGamma, gradBlankPosterior, gradLabelPosterior, targetLabel);
}
}
pipe_.Reset();
}
#pragma endregion
#pragma region Main_Process
__aicore__ inline void OptimizedTransducer::Process()
{
for (int32_t sample = sampleStart_; sample < sampleEnd_; ++sample) {
GetCurrentTU(sample, T_, U_);
ComputeLogSoftmax(sample);
ComputeBeta(sample);
if (clamp_ != -2.0f) {
ComputeAlpha(sample);
ComputeGrad(sample);
}
currentSampleOffset_ += static_cast<int64_t>(T_) * U_;
}
}
#pragma endregion
}
#endif