Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LODTensor (Level of details, or Level of sequences Tensor). #3109

Merged
merged 29 commits into from
Aug 9, 2017

Conversation

Superjomn
Copy link
Contributor

@Superjomn Superjomn commented Jul 30, 2017

About the name, see level of detail

Copy link
Collaborator

@reyoung reyoung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late review.

class LODTensorTester : public ::testing::Test {
public:
virtual void SetUp() override {
lod_tensor = decltype(lod_tensor)(new LODTensor);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe lod_tensor.reset(new LODTensor); could be simpler?

// Tesor.
typedef std::vector<level_t> lod_t;

explicit LODTensor() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicit is not necessary.


explicit LODTensor() {}

LODTensor(LODTensor&& other)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should we move an LODTensor?

Maybe LODTensor(LODTensor&& ) = delete is enough?

Copy link
Contributor Author

@Superjomn Superjomn Jul 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to move shared_ptr<lod_t> and shared_ptr<Tensor>

/*
* Number of elements in a level.
*/
size_t Elements(uint32_t level = 0) const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use size_t not uint32_t for index.

Please always use size_t for index type because it is used widely in C++ standard library.


/*
* Slice of elements of a level, [elem_begin: elem_end]
* NOTE low performance in slice seq_start_positions_.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seq_start_positions_ --> lod_start_pos_

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we use Doxygen comment style. maybe

@note: Low performance in slice lod_start_pos_

is better.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See here


auto tensor = std::make_shared<Tensor>();
DDim dims = make_ddim({20 /*batch size*/, 128 /*dim*/});
tensor->Resize(dims);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tensor->Resize({20, 128})

is good.

// 0 5 10 15 20
// 0 2 5 7 10 12 15 20
auto lod = std::make_shared<LODTensor::lod_t>();
lod->emplace_back(LODTensor::level_t{0, 10, 20});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto lod = std::make_shared<LODTensor::lod_t>({
  {0, 10, 20},
  {0, 5, 10, 15, 20},
  {0, 2, 5, 7, 10, 12, 15, 17, 20}
});

I am not sure which is more readable, but C++ 11 make initialization of an std container very easy.

Copy link
Contributor

@luotao1 luotao1 Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto lod = std::make_shared<LODTensor::lod_t>({
  {0, 2, 5, 7, 10, 12, 15, 17, 20}
  {0, 5, 10, 15, 20},
  {0, 10, 20},
});

上次讨论的是,从小粒度开始存吧

Copy link
Contributor Author

@Superjomn Superjomn Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod->back() 。。 还没写完的版本
我下午写完,再推上来 @luotao1

