chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
file(
|
||||
GLOB func_cc_srcs
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"*.cc")
|
||||
list(FILTER func_cc_srcs EXCLUDE REGEX ".*_xpu\\.cc$")
|
||||
|
||||
if(WITH_GPU OR WITH_ROCM)
|
||||
file(
|
||||
GLOB func_cu_srcs
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"*.cu")
|
||||
endif()
|
||||
|
||||
if(WITH_XPU)
|
||||
file(
|
||||
GLOB func_xpu_srcs
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"*_xpu.cc")
|
||||
endif()
|
||||
|
||||
collect_srcs(kernels_srcs SRCS ${func_cc_srcs} ${func_cu_srcs} ${func_xpu_srcs})
|
||||
@@ -0,0 +1,311 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/beam_search.h"
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/backends/cpu/cpu_context.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template <typename T>
|
||||
class BeamSearchFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext &dev_ctx UNUSED,
|
||||
const DenseTensor *pre_ids,
|
||||
const DenseTensor *pre_scores,
|
||||
const DenseTensor *ids,
|
||||
const DenseTensor *scores,
|
||||
DenseTensor *selected_ids,
|
||||
DenseTensor *selected_scores,
|
||||
DenseTensor *parent_idx,
|
||||
size_t level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated) {
|
||||
auto abs_lod = ToAbsOffset(scores->lod());
|
||||
auto &high_level = abs_lod[level];
|
||||
|
||||
auto items = SelectTopBeamSizeItems(pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
level,
|
||||
beam_size,
|
||||
end_id,
|
||||
is_accumulated);
|
||||
auto selected_items = ToMap(items, high_level.back());
|
||||
if (FLAGS_v == 3) {
|
||||
VLOG(3) << "selected_items:";
|
||||
for (size_t i = 0; i < selected_items.size(); ++i) {
|
||||
VLOG(3) << "offset: " << i;
|
||||
for (auto &item : selected_items[i]) {
|
||||
VLOG(3) << item.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PruneEndBeams(pre_ids, abs_lod, &selected_items, level, end_id);
|
||||
// calculate the output tensor's height
|
||||
size_t num_instances = std::accumulate(
|
||||
std::begin(selected_items),
|
||||
std::end(selected_items),
|
||||
0,
|
||||
[](size_t a, std::vector<Item> &b) { return a + b.size(); });
|
||||
// the output tensor shape should be [num_instances, 1]
|
||||
auto dims =
|
||||
make_ddim(std::vector<int64_t>({static_cast<int>(num_instances), 1}));
|
||||
selected_ids->Resize(dims);
|
||||
auto *selected_ids_data = dev_ctx.template Alloc<int64_t>(selected_ids);
|
||||
selected_scores->Resize(dims);
|
||||
auto *selected_scores_data = dev_ctx.template Alloc<float>(selected_scores);
|
||||
if (parent_idx != nullptr) {
|
||||
parent_idx->Resize({static_cast<int64_t>(num_instances)});
|
||||
}
|
||||
auto *parent_idx_data =
|
||||
parent_idx ? dev_ctx.template Alloc<int>(parent_idx) : nullptr;
|
||||
|
||||
// fill in data
|
||||
std::vector<size_t> low_level;
|
||||
size_t low_offset = 0;
|
||||
for (auto &items : selected_items) {
|
||||
low_level.push_back(low_offset);
|
||||
for (auto &item : items) {
|
||||
if (parent_idx) {
|
||||
parent_idx_data[low_offset] = static_cast<int>(low_level.size() - 1);
|
||||
}
|
||||
selected_ids_data[low_offset] = item.id;
|
||||
selected_scores_data[low_offset] = item.score;
|
||||
low_offset++;
|
||||
}
|
||||
}
|
||||
low_level.push_back(low_offset);
|
||||
|
||||
// fill lod
|
||||
LegacyLoD lod(2);
|
||||
lod[0].assign(high_level.begin(), high_level.end());
|
||||
lod[1].assign(low_level.begin(), low_level.end());
|
||||
if (!CheckLegacyLoD(lod)) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"lod %s is not right in"
|
||||
" beam_search, please check your code.",
|
||||
LoDToString(lod)));
|
||||
}
|
||||
selected_ids->set_lod(lod);
|
||||
selected_scores->set_lod(lod);
|
||||
}
|
||||
|
||||
/*
|
||||
* The basic items help to sort.
|
||||
*/
|
||||
struct Item {
|
||||
Item() = default;
|
||||
Item(size_t offset, size_t id, float score)
|
||||
: offset(offset), id(id), score(score) {}
|
||||
// offset in the higher lod level.
|
||||
size_t offset;
|
||||
// prefix id in the lower lod level.
|
||||
// size_t prefix;
|
||||
// the candidate id
|
||||
size_t id;
|
||||
// the corresponding score
|
||||
float score;
|
||||
|
||||
inline bool operator<(const Item &in) const {
|
||||
return (score < in.score) ||
|
||||
((score == in.score) && (offset < in.offset));
|
||||
}
|
||||
|
||||
inline Item &operator=(const Item &in) {
|
||||
if (this != &in) {
|
||||
this->offset = in.offset;
|
||||
this->id = in.id;
|
||||
this->score = in.score;
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string ToString() {
|
||||
std::ostringstream os;
|
||||
os << "{";
|
||||
os << "offset: " << offset << ", ";
|
||||
os << "id: " << id << ", ";
|
||||
os << "score: " << score << "";
|
||||
os << "}";
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Prune the source sentences all branches finished, and it is optional.
|
||||
* Pruning must one step later than finishing (thus pre_ids is needed here),
|
||||
* since the end tokens must be written out.
|
||||
*/
|
||||
void PruneEndBeams(const DenseTensor *pre_ids,
|
||||
const LegacyLoD &abs_lod,
|
||||
std::vector<std::vector<Item>> *items,
|
||||
size_t lod_level,
|
||||
int end_id) {
|
||||
auto *pre_ids_data = pre_ids->data<int64_t>();
|
||||
auto &high_level = abs_lod[lod_level];
|
||||
for (size_t src_idx = 0; src_idx < high_level.size() - 1; ++src_idx) {
|
||||
size_t src_prefix_start = high_level[src_idx];
|
||||
size_t src_prefix_end = high_level[src_idx + 1];
|
||||
bool finish_flag = true;
|
||||
for (size_t offset = src_prefix_start; offset < src_prefix_end;
|
||||
offset++) {
|
||||
for (auto &item : items->at(offset)) {
|
||||
if (item.id != static_cast<size_t>(end_id) ||
|
||||
pre_ids_data[offset] != end_id) {
|
||||
finish_flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!finish_flag) break;
|
||||
}
|
||||
if (finish_flag) { // all branches of the beam (source sentence) end and
|
||||
// prune this beam
|
||||
for (size_t offset = src_prefix_start; offset < src_prefix_end;
|
||||
offset++)
|
||||
items->at(offset).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform the items into a map whose key is offset, value is the items.
|
||||
* NOTE low performance.
|
||||
*/
|
||||
std::vector<std::vector<Item>> ToMap(
|
||||
const std::vector<std::vector<Item>> &items, size_t element_num) {
|
||||
std::vector<std::vector<Item>> result;
|
||||
result.resize(element_num);
|
||||
for (auto &entries : items) {
|
||||
for (const auto &item : entries) {
|
||||
result[item.offset].push_back(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Insert(std::vector<Item> *top_beam_ptr,
|
||||
const Item &item,
|
||||
size_t beam_size) {
|
||||
std::vector<Item> &top_beam = *top_beam_ptr;
|
||||
|
||||
size_t num_beams = top_beam.size();
|
||||
if (num_beams < beam_size) {
|
||||
top_beam.resize(num_beams + 1);
|
||||
num_beams++;
|
||||
} else {
|
||||
if (item < top_beam[beam_size - 1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = static_cast<int>(num_beams) - 2; k >= 0; --k) {
|
||||
if (top_beam[k] < item) {
|
||||
top_beam[k + 1] = top_beam[k];
|
||||
} else {
|
||||
top_beam[k + 1] = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
top_beam[0] = item;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each source, select top beam_size records.
|
||||
*/
|
||||
std::vector<std::vector<Item>> SelectTopBeamSizeItems(
|
||||
const DenseTensor *pre_ids,
|
||||
const DenseTensor *pre_scores,
|
||||
const DenseTensor *ids,
|
||||
const DenseTensor *scores,
|
||||
size_t lod_level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated) {
|
||||
std::vector<std::vector<Item>> result;
|
||||
|
||||
// find the current candidates
|
||||
auto abs_lod = ToAbsOffset(scores->lod());
|
||||
|
||||
auto *pre_ids_data = pre_ids->data<int64_t>();
|
||||
auto *pre_scores_data = pre_scores->data<float>();
|
||||
|
||||
auto *ids_data = ids ? ids->data<int64_t>() : nullptr;
|
||||
auto *scores_data = scores->data<float>();
|
||||
|
||||
size_t num_seqs = scores->NumElements(lod_level);
|
||||
size_t seq_width = 1;
|
||||
for (int i = 1; i < scores->dims().size(); i++) {
|
||||
seq_width *= scores->dims()[i];
|
||||
}
|
||||
|
||||
for (size_t seq_id = 0; seq_id < num_seqs; ++seq_id) {
|
||||
size_t seq_offset_start = abs_lod[lod_level][seq_id];
|
||||
size_t seq_offset_end = abs_lod[lod_level][seq_id + 1];
|
||||
|
||||
std::vector<Item> top_beam;
|
||||
top_beam.reserve(beam_size);
|
||||
|
||||
for (size_t offset = seq_offset_start; offset < seq_offset_end;
|
||||
++offset) {
|
||||
auto pre_id = pre_ids_data[offset];
|
||||
auto pre_score = pre_scores_data[offset];
|
||||
if (pre_id == end_id) {
|
||||
// Allocate all probability mass to end_id for finished branches and
|
||||
// the other candidate ids can be ignored.
|
||||
Item item(offset, end_id, pre_score);
|
||||
Insert(&top_beam, item, beam_size);
|
||||
} else {
|
||||
size_t index = offset * seq_width;
|
||||
for (size_t d = 0; d < seq_width; d++, index++) {
|
||||
int64_t id = ids_data ? ids_data[index] : static_cast<int64_t>(d);
|
||||
float score = is_accumulated
|
||||
? scores_data[index]
|
||||
: pre_score + std::log(scores_data[index]);
|
||||
Item item(offset, id, score);
|
||||
Insert(&top_beam, item, beam_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(top_beam);
|
||||
}
|
||||
|
||||
if (FLAGS_v == 3) {
|
||||
VLOG(3) << "SelectTopBeamSizeItems result size " << result.size();
|
||||
for (auto &items : result) {
|
||||
VLOG(3) << "item set:";
|
||||
for (auto &item : items) {
|
||||
VLOG(3) << item.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template class PADDLE_API BeamSearchFunctor<CPUContext, int>;
|
||||
template class PADDLE_API BeamSearchFunctor<CPUContext, int64_t>;
|
||||
template class PADDLE_API BeamSearchFunctor<CPUContext, float>;
|
||||
template class PADDLE_API BeamSearchFunctor<CPUContext, double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,541 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/beam_search.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_device_function.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_launch_config.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
struct Triple {
|
||||
__device__ __forceinline__ Triple() = default;
|
||||
__device__ __forceinline__ Triple(int o, int i, float s)
|
||||
: offset(o), id(i), score(s) {}
|
||||
|
||||
__device__ __forceinline__ void set(int o, int i, float s) {
|
||||
offset = o;
|
||||
id = i;
|
||||
score = s;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void operator=(const Triple& in) {
|
||||
offset = in.offset;
|
||||
id = in.id;
|
||||
score = in.score;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool operator<(const float s) const {
|
||||
return score < s;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool operator<(const Triple& in) const {
|
||||
return (score < in.score) || ((score == in.score) && (offset < in.offset));
|
||||
}
|
||||
|
||||
int offset;
|
||||
int id;
|
||||
float score;
|
||||
};
|
||||
|
||||
__device__ __forceinline__ void Insert(Triple* top_beam,
|
||||
const Triple& p,
|
||||
int beam_size) {
|
||||
if (p < top_beam[beam_size - 1]) {
|
||||
return;
|
||||
}
|
||||
for (int k = beam_size - 2; k >= 0; --k) {
|
||||
if (top_beam[k] < p) {
|
||||
top_beam[k + 1] = top_beam[k];
|
||||
} else {
|
||||
top_beam[k + 1] = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
top_beam[0] = p;
|
||||
}
|
||||
|
||||
template <int MaxThreadsPerSeq, bool IsAccumulated = true>
|
||||
__device__ __forceinline__ int SelectTopBeam(Triple* top_beam,
|
||||
const int64_t* pre_ids,
|
||||
const float* pre_scores,
|
||||
const int64_t* ids,
|
||||
const float* scores,
|
||||
const int seq_offset_start,
|
||||
const int seq_offset_end,
|
||||
const int seq_width,
|
||||
int beam_size,
|
||||
int end_id,
|
||||
int used_threads) {
|
||||
// top_beam is shared memory
|
||||
const int tid = threadIdx.x;
|
||||
const int tid_of_seq = threadIdx.x % MaxThreadsPerSeq;
|
||||
|
||||
int num_used_threads = used_threads;
|
||||
|
||||
Triple* top_beam_local = top_beam + tid * beam_size;
|
||||
if (tid_of_seq < num_used_threads) {
|
||||
for (int i = 0; i < beam_size; ++i) {
|
||||
top_beam_local[i].set(-1, -1, -INFINITY);
|
||||
}
|
||||
|
||||
for (int offset = seq_offset_start; offset < seq_offset_end; ++offset) {
|
||||
int pre_id = static_cast<int>(pre_ids[offset]);
|
||||
if (pre_id == end_id) {
|
||||
if (tid_of_seq == 0) {
|
||||
Triple tmp(offset, end_id, pre_scores[offset]);
|
||||
Insert(top_beam_local, tmp, beam_size);
|
||||
}
|
||||
} else {
|
||||
int64_t index = static_cast<int64_t>(offset) * seq_width + tid_of_seq;
|
||||
if (!IsAccumulated) {
|
||||
float pre_score = pre_scores[offset];
|
||||
for (int i = tid_of_seq; i < seq_width; i += num_used_threads) {
|
||||
float score = pre_score + __logf(scores[index]);
|
||||
int id = ids ? static_cast<int>(ids[index]) : i;
|
||||
Triple tmp(offset, id, score);
|
||||
Insert(top_beam_local, tmp, beam_size);
|
||||
index += num_used_threads;
|
||||
}
|
||||
} else {
|
||||
for (int i = tid_of_seq; i < seq_width; i += num_used_threads) {
|
||||
int id = ids ? static_cast<int>(ids[index]) : i;
|
||||
float score = scores[index];
|
||||
Triple tmp(offset, id, score);
|
||||
Insert(top_beam_local, tmp, beam_size);
|
||||
index += num_used_threads;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (num_used_threads > 1) {
|
||||
if (num_used_threads > 16) {
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
if ((num_used_threads & 0x1) != 0) {
|
||||
// If num_used_threads is a odd number, merge local top_beam of thread 0
|
||||
// and num_used_threads - 1
|
||||
if (tid_of_seq == 0) {
|
||||
int index_in_sh = (num_used_threads - 1 + tid) * beam_size;
|
||||
for (int i = 0; i < beam_size; i++) {
|
||||
Insert(top_beam_local, top_beam[index_in_sh], beam_size);
|
||||
index_in_sh++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
num_used_threads = num_used_threads >> 1;
|
||||
if (tid_of_seq < num_used_threads) {
|
||||
int index_in_sh = (num_used_threads + tid) * beam_size;
|
||||
for (int i = 0; i < beam_size; i++) {
|
||||
Insert(top_beam_local, top_beam[index_in_sh], beam_size);
|
||||
index_in_sh++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tid_of_seq == 0) {
|
||||
int num_items = 0;
|
||||
for (int i = 0; i < beam_size; ++i) {
|
||||
num_items =
|
||||
(top_beam_local[i].score > -INFINITY) ? num_items + 1 : num_items;
|
||||
}
|
||||
return num_items;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool PruneEndBeams(Triple* top_beam_local,
|
||||
const int64_t* pre_ids,
|
||||
const int end_id,
|
||||
int num_items) {
|
||||
bool finish_flag = true;
|
||||
for (int i = 0; i < num_items; ++i) {
|
||||
int offset = top_beam_local[i].offset;
|
||||
if (top_beam_local[i].id != end_id ||
|
||||
static_cast<int>(pre_ids[offset]) != end_id) {
|
||||
finish_flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return finish_flag;
|
||||
}
|
||||
|
||||
template <bool ReturnParentIdx = false>
|
||||
__device__ __forceinline__ void WriteBack(int64_t* selected_ids,
|
||||
float* selected_scores,
|
||||
int* parent_idx,
|
||||
size_t* selected_offsets,
|
||||
Triple* top_beam_local,
|
||||
const int seq_offset_start,
|
||||
const int seq_offset_end,
|
||||
const int selected_seq_start,
|
||||
const int selected_seq_length) {
|
||||
const int tid = threadIdx.x; // use 1 thread only for each sequence
|
||||
int global_index = selected_seq_start;
|
||||
for (int global_offset = seq_offset_start; global_offset < seq_offset_end;
|
||||
++global_offset) {
|
||||
for (int local_index = 0; local_index < selected_seq_length;
|
||||
++local_index) {
|
||||
if (top_beam_local[local_index].offset == global_offset) {
|
||||
selected_ids[global_index] =
|
||||
static_cast<int64_t>(top_beam_local[local_index].id);
|
||||
selected_scores[global_index] = top_beam_local[local_index].score;
|
||||
if (ReturnParentIdx) {
|
||||
parent_idx[global_index] = static_cast<int>(global_offset);
|
||||
}
|
||||
global_index++;
|
||||
}
|
||||
}
|
||||
selected_offsets[global_offset + 1] = static_cast<size_t>(global_index);
|
||||
}
|
||||
}
|
||||
|
||||
template <int MaxLength, int MaxThreadsPerSeq, int MaxSeqs>
|
||||
__device__ void BeamSearchDetails(int64_t* selected_ids,
|
||||
float* selected_scores,
|
||||
int* parent_idx,
|
||||
size_t* selected_offsets,
|
||||
const int64_t* pre_ids,
|
||||
const float* pre_scores,
|
||||
const int64_t* ids,
|
||||
const float* scores,
|
||||
const int seq_offset_start,
|
||||
const int seq_offset_end,
|
||||
const int seq_width,
|
||||
int beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated,
|
||||
int num_used_threads) {
|
||||
__shared__ Triple top_beam[MaxLength];
|
||||
|
||||
int num_items = 0;
|
||||
if (is_accumulated) {
|
||||
num_items = SelectTopBeam<MaxThreadsPerSeq, true>(top_beam,
|
||||
pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
seq_width,
|
||||
beam_size,
|
||||
end_id,
|
||||
num_used_threads);
|
||||
} else {
|
||||
num_items = SelectTopBeam<MaxThreadsPerSeq, false>(top_beam,
|
||||
pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
seq_width,
|
||||
beam_size,
|
||||
end_id,
|
||||
num_used_threads);
|
||||
}
|
||||
|
||||
const int tid = threadIdx.x; // use 1 thread only for each sequence
|
||||
const int tid_of_seq = tid % MaxThreadsPerSeq;
|
||||
if (tid_of_seq == 0) {
|
||||
// Use 1 thread for each sequence.
|
||||
Triple* top_beam_local = top_beam + tid * beam_size;
|
||||
bool finish_flag =
|
||||
PruneEndBeams(top_beam_local, pre_ids, end_id, num_items);
|
||||
|
||||
int selected_seq_start = 0;
|
||||
int selected_seq_length = finish_flag ? 0 : num_items;
|
||||
|
||||
if (MaxSeqs > 1) {
|
||||
const int seq_id = (MaxSeqs > 1) ? tid / MaxThreadsPerSeq : tid;
|
||||
__shared__ int shared_mem[MaxSeqs];
|
||||
|
||||
// [0, MaxSeqs - 1], length of each sequences
|
||||
shared_mem[seq_id] = selected_seq_length;
|
||||
__syncthreads();
|
||||
|
||||
for (int s = 0; s < seq_id; ++s) {
|
||||
selected_seq_start += shared_mem[s];
|
||||
}
|
||||
|
||||
if (seq_id == 0) {
|
||||
selected_offsets[0] = 0;
|
||||
}
|
||||
} else {
|
||||
selected_offsets[0] = 0;
|
||||
}
|
||||
|
||||
if (parent_idx) {
|
||||
WriteBack<true>(selected_ids,
|
||||
selected_scores,
|
||||
parent_idx,
|
||||
selected_offsets,
|
||||
top_beam_local,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
selected_seq_start,
|
||||
selected_seq_length);
|
||||
} else {
|
||||
WriteBack<false>(selected_ids,
|
||||
selected_scores,
|
||||
parent_idx,
|
||||
selected_offsets,
|
||||
top_beam_local,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
selected_seq_start,
|
||||
selected_seq_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int MaxLength, int MaxThreadsPerSeq, int MaxSeqs>
|
||||
__global__ void BeamSearchKernel(int64_t* selected_ids,
|
||||
float* selected_scores,
|
||||
int* parent_idx,
|
||||
size_t* selected_offsets,
|
||||
const int64_t* pre_ids,
|
||||
const float* pre_scores,
|
||||
const int64_t* ids,
|
||||
const float* scores,
|
||||
const size_t* seq_offsets,
|
||||
const int num_seqs,
|
||||
const int seq_width,
|
||||
int beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated,
|
||||
int num_used_threads) {
|
||||
const int tid = threadIdx.x;
|
||||
const int seq_id = (MaxSeqs > 1) ? tid / MaxThreadsPerSeq : tid;
|
||||
|
||||
int seq_offset_start = static_cast<int>(seq_offsets[seq_id]);
|
||||
int seq_offset_end = static_cast<int>(seq_offsets[seq_id + 1]);
|
||||
|
||||
BeamSearchDetails<MaxLength, MaxThreadsPerSeq, MaxSeqs>(selected_ids,
|
||||
selected_scores,
|
||||
parent_idx,
|
||||
selected_offsets,
|
||||
pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
seq_width,
|
||||
beam_size,
|
||||
end_id,
|
||||
is_accumulated,
|
||||
num_used_threads);
|
||||
}
|
||||
|
||||
template <int MaxLength, int MaxThreadsPerSeq>
|
||||
__global__ void BeamSearchKernelSingle(int64_t* selected_ids,
|
||||
float* selected_scores,
|
||||
int* parent_idx,
|
||||
size_t* selected_offsets,
|
||||
const int64_t* pre_ids,
|
||||
const float* pre_scores,
|
||||
const int64_t* ids,
|
||||
const float* scores,
|
||||
const int seq_length,
|
||||
const int seq_width,
|
||||
int beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated,
|
||||
int num_used_threads) {
|
||||
const int seq_offset_start = 0;
|
||||
const int seq_offset_end = seq_length;
|
||||
|
||||
BeamSearchDetails<MaxLength, MaxThreadsPerSeq, 1>(selected_ids,
|
||||
selected_scores,
|
||||
parent_idx,
|
||||
selected_offsets,
|
||||
pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
seq_offset_start,
|
||||
seq_offset_end,
|
||||
seq_width,
|
||||
beam_size,
|
||||
end_id,
|
||||
is_accumulated,
|
||||
num_used_threads);
|
||||
}
|
||||
|
||||
static inline int GetNumUsedThreads(const int max_threads_per_seq,
|
||||
const int seq_width,
|
||||
int beam_size) {
|
||||
int num_used_threads = (seq_width + beam_size - 1) / beam_size;
|
||||
num_used_threads = max_threads_per_seq < num_used_threads
|
||||
? max_threads_per_seq
|
||||
: num_used_threads;
|
||||
|
||||
num_used_threads =
|
||||
num_used_threads > 32
|
||||
? (num_used_threads >> 5) << 5
|
||||
: (num_used_threads > 16
|
||||
? 32
|
||||
: (num_used_threads > 8
|
||||
? 16
|
||||
: (num_used_threads > 4
|
||||
? 8
|
||||
: (num_used_threads > 2 ? 4
|
||||
: num_used_threads))));
|
||||
return num_used_threads;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class BeamSearchFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& dev_ctx,
|
||||
const DenseTensor* pre_ids,
|
||||
const DenseTensor* pre_scores,
|
||||
const DenseTensor* ids,
|
||||
const DenseTensor* scores,
|
||||
DenseTensor* selected_ids,
|
||||
DenseTensor* selected_scores,
|
||||
DenseTensor* parent_idx,
|
||||
size_t level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated) {
|
||||
auto abs_lod = ToAbsOffset(scores->lod());
|
||||
|
||||
const int64_t* pre_ids_data = pre_ids->data<int64_t>();
|
||||
const float* pre_scores_data = pre_scores->data<float>();
|
||||
const int64_t* ids_data = ids ? ids->data<int64_t>() : nullptr;
|
||||
const float* scores_data = scores->data<float>();
|
||||
|
||||
const size_t num_seqs = abs_lod[level].size() - 1;
|
||||
size_t seq_width = 1;
|
||||
for (int i = 1; i < scores->dims().size(); i++) {
|
||||
seq_width *= scores->dims()[i];
|
||||
}
|
||||
|
||||
// Reserve a big enough memory.
|
||||
auto selected_dims =
|
||||
make_ddim({static_cast<int64_t>(num_seqs * beam_size), 1});
|
||||
selected_ids->Resize(selected_dims);
|
||||
int64_t* selected_ids_data = dev_ctx.template Alloc<int64_t>(selected_ids);
|
||||
selected_scores->Resize(selected_dims);
|
||||
float* selected_scores_data =
|
||||
dev_ctx.template Alloc<float>(selected_scores);
|
||||
if (parent_idx != nullptr) {
|
||||
parent_idx->Resize({static_cast<int64_t>(num_seqs * beam_size)});
|
||||
}
|
||||
int* parent_idx_data =
|
||||
parent_idx ? dev_ctx.template Alloc<int>(parent_idx) : nullptr;
|
||||
|
||||
LegacyLoD selected_lod(2);
|
||||
selected_lod[0].assign(abs_lod[level].begin(), abs_lod[level].end());
|
||||
selected_lod[1].resize(scores->dims()[0] + 1);
|
||||
phi::MixVector<size_t> mix_vector(&selected_lod[1]);
|
||||
phi::MixVector<size_t> mixv_abs(&abs_lod[level]);
|
||||
size_t* selected_offsets = mix_vector.CUDAMutableData(dev_ctx.GetPlace());
|
||||
|
||||
if (num_seqs == 1) {
|
||||
const int seq_length = static_cast<int>(abs_lod[level][1]);
|
||||
const int kMaxThreadsPerSeq = 1024;
|
||||
int num_used_threads = GetNumUsedThreads(kMaxThreadsPerSeq,
|
||||
static_cast<int>(seq_width),
|
||||
static_cast<int>(beam_size));
|
||||
switch (phi::backends::gpu::RoundToPowerOfTwo(beam_size * seq_width)) {
|
||||
CUDA_LAUNCH_KERNEL_HELPER(
|
||||
BeamSearchKernelSingle<kPowerOfTwoDim, kMaxThreadsPerSeq>
|
||||
<<<1, kMaxThreadsPerSeq, 0, dev_ctx.stream()>>>(
|
||||
selected_ids_data,
|
||||
selected_scores_data,
|
||||
parent_idx_data,
|
||||
selected_offsets,
|
||||
pre_ids_data,
|
||||
pre_scores_data,
|
||||
ids_data,
|
||||
scores_data,
|
||||
seq_length,
|
||||
static_cast<int>(seq_width),
|
||||
static_cast<int>(beam_size),
|
||||
static_cast<int>(end_id),
|
||||
is_accumulated,
|
||||
num_used_threads));
|
||||
}
|
||||
} else if (num_seqs <= 4) {
|
||||
const size_t* seq_offsets = mixv_abs.CUDAData(dev_ctx.GetPlace());
|
||||
// Use only 1 block
|
||||
const int kMaxThreadsPerSeq = 32;
|
||||
const int kMaxSeqs = 4;
|
||||
int num_used_threads = GetNumUsedThreads(kMaxThreadsPerSeq,
|
||||
static_cast<int>(seq_width),
|
||||
static_cast<int>(beam_size));
|
||||
switch (
|
||||
phi::backends::gpu::RoundToPowerOfTwo(beam_size * num_seqs * 32)) {
|
||||
CUDA_LAUNCH_KERNEL_HELPER(
|
||||
BeamSearchKernel<kPowerOfTwoDim, kMaxThreadsPerSeq, kMaxSeqs>
|
||||
<<<1, num_seqs * kMaxThreadsPerSeq, 0, dev_ctx.stream()>>>(
|
||||
selected_ids_data,
|
||||
selected_scores_data,
|
||||
parent_idx_data,
|
||||
selected_offsets,
|
||||
pre_ids_data,
|
||||
pre_scores_data,
|
||||
ids_data,
|
||||
scores_data,
|
||||
seq_offsets,
|
||||
static_cast<int>(num_seqs),
|
||||
static_cast<int>(seq_width),
|
||||
static_cast<int>(beam_size),
|
||||
end_id,
|
||||
is_accumulated,
|
||||
num_used_threads));
|
||||
}
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Not implemented other number of sequences yet."));
|
||||
}
|
||||
|
||||
dev_ctx.Wait();
|
||||
mix_vector.CopyToCPU();
|
||||
if (!CheckLegacyLoD(selected_lod)) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"lod %s is not right in"
|
||||
" beam_search, please check your code.",
|
||||
LoDToString(selected_lod)));
|
||||
}
|
||||
|
||||
selected_ids->set_lod(selected_lod);
|
||||
selected_scores->set_lod(selected_lod);
|
||||
if (selected_lod[1].back() < num_seqs * beam_size) {
|
||||
auto final_selected_dims =
|
||||
make_ddim({static_cast<int64_t>(selected_lod[1].back()), 1});
|
||||
selected_ids->Resize(final_selected_dims);
|
||||
selected_scores->Resize(final_selected_dims);
|
||||
if (parent_idx) {
|
||||
parent_idx->Resize({static_cast<int64_t>(selected_lod[1].back())});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template class BeamSearchFunctor<GPUContext, int>;
|
||||
template class BeamSearchFunctor<GPUContext, int64_t>;
|
||||
template class PADDLE_API BeamSearchFunctor<GPUContext, float>;
|
||||
template class BeamSearchFunctor<GPUContext, double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2024 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 <string>
|
||||
#include <vector>
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/lod_utils.h"
|
||||
#include "paddle/phi/core/mixed_vector.h"
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
static inline std::string LoDToString(const LegacyLoD& lod) {
|
||||
std::ostringstream stream;
|
||||
for (const auto& row : lod) {
|
||||
for (const auto& element : row) {
|
||||
stream << element << " ";
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
static inline bool CheckLegacyLoD(const LoD& in, int tensor_height = -1) {
|
||||
if (in.empty()) return true;
|
||||
for (const auto& level : in) {
|
||||
// check: there should be more than 2 offsets existing in each level.
|
||||
if (level.size() < 2) return false;
|
||||
// check: the first offset(the begin offset) of each level should be 0.
|
||||
if (level.front() != 0) return false;
|
||||
// check: all the offsets in a level should be non-descending
|
||||
if (!std::is_sorted(level.begin(), level.end())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check: the lowest level's last offset should equals `tensor_height` if
|
||||
// tensor_height>0.
|
||||
if (tensor_height > 0 &&
|
||||
static_cast<size_t>(tensor_height) != in.back().back())
|
||||
return false;
|
||||
|
||||
// check: the higher level's last offset should equals the lower level's
|
||||
// size-1.
|
||||
// NOTE LoD store the levels from top to bottom, so the higher level goes
|
||||
// first.
|
||||
for (size_t level = 0; level < in.size() - 1; level++) {
|
||||
if (in[level].back() != in[level + 1].size() - 1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an implementation of beam search.
|
||||
*
|
||||
* To explain the details, lets take machine translation task for example, in
|
||||
* this task, one source sentence is translated to multiple target sentences,
|
||||
* during this period, one sentence will be translated to multiple translation
|
||||
* prefixes(target sentence that have not ended), in each time step a prefix
|
||||
* will have some candidates, input the candidate ids and their corresponding
|
||||
* scores (probabilities), it will sort and select the top beam_size candidates
|
||||
* for each source sentence, and store the selected candidates's score and their
|
||||
* corresponding ids to DenseTensors.
|
||||
*
|
||||
* A detailed example:
|
||||
*
|
||||
* Input
|
||||
*
|
||||
* ids:
|
||||
* - LoD (should have 2 levels)
|
||||
* - first level: [0, 1, 4]
|
||||
* - second level: [0, 1, 2, 3, 4]
|
||||
* - tensor's data:
|
||||
* [[4, 2, 5]
|
||||
* [2, 1, 3]
|
||||
* [3, 5, 2]
|
||||
* [8, 2, 1]]
|
||||
*
|
||||
* scores:
|
||||
* - LoD same as `ids`
|
||||
* - tensor's data
|
||||
* [[0.5, 0.3, 0.2]
|
||||
* [0.6, 0.3, 0.1]
|
||||
* [0.9, 0.5, 0.1]
|
||||
* [0.7, 0.5, 0.1]]
|
||||
*
|
||||
* The inputs means that there are 2 source sentences to translate, and the
|
||||
* first source has 1 prefix, the second source has 2 prefix.
|
||||
*
|
||||
* Lets assume beam size is 2, and the beam search's output should be
|
||||
* - LoD
|
||||
* - first level: [0, 1, 2]
|
||||
* - second level: [0, 2, 4]
|
||||
* - id tensor's data
|
||||
* [[4,
|
||||
* 1,
|
||||
* 3,
|
||||
* 8]]
|
||||
* - score tensor's data
|
||||
* [[0.5,
|
||||
* 0.3,
|
||||
* 0.9,
|
||||
* 0.7]]
|
||||
*
|
||||
* TODO all the prune operations should be in the beam search, so it is better
|
||||
* to split the beam search algorithm into a sequence of smaller operators, and
|
||||
* the prune operators can be inserted in this sequence.
|
||||
*/
|
||||
template <typename DeviceContext, typename T>
|
||||
class BeamSearchFunctor {
|
||||
public:
|
||||
/*
|
||||
* The main function of beam search.
|
||||
*
|
||||
* @selected_ids: a [None, 1]-shaped tensor with LoD.
|
||||
* In a machine translation model, it might be the candidate term id sets,
|
||||
* each set stored as a variance-length sequence.
|
||||
* The format might be described with a two-level LoD
|
||||
* - [[0 1],
|
||||
* [0 1 2]]
|
||||
* - [[]
|
||||
* [0 1]]
|
||||
* the first level of LoD tells that there are two source sentences. The
|
||||
* second level describes the details of the candidate id set's offsets in
|
||||
* the source sentences.
|
||||
*
|
||||
* @selected_scores: a LoD tensor with the same shape and LoD with
|
||||
* selected_ids.
|
||||
* It stores the corresponding scores of candidate ids in selected_ids.
|
||||
*
|
||||
* Return false if all the input tensor is empty, in machine translation task
|
||||
* that means no candidates is provided, and the task will stop running.
|
||||
*/
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor* pre_ids,
|
||||
const DenseTensor* pre_scores,
|
||||
const DenseTensor* ids,
|
||||
const DenseTensor* scores,
|
||||
DenseTensor* selected_ids,
|
||||
DenseTensor* selected_scores,
|
||||
DenseTensor* parent_idx,
|
||||
size_t level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated);
|
||||
};
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,364 @@
|
||||
// Copyright (c) 2024 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 "paddle/phi/common/memory_utils.h"
|
||||
#include "paddle/phi/kernels/funcs/math/beam_search.h"
|
||||
#ifdef PADDLE_WITH_XPU
|
||||
#include "paddle/phi/backends/xpu/enforce_xpu.h"
|
||||
#include "paddle/phi/backends/xpu/xpu_context.h"
|
||||
#include "paddle/phi/backends/xpu/xpu_header.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
template <typename T>
|
||||
int CopyData(const T *x, T **y, int len, const Place &place) {
|
||||
if (nullptr == x || nullptr == y || len <= 0)
|
||||
return xpu::Error_t::INVALID_PARAM;
|
||||
|
||||
*y = reinterpret_cast<T *>(malloc(sizeof(T) * len));
|
||||
|
||||
phi::memory_utils::Copy(CPUPlace(), *y, place, x, len * sizeof(T));
|
||||
return xpu::Error_t::SUCCESS;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CopyDataByCondition(const T *x, T **y, int len, const Place &place) {
|
||||
if (x != nullptr) {
|
||||
int r = CopyData(x, y, len, place);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
r,
|
||||
xpu::Error_t::SUCCESS,
|
||||
common::errors::External("Copy data form xpu to cpu failed"));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class BeamSearchFunctor<XPUContext, T> {
|
||||
public:
|
||||
void operator()(const XPUContext &dev_ctx,
|
||||
const DenseTensor *pre_ids,
|
||||
const DenseTensor *pre_scores,
|
||||
const DenseTensor *ids,
|
||||
const DenseTensor *scores,
|
||||
DenseTensor *selected_ids,
|
||||
DenseTensor *selected_scores,
|
||||
DenseTensor *parent_idx,
|
||||
size_t level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated) {
|
||||
auto abs_lod = ToAbsOffset(scores->lod());
|
||||
auto &high_level = abs_lod[level];
|
||||
|
||||
auto items = SelectTopBeamSizeItems(pre_ids,
|
||||
pre_scores,
|
||||
ids,
|
||||
scores,
|
||||
level,
|
||||
beam_size,
|
||||
end_id,
|
||||
is_accumulated,
|
||||
ids->place());
|
||||
auto selected_items = ToMap(items, high_level.back());
|
||||
if (FLAGS_v == 3) {
|
||||
VLOG(3) << "selected_items:";
|
||||
for (size_t i = 0; i < selected_items.size(); ++i) {
|
||||
VLOG(3) << "offset: " << i;
|
||||
for (auto &item : selected_items[i]) {
|
||||
VLOG(3) << item.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PruneEndBeams(
|
||||
pre_ids, abs_lod, &selected_items, level, end_id, ids->place());
|
||||
// calculate the output tensor's height
|
||||
size_t num_instances = std::accumulate(
|
||||
std::begin(selected_items),
|
||||
std::end(selected_items),
|
||||
0,
|
||||
[](size_t a, std::vector<Item> &b) { return a + b.size(); });
|
||||
// the output tensor shape should be [num_instances, 1]
|
||||
auto dims =
|
||||
make_ddim(std::vector<int64_t>({static_cast<int>(num_instances), 1}));
|
||||
selected_ids->Resize(dims);
|
||||
auto *selected_ids_data = dev_ctx.template HostAlloc<int64_t>(selected_ids);
|
||||
selected_scores->Resize(dims);
|
||||
auto *selected_scores_data =
|
||||
dev_ctx.template HostAlloc<float>(selected_scores);
|
||||
if (parent_idx != nullptr) {
|
||||
parent_idx->Resize({static_cast<int64_t>(num_instances)});
|
||||
}
|
||||
auto *parent_idx_data =
|
||||
parent_idx ? dev_ctx.template HostAlloc<int>(parent_idx) : nullptr;
|
||||
|
||||
// fill in data
|
||||
std::vector<size_t> low_level;
|
||||
size_t low_offset = 0;
|
||||
for (auto &items : selected_items) {
|
||||
low_level.push_back(low_offset);
|
||||
for (auto &item : items) {
|
||||
if (parent_idx) {
|
||||
parent_idx_data[low_offset] = static_cast<int>(low_level.size() - 1);
|
||||
}
|
||||
selected_ids_data[low_offset] = item.id;
|
||||
selected_scores_data[low_offset] = item.score;
|
||||
low_offset++;
|
||||
}
|
||||
}
|
||||
low_level.push_back(low_offset);
|
||||
|
||||
// fill lod
|
||||
LegacyLoD lod(2);
|
||||
lod[0].assign(high_level.begin(), high_level.end());
|
||||
lod[1].assign(low_level.begin(), low_level.end());
|
||||
if (!CheckLegacyLoD(lod)) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"lod %s is not right in"
|
||||
" beam_search, please check your code.",
|
||||
LoDToString(lod)));
|
||||
}
|
||||
selected_ids->set_lod(lod);
|
||||
selected_scores->set_lod(lod);
|
||||
}
|
||||
|
||||
/*
|
||||
* The basic items help to sort.
|
||||
*/
|
||||
struct Item {
|
||||
Item() {}
|
||||
Item(size_t offset, size_t id, float score)
|
||||
: offset(offset), id(id), score(score) {}
|
||||
// offset in the higher lod level.
|
||||
size_t offset;
|
||||
// prefix id in the lower lod level.
|
||||
// size_t prefix;
|
||||
// the candidate id
|
||||
size_t id;
|
||||
// the corresponding score
|
||||
float score;
|
||||
|
||||
inline bool operator<(const Item &in) const {
|
||||
return (score < in.score) ||
|
||||
((score == in.score) && (offset < in.offset));
|
||||
}
|
||||
|
||||
inline void operator=(const Item &in) {
|
||||
offset = in.offset;
|
||||
id = in.id;
|
||||
score = in.score;
|
||||
}
|
||||
|
||||
std::string ToString() {
|
||||
std::ostringstream os;
|
||||
os << "{";
|
||||
os << "offset: " << offset << ", ";
|
||||
os << "id: " << id << ", ";
|
||||
os << "score: " << score << "";
|
||||
os << "}";
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Prune the source sentences all branches finished, and it is optional.
|
||||
* Pruning must one step later than finishing (thus pre_ids is needed here),
|
||||
* since the end tokens must be written out.
|
||||
*/
|
||||
void PruneEndBeams(const DenseTensor *pre_ids,
|
||||
const LegacyLoD &abs_lod,
|
||||
std::vector<std::vector<Item>> *items,
|
||||
size_t lod_level,
|
||||
int end_id,
|
||||
const Place &place) {
|
||||
auto *pre_ids_data_xpu = pre_ids->data<int64_t>();
|
||||
int64_t *pre_ids_data = nullptr;
|
||||
CopyDataByCondition<int64_t>(
|
||||
pre_ids_data_xpu, &pre_ids_data, pre_ids->numel(), place);
|
||||
|
||||
auto &high_level = abs_lod[lod_level];
|
||||
for (size_t src_idx = 0; src_idx < high_level.size() - 1; ++src_idx) {
|
||||
size_t src_prefix_start = high_level[src_idx];
|
||||
size_t src_prefix_end = high_level[src_idx + 1];
|
||||
bool finish_flag = true;
|
||||
for (size_t offset = src_prefix_start; offset < src_prefix_end;
|
||||
offset++) {
|
||||
for (auto &item : items->at(offset)) {
|
||||
if (item.id != static_cast<size_t>(end_id) ||
|
||||
pre_ids_data[offset] != end_id) {
|
||||
finish_flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!finish_flag) break;
|
||||
}
|
||||
if (finish_flag) { // all branches of the beam (source sentence) end and
|
||||
// prune this beam
|
||||
for (size_t offset = src_prefix_start; offset < src_prefix_end;
|
||||
offset++)
|
||||
items->at(offset).clear();
|
||||
}
|
||||
}
|
||||
free(pre_ids_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform the items into a map whose key is offset, value is the items.
|
||||
* NOTE low performance.
|
||||
*/
|
||||
std::vector<std::vector<Item>> ToMap(
|
||||
const std::vector<std::vector<Item>> &items, size_t element_num) {
|
||||
std::vector<std::vector<Item>> result;
|
||||
result.resize(element_num);
|
||||
for (auto &entries : items) {
|
||||
for (const auto &item : entries) {
|
||||
result[item.offset].push_back(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Insert(std::vector<Item> *top_beam_ptr,
|
||||
const Item &item,
|
||||
size_t beam_size) {
|
||||
std::vector<Item> &top_beam = *top_beam_ptr;
|
||||
|
||||
size_t num_beams = top_beam.size();
|
||||
|
||||
if (num_beams < beam_size) {
|
||||
top_beam.resize(num_beams + 1);
|
||||
num_beams++;
|
||||
} else {
|
||||
if (item < top_beam[beam_size - 1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = static_cast<int>(num_beams) - 2; k >= 0; --k) {
|
||||
if (top_beam[k] < item) {
|
||||
top_beam[k + 1] = top_beam[k];
|
||||
} else {
|
||||
top_beam[k + 1] = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
top_beam[0] = item;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each source, select top beam_size records.
|
||||
*/
|
||||
std::vector<std::vector<Item>> SelectTopBeamSizeItems(
|
||||
const DenseTensor *pre_ids,
|
||||
const DenseTensor *pre_scores,
|
||||
const DenseTensor *ids,
|
||||
const DenseTensor *scores,
|
||||
size_t lod_level,
|
||||
size_t beam_size,
|
||||
int end_id,
|
||||
bool is_accumulated,
|
||||
const Place &place) {
|
||||
std::vector<std::vector<Item>> result;
|
||||
|
||||
// find the current candidates
|
||||
auto abs_lod = ToAbsOffset(scores->lod());
|
||||
|
||||
auto *pre_ids_data_xpu = pre_ids->data<int64_t>();
|
||||
int64_t *pre_ids_data = nullptr;
|
||||
CopyDataByCondition<int64_t>(
|
||||
pre_ids_data_xpu, &pre_ids_data, pre_ids->numel(), place);
|
||||
|
||||
auto *pre_scores_data_xpu = pre_scores->data<float>();
|
||||
float *pre_scores_data = nullptr;
|
||||
CopyDataByCondition<float>(
|
||||
pre_scores_data_xpu, &pre_scores_data, pre_scores->numel(), place);
|
||||
|
||||
auto *ids_data_xpu = ids ? ids->data<int64_t>() : nullptr;
|
||||
int64_t *ids_data = nullptr;
|
||||
CopyDataByCondition<int64_t>(ids_data_xpu, &ids_data, ids->numel(), place);
|
||||
|
||||
auto *scores_data_xpu = scores->data<float>();
|
||||
float *scores_data = nullptr;
|
||||
CopyDataByCondition<float>(
|
||||
scores_data_xpu, &scores_data, scores->numel(), place);
|
||||
|
||||
size_t num_seqs = scores->NumElements(lod_level);
|
||||
size_t seq_width = 1;
|
||||
for (int i = 1; i < scores->dims().size(); i++) {
|
||||
seq_width *= scores->dims()[i];
|
||||
}
|
||||
|
||||
for (size_t seq_id = 0; seq_id < num_seqs; ++seq_id) {
|
||||
size_t seq_offset_start = abs_lod[lod_level][seq_id];
|
||||
size_t seq_offset_end = abs_lod[lod_level][seq_id + 1];
|
||||
|
||||
std::vector<Item> top_beam;
|
||||
top_beam.reserve(beam_size);
|
||||
|
||||
for (size_t offset = seq_offset_start; offset < seq_offset_end;
|
||||
++offset) {
|
||||
auto pre_id = pre_ids_data[offset];
|
||||
auto pre_score = pre_scores_data[offset];
|
||||
|
||||
if (pre_id == end_id) {
|
||||
// Allocate all probability mass to end_id for finished branches and
|
||||
// the other candidate ids can be ignored.
|
||||
Item item(offset, end_id, pre_score);
|
||||
Insert(&top_beam, item, beam_size);
|
||||
} else {
|
||||
size_t index = offset * seq_width;
|
||||
for (size_t d = 0; d < seq_width; d++, index++) {
|
||||
int64_t id = ids_data ? ids_data[index] : static_cast<int64_t>(d);
|
||||
float score = is_accumulated
|
||||
? scores_data[index]
|
||||
: pre_score + std::log(scores_data[index]);
|
||||
|
||||
Item item(offset, id, score);
|
||||
Insert(&top_beam, item, beam_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(top_beam);
|
||||
}
|
||||
|
||||
if (FLAGS_v == 3) {
|
||||
VLOG(3) << "SelectTopBeamSizeItems result size " << result.size();
|
||||
for (auto &items : result) {
|
||||
VLOG(3) << "item set:";
|
||||
for (auto &item : items) {
|
||||
VLOG(3) << item.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(pre_ids_data);
|
||||
free(pre_scores_data);
|
||||
free(ids_data);
|
||||
free(scores_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template class BeamSearchFunctor<XPUContext, int>;
|
||||
template class BeamSearchFunctor<XPUContext, int64_t>;
|
||||
template class BeamSearchFunctor<XPUContext, float>;
|
||||
template class BeamSearchFunctor<XPUContext, double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,415 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/bert_encoder_functor.h"
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
#include "paddle/phi/kernels/funcs/math_cuda_utils.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
// NOTE(chenfeiyu): explicitly use operator+ for float2
|
||||
// since float2 is not in namespace phi::funcs, ADL won't help
|
||||
using funcs::operator+;
|
||||
|
||||
template <typename T>
|
||||
__device__ __forceinline__ T local_rsqrt(T num) {
|
||||
return rsqrt(static_cast<float>(num));
|
||||
}
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
__device__ __forceinline__ half local_rsqrt(half num) { return hrsqrt(num); }
|
||||
#endif
|
||||
|
||||
template <typename T, int TPB>
|
||||
__device__ inline void LayerNormSmall(T val,
|
||||
const funcs::kvp<T> &thread_data,
|
||||
const int ld,
|
||||
const int idx,
|
||||
const T *bias,
|
||||
const T *scale,
|
||||
T *output,
|
||||
T eps) {
|
||||
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
|
||||
__shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
__shared__ T mu; // mean
|
||||
__shared__ T rsigma; // 1 / std.dev.
|
||||
|
||||
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
mu = sum_kv.key;
|
||||
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x < ld) {
|
||||
const T g(scale[threadIdx.x]);
|
||||
const T b(bias[threadIdx.x]);
|
||||
output[idx] = g * (val - mu) * rsigma + b;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int TPB>
|
||||
__device__ inline void LayerNorm(const funcs::kvp<T> &thread_data,
|
||||
const int ld,
|
||||
const int offset,
|
||||
const T *bias,
|
||||
const T *scale,
|
||||
T *output,
|
||||
T eps) {
|
||||
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
|
||||
__shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
__shared__ T mu; // mean
|
||||
__shared__ T rsigma; // 1 / std.dev.
|
||||
|
||||
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
mu = sum_kv.key;
|
||||
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (int i = threadIdx.x; i < ld; i += TPB) {
|
||||
const int idx = offset + i;
|
||||
const T val = output[idx];
|
||||
const T g(scale[i]);
|
||||
const T b(bias[i]);
|
||||
output[idx] = g * (val - mu) * rsigma + b;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename T2, int TPB>
|
||||
__device__ inline void LayerNorm2(const funcs::kvp<T> &thread_data,
|
||||
const int ld,
|
||||
const int offset,
|
||||
const T2 *bias,
|
||||
const T2 *scale,
|
||||
T2 *output,
|
||||
T eps) {
|
||||
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
|
||||
__shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
__shared__ T mu; // mean
|
||||
__shared__ T rsigma; // 1 / std.dev.
|
||||
|
||||
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
mu = sum_kv.key;
|
||||
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (int i = threadIdx.x; i < ld; i += TPB) {
|
||||
const int idx = offset + i;
|
||||
T2 val = output[idx];
|
||||
const T2 g = scale[i];
|
||||
const T2 b = bias[i];
|
||||
val.x = T(g.x) * (val.x - mu) * rsigma + T(b.x);
|
||||
val.y = T(g.y) * (val.y - mu) * rsigma + T(b.y);
|
||||
output[idx] = val;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, unsigned TPB>
|
||||
__global__ void SkipLayerNormSmallKernel(int num,
|
||||
int hidden,
|
||||
const T *input1,
|
||||
const T *input2,
|
||||
T *output,
|
||||
const T *scale,
|
||||
const T *bias,
|
||||
T eps) {
|
||||
const T rld = T(1) / T(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<T> thread_data(0, 0);
|
||||
const int idx = offset + threadIdx.x;
|
||||
T val = 0;
|
||||
if (threadIdx.x < hidden) {
|
||||
val = input1[idx] + input2[idx];
|
||||
const T rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<T>(rldval, rldval * val));
|
||||
}
|
||||
LayerNormSmall<T, TPB>(
|
||||
val, thread_data, hidden, idx, bias, scale, output, eps);
|
||||
}
|
||||
|
||||
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
|
||||
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormSmallKernel
|
||||
template <>
|
||||
__global__ void SkipLayerNormSmallKernel<half, 32>(int num,
|
||||
int hidden,
|
||||
const half *input1,
|
||||
const half *input2,
|
||||
half *output,
|
||||
const half *scale,
|
||||
const half *bias,
|
||||
half eps) {
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
const half rld = half(1) / half(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<half> thread_data(0, 0);
|
||||
const int idx = offset + threadIdx.x;
|
||||
half val = 0;
|
||||
if (threadIdx.x < hidden) {
|
||||
val = input1[idx] + input2[idx];
|
||||
const half rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
|
||||
}
|
||||
LayerNormSmall<half, 32>(
|
||||
val, thread_data, hidden, idx, bias, scale, output, eps);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
__global__ void SkipLayerNormSmallKernel<half, 128>(int num,
|
||||
int hidden,
|
||||
const half *input1,
|
||||
const half *input2,
|
||||
half *output,
|
||||
const half *scale,
|
||||
const half *bias,
|
||||
half eps) {
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
const half rld = half(1) / half(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<half> thread_data(0, 0);
|
||||
const int idx = offset + threadIdx.x;
|
||||
half val = 0;
|
||||
if (threadIdx.x < hidden) {
|
||||
val = input1[idx] + input2[idx];
|
||||
const half rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
|
||||
}
|
||||
LayerNormSmall<half, 128>(
|
||||
val, thread_data, hidden, idx, bias, scale, output, eps);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
__global__ void SkipLayerNormSmallKernel<half, 384>(int num,
|
||||
int hidden,
|
||||
const half *input1,
|
||||
const half *input2,
|
||||
half *output,
|
||||
const half *scale,
|
||||
const half *bias,
|
||||
half eps) {
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
const half rld = half(1) / half(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<half> thread_data(0, 0);
|
||||
const int idx = offset + threadIdx.x;
|
||||
half val = 0;
|
||||
if (threadIdx.x < hidden) {
|
||||
val = input1[idx] + input2[idx];
|
||||
const half rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
|
||||
}
|
||||
LayerNormSmall<half, 384>(
|
||||
val, thread_data, hidden, idx, bias, scale, output, eps);
|
||||
#endif
|
||||
}
|
||||
#endif // @} End Half kernel: SkipLayerNormSmallKernel
|
||||
|
||||
template <typename T, unsigned TPB>
|
||||
__global__ void SkipLayerNormKernel(int num,
|
||||
int hidden,
|
||||
const T *input1,
|
||||
const T *input2,
|
||||
T *output,
|
||||
const T *scale,
|
||||
const T *bias,
|
||||
T eps) {
|
||||
const T rld = T(1) / T(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<T> thread_data(0, 0);
|
||||
|
||||
for (int it = threadIdx.x; it < hidden; it += TPB) {
|
||||
const int idx = offset + it;
|
||||
const T val = input1[idx] + input2[idx];
|
||||
const T rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<T>(rldval, rldval * val));
|
||||
output[idx] = val;
|
||||
}
|
||||
LayerNorm<T, TPB>(thread_data, hidden, offset, bias, scale, output, eps);
|
||||
}
|
||||
|
||||
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
|
||||
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormKernel
|
||||
template <>
|
||||
__global__ void SkipLayerNormKernel<half, 256>(int num,
|
||||
int hidden,
|
||||
const half *input1,
|
||||
const half *input2,
|
||||
half *output,
|
||||
const half *scale,
|
||||
const half *bias,
|
||||
half eps) {
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
const half rld = half(1) / half(hidden);
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<half> thread_data(0, 0);
|
||||
|
||||
for (int it = threadIdx.x; it < hidden; it += 256) {
|
||||
const int idx = offset + it;
|
||||
const half val = input1[idx] + input2[idx];
|
||||
const half rldval = rld * val;
|
||||
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
|
||||
output[idx] = val;
|
||||
}
|
||||
LayerNorm<half, 256>(thread_data, hidden, offset, bias, scale, output, eps);
|
||||
#endif
|
||||
}
|
||||
#endif // @} End Half kernel: SkipLayerNormKernel
|
||||
|
||||
template <typename T, typename T2, unsigned TPB>
|
||||
__global__ void SkipLayerNormKernel2(int num,
|
||||
int hidden,
|
||||
const T2 *input1,
|
||||
const T2 *input2,
|
||||
T2 *output,
|
||||
const T2 *scale,
|
||||
const T2 *bias,
|
||||
float eps) {
|
||||
const T rld = T(0.5f / hidden); // because hidden is hidden/2
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<T> thread_data(0, 0);
|
||||
|
||||
for (int it = threadIdx.x; it < hidden; it += TPB) {
|
||||
const int idx = offset + it;
|
||||
const T2 val2 = input1[idx] + input2[idx];
|
||||
thread_data =
|
||||
pair_sum(thread_data,
|
||||
funcs::kvp<T>(rld * (val2.x + val2.y),
|
||||
rld * val2.x * val2.x + rld * val2.y * val2.y));
|
||||
output[idx] = val2;
|
||||
}
|
||||
LayerNorm2<T, T2, TPB>(thread_data, hidden, offset, bias, scale, output, eps);
|
||||
}
|
||||
|
||||
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
|
||||
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormKernel2
|
||||
template <>
|
||||
__global__ void SkipLayerNormKernel2<half, half2, 256>(int num,
|
||||
int hidden,
|
||||
const half2 *input1,
|
||||
const half2 *input2,
|
||||
half2 *output,
|
||||
const half2 *scale,
|
||||
const half2 *bias,
|
||||
float eps) {
|
||||
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
|
||||
const half rld = half(0.5f / hidden); // because hidden is hidden/2
|
||||
const int offset = blockIdx.x * hidden;
|
||||
cub::Sum pair_sum;
|
||||
funcs::kvp<half> thread_data(0, 0);
|
||||
|
||||
for (int it = threadIdx.x; it < hidden; it += 256) {
|
||||
const int idx = offset + it;
|
||||
const half2 val2 = input1[idx] + input2[idx];
|
||||
thread_data = pair_sum(
|
||||
thread_data,
|
||||
funcs::kvp<half>(rld * (val2.x + val2.y),
|
||||
rld * val2.x * val2.x + rld * val2.y * val2.y));
|
||||
output[idx] = val2;
|
||||
}
|
||||
LayerNorm2<half, half2, 256>(
|
||||
thread_data, hidden, offset, bias, scale, output, eps);
|
||||
#endif
|
||||
}
|
||||
#endif // @} End Half kernel: SkipLayerNormKernel2
|
||||
|
||||
template <typename T>
|
||||
void SkipLayerNormFunctor<T>::operator()(const int num,
|
||||
const int hidden,
|
||||
const T *input1,
|
||||
const T *input2,
|
||||
const T *scale,
|
||||
const T *bias,
|
||||
T *output,
|
||||
float eps,
|
||||
gpuStream_t stream) {
|
||||
int block = num / hidden;
|
||||
if (hidden <= WARP_SIZE) {
|
||||
const int threads = WARP_SIZE;
|
||||
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
|
||||
num, hidden, input1, input2, output, scale, bias, eps);
|
||||
} else if (hidden <= 128) {
|
||||
const int threads = 128;
|
||||
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
|
||||
num, hidden, input1, input2, output, scale, bias, eps);
|
||||
} else if (hidden == 384) {
|
||||
const int threads = 384;
|
||||
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
|
||||
num, hidden, input1, input2, output, scale, bias, eps);
|
||||
} else {
|
||||
const int threads = 256;
|
||||
if (hidden % 2 == 0) {
|
||||
if (std::is_same<T, float>::value) {
|
||||
SkipLayerNormKernel2<float, float2, threads>
|
||||
<<<block, threads, 0, stream>>>(
|
||||
num,
|
||||
hidden / 2,
|
||||
reinterpret_cast<const float2 *>(input1),
|
||||
reinterpret_cast<const float2 *>(input2),
|
||||
reinterpret_cast<float2 *>(output),
|
||||
reinterpret_cast<const float2 *>(scale),
|
||||
reinterpret_cast<const float2 *>(bias),
|
||||
eps);
|
||||
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
|
||||
#ifndef __HIPCC__
|
||||
} else if (std::is_same<T, __half>::value) {
|
||||
SkipLayerNormKernel2<__half, __half2, threads>
|
||||
<<<block, threads, 0, stream>>>(
|
||||
num,
|
||||
hidden / 2,
|
||||
reinterpret_cast<const __half2 *>(input1),
|
||||
reinterpret_cast<const __half2 *>(input2),
|
||||
reinterpret_cast<__half2 *>(output),
|
||||
reinterpret_cast<const __half2 *>(scale),
|
||||
reinterpret_cast<const __half2 *>(bias),
|
||||
eps);
|
||||
#endif
|
||||
} else {
|
||||
assert(false);
|
||||
// should not be here
|
||||
}
|
||||
} else {
|
||||
SkipLayerNormKernel<T, threads><<<block, threads, 0, stream>>>(
|
||||
num, hidden, input1, input2, output, scale, bias, eps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template class SkipLayerNormFunctor<float>;
|
||||
|
||||
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
template class SkipLayerNormFunctor<half>;
|
||||
#endif
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2024 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
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime.h>
|
||||
#endif
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
#include "paddle/phi/backends/all_context.h"
|
||||
#include "paddle/phi/kernels/funcs/cub.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template <typename T>
|
||||
struct CUDATypeTraits;
|
||||
|
||||
template <>
|
||||
struct CUDATypeTraits<half> {
|
||||
typedef phi::float16 TYPE;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CUDATypeTraits<float> {
|
||||
typedef float TYPE;
|
||||
};
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
// This functor involves a fusion calculation in Ernie or Bert.
|
||||
// The fusion mode is as follows:
|
||||
//
|
||||
// | |
|
||||
// other_op1 other_op2
|
||||
// | |
|
||||
// |------elementwise_add
|
||||
// |
|
||||
// layer_norm
|
||||
// |
|
||||
// other_op3
|
||||
// |
|
||||
|
||||
template <typename T>
|
||||
class SkipLayerNormFunctor {
|
||||
public:
|
||||
void operator()(const int num,
|
||||
const int hidden,
|
||||
const T *input1,
|
||||
const T *input2,
|
||||
const T *scale,
|
||||
const T *bias,
|
||||
T *output,
|
||||
float eps,
|
||||
gpuStream_t stream);
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2024 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
|
||||
#define BLOOMFILTER_MAGIC_NUM_NEW 17070416
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(_WIN32)
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // msvc max/min macro conflict with std::min/max
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct bloomfilter {
|
||||
uint64_t magic_num;
|
||||
uint64_t m;
|
||||
uint64_t k;
|
||||
uint64_t count;
|
||||
unsigned char bit_vector[1];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline int bloomfilter_get(const struct bloomfilter *bloomfilter,
|
||||
const void *key,
|
||||
size_t len);
|
||||
inline int bloomfilter_check(struct bloomfilter *filter);
|
||||
|
||||
#define bit_get(v, n) ((v)[(n) >> 3] & (0x1 << (0x7 - ((n)&0x7))))
|
||||
#define ROTL64(x, r) (((x) << (r)) | ((x) >> (64 - (r))))
|
||||
#define BIG_CONSTANT(x) (x##LLU)
|
||||
|
||||
uint64_t fmix64(uint64_t k) {
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
|
||||
k ^= k >> 33;
|
||||
return k;
|
||||
}
|
||||
|
||||
void murmurhash3_x64_128(const void *key,
|
||||
const int len,
|
||||
const uint32_t seed,
|
||||
void *out) {
|
||||
const uint8_t *data = (const uint8_t *)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
int i = 0;
|
||||
|
||||
const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
|
||||
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint64_t *blocks = (const uint64_t *)(data);
|
||||
|
||||
uint64_t k1;
|
||||
uint64_t k2;
|
||||
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
k1 = blocks[i * 2 + 0];
|
||||
k2 = blocks[i * 2 + 1];
|
||||
|
||||
k1 *= c1;
|
||||
k1 = ROTL64(k1, 31);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
|
||||
h1 = ROTL64(h1, 27);
|
||||
h1 += h2;
|
||||
h1 = h1 * 5 + 0x52dce729;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = ROTL64(k2, 33);
|
||||
k2 *= c1;
|
||||
h2 ^= k2;
|
||||
|
||||
h2 = ROTL64(h2, 31);
|
||||
h2 += h1;
|
||||
h2 = h2 * 5 + 0x38495ab5;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
|
||||
uint64_t nk1 = 0;
|
||||
uint64_t nk2 = 0;
|
||||
|
||||
uint64_t tail0_64 = *(uint64_t *)(tail); // NOLINT
|
||||
uint64_t tail_64 = *(uint64_t *)(tail + 8); // NOLINT
|
||||
uint64_t mask0 = 0xffffffffffffffff;
|
||||
uint64_t mask = 0x00ffffffffffffff;
|
||||
|
||||
int flag = len & 15;
|
||||
if (flag && flag <= 8) {
|
||||
tail0_64 &= (mask0 >> ((8 - flag) << 3));
|
||||
} else if (flag > 8) {
|
||||
tail_64 &= (mask >> ((15 - flag) << 3));
|
||||
nk2 ^= tail_64;
|
||||
nk2 *= c2;
|
||||
nk2 = ROTL64(nk2, 33);
|
||||
nk2 *= c1;
|
||||
h2 ^= nk2;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
nk1 ^= tail0_64;
|
||||
nk1 *= c1;
|
||||
nk1 = ROTL64(nk1, 31);
|
||||
nk1 *= c2;
|
||||
h1 ^= nk1;
|
||||
}
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix64(h1);
|
||||
h2 = fmix64(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
reinterpret_cast<uint64_t *>(out)[0] = h1;
|
||||
reinterpret_cast<uint64_t *>(out)[1] = h2;
|
||||
}
|
||||
|
||||
inline int bloomfilter_check(struct bloomfilter *filter) {
|
||||
if (filter->magic_num == BLOOMFILTER_MAGIC_NUM_NEW) {
|
||||
return 1;
|
||||
} else {
|
||||
fprintf(stderr, "error magic_num, %" PRIu64 "\n", filter->magic_num);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline int bloomfilter_get(const struct bloomfilter *bloomfilter,
|
||||
const void *key,
|
||||
size_t len) {
|
||||
uint32_t i;
|
||||
uint64_t result[2];
|
||||
|
||||
for (i = 0; i < bloomfilter->k; i++) {
|
||||
murmurhash3_x64_128(key, len, i, &result);
|
||||
result[0] %= bloomfilter->m;
|
||||
result[1] %= bloomfilter->m;
|
||||
if (!bit_get(bloomfilter->bit_vector, result[0])) {
|
||||
return 0;
|
||||
}
|
||||
if (!bit_get(bloomfilter->bit_vector, result[1])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/context_project.h"
|
||||
#include "paddle/phi/backends/cpu/cpu_context.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template class ContextProjectFunctor<CPUContext, float>;
|
||||
template class ContextProjectFunctor<CPUContext, double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/context_project.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template class ContextProjectFunctor<GPUContext, float>;
|
||||
template class ContextProjectFunctor<GPUContext, double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2024 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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/enforce.h"
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
#include "paddle/phi/kernels/funcs/blas/blas.h"
|
||||
#include "paddle/phi/kernels/funcs/im2col.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
/*
|
||||
* \brief Context projection concatenates features in adjacent time-steps in
|
||||
* a sequence. The i-th row of the output is the concatenation of
|
||||
* context_length rows of the input. The context_length rows are the
|
||||
* consecutive rows from the i+shift_start row.
|
||||
* ContextProjectGradFunctor is the inverse process of ContextProjectFunctor.
|
||||
*
|
||||
* \param in Input data.
|
||||
* \param Shape The shape of Input data:
|
||||
* [mini-batch, input_hidden_size].
|
||||
*
|
||||
* \param padding_data Padding data.
|
||||
* \param Shape The shape of Padding data:
|
||||
* [up_pad + down_pad, input_hidden_size].
|
||||
*
|
||||
* \param col Col data.
|
||||
* \param Shape The shape of Col data:
|
||||
* [mini-batch, context_length * input_hidden_size].
|
||||
*
|
||||
* For a mini-batch of 2 variable lengths sentences, containing 3, and 1
|
||||
* time-steps:
|
||||
*
|
||||
* Assumed input (X) is a [4, M, N] float DenseTensor, and X->lod()[0] =
|
||||
* [0, 3, 4]. Besides, for the sake of simplicity, we assume M=1 and N=2.
|
||||
*
|
||||
* X = [[a1, a2;
|
||||
* b1, b2;
|
||||
* c1, c2]
|
||||
* [d1, d2]]
|
||||
*
|
||||
* This is to say that input (X) has 4 words and the dimension of each word
|
||||
* representation is 2.
|
||||
*
|
||||
* - Case1:
|
||||
* If context_start is -1 and padding_trainable is false, we use zero to pad
|
||||
* instead of learned weight to pad,
|
||||
* and the context_length is 3, the output (Out) is:
|
||||
*
|
||||
* Out =[[0, 0, a1, a2, b1, b2;
|
||||
* a1, a2, b1, b2, c1, c2;
|
||||
* b1, b2, c1, c2, 0, 0 ]
|
||||
* [0, 0, d1, d2, 0, 0 ]]
|
||||
*
|
||||
* - Case2:
|
||||
* If context_start is -1 and padding_trainable is true, we use learned weight
|
||||
* to pad,
|
||||
* and the context_length is 3, the output (Out) is:
|
||||
*
|
||||
* Out = [[w1, w2, a1, a2, b1, b2;
|
||||
* a1, a2, b1, b2, c1, c2;
|
||||
* b1, b2, c1, c2, w3, w4]
|
||||
* [w1, w2, d1, d2, w3, w4]]
|
||||
*
|
||||
*/
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class ContextProjectFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& in,
|
||||
const DenseTensor* padding_data,
|
||||
bool padding_trainable,
|
||||
const int context_start,
|
||||
const int context_length,
|
||||
const int context_stride,
|
||||
const int up_pad,
|
||||
const int down_pad,
|
||||
DenseTensor* col) {
|
||||
auto lod_level_0 = in.lod()[0];
|
||||
|
||||
funcs::Im2ColFunctor<funcs::ColFormat::OCF, DeviceContext, float>
|
||||
im2col_ocf;
|
||||
|
||||
std::vector<int> dilation({1, 1});
|
||||
std::vector<int> padding({up_pad, 0, down_pad, 0});
|
||||
std::vector<int> stride({context_stride, 1});
|
||||
|
||||
int input_row_begin, input_row_end;
|
||||
int sequence_height;
|
||||
int64_t sequence_width = in.dims()[1];
|
||||
|
||||
for (int i = 0; i < static_cast<int>(lod_level_0.size()) - 1; ++i) {
|
||||
if (lod_level_0[i] == lod_level_0[i + 1]) continue;
|
||||
|
||||
input_row_begin = (context_start > 0)
|
||||
? static_cast<int>(lod_level_0[i]) + context_start
|
||||
: static_cast<int>(lod_level_0[i]);
|
||||
input_row_end = static_cast<int>(lod_level_0[i + 1]);
|
||||
|
||||
DenseTensor out_t = col->Slice(static_cast<int>(lod_level_0[i]),
|
||||
static_cast<int>(lod_level_0[i + 1]));
|
||||
|
||||
sequence_height = static_cast<int>(out_t.dims()[0]);
|
||||
|
||||
if (input_row_begin < input_row_end) {
|
||||
DenseTensor in_t = in.Slice(input_row_begin, input_row_end);
|
||||
|
||||
std::vector<int64_t> output_shape(
|
||||
{sequence_height,
|
||||
1,
|
||||
1,
|
||||
context_length,
|
||||
sequence_width}); // output_height, output_width,
|
||||
// input_channels, filter_height, filter_width
|
||||
out_t.Resize(output_shape);
|
||||
|
||||
std::vector<int64_t> input_shape(
|
||||
{1,
|
||||
input_row_end - input_row_begin,
|
||||
sequence_width}); // input_channels, input_height, input_width
|
||||
in_t.Resize(input_shape);
|
||||
im2col_ocf(dev_ctx, in_t, dilation, stride, padding, &out_t);
|
||||
out_t.Resize({sequence_height, context_length * sequence_width});
|
||||
}
|
||||
}
|
||||
if (padding_trainable) {
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
padding_data,
|
||||
common::errors::InvalidArgument(
|
||||
"The input tensor 'padding_data' should not be NULL."));
|
||||
for (int i = 0; i < static_cast<int>(lod_level_0.size()) - 1; ++i) {
|
||||
if (lod_level_0[i] == lod_level_0[i + 1]) continue;
|
||||
|
||||
DenseTensor out_t = col->Slice(static_cast<int>(lod_level_0[i]),
|
||||
static_cast<int>(lod_level_0[i + 1]));
|
||||
|
||||
sequence_height = static_cast<int>(out_t.dims()[0]);
|
||||
|
||||
// add up trainable data
|
||||
out_t.Resize({static_cast<int64_t>(sequence_height) * context_length,
|
||||
sequence_width});
|
||||
|
||||
if (up_pad > 0) { // add up pad
|
||||
int padding_rows = std::min(
|
||||
up_pad, static_cast<int>(lod_level_0[i + 1] - lod_level_0[i]));
|
||||
|
||||
for (int k = 0; k < padding_rows; ++k) {
|
||||
int padding_size =
|
||||
k + context_length < up_pad ? context_length : up_pad - k;
|
||||
DenseTensor out_t_sub = out_t.Slice(
|
||||
k * context_length, k * context_length + padding_size);
|
||||
DenseTensor w_sub = padding_data->Slice(k, k + padding_size);
|
||||
phi::Copy(dev_ctx, w_sub, dev_ctx.GetPlace(), false, &out_t_sub);
|
||||
}
|
||||
}
|
||||
if (down_pad > 0) { // add down pad
|
||||
int down_pad_begin_row =
|
||||
std::max(0,
|
||||
(sequence_height - context_start - context_length) + 1) +
|
||||
1;
|
||||
int padding_begin = std::max(0, context_start - sequence_height);
|
||||
int padding_size =
|
||||
sequence_height - context_start >= context_length
|
||||
? 1
|
||||
: context_length - (sequence_height - context_start);
|
||||
if (context_start >= sequence_height) padding_size = context_length;
|
||||
int padding_idx = padding_begin;
|
||||
for (int t = 0; t + down_pad_begin_row <= sequence_height;
|
||||
++t, ++padding_size) {
|
||||
if (context_start >= sequence_height) padding_size = context_length;
|
||||
if (padding_size > context_length) {
|
||||
padding_size = context_length;
|
||||
padding_idx++;
|
||||
}
|
||||
if (padding_begin > 0 || sequence_height == context_start)
|
||||
padding_idx = padding_begin + t;
|
||||
|
||||
DenseTensor out_t_sub = out_t.Slice(
|
||||
(down_pad_begin_row + t) * context_length - padding_size,
|
||||
(down_pad_begin_row + t) * context_length);
|
||||
DenseTensor w_sub = padding_data->Slice(
|
||||
up_pad + padding_idx, up_pad + padding_idx + padding_size);
|
||||
phi::Copy(dev_ctx, w_sub, dev_ctx.GetPlace(), false, &out_t_sub);
|
||||
}
|
||||
}
|
||||
out_t.Resize({sequence_height,
|
||||
static_cast<int64_t>(context_length) * sequence_width});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class ContextProjectGradFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& in,
|
||||
bool padding_trainable,
|
||||
const int context_start,
|
||||
const int context_length,
|
||||
const int context_stride,
|
||||
const int up_pad,
|
||||
const int down_pad,
|
||||
bool pad_grad,
|
||||
bool input_grad,
|
||||
DenseTensor* padding_data,
|
||||
DenseTensor* col) {
|
||||
auto lod_level_0 = in.lod()[0];
|
||||
|
||||
funcs::Col2ImFunctor<funcs::ColFormat::OCF, DeviceContext, float>
|
||||
col2im_ocf;
|
||||
|
||||
std::vector<int> dilation({1, 1});
|
||||
std::vector<int> padding({up_pad, 0, down_pad, 0});
|
||||
std::vector<int> stride({context_stride, 1});
|
||||
|
||||
int input_row_begin, input_row_end;
|
||||
int sequence_height;
|
||||
int64_t sequence_width = in.dims()[1];
|
||||
auto blas = funcs::GetBlas<DeviceContext, T>(dev_ctx);
|
||||
|
||||
if (input_grad) {
|
||||
for (int i = 0; i < static_cast<int>(lod_level_0.size()) - 1; ++i) {
|
||||
if (lod_level_0[i] == lod_level_0[i + 1]) continue;
|
||||
|
||||
input_row_begin = (context_start > 0)
|
||||
? static_cast<int>(lod_level_0[i]) + context_start
|
||||
: static_cast<int>(lod_level_0[i]);
|
||||
input_row_end = static_cast<int>(lod_level_0[i + 1]);
|
||||
|
||||
DenseTensor out_t = col->Slice(static_cast<int>(lod_level_0[i]),
|
||||
static_cast<int>(lod_level_0[i + 1]));
|
||||
|
||||
sequence_height = static_cast<int>(out_t.dims()[0]);
|
||||
|
||||
if (input_row_begin < input_row_end) {
|
||||
DenseTensor in_t = in.Slice(input_row_begin, input_row_end);
|
||||
|
||||
std::vector<int64_t> output_shape(
|
||||
{sequence_height,
|
||||
1,
|
||||
1,
|
||||
context_length,
|
||||
sequence_width}); // output_height, output_width,
|
||||
// input_channels, filter_height, filter_width
|
||||
out_t.Resize(output_shape);
|
||||
|
||||
std::vector<int64_t> input_shape(
|
||||
{1,
|
||||
input_row_end - input_row_begin,
|
||||
sequence_width}); // input_channels, input_height, input_width
|
||||
in_t.Resize(input_shape);
|
||||
|
||||
col2im_ocf(dev_ctx, out_t, dilation, stride, padding, &in_t);
|
||||
out_t.Resize({sequence_height, context_length * sequence_width});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pad_grad) {
|
||||
if (padding_trainable) {
|
||||
for (int i = 0; i < static_cast<int>(lod_level_0.size()) - 1; ++i) {
|
||||
if (lod_level_0[i] == lod_level_0[i + 1]) continue;
|
||||
|
||||
DenseTensor out_t = col->Slice(static_cast<int>(lod_level_0[i]),
|
||||
static_cast<int>(lod_level_0[i + 1]));
|
||||
|
||||
sequence_height = static_cast<int>(out_t.dims()[0]);
|
||||
out_t.Resize({static_cast<int64_t>(sequence_height) * context_length,
|
||||
sequence_width});
|
||||
|
||||
if (up_pad > 0) {
|
||||
int padding_rows = std::min(
|
||||
up_pad, static_cast<int>(lod_level_0[i + 1] - lod_level_0[i]));
|
||||
|
||||
for (int k = 0; k < padding_rows; ++k) {
|
||||
int padding_size =
|
||||
k + context_length < up_pad ? context_length : up_pad - k;
|
||||
DenseTensor out_t_sub = out_t.Slice(
|
||||
k * context_length, k * context_length + padding_size);
|
||||
DenseTensor w_sub = padding_data->Slice(k, k + padding_size);
|
||||
PADDLE_ENFORCE_LE_INT_MAX(w_sub.numel(),
|
||||
"context_project AXPY size");
|
||||
blas.AXPY(static_cast<int>(w_sub.numel()),
|
||||
static_cast<T>(1),
|
||||
out_t_sub.data<T>(),
|
||||
w_sub.data<T>());
|
||||
}
|
||||
}
|
||||
if (down_pad > 0) {
|
||||
int down_pad_begin_row =
|
||||
std::max(
|
||||
0, (sequence_height - context_start - context_length) + 1) +
|
||||
1;
|
||||
int padding_begin = std::max(0, context_start - sequence_height);
|
||||
int padding_size =
|
||||
sequence_height - context_start >= context_length
|
||||
? 1
|
||||
: context_length - (sequence_height - context_start);
|
||||
if (context_start >= sequence_height) padding_size = context_length;
|
||||
int padding_idx = padding_begin;
|
||||
for (int t = 0; t + down_pad_begin_row <= sequence_height;
|
||||
++t, ++padding_size) {
|
||||
if (context_start >= sequence_height)
|
||||
padding_size = context_length;
|
||||
if (padding_size > context_length) {
|
||||
padding_size = context_length;
|
||||
padding_idx++;
|
||||
}
|
||||
if (padding_begin > 0 || sequence_height == context_start)
|
||||
padding_idx = padding_begin + t;
|
||||
|
||||
DenseTensor out_t_sub = out_t.Slice(
|
||||
(down_pad_begin_row + t) * context_length - padding_size,
|
||||
(down_pad_begin_row + t) * context_length);
|
||||
DenseTensor w_sub = padding_data->Slice(
|
||||
up_pad + padding_idx, up_pad + padding_idx + padding_size);
|
||||
PADDLE_ENFORCE_LE_INT_MAX(w_sub.numel(),
|
||||
"context_project AXPY size");
|
||||
blas.AXPY(static_cast<int>(w_sub.numel()),
|
||||
static_cast<T>(1),
|
||||
out_t_sub.data<T>(),
|
||||
w_sub.data<T>());
|
||||
}
|
||||
}
|
||||
out_t.Resize({sequence_height,
|
||||
static_cast<int64_t>(context_length) * sequence_width});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/cos_sim_functor.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
template <typename T>
|
||||
struct CosSimDyFunctor<CPUContext, T> {
|
||||
void operator()(const CPUContext& dev_ctx,
|
||||
const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
const size_t rows,
|
||||
const size_t cols,
|
||||
T* dy) const {
|
||||
for (size_t row_id = 0; row_id < rows; ++row_id) {
|
||||
auto xy_norm_prod = x_norm[row_id] * y_norm[0];
|
||||
auto dz_data = dz[row_id];
|
||||
auto z_data = z[row_id];
|
||||
auto* x_data = x + cols * row_id;
|
||||
auto reciprocal_xy_norm_prod = 1 / xy_norm_prod;
|
||||
|
||||
auto y_norm_square = y_norm[0] * y_norm[0];
|
||||
auto reciprocal_y_norm_square = 1 / y_norm_square;
|
||||
for (size_t i = 0; i < cols; ++i) {
|
||||
dy[i] += dz_data * (x_data[i] * reciprocal_xy_norm_prod -
|
||||
z_data * y[i] * reciprocal_y_norm_square);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template struct CosSimDyFunctor<CPUContext, float>;
|
||||
template struct CosSimDyFunctor<CPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/cos_sim_functor.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_primitives.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template <typename T>
|
||||
__global__ void CosSimDyKernel(const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
const size_t rows,
|
||||
const size_t cols,
|
||||
T* dy) {
|
||||
int grid_size = blockDim.x * gridDim.x;
|
||||
T y_norm_data = y_norm[0];
|
||||
for (size_t row_id =
|
||||
static_cast<size_t>(blockIdx.x) * static_cast<size_t>(blockDim.x) +
|
||||
static_cast<size_t>(threadIdx.x);
|
||||
row_id < rows;
|
||||
row_id += grid_size) {
|
||||
T xy_norm_prod = x_norm[row_id] * y_norm_data;
|
||||
T dz_data = dz[row_id];
|
||||
T z_data = z[row_id];
|
||||
const T* x_data = x + cols * row_id;
|
||||
T reciprocal_xy_norm_prod = 1 / xy_norm_prod;
|
||||
|
||||
T y_norm_square = y_norm_data * y_norm_data;
|
||||
T reciprocal_y_norm_square = 1 / y_norm_square;
|
||||
for (size_t i = 0; i < cols; ++i) {
|
||||
T dy_data = dz_data * (x_data[i] * reciprocal_xy_norm_prod -
|
||||
z_data * y[i] * reciprocal_y_norm_square);
|
||||
CudaAtomicAdd(dy + i, dy_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct CosSimDyFunctor<GPUContext, T> {
|
||||
void operator()(const GPUContext& dev_ctx,
|
||||
const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
const size_t rows,
|
||||
const size_t cols,
|
||||
T* dy) const {
|
||||
const int block_size = 512;
|
||||
dim3 threads(block_size, 1);
|
||||
dim3 grid((rows + block_size - 1) / block_size, 1);
|
||||
CosSimDyKernel<T><<<grid, threads, 0, dev_ctx.stream()>>>(
|
||||
x_norm, y_norm, x, y, z, dz, rows, cols, dy);
|
||||
}
|
||||
};
|
||||
|
||||
template struct CosSimDyFunctor<GPUContext, float>;
|
||||
template struct CosSimDyFunctor<GPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2024 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "paddle/common/hostdevice.h"
|
||||
#include "paddle/phi/backends/all_context.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
template <typename T, bool same_row>
|
||||
struct CosSimFunctor {
|
||||
CosSimFunctor(const T* x, const T* y, T* x_norm, T* y_norm, T* z, int cols)
|
||||
: x_norm_(x_norm),
|
||||
y_norm_(y_norm),
|
||||
x_(x),
|
||||
y_(y),
|
||||
z_(z),
|
||||
cols_(static_cast<size_t>(cols)) {}
|
||||
|
||||
inline HOSTDEVICE void operator()(size_t row_id) const {
|
||||
auto* x = x_ + cols_ * row_id;
|
||||
T xx = 0, xy = 0, yy = 0;
|
||||
T eps = 1e-8;
|
||||
if (same_row) {
|
||||
auto* y = y_ + cols_ * row_id;
|
||||
T tep_x, tep_y;
|
||||
for (size_t i = 0; i < cols_; ++i) {
|
||||
tep_x = x[i];
|
||||
tep_y = y[i];
|
||||
xx += tep_x * tep_x;
|
||||
|
||||
yy += tep_y * tep_y;
|
||||
xy += tep_x * tep_y;
|
||||
}
|
||||
xx = xx > eps ? xx : eps;
|
||||
yy = yy > eps ? yy : eps;
|
||||
xx = sqrt(xx);
|
||||
yy = sqrt(yy);
|
||||
y_norm_[row_id] = yy;
|
||||
x_norm_[row_id] = xx;
|
||||
z_[row_id] = xy / (xx * yy);
|
||||
} else { // This can be wrote in a better way.
|
||||
T tep_x, tep_y;
|
||||
for (size_t i = 0; i < cols_; ++i) {
|
||||
tep_x = x[i];
|
||||
tep_y = y_[i];
|
||||
xx += tep_x * tep_x;
|
||||
yy += tep_y * tep_y;
|
||||
xy += tep_x * tep_y;
|
||||
}
|
||||
xx = xx > eps ? xx : eps;
|
||||
yy = yy > eps ? yy : eps;
|
||||
xx = sqrt(xx);
|
||||
yy = sqrt(yy);
|
||||
if (row_id == 0) y_norm_[0] = yy;
|
||||
x_norm_[row_id] = xx;
|
||||
z_[row_id] = xy / (xx * yy);
|
||||
}
|
||||
}
|
||||
|
||||
T* x_norm_;
|
||||
T* y_norm_;
|
||||
const T* x_;
|
||||
const T* y_;
|
||||
T* z_;
|
||||
const size_t cols_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CosSimGradFunctor {
|
||||
CosSimGradFunctor(const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
T* dx,
|
||||
int cols)
|
||||
: x_norm_(x_norm),
|
||||
y_norm_(y_norm),
|
||||
x_(x),
|
||||
y_(y),
|
||||
z_(z),
|
||||
dz_(dz),
|
||||
dx_(dx),
|
||||
cols_(static_cast<size_t>(cols)) {}
|
||||
|
||||
inline HOSTDEVICE void operator()(size_t row_id) const {
|
||||
auto x_norm_square = x_norm_[row_id] * x_norm_[row_id];
|
||||
auto xy_norm_prod = x_norm_[row_id] * y_norm_[row_id];
|
||||
auto dz = dz_[row_id];
|
||||
auto z = z_[row_id];
|
||||
|
||||
auto* dx = dx_ + cols_ * row_id;
|
||||
auto* x = x_ + cols_ * row_id;
|
||||
auto* y = y_ + cols_ * row_id;
|
||||
|
||||
auto reciprocal_xy_norm_prod = 1 / xy_norm_prod;
|
||||
auto reciprocal_x_norm_square = 1 / x_norm_square;
|
||||
for (size_t i = 0; i < cols_; ++i) {
|
||||
dx[i] = dz * (y[i] * reciprocal_xy_norm_prod -
|
||||
z * x[i] * reciprocal_x_norm_square);
|
||||
}
|
||||
}
|
||||
|
||||
const T* x_norm_;
|
||||
const T* y_norm_;
|
||||
const T* x_;
|
||||
const T* y_;
|
||||
const T* z_;
|
||||
const T* dz_;
|
||||
T* dx_;
|
||||
const size_t cols_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CosSimDxFunctor {
|
||||
CosSimDxFunctor(const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
T* dx,
|
||||
int cols)
|
||||
: x_norm_(x_norm),
|
||||
y_norm_(y_norm),
|
||||
x_(x),
|
||||
y_(y),
|
||||
z_(z),
|
||||
dz_(dz),
|
||||
dx_(dx),
|
||||
cols_(static_cast<size_t>(cols)) {}
|
||||
|
||||
inline HOSTDEVICE void operator()(size_t row_id) const {
|
||||
auto xy_norm_prod = x_norm_[row_id] * y_norm_[0];
|
||||
auto dz = dz_[row_id];
|
||||
auto z = z_[row_id];
|
||||
auto* x = x_ + cols_ * row_id;
|
||||
auto reciprocal_xy_norm_prod = 1 / xy_norm_prod;
|
||||
auto x_norm_square = x_norm_[row_id] * x_norm_[row_id];
|
||||
auto* dx = dx_ + cols_ * row_id;
|
||||
auto reciprocal_x_norm_square = 1 / x_norm_square;
|
||||
|
||||
for (size_t i = 0; i < cols_; ++i) {
|
||||
dx[i] = dz * (y_[i] * reciprocal_xy_norm_prod -
|
||||
z * x[i] * reciprocal_x_norm_square);
|
||||
}
|
||||
}
|
||||
const T* x_norm_;
|
||||
const T* y_norm_;
|
||||
const T* x_;
|
||||
const T* y_;
|
||||
const T* z_;
|
||||
const T* dz_;
|
||||
T* dx_;
|
||||
const size_t cols_;
|
||||
};
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
struct CosSimDyFunctor {
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const T* x_norm,
|
||||
const T* y_norm,
|
||||
const T* x,
|
||||
const T* y,
|
||||
const T* z,
|
||||
const T* dz,
|
||||
const size_t rows,
|
||||
const size_t cols,
|
||||
T* dy) const;
|
||||
};
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/prelu.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
#define CUDA_NUM_THREADS 1024
|
||||
|
||||
inline static int PADDLE_GET_BLOCKS(const int N) {
|
||||
return (N + CUDA_NUM_THREADS - 1) / CUDA_NUM_THREADS;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void PReluChannelFirstWiseKernel(const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t channel_num,
|
||||
size_t plane_size,
|
||||
size_t numel) {
|
||||
CUDA_KERNEL_LOOP(index, numel) {
|
||||
size_t temp = index / plane_size;
|
||||
size_t channel_index = temp % channel_num;
|
||||
T scale = alpha[channel_index];
|
||||
T x = input[index];
|
||||
T zero = static_cast<T>(0);
|
||||
output[index] = (x > zero) ? x : scale * x;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void PReluChannelLastWiseKernel(const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t channel_num,
|
||||
size_t numel) {
|
||||
CUDA_KERNEL_LOOP(index, numel) {
|
||||
size_t channel_index = index % channel_num;
|
||||
T scale = alpha[channel_index];
|
||||
T x = input[index];
|
||||
T zero = static_cast<T>(0);
|
||||
output[index] = (x > zero) ? x : scale * x;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void PReluElementWiseKernel(const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t spatial_size,
|
||||
size_t numel) {
|
||||
CUDA_KERNEL_LOOP(index, numel) {
|
||||
size_t element_index = index % spatial_size;
|
||||
T scale = alpha[element_index];
|
||||
T x = input[index];
|
||||
T zero = static_cast<T>(0);
|
||||
output[index] = (x > zero) ? x : scale * x;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void PReluScalarKernel(const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t numel) {
|
||||
T scale = alpha[0];
|
||||
CUDA_KERNEL_LOOP(index, numel) {
|
||||
T x = input[index];
|
||||
T zero = static_cast<T>(0);
|
||||
output[index] = (x > zero) ? x : scale * x;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PreluChannelWiseDirectCUDAFunctor<T>::operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t batch_size,
|
||||
size_t channel,
|
||||
bool channel_last,
|
||||
size_t numel) {
|
||||
if (channel_last) {
|
||||
PReluChannelLastWiseKernel<<<PADDLE_GET_BLOCKS(numel),
|
||||
CUDA_NUM_THREADS,
|
||||
0,
|
||||
stream>>>(
|
||||
input, alpha, output, channel, numel);
|
||||
} else {
|
||||
PReluChannelFirstWiseKernel<<<PADDLE_GET_BLOCKS(numel),
|
||||
CUDA_NUM_THREADS,
|
||||
0,
|
||||
stream>>>(
|
||||
input, alpha, output, channel, numel / batch_size / channel, numel);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PreluElementWiseDirectCUDAFunctor<T>::operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t batch_size,
|
||||
size_t numel) {
|
||||
PReluElementWiseKernel<<<PADDLE_GET_BLOCKS(numel),
|
||||
CUDA_NUM_THREADS,
|
||||
0,
|
||||
stream>>>(
|
||||
input, alpha, output, numel / batch_size, numel);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PreluScalarDirectCUDAFunctor<T>::operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t numel) {
|
||||
PReluScalarKernel<<<PADDLE_GET_BLOCKS(numel), CUDA_NUM_THREADS, 0, stream>>>(
|
||||
input, alpha, output, numel);
|
||||
}
|
||||
|
||||
template class PreluChannelWiseDirectCUDAFunctor<float>;
|
||||
template class PreluChannelWiseDirectCUDAFunctor<phi::float16>;
|
||||
template class PreluChannelWiseDirectCUDAFunctor<phi::bfloat16>;
|
||||
template class PreluChannelWiseDirectCUDAFunctor<double>;
|
||||
|
||||
template class PreluElementWiseDirectCUDAFunctor<float>;
|
||||
template class PreluElementWiseDirectCUDAFunctor<phi::float16>;
|
||||
template class PreluElementWiseDirectCUDAFunctor<phi::bfloat16>;
|
||||
template class PreluElementWiseDirectCUDAFunctor<double>;
|
||||
|
||||
template class PreluScalarDirectCUDAFunctor<float>;
|
||||
template class PreluScalarDirectCUDAFunctor<phi::float16>;
|
||||
template class PreluScalarDirectCUDAFunctor<phi::bfloat16>;
|
||||
template class PreluScalarDirectCUDAFunctor<double>;
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2024 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 <vector>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_dnn.h"
|
||||
#include "paddle/phi/kernels/funcs/math_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
template <typename T>
|
||||
class PreluChannelWiseDirectCUDAFunctor {
|
||||
public:
|
||||
void operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t batch_size,
|
||||
size_t channel,
|
||||
bool channel_last,
|
||||
size_t numel);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PreluElementWiseDirectCUDAFunctor {
|
||||
public:
|
||||
void operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t batch_size,
|
||||
size_t numel);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PreluScalarDirectCUDAFunctor {
|
||||
public:
|
||||
void operator()(gpuStream_t stream,
|
||||
const T *input,
|
||||
const T *alpha,
|
||||
T *output,
|
||||
size_t numel);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/sampler.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "paddle/phi/core/generator.h"
|
||||
|
||||
namespace phi::math {
|
||||
|
||||
Sampler::~Sampler() = default;
|
||||
|
||||
UniformSampler::UniformSampler(int64_t range, unsigned int seed)
|
||||
: Sampler(range, seed), inv_range_(1.0f / (range + 1)) { // NOLINT
|
||||
random_engine_ = phi::GetCPURandomEngine(seed_);
|
||||
dist_ = std::make_shared<std::uniform_int_distribution<>>(0, range);
|
||||
}
|
||||
|
||||
int64_t UniformSampler::Sample() const { return (*dist_)(*random_engine_); }
|
||||
|
||||
float UniformSampler::Probability(int64_t value) const { return inv_range_; }
|
||||
|
||||
LogUniformSampler::LogUniformSampler(int64_t range, unsigned int seed)
|
||||
: Sampler(range, seed), log_range_(log(range + 1)) { // NOLINT
|
||||
random_engine_ = phi::GetCPURandomEngine(seed_);
|
||||
dist_ = std::make_shared<std::uniform_real_distribution<>>(0, 1);
|
||||
}
|
||||
|
||||
int64_t LogUniformSampler::Sample() const {
|
||||
// Got Log Uniform distribution from uniform distribution by
|
||||
// inverse_transform_sampling method
|
||||
// More details:
|
||||
// https://wanghaoshuang.github.io/2017/11/Log-uniform-distribution-sampler/
|
||||
auto cur_random = (*dist_)(*random_engine_);
|
||||
const int64_t value = static_cast<int64_t>(exp(cur_random * log_range_)) - 1;
|
||||
// Mathematically, value should be <= range_, but might not be due to some
|
||||
// floating point roundoff, so we mod by range_.
|
||||
return value % range_;
|
||||
}
|
||||
|
||||
float LogUniformSampler::Probability(int64_t value) const {
|
||||
// Given f(x) = 1/[(x+1) * log_range_]
|
||||
// The value's probability is integral of f(x) from value to (value + 1)
|
||||
// More details:
|
||||
// https://wanghaoshuang.github.io/2017/11/Log-uniform-distribution-sampler
|
||||
return (log((value + 2.0) / (value + 1.0))) / log_range_; // NOLINT
|
||||
}
|
||||
|
||||
CustomSampler::CustomSampler(int64_t range,
|
||||
const float *probabilities,
|
||||
const int *alias,
|
||||
const float *alias_probabilities,
|
||||
unsigned int seed)
|
||||
: Sampler(range, seed) {
|
||||
random_engine_ = phi::GetCPURandomEngine(seed_);
|
||||
real_dist_ = std::make_shared<std::uniform_real_distribution<>>(0, 1);
|
||||
int_dist_ = std::make_shared<std::uniform_int_distribution<>>(0, range);
|
||||
|
||||
alias_probs_ = alias_probabilities;
|
||||
probs_ = probabilities;
|
||||
alias_ = alias;
|
||||
}
|
||||
|
||||
int64_t CustomSampler::Sample() const {
|
||||
auto index = (*int_dist_)(*random_engine_);
|
||||
auto p = (*real_dist_)(*random_engine_);
|
||||
if (p > alias_probs_[index]) {
|
||||
int alias = alias_[index];
|
||||
|
||||
if (alias == exceptional_val) {
|
||||
LOG(WARNING) << "WARNING: CustomSampler get alias " << exceptional_val;
|
||||
return index;
|
||||
}
|
||||
|
||||
return alias;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
float CustomSampler::Probability(int64_t value) const { return probs_[value]; }
|
||||
|
||||
} // namespace phi::math
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2024 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 <cstdint>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
|
||||
// TODO(wanghaoshuang): Support for GPU
|
||||
|
||||
/**
|
||||
* Sample integers from [0, range).
|
||||
*/
|
||||
class Sampler {
|
||||
public:
|
||||
explicit Sampler(int64_t range, unsigned int seed = 0UL) : range_(range) {
|
||||
PADDLE_ENFORCE_GT(
|
||||
range,
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"Range should be greater than 0, but received %d.", range));
|
||||
if (seed == 0) {
|
||||
std::random_device r;
|
||||
seed_ = r();
|
||||
} else {
|
||||
seed_ = seed;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Sampler();
|
||||
|
||||
// Sample a single value
|
||||
virtual int64_t Sample() const = 0;
|
||||
|
||||
// The probability that a single call to Sample() returns the given value.
|
||||
virtual float Probability(int64_t value) const = 0;
|
||||
|
||||
int64_t range() { return range_; }
|
||||
|
||||
protected:
|
||||
const int64_t range_;
|
||||
unsigned int seed_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample integers from [0, range).
|
||||
* And the distribution function is:
|
||||
* P(x) = 1 / range
|
||||
*/
|
||||
class UniformSampler : public Sampler {
|
||||
public:
|
||||
explicit UniformSampler(int64_t range, unsigned int seed = 0UL);
|
||||
|
||||
~UniformSampler() override {}
|
||||
|
||||
int64_t Sample() const override;
|
||||
|
||||
float Probability(int64_t value) const override;
|
||||
|
||||
private:
|
||||
const float inv_range_;
|
||||
std::shared_ptr<std::mt19937_64> random_engine_;
|
||||
std::shared_ptr<std::uniform_int_distribution<>> dist_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample integers from [0, range).
|
||||
* And the distribution function is:
|
||||
* P(x) = (1/ln(range+1)) * ln(1 + 1/(x + 1))
|
||||
*/
|
||||
class LogUniformSampler : public Sampler {
|
||||
public:
|
||||
explicit LogUniformSampler(int64_t range, unsigned int seed = 0UL);
|
||||
|
||||
~LogUniformSampler() override {}
|
||||
|
||||
int64_t Sample() const override;
|
||||
|
||||
float Probability(int64_t value) const override;
|
||||
|
||||
private:
|
||||
const float log_range_;
|
||||
std::shared_ptr<std::mt19937_64> random_engine_;
|
||||
std::shared_ptr<std::uniform_real_distribution<>> dist_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample integers from [0, range) from custom distribution.
|
||||
*/
|
||||
class CustomSampler : public Sampler {
|
||||
public:
|
||||
explicit CustomSampler(int64_t range,
|
||||
const float* probabilities,
|
||||
const int* alias,
|
||||
const float* alias_probabilities,
|
||||
unsigned int seed = 0UL);
|
||||
|
||||
~CustomSampler() override {}
|
||||
|
||||
int64_t Sample() const override;
|
||||
|
||||
float Probability(int64_t value) const override;
|
||||
|
||||
private:
|
||||
const float* alias_probs_;
|
||||
const int* alias_;
|
||||
const float* probs_;
|
||||
const int exceptional_val = -1;
|
||||
std::shared_ptr<std::mt19937_64> random_engine_;
|
||||
std::shared_ptr<std::uniform_real_distribution<>> real_dist_;
|
||||
std::shared_ptr<std::uniform_int_distribution<>> int_dist_;
|
||||
};
|
||||
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/tree2col.h"
|
||||
|
||||
#include <deque>
|
||||
#include <stack>
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
std::vector<TreeNode> Tree2ColUtil::construct_patch(
|
||||
size_t root, int max_depth, const std::vector<std::vector<int>> &tr) {
|
||||
std::stack<TreeNode, std::deque<TreeNode>> stack;
|
||||
std::unordered_map<int, bool> visited;
|
||||
std::vector<TreeNode> patch;
|
||||
|
||||
stack.emplace(root, 1, 1, 0);
|
||||
patch.emplace_back(root, 1, 1, 0);
|
||||
visited[static_cast<int>(root)] = true;
|
||||
|
||||
while (!stack.empty()) {
|
||||
TreeNode &u = stack.top();
|
||||
bool end = true;
|
||||
size_t node = u.get_node(), sz = tr[node].size();
|
||||
visited[static_cast<int>(node)] = true;
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
size_t v = tr[node][i];
|
||||
if (!visited[static_cast<int>(v)] &&
|
||||
static_cast<int>(u.get_depth()) + 1 < max_depth) {
|
||||
visited[static_cast<int>(v)] = true;
|
||||
stack.emplace(v, i, sz, u.get_depth() + 1);
|
||||
patch.emplace_back(v, i + 1, sz, u.get_depth() + 1);
|
||||
end = false;
|
||||
}
|
||||
}
|
||||
if (end) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
return patch;
|
||||
}
|
||||
|
||||
void Tree2ColUtil::construct_tree(const DenseTensor &EdgeSet,
|
||||
std::vector<std::vector<int>> *tr,
|
||||
size_t *node_count) {
|
||||
const auto &edge_set_dims = EdgeSet.dims();
|
||||
PADDLE_ENFORCE_EQ(edge_set_dims[1],
|
||||
2,
|
||||
common::errors::InvalidArgument(
|
||||
"The second dimension of the EdgeSet shall be 2, but "
|
||||
"got %ld != 2. Please check the input value.",
|
||||
edge_set_dims[1]));
|
||||
int64_t edge_count = EdgeSet.numel();
|
||||
|
||||
const int *edge_data = EdgeSet.data<int>();
|
||||
|
||||
for (int64_t i = 0; i < edge_count; i += 2) {
|
||||
int u = edge_data[i], v = edge_data[i + 1];
|
||||
if (u != 0 && v != 0) (*node_count)++;
|
||||
}
|
||||
(*node_count)++;
|
||||
|
||||
tr->resize(static_cast<size_t>(*node_count + 1));
|
||||
|
||||
for (int64_t i = 0; i < edge_count; i += 2) {
|
||||
int u = edge_data[i], v = edge_data[i + 1];
|
||||
if (u != 0 && v != 0) {
|
||||
tr->at(u).push_back(v);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Tree2ColFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext &dev_ctx,
|
||||
const DenseTensor &EdgeSet,
|
||||
const DenseTensor &node_features,
|
||||
DenseTensor *patch,
|
||||
int max_depth) {
|
||||
std::vector<std::vector<int>> tr;
|
||||
const auto &feature_dims = node_features.dims();
|
||||
funcs::SetConstant<CPUContext, T> constant;
|
||||
int64_t feature_size = feature_dims[1];
|
||||
size_t patch_elem_size = 3 * static_cast<size_t>(feature_size);
|
||||
size_t node_count = 0, patch_count = 0, patch_size = 0;
|
||||
Tree2ColUtil::construct_tree(EdgeSet, &tr, &node_count);
|
||||
std::vector<std::vector<TreeNode>> processing_list;
|
||||
for (size_t u = 1; u <= node_count; u++) {
|
||||
std::vector<TreeNode> temp_patch =
|
||||
Tree2ColUtil::construct_patch(u, max_depth, tr);
|
||||
if (!temp_patch.empty()) {
|
||||
processing_list.emplace_back(temp_patch);
|
||||
}
|
||||
}
|
||||
patch_size = processing_list.size();
|
||||
|
||||
patch->Resize({static_cast<int64_t>(patch_size),
|
||||
static_cast<int64_t>(patch_elem_size)});
|
||||
T *patch_data = dev_ctx.template Alloc<T>(patch);
|
||||
constant(dev_ctx, patch, 0);
|
||||
const T *features = node_features.data<T>();
|
||||
|
||||
for (auto &patch_item : processing_list) {
|
||||
size_t pointer_base = patch_count * patch_elem_size;
|
||||
for (auto &v : patch_item) {
|
||||
T eta_l = v.eta_l<T>(max_depth), eta_r = v.eta_r<T>(max_depth),
|
||||
eta_t = v.eta_t<T>(max_depth);
|
||||
size_t id = v.get_node() - 1;
|
||||
for (int i = 0; i < feature_size; i++) {
|
||||
patch_data[pointer_base + i * 3] +=
|
||||
eta_l * features[id * feature_size + i];
|
||||
patch_data[pointer_base + i * 3 + 1] +=
|
||||
eta_r * features[id * feature_size + i];
|
||||
patch_data[pointer_base + i * 3 + 2] +=
|
||||
eta_t * features[id * feature_size + i];
|
||||
}
|
||||
}
|
||||
patch_count++;
|
||||
}
|
||||
patch->Resize({static_cast<int64_t>(patch_count),
|
||||
static_cast<int64_t>(patch_elem_size)});
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class Col2TreeFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext &dev_ctx,
|
||||
const DenseTensor &EdgeSet,
|
||||
const DenseTensor &out_grad,
|
||||
DenseTensor *in_grad,
|
||||
int max_depth) {
|
||||
std::vector<std::vector<int>> tr;
|
||||
const auto &output_dims = out_grad.dims();
|
||||
funcs::SetConstant<CPUContext, T> constant;
|
||||
int64_t output_size = output_dims[1];
|
||||
size_t grad_elem_size = 3 * static_cast<size_t>(output_size);
|
||||
size_t node_count = 0, grad_count = 0;
|
||||
Tree2ColUtil::construct_tree(EdgeSet, &tr, &node_count);
|
||||
std::vector<std::vector<TreeNode>> processing_list;
|
||||
std::vector<std::vector<TreeNode>> grad_list;
|
||||
grad_list.resize(node_count);
|
||||
for (size_t u = 1; u <= node_count; u++) {
|
||||
std::vector<TreeNode> tmp =
|
||||
Tree2ColUtil::construct_patch(u, max_depth, tr);
|
||||
if (!tmp.empty()) {
|
||||
processing_list.push_back(tmp);
|
||||
}
|
||||
}
|
||||
for (size_t patch_id = 0; patch_id < processing_list.size(); patch_id++) {
|
||||
for (auto v : processing_list[patch_id]) {
|
||||
grad_list[v.get_node() - 1].push_back(v.change_node(patch_id + 1));
|
||||
}
|
||||
}
|
||||
in_grad->Resize({static_cast<int64_t>(node_count),
|
||||
static_cast<int64_t>(grad_elem_size)});
|
||||
T *grad_data = dev_ctx.template Alloc<T>(in_grad);
|
||||
|
||||
constant(dev_ctx, in_grad, 0);
|
||||
const T *out_g = out_grad.data<T>();
|
||||
for (auto &patch_item : grad_list) {
|
||||
size_t pointer_base = grad_count * grad_elem_size;
|
||||
for (auto &v : patch_item) {
|
||||
T eta_l = v.eta_l<T>(max_depth), eta_r = v.eta_r<T>(max_depth),
|
||||
eta_t = v.eta_t<T>(max_depth);
|
||||
size_t id = v.get_node() - 1;
|
||||
for (int i = 0; i < output_size; i++) {
|
||||
grad_data[pointer_base + i * 3] +=
|
||||
eta_l * out_g[id * output_size + i];
|
||||
grad_data[pointer_base + i * 3 + 1] +=
|
||||
eta_r * out_g[id * output_size + i];
|
||||
grad_data[pointer_base + i * 3 + 2] +=
|
||||
eta_t * out_g[id * output_size + i];
|
||||
}
|
||||
}
|
||||
grad_count++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template class Tree2ColFunctor<CPUContext, float>;
|
||||
template class Tree2ColFunctor<CPUContext, double>;
|
||||
template class Col2TreeFunctor<CPUContext, float>;
|
||||
template class Col2TreeFunctor<CPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2024 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 <stack>
|
||||
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
#include "paddle/phi/kernels/funcs/math/tree2col.h"
|
||||
#include "paddle/phi/kernels/funcs/math_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
using Node = phi::math::TreeNode;
|
||||
template <typename T>
|
||||
__global__ void tree2col(const T* eta,
|
||||
const int* node,
|
||||
const int* index,
|
||||
const T* vectors,
|
||||
T* result,
|
||||
int feature_size,
|
||||
int n) {
|
||||
const int thread_id =
|
||||
(blockIdx.x * gridDim.y + blockIdx.y) * blockDim.x + threadIdx.x;
|
||||
const int patch_id = thread_id / feature_size;
|
||||
const int j = thread_id % feature_size;
|
||||
if (patch_id < n) {
|
||||
const int begin_o = patch_id * 3 * feature_size;
|
||||
const int begin = index[patch_id * 2], end = index[patch_id * 2 + 1];
|
||||
T res_l = 0, res_r = 0, res_t = 0;
|
||||
for (int i = begin; i < end; i++) {
|
||||
const int id = node[i];
|
||||
const T vec = vectors[id * feature_size + j];
|
||||
res_l += eta[i * 3] * vec;
|
||||
res_r += eta[i * 3 + 1] * vec;
|
||||
res_t += eta[i * 3 + 2] * vec;
|
||||
}
|
||||
result[begin_o + j * 3] = res_l;
|
||||
result[begin_o + j * 3 + 1] = res_r;
|
||||
result[begin_o + j * 3 + 2] = res_t;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
class Tree2ColFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& dev_ctx,
|
||||
const DenseTensor& EdgeSet,
|
||||
const DenseTensor& node_features,
|
||||
DenseTensor* patch,
|
||||
int max_depth) {
|
||||
std::vector<std::vector<int>> tr;
|
||||
auto gpu_place = dev_ctx.GetPlace();
|
||||
auto cpu_place = CPUPlace();
|
||||
auto stream = dev_ctx.stream();
|
||||
auto feature_dims = node_features.dims();
|
||||
funcs::SetConstant<GPUContext, T> constant;
|
||||
|
||||
DenseTensor EdgeSet_cpu;
|
||||
phi::Copy(dev_ctx, EdgeSet, cpu_place, false, &EdgeSet_cpu);
|
||||
int64_t feature_size = feature_dims[1];
|
||||
size_t patch_elem_size = 3 * static_cast<size_t>(feature_size);
|
||||
size_t node_count = 0, patch_count = 0, total_size = 0;
|
||||
size_t max_size = feature_dims[0];
|
||||
Tree2ColUtil::construct_tree(EdgeSet_cpu, &tr, &node_count);
|
||||
|
||||
std::vector<std::vector<Node>> processing_list;
|
||||
for (size_t u = 1; u <= node_count; u++) {
|
||||
std::vector<Node> tmp = Tree2ColUtil::construct_patch(u, max_depth, tr);
|
||||
if (!tmp.empty()) {
|
||||
processing_list.push_back(tmp);
|
||||
total_size += tmp.size();
|
||||
}
|
||||
}
|
||||
|
||||
size_t patch_size = processing_list.size();
|
||||
DenseTensor node_cpu, node_gpu, eta_cpu, eta_gpu, index_cpu, index_gpu;
|
||||
node_cpu.Resize({static_cast<int64_t>(total_size)});
|
||||
int* node = dev_ctx.template Alloc<int>(&node_cpu);
|
||||
eta_cpu.Resize({static_cast<int64_t>(total_size * 3)});
|
||||
T* eta = dev_ctx.template Alloc<T>(&eta_cpu);
|
||||
index_cpu.Resize({static_cast<int64_t>(patch_size * 2)});
|
||||
int* index = dev_ctx.template Alloc<int>(&index_cpu);
|
||||
|
||||
int idx = 0, index_idx = 0;
|
||||
for (auto& tmp : processing_list) {
|
||||
index[index_idx++] = idx;
|
||||
for (auto& v : tmp) {
|
||||
node[idx] = static_cast<int>(v.node - 1);
|
||||
eta[idx * 3] = v.eta_l<T>(max_depth);
|
||||
eta[idx * 3 + 1] = v.eta_r<T>(max_depth);
|
||||
eta[idx * 3 + 2] = v.eta_t<T>(max_depth);
|
||||
idx++;
|
||||
}
|
||||
index[index_idx++] = idx;
|
||||
}
|
||||
phi::Copy(dev_ctx, node_cpu, gpu_place, false, &node_gpu);
|
||||
phi::Copy(dev_ctx, eta_cpu, gpu_place, false, &eta_gpu);
|
||||
phi::Copy(dev_ctx, index_cpu, gpu_place, false, &index_gpu);
|
||||
|
||||
PADDLE_ENFORCE_LE_INT_MAX(feature_size, "tree2col kernel feature_size");
|
||||
PADDLE_ENFORCE_LE_INT_MAX(static_cast<int64_t>(patch_size),
|
||||
"tree2col kernel patch_size");
|
||||
const int feature_size_int = static_cast<int>(feature_size);
|
||||
const int patch_size_int = static_cast<int>(patch_size);
|
||||
|
||||
int64_t elem_size = static_cast<int64_t>(patch_size) * feature_size;
|
||||
int64_t blocks64 = (elem_size + 1024 - 1) / 1024;
|
||||
PADDLE_ENFORCE_LE_INT_MAX(blocks64, "CUDA launch grid blocks");
|
||||
int block_x = 512;
|
||||
int64_t block_y64 = (blocks64 + block_x - 1) / block_x;
|
||||
PADDLE_ENFORCE_LE_INT_MAX(block_y64, "CUDA launch grid.y");
|
||||
int block_y = static_cast<int>(block_y64);
|
||||
dim3 threads(1024, 1);
|
||||
dim3 grid(block_x, block_y);
|
||||
|
||||
patch->Resize({static_cast<int64_t>(max_size),
|
||||
static_cast<int64_t>(patch_elem_size)});
|
||||
dev_ctx.template Alloc<T>(patch);
|
||||
constant(dev_ctx, patch, 0);
|
||||
tree2col<T><<<grid, threads, 0, stream>>>(eta_gpu.data<T>(),
|
||||
node_gpu.data<int>(),
|
||||
index_gpu.data<int>(),
|
||||
node_features.data<T>(),
|
||||
patch->data<T>(),
|
||||
feature_size_int,
|
||||
patch_size_int);
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class Col2TreeFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& dev_ctx,
|
||||
const DenseTensor& EdgeSet,
|
||||
const DenseTensor& patch_grad,
|
||||
DenseTensor* embedding_grad,
|
||||
int max_depth) {
|
||||
std::vector<std::vector<int>> tr;
|
||||
auto gpu_place = dev_ctx.GetPlace();
|
||||
auto cpu_place = CPUPlace();
|
||||
auto stream = dev_ctx.stream();
|
||||
auto output_dims = patch_grad.dims();
|
||||
funcs::SetConstant<GPUContext, T> constant;
|
||||
|
||||
DenseTensor EdgeSet_cpu;
|
||||
phi::Copy(dev_ctx, EdgeSet, cpu_place, false, &EdgeSet_cpu);
|
||||
int64_t output_size = output_dims[1];
|
||||
size_t patch_elem_size = 3 * static_cast<size_t>(output_size);
|
||||
size_t node_count = 0, patch_count = 0;
|
||||
size_t max_size = output_dims[0];
|
||||
Tree2ColUtil::construct_tree(EdgeSet_cpu, &tr, &node_count);
|
||||
std::vector<std::vector<Node>> processing_list;
|
||||
std::vector<std::vector<Node>> grad_list;
|
||||
grad_list.resize(node_count);
|
||||
size_t total_size = 0, grad_size = node_count;
|
||||
for (size_t u = 1; u <= node_count; u++) {
|
||||
std::vector<Node> tmp = Tree2ColUtil::construct_patch(u, max_depth, tr);
|
||||
if (!tmp.empty()) {
|
||||
processing_list.push_back(tmp);
|
||||
}
|
||||
}
|
||||
for (size_t patch_id = 0; patch_id < processing_list.size(); patch_id++) {
|
||||
for (auto v : processing_list[patch_id]) {
|
||||
grad_list[v.get_node() - 1].push_back(v.change_node(patch_id + 1));
|
||||
}
|
||||
}
|
||||
for (auto& tmp : grad_list) {
|
||||
total_size += tmp.size();
|
||||
}
|
||||
|
||||
DenseTensor node_cpu, node_gpu, eta_cpu, eta_gpu, index_cpu, index_gpu;
|
||||
node_cpu.Resize({static_cast<int64_t>(total_size)});
|
||||
int* node = dev_ctx.template Alloc<int>(&node_cpu);
|
||||
eta_cpu.Resize({static_cast<int64_t>(total_size * 3)});
|
||||
T* eta = dev_ctx.template Alloc<T>(&eta_cpu);
|
||||
index_cpu.Resize({static_cast<int64_t>(grad_size * 2)});
|
||||
int* index = dev_ctx.template Alloc<int>(&index_cpu);
|
||||
|
||||
size_t idx = 0, index_idx = 0;
|
||||
for (auto& tmp : grad_list) {
|
||||
index[index_idx++] = idx;
|
||||
for (auto& v : tmp) {
|
||||
node[idx] = static_cast<int>(v.node - 1);
|
||||
eta[idx * 3] = v.eta_l<T>(max_depth);
|
||||
eta[idx * 3 + 1] = v.eta_r<T>(max_depth);
|
||||
eta[idx * 3 + 2] = v.eta_t<T>(max_depth);
|
||||
idx++;
|
||||
}
|
||||
index[index_idx++] = idx;
|
||||
}
|
||||
phi::Copy(dev_ctx, node_cpu, gpu_place, false, &node_gpu);
|
||||
phi::Copy(dev_ctx, eta_cpu, gpu_place, false, &eta_gpu);
|
||||
phi::Copy(dev_ctx, index_cpu, gpu_place, false, &index_gpu);
|
||||
|
||||
PADDLE_ENFORCE_LE_INT_MAX(output_size, "tree2col kernel output_size");
|
||||
PADDLE_ENFORCE_LE_INT_MAX(static_cast<int64_t>(grad_size),
|
||||
"tree2col kernel grad_size");
|
||||
const int output_size_int = static_cast<int>(output_size);
|
||||
const int grad_size_int = static_cast<int>(grad_size);
|
||||
|
||||
int64_t elem_size = static_cast<int64_t>(output_size) * grad_size;
|
||||
int64_t blocks64 = (elem_size + 1024 - 1) / 1024;
|
||||
PADDLE_ENFORCE_LE_INT_MAX(blocks64, "CUDA launch grid blocks");
|
||||
int block_x = 512;
|
||||
int64_t block_y64 = (blocks64 + block_x - 1) / block_x;
|
||||
PADDLE_ENFORCE_LE_INT_MAX(block_y64, "CUDA launch grid.y");
|
||||
int block_y = static_cast<int>(block_y64);
|
||||
dim3 threads(1024, 1);
|
||||
dim3 grid(block_x, block_y);
|
||||
|
||||
embedding_grad->Resize({static_cast<int64_t>(max_size),
|
||||
static_cast<int64_t>(patch_elem_size)});
|
||||
dev_ctx.template Alloc<T>(embedding_grad);
|
||||
|
||||
constant(dev_ctx, embedding_grad, 0);
|
||||
tree2col<T><<<grid, threads, 0, stream>>>(eta_gpu.data<T>(),
|
||||
node_gpu.data<int>(),
|
||||
index_gpu.data<int>(),
|
||||
patch_grad.data<T>(),
|
||||
embedding_grad->data<T>(),
|
||||
output_size_int,
|
||||
grad_size_int);
|
||||
}
|
||||
};
|
||||
|
||||
template class Tree2ColFunctor<GPUContext, float>;
|
||||
template class Tree2ColFunctor<GPUContext, double>;
|
||||
template class Col2TreeFunctor<GPUContext, float>;
|
||||
template class Col2TreeFunctor<GPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2024 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 <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
#include "paddle/phi/kernels/funcs/math_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
class TreeNode {
|
||||
public:
|
||||
size_t node;
|
||||
explicit TreeNode(size_t node = 0,
|
||||
size_t index = 0,
|
||||
size_t pclen = 0,
|
||||
size_t depth = 0)
|
||||
: node(node), index(index), pclen(pclen), depth(depth) {}
|
||||
template <typename T>
|
||||
T eta_t(T filter_depth) {
|
||||
return ((filter_depth - this->depth) / filter_depth);
|
||||
}
|
||||
template <typename T>
|
||||
T eta_l(T filter_depth) {
|
||||
T temp;
|
||||
if (this->pclen == 1) {
|
||||
temp = 0.5;
|
||||
} else {
|
||||
temp = (this->index - 1.0) / (this->pclen - 1.0);
|
||||
}
|
||||
return (1.0 - this->eta_t<T>(filter_depth)) * temp;
|
||||
}
|
||||
template <typename T>
|
||||
T eta_r(T filter_depth) {
|
||||
return (1.0 - this->eta_t<T>(filter_depth)) *
|
||||
(1.0 - this->eta_l<T>(filter_depth));
|
||||
}
|
||||
TreeNode change_node(size_t v) {
|
||||
return TreeNode(v, this->index, this->pclen, this->depth);
|
||||
}
|
||||
size_t get_node() { return this->node; }
|
||||
size_t get_depth() { return this->depth; }
|
||||
|
||||
private:
|
||||
size_t index, pclen, depth;
|
||||
};
|
||||
class Tree2ColUtil {
|
||||
public:
|
||||
static std::vector<TreeNode> construct_patch(
|
||||
size_t root, int max_depth, const std::vector<std::vector<int>> &tr);
|
||||
|
||||
static void construct_tree(const DenseTensor &EdgeSet,
|
||||
std::vector<std::vector<int>> *tr,
|
||||
size_t *node_count);
|
||||
};
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Tree2ColFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext &dev_ctx,
|
||||
const DenseTensor &EdgeSet,
|
||||
const DenseTensor &node_features,
|
||||
DenseTensor *patch,
|
||||
int max_depth);
|
||||
};
|
||||
template <typename DeviceContext, typename T>
|
||||
class Col2TreeFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext &dev_ctx,
|
||||
const DenseTensor &EdgeSet,
|
||||
const DenseTensor &out_grad,
|
||||
DenseTensor *in_grad,
|
||||
int max_depth);
|
||||
};
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/unpooling.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
template <typename T>
|
||||
class Unpool2dMaxFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output) {
|
||||
const int batch_size = static_cast<int>(input.dims()[0]);
|
||||
const int input_height = static_cast<int>(input.dims()[2]);
|
||||
const int input_width = static_cast<int>(input.dims()[3]);
|
||||
const int output_channels = static_cast<int>(output->dims()[1]);
|
||||
const int output_height = static_cast<int>(output->dims()[2]);
|
||||
const int output_width = static_cast<int>(output->dims()[3]);
|
||||
int64_t input_feasize = static_cast<int64_t>(input_height) * input_width;
|
||||
int64_t output_feasize = static_cast<int64_t>(output_height) * output_width;
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
T* output_data = context.template Alloc<T>(output);
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int c = 0; c < output_channels; ++c) {
|
||||
for (int64_t i = 0; i < input_feasize; ++i) {
|
||||
int index = indices_data[i];
|
||||
|
||||
PADDLE_ENFORCE_LT(
|
||||
index,
|
||||
output_feasize,
|
||||
common::errors::InvalidArgument(
|
||||
"index should less than output tensor height * output tensor "
|
||||
"width. Expected %ld < %ld, but got "
|
||||
"%ld >= %ld. Please check input value.",
|
||||
index,
|
||||
output_feasize,
|
||||
index,
|
||||
output_feasize));
|
||||
output_data[index] = input_data[i];
|
||||
}
|
||||
input_data += input_feasize;
|
||||
indices_data += input_feasize;
|
||||
output_data += output_feasize;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
template <class T>
|
||||
class Unpool2dMaxGradFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad) {
|
||||
const int batch_size = static_cast<int>(input.dims()[0]);
|
||||
const int input_height = static_cast<int>(input.dims()[2]);
|
||||
const int input_width = static_cast<int>(input.dims()[3]);
|
||||
const int output_channels = static_cast<int>(output.dims()[1]);
|
||||
const int output_height = static_cast<int>(output.dims()[2]);
|
||||
const int output_width = static_cast<int>(output.dims()[3]);
|
||||
int64_t input_feasize = static_cast<int64_t>(input_height) * input_width;
|
||||
int64_t output_feasize = static_cast<int64_t>(output_height) * output_width;
|
||||
const int* indices_data = indices.data<int>();
|
||||
const T* output_grad_data = output_grad.data<T>();
|
||||
T* input_grad_data = context.template Alloc<T>(input_grad);
|
||||
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int c = 0; c < output_channels; ++c) {
|
||||
for (int64_t i = 0; i < input_feasize; ++i) {
|
||||
int index = indices_data[i];
|
||||
PADDLE_ENFORCE_LT(
|
||||
index,
|
||||
output_feasize,
|
||||
common::errors::InvalidArgument(
|
||||
"index should less than output tensor height * output tensor "
|
||||
"width. Expected %ld < %ld, but got "
|
||||
"%ld >= %ld. Please check input value.",
|
||||
index,
|
||||
output_feasize,
|
||||
index,
|
||||
output_feasize));
|
||||
input_grad_data[i] = output_grad_data[index];
|
||||
}
|
||||
input_grad_data += input_feasize;
|
||||
indices_data += input_feasize;
|
||||
output_grad_data += output_feasize;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Unpool3dMaxFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output) {
|
||||
const int batch_size = static_cast<int>(input.dims()[0]);
|
||||
const int input_depth = static_cast<int>(input.dims()[2]);
|
||||
const int input_height = static_cast<int>(input.dims()[3]);
|
||||
const int input_width = static_cast<int>(input.dims()[4]);
|
||||
const int output_channels = static_cast<int>(output->dims()[1]);
|
||||
const int output_depth = static_cast<int>(output->dims()[2]);
|
||||
const int output_height = static_cast<int>(output->dims()[3]);
|
||||
const int output_width = static_cast<int>(output->dims()[4]);
|
||||
int64_t input_feasize =
|
||||
static_cast<int64_t>(input_depth) * input_height * input_width;
|
||||
int64_t output_feasize =
|
||||
static_cast<int64_t>(output_depth) * output_height * output_width;
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
T* output_data = context.template Alloc<T>(output);
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int c = 0; c < output_channels; ++c) {
|
||||
for (int64_t i = 0; i < input_feasize; ++i) {
|
||||
int index = indices_data[i];
|
||||
|
||||
PADDLE_ENFORCE_LT(
|
||||
index,
|
||||
output_feasize,
|
||||
common::errors::InvalidArgument(
|
||||
"index should less than output tensor depth * output tensor "
|
||||
"height "
|
||||
"* output tensor width. Expected %ld < %ld, but got "
|
||||
"%ld >= %ld. Please check input value.",
|
||||
index,
|
||||
output_feasize,
|
||||
index,
|
||||
output_feasize));
|
||||
output_data[index] = input_data[i];
|
||||
}
|
||||
input_data += input_feasize;
|
||||
indices_data += input_feasize;
|
||||
output_data += output_feasize;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
template <class T>
|
||||
class Unpool3dMaxGradFunctor<CPUContext, T> {
|
||||
public:
|
||||
void operator()(const CPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad) {
|
||||
const int batch_size = static_cast<int>(input.dims()[0]);
|
||||
const int input_depth = static_cast<int>(input.dims()[2]);
|
||||
const int input_height = static_cast<int>(input.dims()[3]);
|
||||
const int input_width = static_cast<int>(input.dims()[4]);
|
||||
const int output_channels = static_cast<int>(output.dims()[1]);
|
||||
const int output_depth = static_cast<int>(output.dims()[2]);
|
||||
const int output_height = static_cast<int>(output.dims()[3]);
|
||||
const int output_width = static_cast<int>(output.dims()[4]);
|
||||
int64_t input_feasize =
|
||||
static_cast<int64_t>(input_depth) * input_height * input_width;
|
||||
int64_t output_feasize =
|
||||
static_cast<int64_t>(output_depth) * output_height * output_width;
|
||||
const int* indices_data = indices.data<int>();
|
||||
const T* output_grad_data = output_grad.data<T>();
|
||||
T* input_grad_data = context.template Alloc<T>(input_grad);
|
||||
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int c = 0; c < output_channels; ++c) {
|
||||
for (int64_t i = 0; i < input_feasize; ++i) {
|
||||
int index = indices_data[i];
|
||||
PADDLE_ENFORCE_LT(
|
||||
index,
|
||||
output_feasize,
|
||||
common::errors::InvalidArgument(
|
||||
"index should less than output tensor depth * output tensor "
|
||||
"height "
|
||||
"* output tensor width. Expected %ld < %ld, but got "
|
||||
"%ld >= %ld. Please check input value.",
|
||||
index,
|
||||
output_feasize,
|
||||
index,
|
||||
output_feasize));
|
||||
input_grad_data[i] = output_grad_data[index];
|
||||
}
|
||||
input_grad_data += input_feasize;
|
||||
indices_data += input_feasize;
|
||||
output_grad_data += output_feasize;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template class Unpool2dMaxGradFunctor<CPUContext, float>;
|
||||
template class Unpool2dMaxGradFunctor<CPUContext, double>;
|
||||
template class Unpool2dMaxFunctor<CPUContext, float>;
|
||||
template class Unpool2dMaxFunctor<CPUContext, double>;
|
||||
template class Unpool3dMaxGradFunctor<CPUContext, float>;
|
||||
template class Unpool3dMaxGradFunctor<CPUContext, double>;
|
||||
template class Unpool3dMaxFunctor<CPUContext, float>;
|
||||
template class Unpool3dMaxFunctor<CPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,361 @@
|
||||
// Copyright (c) 2024 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/phi/kernels/funcs/math/unpooling.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_primitives.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
template <typename T>
|
||||
__global__ void KernelUnpool2dMax(const int64_t nthreads,
|
||||
const T* input_data,
|
||||
const int* indices_data,
|
||||
const int input_height,
|
||||
const int input_width,
|
||||
const int channels,
|
||||
T* output_data,
|
||||
const int output_height,
|
||||
const int output_width) {
|
||||
CUDA_KERNEL_LOOP_TYPE(linearIndex, nthreads, int64_t) {
|
||||
int64_t c = (linearIndex / input_width / input_height) % channels;
|
||||
int64_t n = linearIndex / input_width / input_height / channels;
|
||||
output_data += (n * channels + c) * output_height * output_width;
|
||||
int maxind = indices_data[linearIndex];
|
||||
output_data[maxind] = input_data[linearIndex];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void KernelUnpool2dMaxGrad(const int64_t nthreads,
|
||||
const T* input_data,
|
||||
const int* indices_data,
|
||||
const int input_height,
|
||||
const int input_width,
|
||||
const int channels,
|
||||
const T* output_data,
|
||||
const T* output_grad,
|
||||
const int output_height,
|
||||
const int output_width,
|
||||
T* input_grad) {
|
||||
CUDA_KERNEL_LOOP_TYPE(linearIndex, nthreads, int64_t) {
|
||||
int64_t c = (linearIndex / input_width / input_height) % channels;
|
||||
int64_t n = linearIndex / input_width / input_height / channels;
|
||||
output_grad += (n * channels + c) * output_height * output_width;
|
||||
int maxind = indices_data[linearIndex];
|
||||
input_grad[linearIndex] = output_grad[maxind];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* All tensors are in NCHW format.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
__global__ void KernelUnpool3dMax(const int64_t nthreads,
|
||||
const T* input_data,
|
||||
const int* indices_data,
|
||||
const int input_depth,
|
||||
const int input_height,
|
||||
const int input_width,
|
||||
const int channels,
|
||||
T* output_data,
|
||||
const int output_depth,
|
||||
const int output_height,
|
||||
const int output_width) {
|
||||
CUDA_KERNEL_LOOP_TYPE(linearIndex, nthreads, int64_t) {
|
||||
int64_t c =
|
||||
(linearIndex / input_depth / input_width / input_height) % channels;
|
||||
int64_t n =
|
||||
linearIndex / input_depth / input_width / input_height / channels;
|
||||
output_data +=
|
||||
(n * channels + c) * output_depth * output_height * output_width;
|
||||
int maxind = indices_data[linearIndex];
|
||||
output_data[maxind] = input_data[linearIndex];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void KernelUnpool3dMaxGrad(const int64_t nthreads,
|
||||
const T* input_data,
|
||||
const int* indices_data,
|
||||
const int input_depth,
|
||||
const int input_height,
|
||||
const int input_width,
|
||||
const int channels,
|
||||
const T* output_data,
|
||||
const T* output_grad,
|
||||
const int output_depth,
|
||||
const int output_height,
|
||||
const int output_width,
|
||||
T* input_grad) {
|
||||
CUDA_KERNEL_LOOP_TYPE(linearIndex, nthreads, int64_t) {
|
||||
int64_t c =
|
||||
(linearIndex / input_depth / input_width / input_height) % channels;
|
||||
int64_t n =
|
||||
linearIndex / input_depth / input_width / input_height / channels;
|
||||
output_grad +=
|
||||
(n * channels + c) * output_depth * output_height * output_width;
|
||||
int maxind = indices_data[linearIndex];
|
||||
input_grad[linearIndex] = output_grad[maxind];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* All tensors are in NCDHW format.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class Unpool2dMaxFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output) {
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t batch_size = input.dims()[0];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_height = input.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_width = input.dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_channels = output->dims()[1];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_height = output->dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_width = output->dims()[3];
|
||||
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
T* output_data = context.template Alloc<T>(output);
|
||||
int threads = 1024;
|
||||
int64_t max_grid = context.GetCUDAMaxGridDimSize()[0];
|
||||
int grid = std::min((input.numel() + threads - 1) / threads, max_grid);
|
||||
KernelUnpool2dMax<T>
|
||||
<<<grid, threads, 0, context.stream()>>>(input.numel(),
|
||||
input_data,
|
||||
indices_data,
|
||||
input_height,
|
||||
input_width,
|
||||
output_channels,
|
||||
output_data,
|
||||
output_height,
|
||||
output_width);
|
||||
}
|
||||
};
|
||||
/*
|
||||
* All tensors are in NCHW format.
|
||||
*/
|
||||
template <typename T>
|
||||
class Unpool2dMaxGradFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad) {
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t batch_size = input.dims()[0];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_height = input.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_width = input.dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_channels = output.dims()[1];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_height = output.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_width = output.dims()[3];
|
||||
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
const T* output_data = output.data<T>();
|
||||
const T* output_grad_data = output_grad.data<T>();
|
||||
T* input_grad_data = context.template Alloc<T>(input_grad);
|
||||
int threads = 1024;
|
||||
int64_t max_grid = context.GetCUDAMaxGridDimSize()[0];
|
||||
int grid = std::min((input.numel() + threads - 1) / threads, max_grid);
|
||||
KernelUnpool2dMaxGrad<T>
|
||||
<<<grid, threads, 0, context.stream()>>>(input.numel(),
|
||||
input_data,
|
||||
indices_data,
|
||||
input_height,
|
||||
input_width,
|
||||
output_channels,
|
||||
output_data,
|
||||
output_grad_data,
|
||||
output_height,
|
||||
output_width,
|
||||
input_grad_data);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Unpool3dMaxFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output) {
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t batch_size = input.dims()[0];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_depth = input.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_height = input.dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_width = input.dims()[4];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_channels = output->dims()[1];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_depth = output->dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_height = output->dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_width = output->dims()[4];
|
||||
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
T* output_data = context.template Alloc<T>(output);
|
||||
int threads = 1024;
|
||||
int64_t max_grid = context.GetCUDAMaxGridDimSize()[0];
|
||||
int grid = std::min((input.numel() + threads - 1) / threads, max_grid);
|
||||
KernelUnpool3dMax<T>
|
||||
<<<grid, threads, 0, context.stream()>>>(input.numel(),
|
||||
input_data,
|
||||
indices_data,
|
||||
input_depth,
|
||||
input_height,
|
||||
input_width,
|
||||
output_channels,
|
||||
output_data,
|
||||
output_depth,
|
||||
output_height,
|
||||
output_width);
|
||||
}
|
||||
};
|
||||
/*
|
||||
* All tensors are in NCDHW format.
|
||||
*/
|
||||
template <typename T>
|
||||
class Unpool3dMaxGradFunctor<GPUContext, T> {
|
||||
public:
|
||||
void operator()(const GPUContext& context,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad) {
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t batch_size = input.dims()[0];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_depth = input.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_height = input.dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t input_width = input.dims()[4];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_channels = output.dims()[1];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_depth = output.dims()[2];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_height = output.dims()[3];
|
||||
|
||||
// TODO(large-tensor): downstream functors may still use int; guard until
|
||||
// upgraded.
|
||||
int64_t output_width = output.dims()[4];
|
||||
|
||||
const T* input_data = input.data<T>();
|
||||
const int* indices_data = indices.data<int>();
|
||||
const T* output_data = output.data<T>();
|
||||
const T* output_grad_data = output_grad.data<T>();
|
||||
T* input_grad_data = context.template Alloc<T>(input_grad);
|
||||
int threads = 1024;
|
||||
int64_t max_grid = context.GetCUDAMaxGridDimSize()[0];
|
||||
int grid = std::min((input.numel() + threads - 1) / threads, max_grid);
|
||||
KernelUnpool3dMaxGrad<T>
|
||||
<<<grid, threads, 0, context.stream()>>>(input.numel(),
|
||||
input_data,
|
||||
indices_data,
|
||||
input_depth,
|
||||
input_height,
|
||||
input_width,
|
||||
output_channels,
|
||||
output_data,
|
||||
output_grad_data,
|
||||
output_depth,
|
||||
output_height,
|
||||
output_width,
|
||||
input_grad_data);
|
||||
}
|
||||
};
|
||||
|
||||
template class Unpool2dMaxGradFunctor<GPUContext, float>;
|
||||
template class Unpool2dMaxGradFunctor<GPUContext, double>;
|
||||
template class Unpool2dMaxFunctor<GPUContext, float>;
|
||||
template class Unpool2dMaxFunctor<GPUContext, double>;
|
||||
template class Unpool3dMaxGradFunctor<GPUContext, float>;
|
||||
template class Unpool3dMaxGradFunctor<GPUContext, double>;
|
||||
template class Unpool3dMaxFunctor<GPUContext, float>;
|
||||
template class Unpool3dMaxFunctor<GPUContext, double>;
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2024 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/phi/backends/all_context.h"
|
||||
#include "paddle/phi/core/tensor_utils.h"
|
||||
|
||||
namespace phi {
|
||||
namespace math {
|
||||
template <typename DeviceContext, typename T>
|
||||
class Unpool2dMaxFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output);
|
||||
};
|
||||
template <typename DeviceContext, class T>
|
||||
class Unpool2dMaxGradFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad);
|
||||
};
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Unpool3dMaxFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
DenseTensor* output);
|
||||
};
|
||||
template <typename DeviceContext, class T>
|
||||
class Unpool3dMaxGradFunctor {
|
||||
public:
|
||||
void operator()(const DeviceContext& dev_ctx,
|
||||
const DenseTensor& input,
|
||||
const DenseTensor& indices,
|
||||
const DenseTensor& output,
|
||||
const DenseTensor& output_grad,
|
||||
DenseTensor* input_grad);
|
||||
};
|
||||
} // namespace math
|
||||
} // namespace phi
|
||||
Reference in New Issue
Block a user