recurrent_op_test.cc 12.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
  Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
  Licensed 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.
*/

14 15
#include "paddle/operators/recurrent_op.h"

16 17 18
#include <glog/logging.h>
#include <gtest/gtest.h>

Y
Yi Wang 已提交
19
#include "paddle/framework/ddim.h"
20 21 22
#include "paddle/framework/op_registry.h"
#include "paddle/framework/operator.h"
#include "paddle/framework/tensor.h"
23
#include "paddle/operators/net_op.h"
24 25 26 27

namespace paddle {
namespace operators {

Y
Yi Wang 已提交
28 29
using framework::make_ddim;
using framework::DDim;
D
dongzhihong 已提交
30 31 32 33
using framework::Tensor;
using framework::Variable;
using framework::Scope;
using framework::OpRegistry;
Y
Yi Wang 已提交
34

35
class RecurrentOpTest : public ::testing::Test {
36
 protected:
37 38 39 40 41 42 43 44 45 46 47 48
  virtual void SetUp() override {
    CreateGlobalVariables();
    CreateStepNet();
    CreateRNNOp();
  }

  virtual void TearDown() override {}

  void CreateGlobalVariables() {
    // create input, and init content
    LOG(INFO) << "create global variable x";
    for (auto inlink : std::vector<std::string>{"x", "x0", "x1", "h"}) {
49
      Variable* x = scope_.NewVar(inlink);
50 51 52 53 54 55
      DDim dims = make_ddim(std::vector<int>{
          10 /*sent size*/, 20 /*batch size*/, 30 /*input dim*/});
      x->GetMutable<Tensor>()->mutable_data<float>(dims, platform::CPUPlace());
    }
    // create output alias just for test
    for (auto inlink : std::vector<std::string>{"h@alias"}) {
56
      Variable* x = scope_.NewVar(inlink);
57 58 59 60 61 62
      DDim dims =
          make_ddim(std::vector<int>{20 /*batch size*/, 30 /*input dim*/});
      x->GetMutable<Tensor>()->mutable_data<float>(dims, platform::CPUPlace());
    }

    LOG(INFO) << "create global variable w";
63
    Variable* w = scope_.NewVar("rnn/w");
64 65 66
    w->GetMutable<Tensor>()->mutable_data<float>(
        make_ddim(std::vector<int>{30, 30}), platform::CPUPlace());

67
    for (auto boot : std::vector<std::string>{"h_boot"}) {
68
      LOG(INFO) << "create global variable " << boot;
69
      Variable* h_boot = scope_.NewVar(boot);
70 71 72 73 74 75
      h_boot->GetMutable<Tensor>()->mutable_data<float>(
          make_ddim(std::vector<int>{20 /*batch size*/, 30 /*input dim*/}),
          platform::CPUPlace());
    }

    LOG(INFO) << "create variable step_scopes";
76
    scope_.NewVar("step_scopes");
77 78

    LOG(INFO) << "create variable h";
79
    scope_.NewVar("h");
80 81 82
  }

