1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "utils/tflite/token_encoder.h"
18 
19 #include <vector>
20 
21 #include "utils/tflite/encoder_common.h"
22 #include "tensorflow/lite/kernels/kernel_util.h"
23 #include "tensorflow/lite/model.h"
24 
25 namespace libtextclassifier3 {
26 namespace {
27 
28 // Input parameters for the op.
29 // The number of tokens per message as (1, conversation length) int tensor.
30 constexpr const int kInputNumTokens = 0;
31 
32 // The number of messages, the conversation length, int scalar.
33 constexpr const int kInputNumInputs = 1;
34 
35 // Maximum output length of the encoding, int scalar.
36 constexpr const int kInputMaxLength = 2;
37 
38 // Additional attributes to align to the sentence pieces, e.g. user ids per
39 // message.
40 constexpr const int kInputAttr = 3;
41 
42 // Output parameters for the op.
43 // Relative position of each token in the input text,
44 // (1, max output length) int tensor.
45 constexpr const int kOutputPosition = 0;
46 
47 // Output length after trimming to the maximum output length specified.
48 // int scalar.
49 constexpr const int kOutputLengths = 1;
50 
51 // Padded and sentence piece aligned provided attributes, e.g. user id per
52 // sentence piece.
53 constexpr const int kOutputAttr = 2;
54 
ResizeOutputTensors(TfLiteContext * context,TfLiteNode * node,int max_output_length)55 TfLiteStatus ResizeOutputTensors(TfLiteContext* context, TfLiteNode* node,
56                                  int max_output_length) {
57   TF_LITE_ENSURE_OK(
58       context,
59       ResizeOutputTensor(
60           max_output_length,
61           &context->tensors[node->outputs->data[kOutputPosition]], context));
62 
63   const int num_output_attrs = node->outputs->size - kOutputAttr;
64   for (int i = 0; i < num_output_attrs; ++i) {
65     TF_LITE_ENSURE_OK(
66         context,
67         ResizeOutputTensor(
68             max_output_length,
69             &context->tensors[node->outputs->data[kOutputAttr + i]], context));
70   }
71   return kTfLiteOk;
72 }
73 
Prepare(TfLiteContext * context,TfLiteNode * node)74 TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
75   // Check that the batch dimension is kBatchSize.
76   const TfLiteTensor& num_tokens =
77       context->tensors[node->inputs->data[kInputNumTokens]];
78   TF_LITE_ENSURE_EQ(context, num_tokens.dims->size, kEncoderInputRank);
79   TF_LITE_ENSURE_EQ(context, num_tokens.dims->data[0], kEncoderBatchSize);
80 
81   TfLiteTensor& output_lengths =
82       context->tensors[node->outputs->data[kOutputLengths]];
83   TfLiteTensor& output_positions =
84       context->tensors[node->outputs->data[kOutputPosition]];
85 
86   TF_LITE_ENSURE_OK(context,
87                     context->ResizeTensor(context, &output_lengths,
88                                           CreateIntArray({kEncoderBatchSize})));
89 
90   // Check that there are enough outputs for attributes.
91   const int num_output_attrs = node->outputs->size - kOutputAttr;
92   TF_LITE_ENSURE_EQ(context, node->inputs->size - kInputAttr, num_output_attrs);
93 
94   // Copy attribute types from input to output tensors.
95   for (int i = 0; i < num_output_attrs; ++i) {
96     TfLiteTensor& input = context->tensors[node->inputs->data[kInputAttr + i]];
97     TfLiteTensor& output =
98         context->tensors[node->outputs->data[kOutputAttr + i]];
99     output.type = input.type;
100   }
101 
102   const TfLiteTensor& output_length =
103       context->tensors[node->inputs->data[kInputMaxLength]];
104 
105   if (tflite::IsConstantTensor(&output_length)) {
106     return ResizeOutputTensors(context, node, output_length.data.i64[0]);
107   } else {
108     tflite::SetTensorToDynamic(&output_positions);
109     for (int i = 0; i < num_output_attrs; ++i) {
110       TfLiteTensor& output_attr =
111           context->tensors[node->outputs->data[kOutputAttr + i]];
112       tflite::SetTensorToDynamic(&output_attr);
113     }
114   }
115 
116   return kTfLiteOk;
117 }
118 
Eval(TfLiteContext * context,TfLiteNode * node)119 TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
120   const TfLiteTensor& num_tokens =
121       context->tensors[node->inputs->data[kInputNumTokens]];
122   const int num_inputs =
123       context->tensors[node->inputs->data[kInputNumInputs]].data.i32[0];
124 
125   const TfLiteTensor& output_length =
126       context->tensors[node->inputs->data[kInputMaxLength]];
127   TfLiteTensor& output_positions =
128       context->tensors[node->outputs->data[kOutputPosition]];
129   if (!tflite::IsConstantTensor(&output_length)) {
130     TF_LITE_ENSURE_OK(
131         context, ResizeOutputTensors(context, node, output_length.data.i64[0]));
132   }
133 
134   std::vector<int> encoded_offsets;
135   std::vector<int> encoded_positions;
136   encoded_offsets.reserve(num_inputs);
137   const int max_output_length = output_positions.dims->data[1];
138   const int max_encoded_position = max_output_length;
139   int total_tokens = 0;
140 
141   for (int i = 0; i < num_inputs; ++i) {
142     const int num_message_tokens =
143         num_tokens.data.i32[i] + 2; /* num_tokens + start and end token. */
144     total_tokens += num_message_tokens;
145     encoded_offsets.push_back(total_tokens);
146     for (int k = 0; k < num_message_tokens; k++) {
147       encoded_positions.push_back(std::min(k, max_encoded_position - 1));
148     }
149   }
150 
151   const int num_skip = CopyDataToTensorAndPadOrTruncate(
152       max_output_length, encoded_positions,
153       /*padding_value=*/max_encoded_position, &output_positions);
154   TfLiteTensor& output_lengths =
155       context->tensors[node->outputs->data[kOutputLengths]];
156   output_lengths.data.i32[0] = encoded_positions.size() - num_skip;
157 
158   // Process attributes, all checks of sizes and types are done in Prepare.
159   const int num_output_attrs = node->outputs->size - kOutputAttr;
160   TF_LITE_ENSURE_EQ(context, node->inputs->size - kInputAttr, num_output_attrs);
161   for (int i = 0; i < num_output_attrs; ++i) {
162     TfLiteStatus attr_status = CopyValuesToTensorAndPadOrTruncate(
163         context->tensors[node->inputs->data[kInputAttr + i]], encoded_offsets,
164         num_skip, context,
165         &context->tensors[node->outputs->data[kOutputAttr + i]]);
166     if (attr_status != kTfLiteOk) {
167       return attr_status;
168     }
169   }
170 
171   return kTfLiteOk;
172 }
173 
174 }  // namespace
175 }  // namespace libtextclassifier3
176 
177 namespace tflite {
178 namespace ops {
179 namespace custom {
180 
Register_TOKEN_ENCODER()181 TfLiteRegistration* Register_TOKEN_ENCODER() {
182   static TfLiteRegistration registration = {/*init=*/nullptr, /*free=*/nullptr,
183                                             libtextclassifier3::Prepare,
184                                             libtextclassifier3::Eval};
185   return &registration;
186 }
187 
188 }  // namespace custom
189 }  // namespace ops
190 }  // namespace tflite
191