blob: 0460b0f661b734ee855d517864770a45da0a0a50 [file] [log] [blame]
/************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
*************************************************************/
#include "../src/model/layer/cudnn_rnn.h"
#ifdef USE_CUDNN
#if CUDNN_VERSION >= 5005
#include "gtest/gtest.h"
using singa::CudnnRNN;
using singa::Shape;
using singa::Tensor;
class TestCudnnRNN : public ::testing::Test {
protected:
virtual void SetUp() {
singa::RNNConf *rnnconf = conf.mutable_rnn_conf();
rnnconf->set_hidden_size(hidden_size);
rnnconf->set_num_stacks(1);
rnnconf->set_dropout(0);
rnnconf->set_input_mode("linear");
rnnconf->set_direction("unidirectional");
rnnconf->set_rnn_mode("tanh");
}
singa::LayerConf conf;
size_t hidden_size = 4;
};
TEST_F(TestCudnnRNN, Setup) {
CudnnRNN rnn;
// EXPECT_EQ("CudnnRNN", rnn.layer_type());
rnn.Setup(Shape{2}, conf);
auto weight = rnn.param_values().at(0);
EXPECT_EQ(weight.Size(), hidden_size * (2 + hidden_size + 2));
}
TEST_F(TestCudnnRNN, Forward) {
auto cuda = std::make_shared<singa::CudaGPU>();
const size_t seqLength = 4, batchsize = 1, dim = 2;
const float x[seqLength * batchsize * dim] = {1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f};
vector<Tensor> inputs;
for (size_t i = 0; i < seqLength; i++) {
Tensor t(Shape{batchsize, dim}, cuda);
t.CopyDataFromHostPtr(x + i * t.Size(), t.Size());
inputs.push_back(t);
}
singa::Tensor hx;
inputs.push_back(hx);
CudnnRNN rnn;
rnn.Setup(Shape{dim}, conf);
rnn.ToDevice(cuda);
auto weight = rnn.param_values().at(0);
size_t weightSize = weight.Size();
float we[weightSize];
float wvalue = 0.1f;
for (size_t i = 0; i < weightSize; i++) we[i] = wvalue;
weight.CopyDataFromHostPtr(we, weightSize);
const auto ret = rnn.Forward(singa::kEval, inputs);
EXPECT_EQ(ret.size(), seqLength + 1);
vector<float> hxptr(hidden_size, 0.0f);
for (size_t i = 0; i < seqLength; i++) {
auto y = ret[i];
y.ToHost();
auto yptr = y.data<float>();
vector<float> tmp;
for (size_t j = 0; j < hidden_size; j++) {
float ty = 0;
for (size_t k = 0; k < dim; k++) {
ty += x[i * dim + k] * wvalue;
}
ty += wvalue;
for (size_t k = 0; k < hidden_size; k++) {
ty += hxptr[k] * wvalue;
}
ty += wvalue;
ty = tanh(ty);
EXPECT_NEAR(ty, yptr[j], 1e-4);
tmp.push_back(ty);
}
std::copy(tmp.begin(), tmp.end(), hxptr.begin());
}
}
TEST_F(TestCudnnRNN, Backward) {
auto cuda = std::make_shared<singa::CudaGPU>();
const size_t seqLength = 4, batchsize = 1, dim = 2;
const float x[seqLength * batchsize * dim] = {1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f};
vector<Tensor> inputs;
for (size_t i = 0; i < seqLength; i++) {
Tensor t(Shape{batchsize, dim}, cuda);
t.CopyDataFromHostPtr(x + i * t.Size(), t.Size());
inputs.push_back(t);
}
singa::Tensor hx;
inputs.push_back(hx);
CudnnRNN rnn;
rnn.Setup(Shape{dim}, conf);
rnn.ToDevice(cuda);
auto weight = rnn.param_values().at(0);
size_t weightSize = weight.Size();
float we[weightSize];
float wvalue = 0.1f;
for (size_t i = 0; i < weightSize; i++) we[i] = wvalue;
weight.CopyDataFromHostPtr(we, weightSize);
const auto outs = rnn.Forward(singa::kTrain, inputs);
float dyptr[seqLength * batchsize * hidden_size];
for (size_t i = 0; i < seqLength * batchsize * hidden_size; i++)
dyptr[i] = i * 0.1f;
vector<Tensor> grads;
for (size_t i = 0; i < seqLength; i++) {
Tensor dy(Shape{batchsize, hidden_size}, cuda);
dy.CopyDataFromHostPtr(dyptr + i * dy.Size(), dy.Size());
grads.push_back(dy);
}
Tensor dhy;
grads.push_back(dhy);
vector<float> dhyptr(hidden_size, 0.0f);
const auto ret = rnn.Backward(singa::kTrain, grads);
for (size_t i = seqLength - 1; i > 0; i--) {
auto dx = ret.first[i];
auto y = outs[i].Clone();
y.ToHost();
dx.ToHost();
auto dxptr = dx.data<float>();
auto yptr = y.data<float>();
for (size_t j = 0; j < hidden_size; j++) {
dhyptr[j] += dyptr[i * hidden_size + j];
dhyptr[j] *= 1 - yptr[j] * yptr[j];
}
for (size_t k = 0; k < dim; k++) {
float tdx = 0;
for (size_t j = 0; j < hidden_size; j++) {
tdx += dhyptr[j] * wvalue;
}
EXPECT_NEAR(tdx, dxptr[k], 1e-4);
}
vector<float> tmp;
for (size_t k = 0; k < hidden_size; k++) {
float tdhy = 0;
for (size_t j = 0; j < hidden_size; j++) {
tdhy += dhyptr[j] * wvalue;
}
tmp.push_back(tdhy);
}
std::copy(tmp.begin(), tmp.end(), dhyptr.begin());
}
}
#endif // CUDNN_VERSION >= 5005
#endif // USE_CUDNN