  void CreateRNNOp() {
Y
Yi Wang 已提交
83
    framework::OpDesc op_desc;
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

    op_desc.set_type("recurrent_op");
    // inlinks 0
    op_desc.add_inputs("x");
    op_desc.add_inputs("x0");
    op_desc.add_inputs("x1");
    // boot_memories 3
    op_desc.add_inputs("h_boot");
    // step net 5
    op_desc.add_inputs("step_net");
    // outlinks 6
    op_desc.add_outputs("h");
    // step scopes 7
    op_desc.add_outputs("step_scopes");

    auto _input_format = std::vector<int>{
        0,  // in_link
        3,  // memories
102
        4   // step_net
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    };
    auto input_format = op_desc.add_attrs();
    input_format->set_name("input_format");
    input_format->set_type(paddle::framework::AttrType::INTS);
    for (auto i : _input_format) {
      input_format->add_ints(i);
    }

    auto output_format = op_desc.add_attrs();
    output_format->set_name("output_format");
    output_format->set_type(paddle::framework::AttrType::INTS);
    for (auto i : std::vector<int>{0, 1, 2}) {
      output_format->add_ints(i);
    }

    auto inlink_alias = op_desc.add_attrs();
    inlink_alias->set_name("inlink_alias");
    inlink_alias->set_type(paddle::framework::AttrType::STRINGS);

    auto outlink_alias = op_desc.add_attrs();
    outlink_alias->set_name("outlink_alias");
    outlink_alias->set_type(paddle::framework::AttrType::STRINGS);

    auto pre_memories = op_desc.add_attrs();
    pre_memories->set_name("pre_memories");
    pre_memories->set_type(paddle::framework::AttrType::STRINGS);

    auto memories = op_desc.add_attrs();
    memories->set_name("memories");
    memories->set_type(paddle::framework::AttrType::STRINGS);

    // create inlink_alias
    for (const auto& item :
         std::vector<std::string>{"x@alias", "x0@alias", "x1@alias"}) {
      inlink_alias->add_strings(item);
    }
    // pre memories
140
    for (const auto& item : std::vector<std::string>{"rnn/h@pre"}) {
141 142 143
      pre_memories->add_strings(item);
    }
    // memories
144
    for (const auto& item : std::vector<std::string>{"rnn/h"}) {
145 146 147 148 149 150 151 152 153 154 155 156 157 158
      memories->add_strings(item);
    }
    // output alias
    for (const auto& item : std::vector<std::string>{"h@alias"}) {
      outlink_alias->add_strings(item);
    }

    rnn_op_ = OpRegistry::CreateOp(op_desc);

    LOG(INFO) << "rnn_op finish init";
  }

  void CreateStepNet() {
    LOG(INFO) << "create variable step_net";
159
    Variable* var = scope_.NewVar("step_net");
160 161 162 163 164
    auto net = var->GetMutable<NetOp>();
    net->AddOp(
        OpRegistry::CreateOp("mul", {"rnn/h@pre", "rnn/w"}, {"rnn/s"}, {}));

    net->AddOp(
165
        OpRegistry::CreateOp("add_two", {"x@alias", "rnn/s"}, {"rnn/h"}, {}));
166 167 168 169
    net->CompleteAddOp();
  }

  // father scope
170
  Scope scope_;
D
dongzhihong 已提交
171
  std::shared_ptr<framework::OperatorBase> rnn_op_;
172 173 174 175 176 177 178 179 180
};

TEST_F(RecurrentOpTest, Run) {
  platform::CPUDeviceContext ctx;
  rnn_op_->InferShape(scope_);
  rnn_op_->Run(scope_, ctx);
}

class RecurrentGradientAlgorithmTest : public ::testing::Test {
181
 protected:
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
  virtual void SetUp() override {
    CreateGlobalVariables();
    CreateStepScopes();
    CreateStepNet();
    CreateRNNGradientAlgorithm();

    // segment inputs
    SegmentInputs();
    // link forward memories
    LinkeMemories();
  }

  virtual void TearDown() override {}

  void CreateGlobalVariables() {
    // inputs: x
    LOG(INFO) << "create global variable x";
199
    Variable* x = scope_.NewVar("x");
200 201 202 203 204
    DDim dims =
        make_ddim({10 /*sent size*/, 20 /*batch size*/, 30 /*input dim*/});
    x->GetMutable<Tensor>()->mutable_data<float>(dims, platform::CPUPlace());
    // inputs: h_boot
    LOG(INFO) << "create global variable h_boot";
205
    Variable* h_boot = scope_.NewVar("h_boot");
206 207 208 209
    h_boot->GetMutable<Tensor>()->mutable_data<float>(
        make_ddim({20 /*batch size*/, 30 /*input dim*/}), platform::CPUPlace());
    // inputs: w
    LOG(INFO) << "create global variable w";
210
    Variable* w = scope_.NewVar("rnn/w");
211 212 213 214
    w->GetMutable<Tensor>()->mutable_data<float>(make_ddim({30, 30}),
                                                 platform::CPUPlace());
    // inputs: h_grad
    LOG(INFO) << "create variable h_grad";
215
    Variable* dh = scope_.NewVar("h_grad");
216 217 218 219
    dh->GetMutable<Tensor>()->mutable_data<float>(make_ddim({10, 20, 30}),
                                                  platform::CPUPlace());
    // inputs: step_scopes
    LOG(INFO) << "create variable step_scopes";
220
    scope_.NewVar("step_scopes");
221 222
    // inputs: step_net
    LOG(INFO) << "create variable step_net";
223
    scope_.NewVar("step_net");
224 225
    // outputs: w_grad
    LOG(INFO) << "create global variable w_grad";
226
    scope_.NewVar("rnn/w_grad");
227 228
    // outputs: x_grad
    LOG(INFO) << "create global variable x_grad";
229
    scope_.NewVar("x_grad");
230 231
    // outputs: h_boot_grad
    LOG(INFO) << "create global variable h_boot_grad";
232
    scope_.NewVar("h_boot_grad");
233 234 235
  }

