Skip to content

Commit 799b569

Browse files
Nat Jeffriestensorflower-gardener
authored andcommitted
Support models with unused inputs and unused tensors.
PiperOrigin-RevId: 374926880 Change-Id: I62baa82e7deaf5971927b120c4f47312c74474ef
1 parent 11b7aaa commit 799b569

4 files changed

Lines changed: 108 additions & 20 deletions

File tree

tensorflow/lite/micro/micro_allocator.cc

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -242,26 +242,6 @@ TfLiteStatus AllocationInfoBuilder::AddTensors(const SubGraph* subgraph,
242242
}
243243
}
244244
}
245-
246-
// Sanity check for valid tensor lifetime.
247-
for (size_t i = 0; i < tensor_count_; ++i) {
248-
AllocationInfo* current = &info_[i];
249-
// Even though tensor appears to be read only it may still need to be
250-
// allocated.
251-
const bool appears_read_only =
252-
(current->first_created == -1) && (current->last_used != -1);
253-
const bool has_partial_lifetime =
254-
!appears_read_only &&
255-
((current->first_created == -1) || (current->last_used == -1));
256-
if (has_partial_lifetime && current->needs_allocating) {
257-
TF_LITE_REPORT_ERROR(
258-
reporter_,
259-
"Logic error in memory planner, tensor %d has an invalid lifetime: "
260-
"first_created: %d, last_used: %d",
261-
i, current->first_created, current->last_used);
262-
return kTfLiteError;
263-
}
264-
}
265245
return kTfLiteOk;
266246
}
267247

tensorflow/lite/micro/micro_allocator_test.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,4 +869,35 @@ TF_LITE_MICRO_TEST(TestTypicalFirstOpAndSecondOpWithScratchTensors) {
869869
0, subgraph_allocations[0].tensors[5].data.uint8 - start);
870870
}
871871

872+
TF_LITE_MICRO_TEST(TestModelWithUnusedTensors) {
873+
tflite::AllOpsResolver op_resolver = tflite::testing::GetOpResolver();
874+
875+
const tflite::Model* model = tflite::testing::GetModelWithUnusedInputs();
876+
877+
tflite::ScratchBufferHandle* scratch_buffer_handles = nullptr;
878+
constexpr size_t arena_size = 4096;
879+
uint8_t arena[arena_size];
880+
tflite::MicroAllocator* allocator = tflite::MicroAllocator::Create(
881+
arena, arena_size, tflite::GetMicroErrorReporter());
882+
883+
tflite::SubgraphAllocations* subgraph_allocations =
884+
allocator->StartModelAllocation(model);
885+
TF_LITE_MICRO_EXPECT(nullptr != subgraph_allocations);
886+
TF_LITE_MICRO_EXPECT_EQ(
887+
kTfLiteOk, allocator->FinishModelAllocation(model, subgraph_allocations,
888+
&scratch_buffer_handles));
889+
890+
// Unused input tensor should not occupy any space.
891+
uint8_t* start = subgraph_allocations[0].tensors[2].data.uint8;
892+
TF_LITE_MICRO_EXPECT_EQ(
893+
64, subgraph_allocations[0].tensors[0].data.uint8 - start);
894+
TF_LITE_MICRO_EXPECT_EQ(
895+
0, subgraph_allocations[0].tensors[1].data.uint8 - start);
896+
TF_LITE_MICRO_EXPECT_EQ(
897+
0, subgraph_allocations[0].tensors[2].data.uint8 - start);
898+
// Unused tensor should not occupy any space.
899+
TF_LITE_MICRO_EXPECT_EQ(
900+
0, subgraph_allocations[0].tensors[3].data.uint8 - start);
901+
}
902+
872903
TF_LITE_MICRO_TESTS_END

tensorflow/lite/micro/test_helpers.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,72 @@ const Model* BuildModelWithOfflinePlanning(int number_of_tensors,
341341
node_conn[0].input, node_conn[num_conns - 1].output, num_subgraph_inputs);
342342
}
343343

