From 53cb4df0a2b9deb6b1fd5e9c8e2027c4ad27b352 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Thu, 9 Nov 2017 13:32:29 +0800 Subject: [PATCH] design/sequence decoder (#4905) --- .../LOD-and-shape-changes-during-decoding.jpg | Bin 0 -> 62624 bytes doc/design/ops/sequence_decoder.md | 245 ++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg create mode 100644 doc/design/ops/sequence_decoder.md diff --git a/doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg b/doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b0d90f7b9d8184b314b0ee4e521f53eb5f1b455 GIT binary patch literal 62624 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 L{?BXh$MpXK&hBrf literal 0 HcmV?d00001 diff --git a/doc/design/ops/sequence_decoder.md b/doc/design/ops/sequence_decoder.md new file mode 100644 index 0000000000..9007aae7a8 --- /dev/null +++ b/doc/design/ops/sequence_decoder.md @@ -0,0 +1,245 @@ +# Design: Sequence Decoder Generating LoDTensors +In tasks such as machine translation and image to text, +a [sequence decoder](https://github.com/PaddlePaddle/book/blob/develop/08.machine_translation/README.md) is necessary to generate sequences. + +This documentation describes how to implement the sequence decoder as an operator. + +## Beam Search based Decoder +The [beam search algorithm](https://en.wikipedia.org/wiki/Beam_search) is necessary when generating sequences, +it is a heuristic search algorithm that explores the paths by expanding the most promising node in a limited set. + +In the old version of PaddlePaddle, a C++ class `RecurrentGradientMachine` implements the general sequence decoder based on beam search, +due to the complexity, the implementation relays on a lot of special data structures, +quite trivial and hard to be customized by users. + +There are a lot of heuristic tricks in the sequence generation tasks, +so the flexibility of sequence decoder is very important to users. + +During PaddlePaddle's refactoring work, +some new concept is proposed such as [LoDTensor](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/lod_tensor.md) and [TensorArray](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/tensor_array.md) that can better support sequence usage, +and they can help to make the implementation of beam search based sequence decoder **more transparent and modular** . + +For example, the RNN sates, candidates IDs and probabilities of beam search can be represented as `LoDTensors`; +the selected candidate's IDs in each time step can be stored in a `TensorArray`, and `Packed` to the sentences translated. + +## Changing LoD's absolute offset to relative offsets +The current `LoDTensor` is designed to store levels of variable-length sequences, +it stores several arrays of integers each represents a level. + +The integers in each level represents the begin and end (not inclusive) offset of a sequence **in the underlying tensor**, +let's call this format the **absolute-offset LoD** for clear. + +The relative-offset LoD can fast retrieve any sequence but fails to represent empty sequences, for example, a two-level LoD is as follows +```python +[[0, 3, 9] + [0, 2, 3, 3, 3, 9]] +``` +The first level tells that there are two sequences: +- the first's offset is `[0, 3)` +- the second's offset is `[3, 9)` + +while on the second level, there are several empty sequences that both begin and end at `3`. +It is impossible to tell how many empty second-level sequences exist in the first-level sequences. + +There are many scenarios that relay on empty sequence representation, +such as machine translation or image to text, one instance has no translations or the empty candidate set for a prefix. + +So let's introduce another format of LoD, +it stores **the offsets of the lower level sequences** and is called **relative-offset** LoD. + +For example, to represent the same sequences of the above data + +```python +[[0, 3, 6] + [0, 2, 3, 3, 3, 9]] +``` + +the first level represents that there are two sequences, +their offsets in the second-level LoD is `[0, 3)` and `[3, 5)`. + +The second level is the same with the relative offset example because the lower level is a tensor. +It is easy to find out the second sequence in the first-level LoD has two empty sequences. + +The following demos are based on relative-offset LoD. + +## Usage in a simple machine translation model +Let's start from a simple machine translation model that is simplified from [machine translation chapter](https://github.com/PaddlePaddle/book/tree/develop/08.machine_translation) to draw a simple blueprint of what a sequence decoder can do and how to use it. + +The model has an encoder that learns the semantic vector from a sequence, +and a decoder which uses the sequence decoder to generate new sentences. + +**Encoder** +```python +import paddle as pd + +dict_size = 8000 +source_dict_size = dict_size +target_dict_size = dict_size +word_vector_dim = 128 +encoder_dim = 128 +decoder_dim = 128 +beam_size = 5 +max_length = 120 + +# encoder +src_word_id = pd.data( + name='source_language_word', + type=pd.data.integer_value_sequence(source_dict_dim)) +src_embedding = pd.embedding(size=source_dict_size, size=word_vector_dim) + +src_word_vec = pd.lookup(src_embedding, src_word_id) + +encoder_out_seq = pd.gru(input=src_word_vec, size=encoder_dim) + +encoder_ctx = pd.last_seq(encoder_out_seq) +# encoder_ctx_proj is the learned semantic vector +encoder_ctx_proj = pd.fc( + encoder_ctx, size=decoder_dim, act=pd.activation.Tanh(), bias=None) +``` + +**Decoder** + +```python +def generate(): + decoder = pd.while_loop() + with decoder.step(): + decoder_mem = decoder.memory(init=encoder_ctx) # mark the memory + generated_ids = decoder.memory() # TODO init to batch_size s + generated_scores = decoder.memory() # TODO init to batch_size 1s or 0s + + target_word = pd.lookup(trg_embedding, gendrated_ids) + # expand encoder_ctx's batch to fit target_word's lod + # for example + # decoder_mem.lod is + # [[0 1 3], + # [0 1 3 6]] + # its tensor content is [a1 a2 a3 a4 a5] + # which means there are 2 sentences to translate + # - the first sentence has 1 translation prefixes, the offsets are [0, 1) + # - the second sentence has 2 translation prefixes, the offsets are [1, 3) and [3, 6) + # the target_word.lod is + # [[0, 1, 6] + # [0, 2, 4, 7, 9 12]] + # which means 2 sentences to translate, each has 1 and 5 prefixes + # the first prefix has 2 candidates + # the following has 2, 3, 2, 3 candidates + # the encoder_ctx_expanded's content will be + # [a1 a1 a2 a2 a3 a3 a3 a4 a4 a5 a5 a5] + encoder_ctx_expanded = pd.lod_expand(encoder_ctx, target_word) + decoder_input = pd.fc( + act=pd.activation.Linear(), + input=[target_word, encoder_ctx], + size=3 * decoder_dim) + gru_out, cur_mem = pd.gru_step( + decoder_input, mem=decoder_mem, size=decoder_dim) + scores = pd.fc( + gru_out, + size=trg_dic_size, + bias=None, + act=pd.activation.Softmax()) + # K is an config + topk_scores, topk_ids = pd.top_k(scores, K) + topk_generated_scores = pd.add_scalar(topk_scores, generated_scores) + + selected_ids, selected_generation_scores = decoder.beam_search( + topk_ids, topk_generated_scores) + + # update the states + decoder_mem.update(cur_mem) # tells how to update state + generated_ids.update(selected_ids) + generated_scores.update(selected_generation_scores) + + decoder.output(selected_ids) + decoder.output(selected_generation_scores) + +translation_ids, translation_scores = decoder() +``` +The `decoder.beam_search` is a operator that given the candidates and the scores of translations including the candidates, +return the result of the beam search algorithm. + +In this way, users can customize anything on the inputs or outputs of beam search, for example, two ways to prune some translation prefixes + +1. meke the correspondind elements in `topk_generated_scores` zero or some small values, beam_search will discard this candidate. +2. remove some specific candidate in `selected_ids` +3. get the final `translation_ids`, remove the translation sequence in it. + +The implementation of sequence decoder can reuse the C++ class [RNNAlgorithm](https://github.com/Superjom/Paddle/blob/68cac3c0f8451fe62a4cdf156747d6dc0ee000b3/paddle/operators/dynamic_recurrent_op.h#L30), +so the python syntax is quite similar to a [RNN](https://github.com/Superjom/Paddle/blob/68cac3c0f8451fe62a4cdf156747d6dc0ee000b3/doc/design/block.md#blocks-with-for-and-rnnop). + +Both of them are two-level `LoDTensors` + +- the first level represents `batch_size` of (source) sentences; +- the second level represents the candidate ID sets for translation prefix. + +for example, 3 source sentences to translate, and has 2, 3, 1 candidates. + +Unlike an RNN, in sequence decoder, the previous state and the current state have different LoD and shape, +a `lod_expand` operator is used to expand the LoD of the previous state to fit the current state. + +For example, the previous state + +* LoD is `[0, 1, 3][0, 2, 5, 6]` +* content of tensor is `a1 a2 b1 b2 b3 c1` + +the current state stored in `encoder_ctx_expanded` + +* LoD is `[0, 2, 7][0 3 5 8 9 11 11]` +* the content is + - a1 a1 a1 (a1 has 3 candidates, so the state should be copied 3 times for each candidates) + - a2 a2 + - b1 b1 b1 + - b2 + - b3 b3 + - None (c1 has 0 candidates, so c1 is dropped) + +Benefit from the relative offset LoD, empty candidate set can be represented naturally. + +the status in each time step can be stored in `TensorArray`, and `Pack`ed to a final LoDTensor, the corresponding syntax is + +```python +decoder.output(selected_ids) +decoder.output(selected_generation_scores) +``` + +the `selected_ids` is the candidate ids for the prefixes, +it will be `Packed` by `TensorArray` to a two-level `LoDTensor`, +the first level represents the source sequences, +the second level represents generated sequences. + +Pack the `selected_scores` will get a `LoDTensor` that stores scores of each candidate of translations. + +Pack the `selected_generation_scores` will get a `LoDTensor`, and each tail is the probability of the translation. + +## LoD and shape changes during decoding +

+ +

+ +According the image above, the only phrase to change LoD is beam search. + +## Beam search design +The beam search algorthm will be implemented as one method of the sequence decoder, it has 3 inputs + +1. `topk_ids`, top K candidate ids for each prefix. +2. `topk_scores`, the corresponding scores for `topk_ids` +3. `generated_scores`, the score of the prefixes. + +All of the are LoDTensors, so that the sequence affilication is clear. +Beam search will keep a beam for each prefix and select a smaller candidate set for each prefix. + +It will return three variables + +1. `selected_ids`, the final candidate beam search function selected for the next step. +2. `selected_scores`, the scores for the candidates. +3. `generated_scores`, the updated scores for each prefixes (with the new candidates appended). + +## Introducing the LoD-based `Pack` and `Unpack` methods in `TensorArray` +The `selected_ids`, `selected_scores` and `generated_scores` are LoDTensors, +and they exist in each time step, +so it is natural to store them in arrays. + +Currently, PaddlePaddle has a module called `TensorArray` which can store an array of tensors, +the results of beam search are better to store in a `TensorArray`. + +The `Pack` and `UnPack` in `TensorArray` are used to package tensors in the array to a `LoDTensor` or split the `LoDTensor` to an array of tensors. +It needs some extensions to support pack or unpack an array of `LoDTensors`.