未验证 提交 9b14117c 编写于 作者: Z zhupengyang 提交者: GitHub

logsumexp: impl kernel, refine docs (#26307)

上级 5c2b9258
// Copyright (c) 2020 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 "paddle/fluid/operators/reduce_ops/logsumexp_op.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace paddle {
namespace operators {
class LogsumexpOpMaker : public ops::ReduceOpMaker {
protected:
virtual std::string GetName() const { return "logsumexp"; }
virtual std::string GetOpType() const { return "Reduce logsumexp"; }
};
template <typename T>
class LogsumexpGradOpMaker : public framework::SingleGradOpMaker<T> {
public:
using framework::SingleGradOpMaker<T>::SingleGradOpMaker;
protected:
void Apply(GradOpPtr<T> op) const override {
op->SetType("logsumexp_grad");
op->SetInput("X", this->Input("X"));
op->SetInput("Out", this->Output("Out"));
op->SetInput(framework::GradVarName("Out"), this->OutputGrad("Out"));
op->SetAttrMap(this->Attrs());
op->SetOutput(framework::GradVarName("X"), this->InputGrad("X"));
}
};
} // namespace operators
} // namespace paddle
REGISTER_OPERATOR(logsumexp, ops::ReduceOp, ops::LogsumexpOpMaker,
ops::LogsumexpGradOpMaker<paddle::framework::OpDesc>,
ops::LogsumexpGradOpMaker<paddle::imperative::OpBase>);
REGISTER_OPERATOR(logsumexp_grad, ops::ReduceGradOp);
REGISTER_OP_CPU_KERNEL(logsumexp,
ops::ReduceKernel<paddle::platform::CPUDeviceContext,
float, ops::LogsumexpFunctor>,
ops::ReduceKernel<paddle::platform::CPUDeviceContext,
double, ops::LogsumexpFunctor>);
REGISTER_OP_CPU_KERNEL(
logsumexp_grad, ops::ReduceGradKernel<paddle::platform::CPUDeviceContext,
float, ops::LogsumexpGradFunctor>,
ops::ReduceGradKernel<paddle::platform::CPUDeviceContext, double,
ops::LogsumexpGradFunctor>);
// 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 "paddle/fluid/operators/reduce_ops/cub_reduce.h"
#include "paddle/fluid/operators/reduce_ops/logsumexp_op.h"
REGISTER_OP_CUDA_KERNEL(logsumexp,
ops::ReduceKernel<paddle::platform::CUDADeviceContext,
float, ops::LogsumexpFunctor>,
ops::ReduceKernel<paddle::platform::CUDADeviceContext,
double, ops::LogsumexpFunctor>);
REGISTER_OP_CUDA_KERNEL(
logsumexp_grad, ops::ReduceGradKernel<paddle::platform::CUDADeviceContext,
float, ops::LogsumexpGradFunctor>,
ops::ReduceGradKernel<paddle::platform::CUDADeviceContext, double,
ops::LogsumexpGradFunctor>);
// Copyright (c) 2020 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.
#pragma once
#include "paddle/fluid/operators/reduce_ops/reduce_op.h"
namespace paddle {
namespace operators {
struct LogsumexpFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
auto x_dim = x->dimensions();
auto t_dim = x_dim;
for (int i = 0; i < static_cast<int>(dim.size()); i++) {
t_dim[dim[i]] = 1;
}
auto r_dim = x_dim;
for (int i = 0; i < static_cast<int>(r_dim.size()); i++) {
r_dim[i] = 1;
}
for (int i = 0; i < static_cast<int>(dim.size()); i++) {
r_dim[dim[i]] = x_dim[dim[i]];
}
auto y_dim = y->dimensions();
auto x_max = x->maximum(dim);
y->device(place) =
(x_max +
(*x - x_max.reshape(t_dim).broadcast(r_dim)).exp().sum(dim).log())
.reshape(y_dim);
}
};
struct LogsumexpGradFunctor {
template <typename DeviceContext, typename X, typename Y, typename DX,
typename DY, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, DX* dx, DY* dy,
const Dim& dim, int size) {
dx->device(place) = dy->broadcast(dim) * (*x - y->broadcast(dim)).exp();
}
};
} // namespace operators
} // namespace paddle
......@@ -12,64 +12,128 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import paddle
import paddle.fluid as fluid
import unittest
import numpy as np
from op_test import OpTest
from paddle.fluid import Program, program_guard
from paddle.fluid.layer_helper import LayerHelper
class TestLogSumOpError(unittest.TestCase):
def ref_logsumexp(x, axis=None, keepdim=False, reduce_all=False):
if isinstance(axis, int):
axis = (axis, )
elif isinstance(axis, list):
axis = tuple(axis)
if reduce_all:
axis = None
out = np.log(np.exp(x).sum(axis=axis, keepdims=keepdim))
return out
class TestLogsumexp(OpTest):
def setUp(self):
self.op_type = 'logsumexp'
self.shape = [2, 3, 4, 5]
self.dtype = 'float64'
self.axis = [-1]
self.keepdim = False
self.reduce_all = False
self.set_attrs()
np.random.seed(10)
x = np.random.uniform(-1, 1, self.shape).astype(self.dtype)
out = ref_logsumexp(x, self.axis, self.keepdim, self.reduce_all)
self.inputs = {'X': x}
self.outputs = {'Out': out}
self.attrs = {
'dim': self.axis,
'keep_dim': self.keepdim,
'reduce_all': self.reduce_all
}
def set_attrs(self):
pass
def test_check_output(self):
self.check_output()
def test_check_grad(self):
self.check_grad(['X'], ['Out'])
class TestLogsumexp_shape(TestLogsumexp):
def set_attrs(self):
self.shape = [4, 5, 6]
class TestLogsumexp_axis(TestLogsumexp):
def set_attrs(self):
self.axis = [0, -1]
class TestLogsumexp_axis_all(TestLogsumexp):
def set_attrs(self):
self.axis = [0, 1, 2, 3]
class TestLogsumexp_keepdim(TestLogsumexp):
def set_attrs(self):
self.keepdim = True
class TestLogsumexp_reduce_all(TestLogsumexp):
def set_attrs(self):
self.reduce_all = True
class TestLogsumexpError(unittest.TestCase):
def test_errors(self):
with program_guard(Program(), Program()):
x1 = fluid.layers.data(name='x1', shape=[120], dtype="uint8")
self.assertRaises(Exception, paddle.logsumexp, x1)
x2 = fluid.layers.data(name='x2', shape=[2, 3], dtype="int")
self.assertRaises(Exception, paddle.logsumexp, x2)
x3 = fluid.layers.data(name='x3', shape=[3], dtype="float16")
self.assertRaises(Exception, paddle.logsumexp, x3)
self.assertRaises(AssertionError, paddle.logsumexp, None)
class TestLogSumExpOp(unittest.TestCase):
def test_dygraph(self):
with fluid.dygraph.guard():
np_x = np.random.uniform(0.1, 1, [123]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
self.assertTrue(
np.allclose(
paddle.logsumexp(x).numpy(), np.log(np.sum(np.exp(np_x)))))
np_x = np.random.uniform(0.1, 1, [2, 3, 4]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
self.assertTrue(
np.allclose(
paddle.logsumexp(
x, dim=[1, 2]).numpy(),
np.log(np.sum(np.exp(np_x), axis=(1, 2)))))
np_x = np.random.uniform(0.1, 1, [2, 3, 4]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
self.assertTrue(
np.allclose(
paddle.logsumexp(
x, dim=[2]).numpy(),
np.log(np.sum(np.exp(np_x), axis=(2)))))
np_x = np.random.uniform(0.1, 1, [2, 3, 4]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
self.assertTrue(
np.allclose(
paddle.logsumexp(
x, keepdim=True).numpy(),
np.log(np.sum(np.exp(np_x), keepdims=True))))
with paddle.static.program_guard(paddle.static.Program()):
self.assertRaises(TypeError, paddle.logsumexp, 1)
x1 = paddle.data(name='x1', shape=[120], dtype="int32")
self.assertRaises(TypeError, paddle.logsumexp, x1)
class TestLogsumexpAPI(unittest.TestCase):
def setUp(self):
self.shape = [2, 3, 4, 5]
self.x = np.random.uniform(-1, 1, self.shape).astype(np.float32)
self.place = paddle.CUDAPlace(0) if paddle.fluid.core.is_compiled_with_cuda() \
else paddle.CPUPlace()
def api_case(self, axis=None, keepdim=False):
out_ref = ref_logsumexp(self.x, axis, keepdim)
with paddle.static.program_guard(paddle.static.Program()):
x = paddle.data('X', self.shape)
out = paddle.logsumexp(x, axis, keepdim)
exe = paddle.static.Executor(self.place)
res = exe.run(feed={'X': self.x}, fetch_list=[out])
self.assertTrue(np.allclose(res[0], out_ref))
paddle.disable_static(self.place)
x = paddle.to_variable(self.x)
out = paddle.logsumexp(x, axis, keepdim)
self.assertTrue(np.allclose(out.numpy(), out_ref))
paddle.enable_static()
def test_api(self):
self.api_case()
self.api_case(2)
self.api_case([-1])
self.api_case([2, -3])
self.api_case((0, 1, -1))
self.api_case(keepdim=True)
def test_alias(self):
paddle.disable_static(self.place)
x = paddle.to_variable(self.x)
out1 = paddle.logsumexp(x)
out2 = paddle.tensor.logsumexp(x)
out3 = paddle.tensor.math.logsumexp(x)
out_ref = ref_logsumexp(self.x)
for out in [out1, out2, out3]:
self.assertTrue(np.allclose(out.numpy(), out_ref))
paddle.enable_static()
if __name__ == '__main__':
......
......@@ -82,6 +82,7 @@ __all__ = [
'floor',
'increment',
'log',
'logsumexp',
'mul',
'multiplex',
'prod',
......@@ -964,69 +965,73 @@ def addmm(input, x, y, beta=1.0, alpha=1.0, name=None):
return out
def logsumexp(x, dim=None, keepdim=False, name=None):
def logsumexp(x, axis=None, keepdim=False, name=None):
"""
:alias_main: paddle.logsumexp
:alias: paddle.logsumexp,paddle.tensor.logsumexp,paddle.tensor.math.logsumexp
This operator calculates the log of the sum of exponentials of the input Tensor.
This OP calculates the log of the sum of exponentials of ``x`` along ``axis`` .
.. math::
logsumexp(x) = \log\sum exp(x)
Parameters:
x (Variable): Input LoDTensor or Tensor. Must be one of the following types: float32, float64.
dim (list|int, optional): The dimensions along which the sum is performed. If :attr:`None`,
sum all elements of :attr:`input` and return a Tensor variable with a single element,
otherwise must be in the range :math:`[-rank(input), rank(input))`. If :math:`dim[i] < 0`,
the dimension to reduce is :math:`rank + dim[i]`.
keep_dim (bool, optional): Whether to reserve the reduced dimension in the output Tensor.
The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim`
is true, default value is False.
name (str, optional): The default value is None. Normally there is no need for user to
set this property. For more information, please refer to :ref:`api_guide_Name`
Args:
x (Tensor): The input Tensor with data type float32, float64.
axis (int|list|tuple, optional): The axis along which to perform
logsumexp calculations. ``axis`` should be int, list(int) or
tuple(int). If ``axis`` is a list/tuple of dimension(s), logsumexp
is calculated along all element(s) of ``axis`` . ``axis`` or
element(s) of ``axis`` should be in range [-D, D), where D is the
dimensions of ``x`` . If ``axis`` or element(s) of ``axis`` is
less than 0, it works the same way as :math:`axis + D` . If
``axis`` is None, logsumexp is calculated along all elements of
``x``. Default is None.
keepdim (bool, optional): Whether to reserve the reduced dimension(s)
in the output Tensor. If ``keep_dim`` is True, the dimensions of
the output Tensor is the same as ``x`` except in the reduced
dimensions(it is of size 1 in this case). Otherwise, the shape of
the output Tensor is squeezed in ``axis`` . Default is False.
name (str, optional): Name for the operation (optional, default is None).
For more information, please refer to :ref:`api_guide_Name`.
Returns:
Variable: The calcuated result Tensor/LoDTensor.
Tensor, results of logsumexp along ``axis`` of ``x``, with the same data
type as ``x``.
Examples:
.. code-block:: python
import paddle
import paddle.fluid as fluid
import numpy as np
with fluid.dygraph.guard():
np_x = np.random.uniform(0.1, 1, [10]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
print(paddle.logsumexp(x).numpy())
.. code-block:: python
import paddle
import paddle.fluid as fluid
import numpy as np
paddle.disable_static()
with fluid.dygraph.guard():
np_x = np.random.uniform(0.1, 1, [2, 3, 4]).astype(np.float32)
x = fluid.dygraph.to_variable(np_x)
print(paddle.logsumexp(x, dim=1).numpy())
print(paddle.logsumexp(x, dim=[0, 2]).numpy())
x = np.array([[-1.5, 0., 2.], [3., 1.2, -2.4]])
x = paddle.to_tensor(x)
out1 = paddle.logsumexp(x) # [3.4691226]
out2 = paddle.logsumexp(x, 1) # [2.15317821, 3.15684602]
"""
op_type = 'logsumexp'
assert x is not None, 'x cannot be None in {}'.format(op_type)
# reduce_sum does not support float16
check_variable_and_dtype(x, 'x', ['float32', 'float64'], op_type)
if isinstance(axis, int):
axis = [axis]
reduce_all = True if axis is None \
or len(axis)==0 \
or len(axis) == len(x.shape) else False
if axis is None or len(axis) == 0:
axis = [0]
exp_out = layers.exp(x)
sum_out = layers.reduce_sum(exp_out, dim, keepdim)
if in_dygraph_mode():
return core.ops.logsumexp(x, 'dim', axis, 'keep_dim', keepdim,
'reduce_all', reduce_all)
return layers.log(sum_out, name)
check_variable_and_dtype(x, 'x',
['float32', 'float64'],
'logsumexp')
helper = LayerHelper('logsumexp', **locals())
attrs = {'dim': axis, 'keep_dim': keepdim, 'reduce_all': reduce_all}
out = helper.create_variable_for_type_inference(x.dtype)
helper.append_op(
type='logsumexp', inputs={'X': x}, outputs={'Out': out}, attrs=attrs)
return out
def inverse(x, name=None):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册