  void CreateStepScopes() {
236 237
    auto step_scopes =
        scope_.FindVar("step_scopes")->GetMutable<std::vector<Scope*>>();
238
    for (int i = 0; i < 10; ++i) {
239 240 241 242 243
      auto& scope = scope_.NewScope();
      auto pre_t = scope.NewVar("rnn/pre_h")->GetMutable<Tensor>();
      pre_t->mutable_data<float>({20, 30}, platform::CPUPlace());
      auto tensor = scope.NewVar("rnn/h")->GetMutable<Tensor>();
      tensor->mutable_data<float>({20, 30}, platform::CPUPlace());
244 245

      // for unit test of ConcatOutputs
246 247
      auto xg = scope.NewVar("rnn/x_grad")->GetMutable<Tensor>();
      xg->mutable_data<float>({20, 30}, platform::CPUPlace());
248

249
      step_scopes->emplace_back(&scope);
250 251 252
    }

    // last time step
253
    auto g = (*step_scopes)[9]->NewVar("rnn/h_pre_grad")->GetMutable<Tensor>();
254
    g->mutable_data<float>({20, 30}, platform::CPUPlace());
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
  }

  void CreateRNNGradientAlgorithm() {
    std::unique_ptr<rnn::Argument> arg(new rnn::Argument());
    arg->step_net = "step_net";
    arg->step_scopes = "step_scopes";
    rnn::Link inlink;
    inlink.external = "h_grad";
    inlink.internal = "rnn/h_grad";
    arg->inlinks = std::vector<rnn::Link>{inlink};

    rnn::Link outlink;
    outlink.external = "x_grad";
    outlink.internal = "rnn/x_grad";
    arg->outlinks = std::vector<rnn::Link>{outlink};

    rnn::MemoryAttr mem_attr;
    mem_attr.pre_var = "rnn/h_pre_grad";
    mem_attr.var = "rnn/h_grad";
    mem_attr.boot_var = "h_boot_grad";
    arg->memories = std::vector<rnn::MemoryAttr>{mem_attr};

    rnn_grad_algo_.Init(std::move(arg));
  }

  void CreateStepNet() {
    LOG(INFO) << "create variable step_net";
282
    Variable* var = scope_.NewVar("step_net");
283
    auto net = var->GetMutable<NetOp>();
284 285
    net->AddOp(OpRegistry::CreateOp("mul", {"rnn/h_pre", "rnn/w", "rnn/s_grad"},
                                    {"rnn/h_pre_grad", "rnn/w_grad"}, {}));
286

287 288
    net->AddOp(OpRegistry::CreateOp("add_two", {"rnn/h_grad"},
                                    {"rnn/x_grad", "rnn/s_grad"}, {}));
289 290 291 292 293 294 295 296 297 298 299
    net->CompleteAddOp();
  }

  void SegmentInputs() {
    LOG(INFO) << "segment inputs";
    std::vector<std::string> inlinks = {"x"};
    std::vector<std::string> inlinks_alias = {"rnn/x"};

    rnn::Link inlink;
    inlink.external = "x";
    inlink.internal = "rnn/x";
300 301
    auto step_scopes =
        scope_.FindVar("step_scopes")->GetMutable<std::vector<Scope*>>();
302
    rnn::SegmentInputs(*step_scopes, std::vector<rnn::Link>{inlink}, 10,
D
dangqingqing 已提交
303
                       true /*infer_shape_mode*/);
304 305 306 307 308 309 310 311 312 313
  }