auto end = lod_start_pos_->at(level)[elem_end];
for (const auto& l : *lod_start_pos_) {
auto it_begin = std::find(l.begin(), l.end(), start);
auto it_end = std::find(it_begin, l.end(), end);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it_begin & it_end could be not found. Add a PADDLE_ENFORCE here.

auto new_tensor = tensor_->Slice<T>(start, end);

LODTensor res;
res.set_tensor(new_tensor);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe set_tensor and set_lod should be constructor of LODTensor. Because all valid LODTensor should set them both.

@@ -138,5 +138,33 @@ inline void Tensor::Resize(const DDim& dims) { dims_ = dims; }

inline const DDim& Tensor::dims() const { return dims_; }

template <typename T>
LODTensor LODTensor::Slice(uint32_t level, uint32_t elem_begin,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit-test for Slice method to make sure that code is implemented right.

@Superjomn Superjomn mentioned this pull request Jul 30, 2017
@Superjomn Superjomn changed the title LOTTensor LODTensor (Level of details, or Level of sequences Tensor). Jul 31, 2017
* in the sentence's view, article, paragraph, sentence are 3 levels.
*/
size_t Levels() const {
return lod_start_pos_.get() ? lod_start_pos_->size() : 0UL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_lod() ? lod_start_pos_->size() : 0UL;

auto start = lod.at(level)[elem_begin];
auto end = lod.at(level)[elem_end];

LOG(INFO) << "start: " << start << " end: " << end;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this line.


std::shared_ptr<LODTensor::lod_t> SliceLOD(const LODTensor::lod_t &lod,
size_t level, size_t elem_begin,
size_t elem_end);
Copy link
Contributor

@qingqing01 qingqing01 Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit test for these two SliceLOD interfaces. And these interfaces need comments. Some examples like:

lod={{0, 3, 7, 9}} ,
level=0, elem_begin=1, elem_end=2,
return new_load = {{3,7}}

It is useful for understanding to other developers.

auto new_tensor = std::make_shared<Tensor>();
new_tensor->template CopyFrom<T>(sliced_tensor, dst_place);

return LODTensor(new_tensor, new_lod);
Copy link
Contributor

@qingqing01 qingqing01 Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod_start_pos_的slice从上面的实现来看,没有对index减去起始值。比如3条句子:

a b c
e f g h
j k

lod={{0, 3, 7, 9}} ,
level=0, elem_begin=1, elem_end=2,
slice之后 new_lod = {{3,7}}, 而不是 {{0,4}}

tensor_进行了Slice,并且拷贝。

这样,新的LODTensor依据new_lod来访问,会有越界吧

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

auto new_tensor = std::make_shared<Tensor>();
new_tensor->CopyFrom<T>(*tensor_, dst_place);

return LODTensor(new_tensor, new_lod);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

level级别的slice,这里对lod_start_pos_进行了slice,tensor_没有slice,是完整拷贝?

觉得最好多加一些注释,或者例子解释,不然developers看到还得思考半天~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

typedef std::vector<level_t> lod_t;

LODTensor() {}
LODTensor(std::shared_ptr<Tensor> tensor, std::shared_ptr<lod_t> lod) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If use shared_ptr, always use const std::shared_ptr<Tensor>& to pass them into a function.
std::shared_ptr<T> could be very slow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here need to share resource, so no const& here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here need to share resource, so no const& here.

const std::shared_ptr<T>& does not mean that the value of T cannot be changed. It means that the pointer of T cannot be changed.

Reset(tensor, lod);
}

void Reset(std::shared_ptr<Tensor> tensor, std::shared_ptr<lod_t> lod) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

PADDLE_ENFORCE(elem < Elements(level),
"element begin [%d] out of range [%d]", elem,
Elements(level));
return lod_start_pos_->at(level)[elem];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod_start_pos_[level][elem]; is better, because the previous PADDLE_ENFORCE has check the index of lod_start_pos_, it is not necessary use at to check it again.

The different between at and [] is at will perform a range check, and throw a std::runtime_error when out of range.

/*
* Number of elements in a level.
*/
size_t Elements(size_t level = 0) const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Levels and Elements are quite confusing names. Maybe just simple NumOfLevels and NumOfElements are better name? Or NumLevels?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/*
* Determine whether LODTensor has a valid LOD info.
*/
bool has_lod() const { return lod_start_pos_.get(); }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return lod_start_pos_;

* Determine whether LODTensor has a valid LOD info.
*/
bool has_lod() const { return lod_start_pos_.get(); }
std::shared_ptr<lod_t> const lod() const { return lod_start_pos_; }
Copy link
Collaborator

@reyoung reyoung Aug 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http://www.cplusplus.com/reference/memory/const_pointer_cast/

std::shared_ptr<const lod_t> lod() const { return std::const_pointer_cast<const lod_t>(lod_start_pos_)}


private:
mutable std::shared_ptr<lod_t> lod_start_pos_;
mutable std::shared_ptr<Tensor> tensor_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use mutable.

return LODTensor(tensor_, new_lod);
}

namespace details {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The details namespace is not necessarily declared in the header file.
Just declare them in *.cc is simpler.

// LOD stores offsets of each level of units, the largest units level first,
// then the smaller units level. Each level_t stores the offsets of units in
// Tesor.
typedef std::vector<level_t> lod_t;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod_t => LOD

Following Google C++ style guide: https://google.github.io/styleguide/cppguide.html#Type_Names

/*
* Determine whether LODTensor has a valid LOD info.
*/
bool has_lod() const { return lod_start_pos_.get(); }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_lod => HasLOD

Follow Google C++ style guide https://google.github.io/styleguide/cppguide.html#Function_Names

mutable std::shared_ptr<Tensor> tensor_;
};

namespace details {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to C++ style https://google.github.io/styleguide/cppguide.html#Namespace_Names, if we want to add namespace details, we must create a corresponding sub-directory named details.

Here it seems that we don't need this namespace nor the sub-directory -- if SliceLOD is supposed to be called outside of framework/lod_tensor*, let's just remove namespace details; otherwise, let's move it into lod_tensor.cc and in the anonymous namespace as described in https://google.github.io/styleguide/cppguide.html#Unnamed_Namespaces_and_Static_Variables

* Slice of levels[level_begin:level_end], with tensor copied.
*/
template <typename T>
LODTensor SliceCopied(size_t level_begin, size_t level_end,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T doesn't appear in the definition of SliceCopied. Do we really need this method a function template?

I have the same question with SliceShared.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tensor's CopyFrom need a T, SliceShared's T will be removed.

@@ -15,5 +15,5 @@
#include "paddle/framework/tensor.h"

namespace paddle {
namespace framework {}
namespace framework {} // namespace framework
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we need this empty .cc file?

std::shared_ptr<LODTensor::lod_t> SliceLOD(const LODTensor::lod_t &lod,
size_t level_begin,
size_t level_end) {
auto new_lod = std::make_shared<LODTensor::lod_t>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new_lod.reserve.

Always reserve vector.

bool tensor_shared) {
// slice the lod.
auto new_lod = std::make_shared<LODTensor::lod_t>();
auto start = lod.at(level)[elem_begin];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is at necessary?

size_t elem_end,
bool tensor_shared) {
// slice the lod.
auto new_lod = std::make_shared<LODTensor::lod_t>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reserve if you can.

* @level_begin: level to begin slice.
* @level_end: level to end slice.
*/
std::shared_ptr<LODTensor::lod_t> SliceLOD(const LODTensor::lod_t &lod,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace details is not necessarily in the header file.


TEST_F(LODTensorTester, SliceShared_Level) {
// slice 1 level
for (int level = 0; level < 3; level++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always use size_t for loop index. always use ++level.

https://google.github.io/styleguide/cppguide.html#Preincrement_and_Predecrement

@Superjomn Superjomn merged commit ede02d7 into PaddlePaddle:develop Aug 9, 2017
@Superjomn Superjomn deleted the lottensor branch August 9, 2017 01:05
Copy link
Collaborator

@wangkuiyi wangkuiyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LODTensor is a Tensor, so it should be derived from class Tensor, so could it inherit all methods from Tensor.

LOD type should be exported in lod.{h,cc,_test.cc}, so that it could be assigned to a Variable instance.

In our applications, many LODTensor instances might share the same LOD instance. We can save the LOD instance in a variable in the global scope, so that it would be referred to by all LODTensor instances.

The constructor of an LODTensor should be

class LODTensor {
 public:
  LODTensor(const LOD& lod, ...) : lod_(lod), Tensor(...) {}

LODTensor doesn't need SliceCopied. Let us remvoe it.

LODTensor needs SliceLevels and SliceInLevel.

Variable type inference along the network should be implemented using simple C++ features. Consider that an RNNOp instance would require its inputs are of type LODTensor, and should return outputs in LODTensor too. An FCOp instance requires Tensor-typed inputs and would return Tensor-typed outputs. However, it is possible that an LODTensor input feeds into an RNNOp instance, the output feeds into a FCOp instance, and the output of FCOp instance feeds in a second RNNOp instance. In such case, we want the FCOp instance creates its outputs in type LODTensor. To implement this features, we need to:

  1. Add virtual Tensor* Tensor::Close() const
  2. Override it in LODTensor as
    class LODTensor {
     public:
      virtual Tensor* Clone() const {
        return new LODTensor(lod_);
      }
  3. In RNNOp and FCOp, the creation of output must be cloned from the input. For example:
    Tensor* output = Input(0)->Clone();


LODTensor LODTensor::SliceShared(size_t level, size_t elem_begin,
size_t elem_end) const {
PADDLE_ENFORCE(HasLOD(), "has no LOD info, can't be sliced.");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一个LODTensor instance里不是一定总有LOD 信息的?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tensor* output = Input(0)->Clone();

这里是否应该改成

output_var.clone(input_var); // clone the tensor type
Tensor* output = output_var.Get<Tensor>();

@wangkuiyi

/*
* Get a element from LOD.
*/
size_t lod_element(size_t level, size_t elem) const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod_element => StartPosition

/*
* Number of elements in a level.
*/
size_t NumElements(size_t level = 0) const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NumElements => ElementsOfLevel

}

/*
* Slice of levels[level_begin:level_end], with tensor copied.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Copy the slice of data from *this into a new LODTensor and return the new LODTensor.

/*
* Slice of levels[level_begin:level_end], with tensor shared.
*/
LODTensor SliceShared(size_t level_begin, size_t level_end) const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Returns a new LODTensor that shares data with *this but provides a view of the specified slice.

* Slice of levels[level_begin:level_end], with tensor copied.
*/
template <typename T>
LODTensor SliceCopied(size_t level_begin, size_t level_end,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this method slice some levels, or elements in a level?

Tensor *raw_tensor() { return tensor_.get(); }

private:
std::shared_ptr<LOD> lod_start_pos_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lod_start_pos_ = > lod_

@wangkuiyi wangkuiyi mentioned this pull request Aug 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants