| #include <ATen/ATen.h> |
| #include <ATen/Dispatch.h> |
| #include <ATen/NamedTensorUtils.h> |
| |
| namespace at { namespace native { |
| |
| namespace { |
| |
| template<bool inplace> |
| using Ctype = typename std::conditional<inplace, Tensor&, Tensor>::type; |
| |
| Tensor make_feature_noise(const Tensor& input) { |
| auto input_sizes = input.sizes(); |
| TORCH_CHECK(input.dim() >= 2, "Feature dropout requires at least 2 dimensions in the input"); |
| std::vector<int64_t> sizes; |
| sizes.reserve(input.dim()); |
| sizes.push_back(input_sizes[0]); |
| sizes.push_back(input_sizes[1]); |
| for (int64_t i = 2; i < input.dim(); ++i) |
| sizes.push_back(1); |
| return at::empty(sizes, input.options()); |
| } |
| |
| bool is_fused_kernel_acceptable(const Tensor& input, double p) { |
| return input.is_cuda() && p > 0 && p < 1 && input.numel() > 0; |
| } |
| |
| // NB: sure, we could have used different overloads here, but I would feel insecure |
| // knowing that this dispatch depends only on the constness of the references |
| template<bool inplace> |
| Tensor& multiply(Tensor& input, const Tensor& noise) { |
| static_assert(inplace, "Wrong multiply overload triggered in Dropout.cpp"); |
| return input.mul_(noise); |
| } |
| |
| template<bool inplace> |
| Tensor multiply(const Tensor& input, const Tensor& noise) { |
| static_assert(!inplace, "Wrong multiply overload triggered in Dropout.cpp"); |
| return input.mul(noise); |
| } |
| |
| template<bool feature_dropout, bool alpha_dropout, bool inplace, typename T> |
| Ctype<inplace> _dropout_impl(T& input, double p, bool train) { |
| TORCH_CHECK(p >= 0 && p <= 1, "dropout probability has to be between 0 and 1, but got ", p); |
| if (p == 0 || !train || input.numel() == 0) { |
| return input; |
| } |
| |
| if (p == 1) { |
| return multiply<inplace>(input, at::zeros({}, input.options())); |
| } |
| |
| at::Tensor b; // used for alpha_dropout only |
| auto noise = feature_dropout ? make_feature_noise(input) : at::empty_like(input, LEGACY_CONTIGUOUS_MEMORY_FORMAT); |
| noise.bernoulli_(1 - p); |
| if (alpha_dropout) { |
| constexpr double alpha = 1.7580993408473766; |
| double a = 1. / std::sqrt((alpha * alpha * p + 1) * (1 - p)); |
| b = noise.add(-1).mul_(alpha * a).add_(alpha * a * p); |
| noise.mul_(a); |
| } else { |
| noise.div_(1 - p); |
| } |
| |
| if (!alpha_dropout) { |
| return multiply<inplace>(input, noise); |
| } else { |
| return multiply<inplace>(input, noise).add_(b); |
| } |
| } |
| |
| #define ALIAS_SPECIALIZATION(ALIAS_NAME, IS_FEATURE, IS_ALPHA) \ |
| template <bool inplace, typename... Args> \ |
| Ctype<inplace> ALIAS_NAME(Args&&... args) { \ |
| return _dropout_impl<IS_FEATURE, IS_ALPHA, inplace>(std::forward<Args>(args)...); \ |
| } |
| |
| ALIAS_SPECIALIZATION(_dropout, false, false) |
| ALIAS_SPECIALIZATION(_feature_dropout, true, false) |
| ALIAS_SPECIALIZATION(_alpha_dropout, false, true ) |
| ALIAS_SPECIALIZATION(_feature_alpha_dropout, true, true ) |
| |
| } // anomymous namepsace |
| |
| Tensor dropout(const Tensor& input, double p, bool train) { |
| auto result = [&]() { |
| NoNamesGuard guard; |
| if (train && is_fused_kernel_acceptable(input, p)) { |
| return std::get<0>(at::_fused_dropout(input, 1 - p)); |
| } |
| return _dropout<false>(input, p, train); |
| }(); |
| namedinference::propagate_names(result, input); |
| return result; |
| } |
| |
| Tensor& dropout_(Tensor& input, double p, bool train) { |
| return _dropout<true>(input, p, train); |
| } |
| |
| Tensor feature_dropout(const Tensor& input, double p, bool train) { |
| return _feature_dropout<false>(input, p, train); |
| } |
| |
| Tensor& feature_dropout_(Tensor& input, double p, bool train) { |
| return _feature_dropout<true>(input, p, train); |
| } |
| |
| Tensor alpha_dropout(const Tensor& input, double p, bool train) { |
| return _alpha_dropout<false>(input, p, train); |
| } |
| |
| Tensor& alpha_dropout_(Tensor& input, double p, bool train) { |
| return _alpha_dropout<true>(input, p, train); |
| } |
| |
| Tensor feature_alpha_dropout(const Tensor& input, double p, bool train) { |
| return _feature_alpha_dropout<false>(input, p, train); |
| } |
| |
| Tensor& feature_alpha_dropout_(Tensor& input, double p, bool train) { |
| return _feature_alpha_dropout<true>(input, p, train); |
| } |
| |
| }} // namespace at::native |