  void LinkeMemories() {
    LOG(INFO) << "link memories";
    rnn::MemoryAttr mem_attr;
    mem_attr.pre_var = "rnn/h_pre";
    mem_attr.var = "rnn/h";
    mem_attr.boot_var = "boot_h";
    std::vector<rnn::MemoryAttr> memories;
    memories.push_back(mem_attr);
314 315
    auto step_scopes =
        scope_.FindVar("step_scopes")->GetMutable<std::vector<Scope*>>();
316
    for (int i = 1; i < 10; ++i) {
317 318
      rnn::LinkMemories(*step_scopes, memories, i, -1,
                        true /*infer_shape_mode*/);
319 320 321
    }
  }

322
  Scope scope_;
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
  RecurrentGradientAlgorithm rnn_grad_algo_;
};

// TEST_F(RecurrentGradientAlgorithmTest, Run) {
//   platform::CPUDeviceContext ctx;
//   rnn_grad_algo_.Run(scope_, ctx);
// }

}  // namespace operators
}  // namespace paddle

TEST(RecurrentOp, LinkMemories) {
  using namespace paddle::framework;
  using namespace paddle::platform;
  using namespace paddle::operators;

  // create and init step scopes
D
dangqingqing 已提交
340
  size_t len = 10;
341
  std::vector<Scope*> step_scopes;
D
dangqingqing 已提交
342
  for (size_t i = 0; i < len; ++i) {
343
    auto scope = new Scope();
344 345
    scope->NewVar("pre_h");
    auto tensor = scope->NewVar("h")->GetMutable<Tensor>();
346
    float* data = tensor->mutable_data<float>({15, 20}, CPUPlace());
D
dangqingqing 已提交
347
    for (size_t j = 0; j < 15 * 20; ++j) {
D
dangqingqing 已提交
348
      data[j] = rand() * (1. / (double)RAND_MAX);
349 350 351 352 353 354 355 356 357 358 359 360
    }
    step_scopes.push_back(scope);
  }

  // create MemoryAttr
  rnn::MemoryAttr mem_attr;
  mem_attr.pre_var = "pre_h";
  mem_attr.var = "h";
  mem_attr.boot_var = "boot_h";
  std::vector<rnn::MemoryAttr> memories;
  memories.push_back(mem_attr);

D
dangqingqing 已提交
361
  for (size_t i = 1; i < len; ++i) {
D
dangqingqing 已提交
362
    rnn::LinkMemories(step_scopes, memories, i, -1, false /*infer_shape_mode*/);
363 364
  }
  // check
D
dangqingqing 已提交
365
  for (size_t i = 0; i < len - 1; ++i) {
366
    const float* a =
367
        step_scopes[i]->FindVar("h")->GetMutable<Tensor>()->data<float>();
368
    const float* b = step_scopes[i + 1]
369
                         ->FindVar("pre_h")
370 371
                         ->GetMutable<Tensor>()
                         ->data<float>();
372 373
    for (size_t j = 0; j < 15 * 20; ++j) {
      ASSERT_FLOAT_EQ(a[j], b[j]);
374 375 376 377
    }
  }

  for (int i = len - 2; i >= 0; --i) {
D
dangqingqing 已提交
378
    rnn::LinkMemories(step_scopes, memories, i, 1, false /*infer_shape_mode*/);
379 380 381
  }
  // check
  for (int i = len - 2; i >= 0; --i) {
382 383 384 385
    const float* a =
        step_scopes[i]->FindVar("pre_h")->GetMutable<Tensor>()->data<float>();
    const float* b =
        step_scopes[i + 1]->FindVar("h")->GetMutable<Tensor>()->data<float>();
386 387
    for (size_t j = 0; j < 15 * 20; ++j) {
      ASSERT_FLOAT_EQ(a[j], b[j]);
388 389
    }
  }
390 391 392 393

  for (auto s : step_scopes) {
    delete s;
  }
394 395 396 397
}

USE_OP(add_two);
USE_OP(mul);
F
Fix bug  
fengjiayi 已提交
398
USE_OP_ITSELF(recurrent_op);