Files
2026-07-13 12:47:05 +08:00

664 lines
34 KiB
C++

/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// implementation of operations for Simple Recurrent Unit: arXiv:1709.02755v2 [cs.CL] 12 Sep 2017
//
//@author Yurii Shyrma
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_sru)
#include <helpers/MmulHelper.h>
#include <helpers/PointersManager.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/sru.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru, 5, 2, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [bS x inSize x time], time - number of time steps, bS - batch size,
// inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [3*inSize x inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [2*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x inSize] at time t=0
auto mask = block.width() > 4 ? INPUT_VARIABLE(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
auto h = OUTPUT_VARIABLE(0); // cell outputs, [bS x inSize x time]
auto c = OUTPUT_VARIABLE(1); // cell states, [bS x inSize x time]
const int rank = x->rankOf(); // = 3
const auto bS = x->sizeAt(0);
const auto inSize = x->sizeAt(1);
const auto time = x->sizeAt(2);
// input shapes validation
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0, "SRU operation: wrong rank of biases array, expected is %i, but got %i instead !",
1, b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {3 * inSize, inSize};
const std::vector<LongType> bCorrectShape = {2 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
// xm = x * mask
auto xm = x;
if (mask) {
xm = new NDArray(x->shapeInfo(), true, block.launchContext());
std::vector<LongType> dims = {0, 1};
x->applyBroadcast(broadcast::Multiply,&dims , mask, xm);
}
// time loop
helpers::sruTimeLoop(block.launchContext(), xm, c0, w, b, h, c);
if (mask) delete xm;
return Status::OK;
}
DECLARE_TYPES(sru) { getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS}); }
DECLARE_SHAPE_FN(sru) {
auto xShapeInfo = inputShape->at(0); // X, input 3d tensor [bS x inSize x time], time - number of time steps, bS -
// batch size, inSize - number of features
auto wShapeInfo = inputShape->at(1); // W, 2d tensor of weights [3*inSize x inSize]
auto bShapeInfo = inputShape->at(2); // B, row of biases with twice length [2*inSize]
auto c0ShapeInfo = inputShape->at(3); // C_{0}, 2d tensor of initial state [bS x inSize] at time t=0
auto maskShapeInfo =
block.width() > 4 ? inputShape->at(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
const int rank = xShapeInfo[0]; // = 3
const int bS = xShapeInfo[1];
const int inSize = xShapeInfo[2];
const int time = xShapeInfo[3];
// input shapes validation
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU operation: wrong rank of biases array, expected is %i, but got %i instead !", 1, bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {3 * inSize, inSize};
const std::vector<LongType> bCorrectShape = {2 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
LongType * newShapeInfo1 = nullptr;
ALLOCATE(newShapeInfo1, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x inSize x time]
newShapeInfo1[0] = rank;
newShapeInfo1[1] = bS;
newShapeInfo1[2] = inSize;
newShapeInfo1[3] = time;
ShapeUtils::updateStridesAndType(newShapeInfo1, xShapeInfo, shape::order(xShapeInfo));
auto result = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo1)->primary();
RELEASE(newShapeInfo1, block.getWorkspace());
return SHAPELIST(result, result);
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bp, 8, 4, true, 0, 0) {
auto x = INPUT_VARIABLE(0);
// X, input 3d tensor [bS x K x N], N - number of time steps, bS - batch size, K - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [3K x K]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [1 x 2*K]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x K] at time t=0
auto c = INPUT_VARIABLE(4); // C, [bS x K x N]
auto inGradCt = INPUT_VARIABLE(5); // [bS x K]
auto inGradH = INPUT_VARIABLE(6); // [bS x K x N]
NDArray* mask = nullptr; // optional, 2d tensor of dropout mask [bS x K]
bool applyMask = false;
if (block.width() > 7) {
mask = INPUT_VARIABLE(7);
applyMask = true;
}
auto gradX = OUTPUT_VARIABLE(0); // [bS x K x N]
auto gradW = OUTPUT_VARIABLE(1); // [bS x 3K x K]
auto gradB = OUTPUT_VARIABLE(2); // [1 x 2K]
auto gradInit = OUTPUT_VARIABLE(3); // [bS x K]
const int bS = x->shapeOf()[0];
const int K = x->shapeOf()[1];
const int N = x->shapeOf()[2]; // N - number of time steps
std::vector<sd::LongType> gradBiasShape = {bS, 2 * K, N};
std::vector<sd::LongType> gradUShape = {bS, 3 * K, N};
std::vector<sd::LongType> gradHXShape = {bS, K, N};
std::vector<sd::LongType> gctShape = {bS, K};
std::vector<sd::LongType> gradTanhShape = {bS, K};
std::vector<sd::LongType> gradCtShape = {bS, K};
std::vector<sd::LongType> ftMinusShape = {bS, K};
std::vector<sd::LongType> rtMinusShape = {bS, K};
std::vector<sd::LongType> temp1Shape = {bS, K};
std::vector<sd::LongType> temp2Shape = {bS, K};
auto gradBias = NDArrayFactory::create_(x->ordering(), gradBiasShape, gradX->dataType(), block.launchContext());
auto gradU = NDArrayFactory::create_(x->ordering(), gradUShape, gradX->dataType(), block.launchContext());
auto gradHX = NDArrayFactory::create_(x->ordering(), gradHXShape, gradX->dataType(), block.launchContext());
auto gct = NDArrayFactory::create_(c->ordering(), gctShape, gradX->dataType(), block.launchContext());
auto gradTanh = NDArrayFactory::create_(c->ordering(), gradTanhShape, gradX->dataType(), block.launchContext());
auto gradCt = NDArrayFactory::create_(c->ordering(), gradCtShape, gradX->dataType(), block.launchContext());
auto ftMinus = NDArrayFactory::create_(c->ordering(), ftMinusShape, gradX->dataType(), block.launchContext());
auto rtMinus = NDArrayFactory::create_(c->ordering(), rtMinusShape, gradX->dataType(), block.launchContext());
auto temp1 = NDArrayFactory::create_(c->ordering(), temp1Shape, gradX->dataType(), block.launchContext());
auto temp2 = NDArrayFactory::create_(c->ordering(), temp2Shape, gradX->dataType(), block.launchContext());
std::vector<LongType> axes = {0, 1};
// x = x * mask
if (applyMask) x->applyBroadcast(broadcast::Multiply, &axes, mask, x); // apply mask
// multiplication matrix wi = matmul(w,x), U = WX
auto wi = MmulHelper::mmul(w, x, nullptr, 1., 0.); // U [bS x 3K x N]
auto wiZ = (*wi)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto wiF = (*wi)({0, 0, K, 2 * K, 0, 0}, true); // forget gate [bS x K x N]
auto wiR = (*wi)({0, 0, 2 * K, 3 * K, 0, 0}, true); // reset gate [bS x K x N]
auto bF = (*b)({0, 0, 0, K}, true); // biases for forget gate [1 x K]
auto bR = (*b)({0, 0, K, 2 * K}, true); // biases for reset gate [1 x K]
auto gradBF = (*gradBias)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto gradBR = (*gradBias)({0, 0, K, 2 * K, 0, 0}, true); // [bS x K x N]
auto gradUZ = (*gradU)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto gradUF = (*gradU)({0, 0, K, 2 * K, 0, 0}, true); // [bS x K x N]
auto gradUR = (*gradU)({0, 0, 2 * K, 3 * K, 0, 0}, true); // [bS x K x N]
NDArray* ct_1 = nullptr;
std::vector<LongType> idx = {0, 0, 0, 0, 0, 0};
for (int t = N - 1; t >= 0; --t) {
// initialization
idx[4] = t;
idx[5] = t + 1;
auto xt = (*x)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto zt = (*wiZ)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto ft = (*wiF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto rt = (*wiR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto ct = (*c)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto inGradHt = (*inGradH)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradBRt = (*gradBR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradBFt = (*gradBF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradHXt = (*gradHX)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradUZt = (*gradUZ)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradUFt = (*gradUF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradURt = (*gradUR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
if (t != 0) {
idx[4] = t - 1;
idx[5] = t;
ct_1 = new NDArray((*c)(idx)); // previous c_{t-1}
} else
ct_1 = c0;
///////////////// forward
// ft = sigmoid(ft + bf), rt = sigmoid(rt + bR)
ft->addRowVector(bF, ft);
rt->addRowVector(bR, rt);
ft->applyTransform(transform::Sigmoid, ft);
rt->applyTransform(transform::Sigmoid, rt);
// TODO T val = (activation_type == 1) ? tanh(cur) : ((activation_type == 2) ? reluf(cur) : cur );
ct->applyTransform(transform::Tanh, gct);
// ftMinus = 1-ft, rtMinus = 1-rt
ft->applyTransform(transform::OneMinus, ftMinus);
rt->applyTransform(transform::OneMinus, rtMinus);
///////////////// backward
// bR, *grad_brt_ptr = inGradHt * (g_ct - xt) * (1.0f - rt) * rt;
gct->applyPairwiseTransform(pairwise::Subtract, xt, temp1); // temp1 = (g_ct - xt)
rtMinus->applyPairwiseTransform(pairwise::Multiply, rt, temp2); // temp2 = (1.0f - rt) * rt;
temp1->applyPairwiseTransform(pairwise::Multiply, temp2); // temp1 = (g_ct - xt) * (1.0f - rt) * rt;
inGradHt->applyPairwiseTransform(pairwise::Multiply, temp1,
gradBRt); // = inGradHt * (g_ct - xt) * (1.0f - rt) * rt;
// bF, TODO - tanh
// gradTanh = (1.0f - g_ct * g_ct);
gct->applyPairwiseTransform(pairwise::Multiply, gct, gradTanh); // gradTanh = g_ct * g_ct
gradTanh->applyTransform(transform::OneMinus, gradTanh); // gradTanh = (1.0f - g_ct * g_ct)
// gradCt = inGradHt * rt * gradTanh
rt->applyPairwiseTransform(pairwise::Multiply, gradTanh, gradCt); // gradCt = rt * gradTanh
inGradHt->applyPairwiseTransform(pairwise::Multiply, gradCt, gradCt); // gradCt = inGradHt * rt * gradTanh
// gradBFt = (gradCt + inGradCt) * (ct_1 - zt) * (1 - ft) * ft;
gradCt->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = (gradCt + inGradCt)
ct_1->applyPairwiseTransform(pairwise::Subtract, zt, temp2); // temp2 = (ct_1 - zt)
temp1->applyPairwiseTransform(pairwise::Multiply, ftMinus, temp1); // temp1 = (gradCt + inGradCt)*(1-ft)
temp1->applyPairwiseTransform(pairwise::Multiply, ft, temp1); // temp1 = (gradCt + inGradCt)*(1-ft)*ft
temp1->applyPairwiseTransform(pairwise::Multiply, temp2,
gradBFt); // gradBFt = (gradCt + inGradCt) * (ct_1 - zt) * (1 - ft) * ft;
// x_t (highway connection), gradHXt = inGradHt * (1.0f - rt);
inGradHt->applyPairwiseTransform(pairwise::Multiply, rtMinus, gradHXt);
// U_t, gradUZt = (inGradHt * rt * grad_tanh + inGradCt) * (1.0f - ft);
rt->applyPairwiseTransform(pairwise::Multiply, gradTanh, temp1); // temp1 = rt * grad_tanh
inGradHt->applyPairwiseTransform(pairwise::Multiply, temp1,temp1); // temp1 = inGradHt * rt * grad_tanh
temp1->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = inGradHt * rt * grad_tanh + inGradCt
temp1->applyPairwiseTransform(pairwise::Multiply, ftMinus,
gradUZt); // gradUZt = (inGradHt * rt * grad_tanh + inGradCt) * (1.0f - ft);
gradUFt->assign(gradBFt);
gradURt->assign(gradBRt);
// c_{t-1}, inGradCt = (gradCt + inGradCt) * ft;
gradCt->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = (gradCt + inGradCt)
temp1->applyPairwiseTransform(pairwise::Multiply, ft, inGradCt); // inGradCt = (gradCt + inGradCt) * ft;
if (t != 0) delete ct_1;
delete xt;
delete zt;
delete ft;
delete rt;
delete ct;
delete inGradHt;
delete gradBRt;
delete gradHXt;
delete gradUZt;
delete gradUFt;
delete gradURt;
}
// gradInit
gradInit->assign(inGradCt);
// gradX
auto weightsT = w->transpose(); // [K x 3K]
MmulHelper::mmul(weightsT, gradU, gradX, 1., 0.); // [bS x K x N]
gradX->applyPairwiseTransform(pairwise::Add, gradHX, gradX);
std::vector<LongType> axes3 = {0, 1};
// + grad_highway_x
if (applyMask) gradX->applyBroadcast(broadcast::Multiply, &axes3, mask, gradX); // apply mask
// gradB
std::vector<sd::LongType> gradBShape = { 2 * K};
auto gradB2 = gradB->reshape(gradB->ordering(), gradBShape);
std::vector<LongType> axes2;
axes.push_back(0);
axes.push_back(2);
gradBias->reduceAlongDimension(reduce::Sum, gradB2, &axes2); // [1 x 2K]
// gradW [bS x 3K x K]
x->permutei({0, 2, 1}, false, false); // [bS x N x K]
MmulHelper::mmul(gradU, x, gradW, 1., 0.); // [bS x 3K x K]
delete gct;
delete gradU;
delete gradHX;
delete temp1;
delete temp2;
delete gradCt;
delete wi;
delete gradTanh;
delete ftMinus;
delete rtMinus;
delete gradBias;
delete weightsT;
return Status::OK;
}
DECLARE_TYPES(sru_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(sru_bp) {
auto inShape = inputShape->at(0); // [bS x inSize x time]
auto bS = inShape[1];
auto inSize = inShape[2];
auto time = inShape[3];
char order = (char)(inShape[9]);
auto ret = SHAPELIST(
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, inSize, time})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, 3 * inSize, inSize})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{1, 2 * inSize})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, inSize})->primary()
);
return ret;
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bi, 5, 2, true, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [time x bS x 2*inSize], time - number of time steps, bS - batch
// size, inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [2*inSize x 6*inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [1 x 4*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x 2*inSize] at time t=0
NDArray* mask =
block.width() > 4 ? INPUT_VARIABLE(4) : nullptr; // optional, 2d tensor of dropout mask [bS x 2*inSize]
auto ht = OUTPUT_VARIABLE(0); // h_t, [time x bS x 2*inSize]
auto ct = OUTPUT_VARIABLE(1); // c_t, [time x bS x 2*inSize]
// input shapes validation
const int rank = x->rankOf();
const LongType bS = x->sizeAt(1);
const LongType inSize = x->sizeAt(2) / 2;
REQUIRE_TRUE(x->rankOf() == rank, 0,
"SRU_BI operation: wrong rank of input array, expected is %i, but got %i instead !", rank, x->rankOf());
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0, "SRU_BI operation: wrong rank of biases array, expected is 1, but got %i instead !",
b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
helpers::sruBI(block.launchContext(), x, w, b, c0, mask, ht, ct);
return Status::OK;
}
DECLARE_TYPES(sru_bi) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(sru_bi) {
auto xShapeInfo = inputShape->at(0); // [time x bS x 2K ]
auto wShapeInfo = inputShape->at(1);
auto bShapeInfo = inputShape->at(2);
auto c0ShapeInfo = inputShape->at(3);
auto maskShapeInfo =
block.width() > 4 ? inputShape->at(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
const int rank = xShapeInfo[0]; // = 3
const LongType time = xShapeInfo[1];
const LongType bS = xShapeInfo[2];
const LongType inSize = xShapeInfo[3] / 2;
// input shapes validation
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU_BI operation: wrong rank of biases array, expected is 1, but got %i instead !", bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
char order = shape::order(xShapeInfo);
ShapeDescriptor *descriptor = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, bS, 2 * inSize});
auto result = ConstantShapeHelper::getInstance().createShapeInfo(descriptor);
return SHAPELIST(result, result);
}
DECLARE_TYPES(sru_bi_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bi_bp, 8, 4, true, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [time x bS x 2*inSize], time - number of time steps, bS - batch
// size, inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [2*inSize x 6*inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [4*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x 2*inSize] at time t=0
auto ct = INPUT_VARIABLE(4); // C, [time x bS x 2*inSize]
auto inGradC0 = INPUT_VARIABLE(5); // [bS x 2*inSize]
auto inGradHt = INPUT_VARIABLE(6); // [time x bS x 2*inSize]
NDArray* mask =
block.width() > 7 ? INPUT_VARIABLE(7) : nullptr; // optional, 2d tensor of dropout mask [bS x 2*inSize]
// input shapes validation
const int rank = x->rankOf();
const LongType time = x->sizeAt(0);
const LongType bS = x->sizeAt(1);
const LongType inSize = x->sizeAt(2) / 2;
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0,
"SRU_BI_BP operation: wrong rank of biases array, expected is 1, but got %i instead !", b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
REQUIRE_TRUE(ct->rankOf() == rank, 0,
"SRU_BI_BP operation: wrong rank of state array, expected is %i, but got %i instead !", rank,
ct->rankOf());
REQUIRE_TRUE(inGradC0->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of gradient c0, expected is %i, but got %i instead !", rank - 1,
inGradC0->rankOf());
REQUIRE_TRUE(inGradHt->rankOf() == rank, 0,
"SRU_BI_BP operation: wrong rank of gradient ht, expected is %i, but got %i instead !", rank,
inGradHt->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> ctCorrectShape = {time, bS, 2 * inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
REQUIRE_TRUE(ct->isSameShape(ctCorrectShape), 0,
"SRU_BI operation: wrong shape of state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(ctCorrectShape).c_str(), ShapeUtils::shapeAsString(ct).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
auto gradI = OUTPUT_VARIABLE(0); // [time x bS x 2*inSize]
auto gradW = OUTPUT_VARIABLE(1); // [time x 2*inSize x 6*inSize]
auto gradB = OUTPUT_VARIABLE(2); // [1 x 4*inSize]
auto gradC0 = OUTPUT_VARIABLE(3); // [bS x 2*inSize]
helpers::sruBIBP(block.launchContext(), x, w, b, c0, ct, inGradC0, inGradHt, mask, gradI, gradW, gradB, gradC0);
return Status::OK;
}
DECLARE_SHAPE_FN(sru_bi_bp) {
auto xShapeInfo = inputShape->at(0); // [time x bS x 2K ]
auto wShapeInfo = inputShape->at(1);
auto bShapeInfo = inputShape->at(2);
auto c0ShapeInfo = inputShape->at(3);
auto ctShapeInfo = inputShape->at(4);
auto inGradC0ShapeInfo = inputShape->at(5);
auto inGradHtShapeInfo = inputShape->at(6);
auto maskShapeInfo =
block.width() > 7 ? inputShape->at(7) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
// input shapes validation
const int rank = xShapeInfo[0];
const LongType time = xShapeInfo[1];
const LongType bS = xShapeInfo[2];
const LongType inSize = xShapeInfo[3] / 2;
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU_BI_BP operation: wrong rank of biases array, expected is 1, but got %i instead !", bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo);
REQUIRE_TRUE(ctShapeInfo[0] == rank, 0,
"SRU_BI_BP operation: wrong rank of state array, expected is %i, but got %i instead !", rank,
ctShapeInfo);
REQUIRE_TRUE(inGradC0ShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of gradient c0, expected is %i, but got %i instead !", rank - 1,
inGradC0ShapeInfo[0]);
REQUIRE_TRUE(inGradHtShapeInfo[0] == rank, 0,
"SRU_BI_BP operation: wrong rank of gradient ht, expected is %i, but got %i instead !", rank,
inGradHtShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> ctCorrectShape = {time, bS, 2 * inSize};
const std::vector<LongType> inGradC0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> inGradHtCorrectShape = {time, bS, 2 * inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(ctShapeInfo, ctCorrectShape), 0,
"SRU_BI operation: wrong shape of state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(ctCorrectShape).c_str(), ShapeUtils::shapeAsString(ctShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(inGradC0ShapeInfo, inGradC0CorrectShape), 0,
"SRU_BI operation: wrong shape of gradient c0 array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(inGradC0CorrectShape).c_str(),
ShapeUtils::shapeAsString(inGradC0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(inGradHtShapeInfo, inGradHtCorrectShape), 0,
"SRU_BI operation: wrong shape of gradient ht array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(inGradHtCorrectShape).c_str(),
ShapeUtils::shapeAsString(inGradHtShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
const char order = shape::order(xShapeInfo);
ShapeDescriptor *descriptor1 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, bS, 2 * inSize});
ShapeDescriptor *descriptor2 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, 2 * inSize, 6 * inSize});
ShapeDescriptor *descriptor3 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {4 * inSize});
ShapeDescriptor *descriptor4 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {bS, 2 * inSize});
return SHAPELIST(ConstantShapeHelper::getInstance().createShapeInfo(descriptor1),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor2),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor3),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor4));
}
} // namespace ops
} // namespace sd
#endif