api_impl_tester.cc 10.4 KB
Newer Older
X
Xin Pan 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

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. */

#include <glog/logging.h>
#include <gtest/gtest.h>

L
Luo Tao 已提交
18
#include <thread>  // NOLINT
T
tensor-tang 已提交
19

X
Xin Pan 已提交
20
#include "gflags/gflags.h"
L
Luo Tao 已提交
21
#include "paddle/fluid/inference/api/api_impl.h"
X
Xin Pan 已提交
22 23
#include "paddle/fluid/inference/tests/test_helper.h"

J
JiabinYang 已提交
24 25 26 27 28 29
#ifdef __clang__
#define ACC_DIFF 4e-3
#else
#define ACC_DIFF 1e-3
#endif

X
Xin Pan 已提交
30 31 32 33 34 35 36 37
DEFINE_string(dirname, "", "Directory of the inference model.");

namespace paddle {

PaddleTensor LodTensorToPaddleTensor(framework::LoDTensor* t) {
  PaddleTensor pt;

  if (t->type() == typeid(int64_t)) {
38
    pt.data.Reset(t->data<void>(), t->numel() * sizeof(int64_t));
X
Xin Pan 已提交
39 40
    pt.dtype = PaddleDType::INT64;
  } else if (t->type() == typeid(float)) {
41
    pt.data.Reset(t->data<void>(), t->numel() * sizeof(float));
X
Xin Pan 已提交
42 43 44 45 46 47 48 49
    pt.dtype = PaddleDType::FLOAT32;
  } else {
    LOG(FATAL) << "unsupported type.";
  }
  pt.shape = framework::vectorize2int(t->dims());
  return pt;
}

Y
Yan Chunwei 已提交
50 51
NativeConfig GetConfig() {
  NativeConfig config;
52
  config.model_dir = FLAGS_dirname + "/word2vec.inference.model";
X
Xin Pan 已提交
53
  LOG(INFO) << "dirname  " << config.model_dir;
X
Xin Pan 已提交
54
  config.fraction_of_gpu_memory = 0.15;
T
tensor-tang 已提交
55
#ifdef PADDLE_WITH_CUDA
Y
Yan Chunwei 已提交
56
  config.use_gpu = true;
T
tensor-tang 已提交
57 58 59
#else
  config.use_gpu = false;
#endif
X
Xin Pan 已提交
60
  config.device = 0;
61 62
  return config;
}
X
Xin Pan 已提交
63

T
tensor-tang 已提交
64
void MainWord2Vec(bool use_gpu) {
Y
Yan Chunwei 已提交
65 66
  NativeConfig config = GetConfig();
  auto predictor = CreatePaddlePredictor<NativeConfig>(config);
T
tensor-tang 已提交
67
  config.use_gpu = use_gpu;
X
Xin Pan 已提交
68 69 70 71 72 73 74 75 76 77

  framework::LoDTensor first_word, second_word, third_word, fourth_word;
  framework::LoD lod{{0, 1}};
  int64_t dict_size = 2073;  // The size of dictionary

  SetupLoDTensor(&first_word, lod, static_cast<int64_t>(0), dict_size - 1);
  SetupLoDTensor(&second_word, lod, static_cast<int64_t>(0), dict_size - 1);
  SetupLoDTensor(&third_word, lod, static_cast<int64_t>(0), dict_size - 1);
  SetupLoDTensor(&fourth_word, lod, static_cast<int64_t>(0), dict_size - 1);

78 79 80 81 82 83 84 85 86
  std::vector<PaddleTensor> paddle_tensor_feeds;
  paddle_tensor_feeds.push_back(LodTensorToPaddleTensor(&first_word));
  paddle_tensor_feeds.push_back(LodTensorToPaddleTensor(&second_word));
  paddle_tensor_feeds.push_back(LodTensorToPaddleTensor(&third_word));
  paddle_tensor_feeds.push_back(LodTensorToPaddleTensor(&fourth_word));

  std::vector<PaddleTensor> outputs;
  ASSERT_TRUE(predictor->Run(paddle_tensor_feeds, &outputs));
  ASSERT_EQ(outputs.size(), 1UL);
87 88
  size_t len = outputs[0].data.length();
  float* data = static_cast<float*>(outputs[0].data.data());
89
  for (size_t j = 0; j < len / sizeof(float); ++j) {
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    ASSERT_LT(data[j], 1.0);
    ASSERT_GT(data[j], -1.0);
  }

  std::vector<paddle::framework::LoDTensor*> cpu_feeds;
  cpu_feeds.push_back(&first_word);
  cpu_feeds.push_back(&second_word);
  cpu_feeds.push_back(&third_word);
  cpu_feeds.push_back(&fourth_word);

  framework::LoDTensor output1;
  std::vector<paddle::framework::LoDTensor*> cpu_fetchs1;
  cpu_fetchs1.push_back(&output1);

  TestInference<platform::CPUPlace>(config.model_dir, cpu_feeds, cpu_fetchs1);

  float* lod_data = output1.data<float>();
107
  for (int i = 0; i < output1.numel(); ++i) {
J
JiabinYang 已提交
108 109
    EXPECT_LT(lod_data[i] - data[i], ACC_DIFF);
    EXPECT_GT(lod_data[i] - data[i], -ACC_DIFF);
110 111 112
  }
}

T
tensor-tang 已提交
113
void MainImageClassification(bool use_gpu) {
114 115
  int batch_size = 2;
  bool repeat = false;
Y
Yan Chunwei 已提交
116
  NativeConfig config = GetConfig();
T
tensor-tang 已提交
117
  config.use_gpu = use_gpu;
118
  config.model_dir =
119
      FLAGS_dirname + "/image_classification_resnet.inference.model";
120 121 122 123 124 125 126 127 128 129

  const bool is_combined = false;
  std::vector<std::vector<int64_t>> feed_target_shapes =
      GetFeedTargetShapes(config.model_dir, is_combined);

  framework::LoDTensor input;
  // Use normilized image pixels as input data,
  // which should be in the range [0.0, 1.0].
  feed_target_shapes[0][0] = batch_size;
  framework::DDim input_dims = framework::make_ddim(feed_target_shapes[0]);
130 131
  SetupTensor<float>(&input, input_dims, static_cast<float>(0),
                     static_cast<float>(1));
132 133 134 135 136 137 138
  std::vector<framework::LoDTensor*> cpu_feeds;
  cpu_feeds.push_back(&input);

  framework::LoDTensor output1;
  std::vector<framework::LoDTensor*> cpu_fetchs1;
  cpu_fetchs1.push_back(&output1);

L
Luo Tao 已提交
139 140
  TestInference<platform::CPUPlace, false, true>(
      config.model_dir, cpu_feeds, cpu_fetchs1, repeat, is_combined);
141

Y
Yan Chunwei 已提交
142
  auto predictor = CreatePaddlePredictor(config);
143 144
  std::vector<PaddleTensor> paddle_tensor_feeds;
  paddle_tensor_feeds.push_back(LodTensorToPaddleTensor(&input));
X
Xin Pan 已提交
145 146

  std::vector<PaddleTensor> outputs;
147
  ASSERT_TRUE(predictor->Run(paddle_tensor_feeds, &outputs));
148
  ASSERT_EQ(outputs.size(), 1UL);
149 150
  size_t len = outputs[0].data.length();
  float* data = static_cast<float*>(outputs[0].data.data());
151 152
  float* lod_data = output1.data<float>();
  for (size_t j = 0; j < len / sizeof(float); ++j) {
J
JiabinYang 已提交
153
    EXPECT_NEAR(lod_data[j], data[j], ACC_DIFF);
X
Xin Pan 已提交
154 155 156
  }
}

T
tensor-tang 已提交
157
void MainThreadsWord2Vec(bool use_gpu) {
T
tensor-tang 已提交
158
  NativeConfig config = GetConfig();
T
tensor-tang 已提交
159
  config.use_gpu = use_gpu;
T
tensor-tang 已提交
160 161
  auto main_predictor = CreatePaddlePredictor<NativeConfig>(config);

162
  // prepare inputs data and reference results
T
tensor-tang 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  constexpr int num_jobs = 3;
  std::vector<std::vector<framework::LoDTensor>> jobs(num_jobs);
  std::vector<std::vector<PaddleTensor>> paddle_tensor_feeds(num_jobs);
  std::vector<framework::LoDTensor> refs(num_jobs);
  for (size_t i = 0; i < jobs.size(); ++i) {
    // each job has 4 words
    jobs[i].resize(4);
    for (size_t j = 0; j < 4; ++j) {
      framework::LoD lod{{0, 1}};
      int64_t dict_size = 2073;  // The size of dictionary
      SetupLoDTensor(&jobs[i][j], lod, static_cast<int64_t>(0), dict_size - 1);
      paddle_tensor_feeds[i].push_back(LodTensorToPaddleTensor(&jobs[i][j]));
    }

    // get reference result of each job
    std::vector<paddle::framework::LoDTensor*> ref_feeds;
    std::vector<paddle::framework::LoDTensor*> ref_fetches(1, &refs[i]);
    for (auto& word : jobs[i]) {
      ref_feeds.push_back(&word);
    }
    TestInference<platform::CPUPlace>(config.model_dir, ref_feeds, ref_fetches);
  }

  // create threads and each thread run 1 job
  std::vector<std::thread> threads;
  for (int tid = 0; tid < num_jobs; ++tid) {
    threads.emplace_back([&, tid]() {
      auto predictor = main_predictor->Clone();
      auto& local_inputs = paddle_tensor_feeds[tid];
      std::vector<PaddleTensor> local_outputs;
      ASSERT_TRUE(predictor->Run(local_inputs, &local_outputs));

      // check outputs range
      ASSERT_EQ(local_outputs.size(), 1UL);
197 198
      const size_t len = local_outputs[0].data.length();
      float* data = static_cast<float*>(local_outputs[0].data.data());
T
tensor-tang 已提交
199 200 201 202 203 204 205
      for (size_t j = 0; j < len / sizeof(float); ++j) {
        ASSERT_LT(data[j], 1.0);
        ASSERT_GT(data[j], -1.0);
      }

      // check outputs correctness
      float* ref_data = refs[tid].data<float>();
206
      EXPECT_EQ(refs[tid].numel(), static_cast<int64_t>(len / sizeof(float)));
T
tensor-tang 已提交
207
      for (int i = 0; i < refs[tid].numel(); ++i) {
S
update  
superjomn 已提交
208
        EXPECT_NEAR(ref_data[i], data[i], 2e-3);
T
tensor-tang 已提交
209
      }
210 211 212 213 214 215 216
    });
  }
  for (int i = 0; i < num_jobs; ++i) {
    threads[i].join();
  }
}

T
tensor-tang 已提交
217
void MainThreadsImageClassification(bool use_gpu) {
218 219 220
  constexpr int num_jobs = 4;  // each job run 1 batch
  constexpr int batch_size = 1;
  NativeConfig config = GetConfig();
T
tensor-tang 已提交
221
  config.use_gpu = use_gpu;
222
  config.model_dir =
223
      FLAGS_dirname + "/image_classification_resnet.inference.model";
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

  auto main_predictor = CreatePaddlePredictor<NativeConfig>(config);
  std::vector<framework::LoDTensor> jobs(num_jobs);
  std::vector<std::vector<PaddleTensor>> paddle_tensor_feeds(num_jobs);
  std::vector<framework::LoDTensor> refs(num_jobs);
  for (size_t i = 0; i < jobs.size(); ++i) {
    // prepare inputs
    std::vector<std::vector<int64_t>> feed_target_shapes =
        GetFeedTargetShapes(config.model_dir, /*is_combined*/ false);
    feed_target_shapes[0][0] = batch_size;
    framework::DDim input_dims = framework::make_ddim(feed_target_shapes[0]);
    SetupTensor<float>(&jobs[i], input_dims, 0.f, 1.f);
    paddle_tensor_feeds[i].push_back(LodTensorToPaddleTensor(&jobs[i]));

    // get reference result of each job
    std::vector<framework::LoDTensor*> ref_feeds(1, &jobs[i]);
    std::vector<framework::LoDTensor*> ref_fetches(1, &refs[i]);
    TestInference<platform::CPUPlace>(config.model_dir, ref_feeds, ref_fetches);
  }
T
tensor-tang 已提交
243

244 245 246 247 248 249 250 251 252 253 254
  // create threads and each thread run 1 job
  std::vector<std::thread> threads;
  for (int tid = 0; tid < num_jobs; ++tid) {
    threads.emplace_back([&, tid]() {
      auto predictor = main_predictor->Clone();
      auto& local_inputs = paddle_tensor_feeds[tid];
      std::vector<PaddleTensor> local_outputs;
      ASSERT_TRUE(predictor->Run(local_inputs, &local_outputs));

      // check outputs correctness
      ASSERT_EQ(local_outputs.size(), 1UL);
255 256
      const size_t len = local_outputs[0].data.length();
      float* data = static_cast<float*>(local_outputs[0].data.data());
257
      float* ref_data = refs[tid].data<float>();
258
      EXPECT_EQ((size_t)refs[tid].numel(), len / sizeof(float));
259
      for (int i = 0; i < refs[tid].numel(); ++i) {
J
JiabinYang 已提交
260
        EXPECT_NEAR(ref_data[i], data[i], ACC_DIFF);
261
      }
T
tensor-tang 已提交
262 263 264 265 266 267 268
    });
  }
  for (int i = 0; i < num_jobs; ++i) {
    threads[i].join();
  }
}

T
tensor-tang 已提交
269 270 271 272 273
TEST(inference_api_native, word2vec_cpu) { MainWord2Vec(false /*use_gpu*/); }
TEST(inference_api_native, word2vec_cpu_threads) {
  MainThreadsWord2Vec(false /*use_gpu*/);
}
TEST(inference_api_native, image_classification_cpu) {
S
superjomn 已提交
274
  MainImageClassification(false /*use_gpu*/);
T
tensor-tang 已提交
275 276 277 278 279 280 281
}
TEST(inference_api_native, image_classification_cpu_threads) {
  MainThreadsImageClassification(false /*use_gpu*/);
}

#ifdef PADDLE_WITH_CUDA
TEST(inference_api_native, word2vec_gpu) { MainWord2Vec(true /*use_gpu*/); }
S
superjomn 已提交
282 283 284 285
// Turn off temporarily for the unstable result.
// TEST(inference_api_native, word2vec_gpu_threads) {
//   MainThreadsWord2Vec(true /*use_gpu*/);
// }
T
tensor-tang 已提交
286
TEST(inference_api_native, image_classification_gpu) {
S
superjomn 已提交
287
  MainImageClassification(true /*use_gpu*/);
T
tensor-tang 已提交
288
}
S
superjomn 已提交
289 290 291 292
// Turn off temporarily for the unstable result.
// TEST(inference_api_native, image_classification_gpu_threads) {
//   MainThreadsImageClassification(true /*use_gpu*/);
// }
T
tensor-tang 已提交
293 294 295

#endif

X
Xin Pan 已提交
296
}  // namespace paddle