uniform_random_op.cc 12.2 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Y
Yu Yang 已提交
2

L
Luo Tao 已提交
3 4 5 6 7 8 9 10 11 12 13
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
#include "paddle/fluid/operators/uniform_random_op.h"
L
Leo Chen 已提交
15

16
#include <string>
L
Leo Chen 已提交
17

Y
yaoxuefeng 已提交
18
#include "paddle/fluid/framework/generator.h"
Y
Yi Wang 已提交
19 20
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
21
#include "paddle/fluid/platform/bfloat16.h"
Y
yaoxuefeng 已提交
22

Y
Yu Yang 已提交
23 24
namespace paddle {
namespace operators {
Y
Yu Yang 已提交
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
namespace {
template <typename T>
inline void UniformRealDistribution(T *data, const int64_t &size,
                                    const float &min, const float &max,
                                    const unsigned int &seed) {
  VLOG(4) << "[CPU] UniformRandomKernel<T>";
  std::uniform_real_distribution<T> dist(static_cast<T>(min),
                                         static_cast<T>(max));
  auto engine = paddle::framework::GetCPURandomEngine(seed);

  for (int64_t i = 0; i < size; ++i) {
    data[i] = dist(*engine);
  }
}

template <>
inline void UniformRealDistribution(paddle::platform::bfloat16 *data,
                                    const int64_t &size, const float &min,
                                    const float &max,
                                    const unsigned int &seed) {
  VLOG(4) << "[CPU] UniformRandomKernel<bfloat16>";
  std::uniform_real_distribution<float> dist(min, max);
  auto engine = paddle::framework::GetCPURandomEngine(seed);

  for (int64_t i = 0; i < size; ++i) {
    data[i] = static_cast<paddle::platform::bfloat16>(dist(*engine));
  }
}
}  // namespace

Q
qijun 已提交
56 57 58 59
// It seems that Eigen::Tensor::random in GPU will SEGFAULT.
// Use std::random and thrust::random(thrust is a std library in CUDA) to
// implement uniform random.
template <typename T>
Y
Yu Yang 已提交
60
class CPUUniformRandomKernel : public framework::OpKernel<T> {
Q
qijun 已提交
61
 public:
C
chengduo 已提交
62 63
  void Compute(const framework::ExecutionContext &ctx) const override {
    framework::Tensor *tensor = nullptr;
Y
Yancey1989 已提交
64
    auto out_var = ctx.OutputVar("Out");
65 66 67 68 69 70
    std::vector<int64_t> new_shape;
    auto list_new_shape_tensor =
        ctx.MultiInput<framework::Tensor>("ShapeTensorList");
    if (list_new_shape_tensor.size() > 0 || ctx.HasInput("ShapeTensor")) {
      if (ctx.HasInput("ShapeTensor")) {
        auto *shape_tensor = ctx.Input<framework::Tensor>("ShapeTensor");
71
        new_shape = GetNewDataFromShapeTensor(shape_tensor);
72
      } else if (list_new_shape_tensor.size() > 0) {
73
        new_shape = GetNewDataFromShapeTensorList(list_new_shape_tensor);
74 75 76 77
      }
    }

    if (out_var->IsType<framework::SelectedRows>()) {
C
chengduo 已提交
78
      auto *selected_rows = out_var->GetMutable<framework::SelectedRows>();
79
      tensor = selected_rows->mutable_value();
80 81
      auto shape = ctx.Attr<std::vector<int64_t>>("shape");
      if (!new_shape.empty()) shape = new_shape;
Y
Yancey1989 已提交
82
      tensor->Resize(framework::make_ddim(shape));
83
      selected_rows->mutable_rows()->reserve(shape[0]);
84 85 86
    } else if (out_var->IsType<framework::LoDTensor>()) {
      tensor = out_var->GetMutable<framework::LoDTensor>();
      if (!new_shape.empty()) tensor->Resize(framework::make_ddim(new_shape));
Y
Yancey1989 已提交
87
    } else {
88 89 90 91 92
      PADDLE_THROW(platform::errors::InvalidArgument(
          "Expected type of Output(out) in uniform_random_op must be Tensor, "
          "SelectedRows. But got "
          "unsupport type: %s.",
          framework::ToTypeName(out_var->Type())));
Y
Yancey1989 已提交
93
    }
C
chengduo 已提交
94
    T *data = tensor->mutable_data<T>(ctx.GetPlace());
Y
yaoxuefeng 已提交
95
    int64_t size = tensor->numel();
L
Leo Chen 已提交
96

97 98 99
    UniformRealDistribution<T>(
        data, size, ctx.Attr<float>("min"), ctx.Attr<float>("max"),
        static_cast<unsigned int>(ctx.Attr<int>("seed")));
Y
yaoxuefeng 已提交
100

101 102 103 104 105 106
    unsigned int diag_num =
        static_cast<unsigned int>(ctx.Attr<int>("diag_num"));
    unsigned int diag_step =
        static_cast<unsigned int>(ctx.Attr<int>("diag_step"));
    auto diag_val = static_cast<T>(ctx.Attr<float>("diag_val"));
    if (diag_num > 0) {
107 108 109 110 111 112 113
      PADDLE_ENFORCE_GT(
          size, (diag_num - 1) * (diag_step + 1),
          platform::errors::InvalidArgument(
              "ShapeInvalid: the diagonal's elements is equal (num-1) "
              "* (step-1) with num %d, step %d,"
              "It should be smaller than %d, but received %d",
              diag_num, diag_step, (diag_num - 1) * (diag_step + 1), size));
114 115 116 117 118
      for (int64_t i = 0; i < diag_num; ++i) {
        int64_t pos = i * diag_step + i;
        data[pos] = diag_val;
      }
    }
Q
qijun 已提交
119 120 121
  }
};

Y
Yu Yang 已提交
122
class UniformRandomOp : public framework::OperatorWithKernel {
Y
Yu Yang 已提交
123 124 125
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

C
chengduo 已提交
126
  void InferShape(framework::InferShapeContext *ctx) const override {
127 128 129 130 131 132 133 134
    OP_INOUT_CHECK(ctx->HasOutput("Out"), "Output", "Out", "UniformRandomOp");

    PADDLE_ENFORCE_LT(
        ctx->Attrs().Get<float>("min"), ctx->Attrs().Get<float>("max"),
        platform::errors::InvalidArgument(
            "The uniform_random's min must less then max. But received min = "
            "%f great than or equal max = %f.",
            ctx->Attrs().Get<float>("min"), ctx->Attrs().Get<float>("max")));
135
    PADDLE_ENFORCE_GE(ctx->Attrs().Get<int>("diag_num"), 0,
136
                      platform::errors::InvalidArgument(
137 138 139
                          "The uniform_random's diag_num must greater than or "
                          "equal 0. But recevied diag_num (%d) < 0.",
                          ctx->Attrs().Get<int>("diag_num")));
140
    PADDLE_ENFORCE_GE(ctx->Attrs().Get<int>("diag_step"), 0,
141
                      platform::errors::InvalidArgument(
142 143 144
                          "The uniform_random's diag_step must greater than or "
                          "equal 0. But recevied diag_step (%d) < 0.",
                          ctx->Attrs().Get<int>("diag_step")));
145 146 147 148

    if (ctx->HasInputs("ShapeTensorList")) {
      // top prority shape
      auto inputs_name = ctx->Inputs("ShapeTensorList");
L
Leo Chen 已提交
149 150 151 152 153 154
      PADDLE_ENFORCE_GT(inputs_name.size(), 0,
                        platform::errors::InvalidArgument(
                            "Input(ShapeTensorList)'size of "
                            "Op(uniform_random) can't be zero."
                            "Please check the Attr(shape)'s size of"
                            "Op(fluid.layers.uniform_random).)"));
155 156 157 158 159 160 161 162 163 164
      auto out_dims = std::vector<int>(inputs_name.size(), -1);
      ctx->SetOutputDim("Out", framework::make_ddim(out_dims));

      return;
    }
    auto &shape = ctx->Attrs().Get<std::vector<int64_t>>("shape");
    if (ctx->HasInput("ShapeTensor") && shape.empty()) {
      auto shape_dims = ctx->GetInputDim("ShapeTensor");
      PADDLE_ENFORCE_EQ(
          shape_dims.size(), 1,
165 166 167 168 169
          platform::errors::InvalidArgument(
              "ShapeError: Input(ShapeTensor)' dimension size of "
              "Op(uniform_random) must be 1."
              "But received ShapeTensor's dimensions = %d, shape = [%s]",
              shape_dims.size(), shape_dims));
170 171 172 173 174 175 176 177 178 179
      int num_ele = 1;
      for (int i = 0; i < shape_dims.size(); ++i) {
        num_ele *= shape_dims[i];
      }
      auto vec_dims = std::vector<int64_t>(num_ele, -1);
      auto out_dims = framework::make_ddim(vec_dims);
      ctx->SetOutputDim("Out", out_dims);
      return;
    }

180 181 182 183 184 185
    PADDLE_ENFORCE_EQ(shape.empty(), false,
                      platform::errors::InvalidArgument(
                          "if there is no Input(ShapeTensorList) and no "
                          "Input(ShapeTensor),the "
                          "attr(shape) information must "
                          "be set by Attr(shape)."));
186 187
    std::vector<int64_t> tensor_shape;
    tensor_shape.reserve(shape.size());
Q
QI JUN 已提交
188
    for (auto dim : shape) {
189
      tensor_shape.push_back(static_cast<int64_t>(dim));
Q
qijun 已提交
190
    }
191
    ctx->SetOutputDim("Out", framework::make_ddim(tensor_shape));
Y
Yu Yang 已提交
192
  }
Y
Yu Yang 已提交
193

194
 protected:
195
  framework::OpKernelType GetExpectedKernelType(
C
chengduo 已提交
196
      const framework::ExecutionContext &ctx) const override {
Y
Yu Yang 已提交
197
    return framework::OpKernelType(
198
        static_cast<framework::proto::VarType::Type>(ctx.Attr<int>("dtype")),
Q
QI JUN 已提交
199
        ctx.GetPlace());
Y
Yu Yang 已提交
200
  }
201 202 203 204 205 206 207 208 209 210