344+
const Model* BuildModelWithUnusedInputs() {
345+
using flatbuffers::Offset;
346+
flatbuffers::FlatBufferBuilder* builder = BuilderInstance();
347+
348+
constexpr size_t buffers_size = 1;
349+
const Offset<Buffer> buffers[buffers_size] = {CreateBuffer(*builder)};
350+
constexpr size_t tensor_shape_size = 2;
351+
const int32_t tensor_shape[tensor_shape_size] = {1, 64};
352+
constexpr size_t tensors_size = 4;
353+
const Offset<Tensor> tensors[tensors_size] = {
354+
CreateTensor(*builder,
355+
builder->CreateVector(tensor_shape, tensor_shape_size),
356+
TensorType_INT8, 0,
357+
builder->CreateString("test_input_tensor"), 0, false),
358+
CreateTensor(*builder,
359+
builder->CreateVector(tensor_shape, tensor_shape_size),
360+
TensorType_INT8, 0,
361+
builder->CreateString("test_unused_input_tensor"), 0, false),
362+
CreateTensor(*builder,
363+
builder->CreateVector(tensor_shape, tensor_shape_size),
364+
TensorType_INT8, 0,
365+
builder->CreateString("test_output_tensor"), 0, false),
366+
CreateTensor(*builder,
367+
builder->CreateVector(tensor_shape, tensor_shape_size),
368+
TensorType_INT8, 0,
369+
builder->CreateString("test_unused_tensor"), 0, false),
370+
};
371+
constexpr size_t inputs_size = 2;
372+
const int32_t inputs[inputs_size] = {0, 1};
373+
constexpr size_t outputs_size = 1;
374+
const int32_t outputs[outputs_size] = {2};
375+
constexpr size_t operator_inputs_size = 1;
376+
const int32_t operator_inputs[operator_inputs_size] = {0};
377+
constexpr size_t operator_outputs_size = 1;
378+
const int32_t operator_outputs[operator_outputs_size] = {2};
379+
constexpr size_t operators_size = 1;
380+
const Offset<Operator> operators[operators_size] = {
381+
CreateOperator(
382+
*builder, 0,
383+
builder->CreateVector(operator_inputs, operator_inputs_size),
384+
builder->CreateVector(operator_outputs, operator_outputs_size),
385+
BuiltinOptions_NONE),
386+
};
387+
constexpr size_t subgraphs_size = 1;
388+
const Offset<SubGraph> subgraphs[subgraphs_size] = {
389+
CreateSubGraph(*builder, builder->CreateVector(tensors, tensors_size),
390+
builder->CreateVector(inputs, inputs_size),
391+
builder->CreateVector(outputs, outputs_size),
392+
builder->CreateVector(operators, operators_size),
393+
builder->CreateString("test_subgraph"))};
394+
constexpr size_t operator_codes_size = 1;
395+
const Offset<OperatorCode> operator_codes[operator_codes_size] = {
396+
CreateOperatorCodeDirect(*builder, /*deprecated_builtin_code=*/0,
397+
"mock_custom",
398+
/*version=*/0, BuiltinOperator_CUSTOM)};
399+
const Offset<Model> model_offset = CreateModel(
400+
*builder, 0, builder->CreateVector(operator_codes, operator_codes_size),
401+
builder->CreateVector(subgraphs, subgraphs_size),
402+
builder->CreateString("test_model"),
403+
builder->CreateVector(buffers, buffers_size));
404+
FinishModelBuffer(*builder, model_offset);
405+
void* model_pointer = builder->GetBufferPointer();
406+
const Model* model = flatbuffers::GetRoot<Model>(model_pointer);
407+
return model;
408+
}
409+
344410
const Model* BuildSimpleMockModel() {
345411
using flatbuffers::Offset;
346412
flatbuffers::FlatBufferBuilder* builder = BuilderInstance();
@@ -953,6 +1019,13 @@ AllOpsResolver GetOpResolver() {
9531019
MultipleInputs::GetMutableRegistration());
9541020
return op_resolver;
9551021
}
1022+
const Model* GetModelWithUnusedInputs() {
1023+
static Model* model = nullptr;
1024+
if (!model) {
1025+
model = const_cast<Model*>(BuildModelWithUnusedInputs());
1026+
}
1027+
return model;
1028+
}
9561029

9571030
const Model* GetSimpleMockModel() {
9581031
static Model* model = nullptr;

tensorflow/lite/micro/test_helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ const Model* GetModelWithOfflinePlanning(int num_tensors,
126126
int num_conns,
127127
int num_subgraph_inputs = 0);
128128

129+
// Returns a flatbuffer with a single operator, two inputs (one unused) and one
130+
// output.
131+
const Model* GetModelWithUnusedInputs();
132+
129133
// Returns a flatbuffer model with `simple_stateful_op`
130134
const Model* GetSimpleStatefulModel();
131135

0 commit comments

Comments
 (0)