  framework::OpKernelType GetKernelTypeForVar(
      const std::string &var_name, const Tensor &tensor,
      const framework::OpKernelType &expected_kernel_type) const override {
    if (var_name == "ShapeTensorList" || var_name == "ShapeTensor") {
      return expected_kernel_type;
    }
    return framework::OpKernelType(expected_kernel_type.data_type_,
                                   tensor.place(), tensor.layout());
  }
Y
Yu Yang 已提交
211 212
};

Y
Yu Yang 已提交
213
class UniformRandomOpMaker : public framework::OpProtoAndCheckerMaker {
Y
Yu Yang 已提交
214
 public:
Y
Yu Yang 已提交
215
  void Make() override {
216
    AddInput("ShapeTensor",
217 218
             "(Tensor<int64_t> or Tensor<int32_t>, optional) . If provided, "
             "uniform_random "
219
             "according to "
220
             "this given shape. It means that it has a higher priority than "
221
             "the shape attribute, while the shape attribute still should be "
T
tianshuo78520a 已提交
222
             "set correctly to guarantee shape inference in compile time.")
223 224
        .AsDispensable();
    AddInput("ShapeTensorList",
225 226 227 228
             "(vector<Tensor<int64_t>> or vector<Tensor<int32_t>>, optional). "
             "If provided, uniform_random use this. The shape of the tensor "
             "must be [1], it has the highest priority comparing with "
             "Input(ShapeTensor) and attr(shape).")
229 230
        .AsDuplicable()
        .AsDispensable();
Y
yuyang18 已提交
231
    AddOutput("Out", "The output tensor of uniform random op");
232
    AddComment(R"DOC(
233
This operator initializes a tensor with random values sampled from a
234
uniform distribution. The random result is in set [min, max).
235

Y
Yu Yang 已提交
236
)DOC");
237 238
    AddAttr<std::vector<int64_t>>("shape", "The shape of the output tensor")
        .SetDefault({});
Y
yuyang18 已提交
239
    AddAttr<float>("min", "Minimum value of uniform random. [default -1.0].")
240
        .SetDefault(-1.0f);
Y
yuyang18 已提交
241
    AddAttr<float>("max", "Maximun value of uniform random. [default 1.0].")
242
        .SetDefault(1.0f);
Q
qijun 已提交
243
    AddAttr<int>("seed",
244
                 "Random seed used for generating samples. "
245 246
                 "0 means use a seed generated by the system."
                 "Note that if seed is not 0, this operator will always "
Y
yuyang18 已提交
247
                 "generate the same random numbers every time. [default 0].")
Q
qijun 已提交
248
        .SetDefault(0);
249 250 251 252 253 254 255 256
    AddAttr<int>("diag_num",
                 "The number of diag elements. Note that if "
                 "diag_num is 0, it means without diag init.[default 0].")
        .SetDefault(0);
    AddAttr<int>("diag_step", "The step between two diag element.[default 0].")
        .SetDefault(0);
    AddAttr<float>("diag_val", "The value of diag element. [default 1.0].")
        .SetDefault(1.0f);
Y
yuyang18 已提交
257
    AddAttr<int>("dtype", "Output tensor data type. [default 5(FP32)].")
258
        .SetDefault(framework::proto::VarType::FP32);
Y
Yu Yang 已提交
259 260
  }
};
Y
Yancey1989 已提交
261 262 263

class UniformRandomOpVarTypeInference : public framework::VarTypeInference {
 public:
M
minqiyang 已提交
264
  void operator()(framework::InferVarTypeContext *ctx) const override {
C
chengduo 已提交
265
    auto var_data_type = static_cast<framework::proto::VarType::Type>(
266
        BOOST_GET_CONST(int, ctx->GetAttr("dtype")));
C
chengduo 已提交
267

268 269
    if (ctx->GetOutputType("Out") != framework::proto::VarType::SELECTED_ROWS) {
      ctx->SetOutputType("Out", framework::proto::VarType::LOD_TENSOR);
Y
Yancey1989 已提交
270
    }
271
    ctx->SetOutputDataType("Out", var_data_type);
Y
Yancey1989 已提交
272 273 274
  }
};

Y
Yu Yang 已提交
275 276 277
}  // namespace operators
}  // namespace paddle

H
hong 已提交
278 279 280 281 282 283
REGISTER_OPERATOR(
    uniform_random, paddle::operators::UniformRandomOp,
    paddle::operators::UniformRandomOpMaker,
    paddle::framework::EmptyGradOpMaker<paddle::framework::OpDesc>,
    paddle::framework::EmptyGradOpMaker<paddle::imperative::OpBase>,
    paddle::operators::UniformRandomOpVarTypeInference);
Y
Yancey1989 已提交
284

285 286 287 288 289 290 291 292 293
REGISTER_OP_CPU_KERNEL(
    uniform_random, paddle::operators::CPUUniformRandomKernel<float>,
    paddle::operators::CPUUniformRandomKernel<double>,
    paddle::operators::CPUUniformRandomKernel<paddle::platform::bfloat16>);
REGISTER_OP_CPU_KERNEL(
    uniform_random_batch_size_like,
    paddle::operators::CPUUniformRandomKernel<float>,
    paddle::operators::CPUUniformRandomKernel<double>,
    paddle::operators::CPUUniformRandomKernel<paddle::platform::bfloat16>);