• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/lite/tools/strip_buffers/stripping_lib.h"
17 
18 #include <stdint.h>
19 
20 #include <cmath>
21 #include <limits>
22 #include <memory>
23 #include <random>
24 #include <vector>
25 
26 #include "absl/container/flat_hash_set.h"
27 #include "tensorflow/core/platform/logging.h"
28 #include "tensorflow/lite/c/common.h"
29 #include "tensorflow/lite/core/api/flatbuffer_conversions.h"
30 #include "tensorflow/lite/model.h"
31 
32 #define TFLITE_SCHEMA_VERSION 3
33 
34 namespace tflite {
35 
36 using ::flatbuffers::FlatBufferBuilder;
37 using ::flatbuffers::Offset;
38 
39 // Parameters for a simple Gaussian distribution to generate values roughly in
40 // [0, 1).
41 constexpr float kGaussianFloatMean = 0.5;
42 constexpr float kGaussianStdDev = 1.0 / 3;
43 
44 template <typename Type, typename TypeT>
CopyToOffsetVector(FlatBufferBuilder * builder,const Type * data,std::vector<Offset<Type>> & vec)45 void CopyToOffsetVector(FlatBufferBuilder* builder, const Type* data,
46                         std::vector<Offset<Type>>& vec) {
47   std::unique_ptr<TypeT> unpacked(data->UnPack());
48   flatbuffers::Offset<Type> offset = Type::Pack(*builder, unpacked.get());
49   vec.push_back(offset);
50 }
51 
GetNumElements(const std::vector<int> & dims)52 int GetNumElements(const std::vector<int>& dims) {
53   int num_elements = 1;
54   for (int i = 0; i < dims.size(); i++) {
55     num_elements *= dims[i];
56   }
57   return num_elements;
58 }
59 
60 // TODO(b/141023954): Reconcile this with the function in
61 // inference_profiler_stage.
62 template <typename T>
GenerateRandomGaussianData(int64_t num_elements,float min,float max,std::vector<T> * data)63 void GenerateRandomGaussianData(int64_t num_elements, float min, float max,
64                                 std::vector<T>* data) {
65   data->clear();
66   data->reserve(num_elements);
67 
68   static std::normal_distribution<double> distribution(kGaussianFloatMean,
69                                                        kGaussianStdDev);
70   static std::default_random_engine generator;
71   for (int i = 0; i < num_elements; ++i) {
72     auto rand_n = distribution(generator);
73     while (rand_n < 0 || rand_n >= 1) {
74       rand_n = distribution(generator);
75     }
76     auto rand_float = min + (max - min) * static_cast<float>(rand_n);
77     data->push_back(static_cast<T>(rand_float));
78   }
79 }
80 
ConvertTensorType(TensorType tensor_type,TfLiteType * type)81 TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType* type) {
82   *type = kTfLiteNoType;
83   switch (tensor_type) {
84     case TensorType_FLOAT32:
85       *type = kTfLiteFloat32;
86       break;
87     case TensorType_INT32:
88       *type = kTfLiteInt32;
89       break;
90     case TensorType_UINT32:
91       *type = kTfLiteUInt32;
92       break;
93     case TensorType_UINT8:
94       *type = kTfLiteUInt8;
95       break;
96     case TensorType_INT8:
97       *type = kTfLiteInt8;
98       break;
99     default:
100       break;
101   }
102   if (*type == kTfLiteNoType) {
103     VLOG(0) << "Unsupported data type %d in tensor: " << tensor_type;
104     return kTfLiteError;
105   }
106   return kTfLiteOk;
107 }
108 
StripWeightsFromFlatbuffer(const Model * input_model,flatbuffers::FlatBufferBuilder * new_model_builder)109 TfLiteStatus StripWeightsFromFlatbuffer(
110     const Model* input_model,
111     flatbuffers::FlatBufferBuilder* new_model_builder) {
112   // TODO(b/141023954): Generalize to N subgraphs.
113   if (input_model->subgraphs()->size() != 1) {
114     VLOG(0) << "Only 1 subgraph supported for now: "
115             << input_model->subgraphs()->size();
116     return kTfLiteError;
117   }
118 
119   // Data structures for output flatbuffer.
120   std::vector<Offset<SubGraph>> output_subgraphs;
121   std::vector<Offset<OperatorCode>> output_opcodes;
122   std::vector<Offset<Buffer>> output_buffers;
123 
124   const SubGraph* input_subgraph = (*input_model->subgraphs())[0];
125   std::unique_ptr<SubGraphT> mutable_subgraph(input_subgraph->UnPack());
126 
127   // For constant tensors that meet requirements:
128   // 1. Set the buffer-id to something larger than total number of buffers.
129   // This indicates to reconstitute_weights_into_fb that random data should be
130   // generated for them.
131   // 2. Mark that buffer for not getting carried into the output flatbuffer.
132   absl::flat_hash_set<uint32_t> erased_buffers;
133   const int num_buffers = input_model->buffers()->size();
134   int i = 0;
135   for (auto& tensor : mutable_subgraph->tensors) {
136     // We don't support Int32 tensors because they could contain
137     // non-randomisable information like Reshape dims.
138     if (tensor->type == TensorType_INT32 &&
139         GetNumElements(tensor->shape) < 10) {
140       // Int32 tensors of elements < 10 could be non-randomisable: for example,
141       // 'shape' input to a Reshape op.
142       continue;
143     } else if (tensor->type != TensorType_INT32 &&
144                tensor->type != TensorType_FLOAT32 &&
145                tensor->type != TensorType_UINT8 &&
146                tensor->type != TensorType_INT8) {
147       continue;
148     }
149 
150     if (auto* buffer = (*input_model->buffers())[tensor->buffer]) {
151       if (auto* array = buffer->data()) {
152         VLOG(1) << "Tensor " << i
153                 << " is constant, with buffer = " << tensor->buffer;
154         // Set tensor buffer to a high value (num_buffers * 2) & put an empty
155         // buffer in place of the original one.
156         erased_buffers.insert(tensor->buffer);
157         tensor->buffer = num_buffers * 2;
158       }
159     }
160     ++i;
161   }
162 
163   i = 0;
164   for (const Buffer* buffer : *(input_model->buffers())) {
165     if (erased_buffers.find(i) == erased_buffers.end()) {
166       // If buffer is not to be erased, insert it into the output flatbuffer to
167       // retain data.
168       CopyToOffsetVector<Buffer, BufferT>(new_model_builder, buffer,
169                                           output_buffers);
170     } else {
171       output_buffers.push_back(CreateBuffer(*new_model_builder));
172     }
173     ++i;
174   }
175 
176   flatbuffers::Offset<SubGraph> output_subgraph =
177       SubGraph::Pack(*new_model_builder, mutable_subgraph.get());
178   output_subgraphs.push_back(output_subgraph);
179 
180   // Write all ops as they are.
181   for (const OperatorCode* opcode : *(input_model->operator_codes())) {
182     CopyToOffsetVector<OperatorCode, OperatorCodeT>(new_model_builder, opcode,
183                                                     output_opcodes);
184   }
185 
186   // Generate output model.
187   auto description =
188       new_model_builder->CreateString("Generated by strip_buffers_from_fb");
189   auto new_model_offset =
190       CreateModel(*new_model_builder, TFLITE_SCHEMA_VERSION,
191                   new_model_builder->CreateVector(output_opcodes),
192                   new_model_builder->CreateVector(output_subgraphs),
193                   description, new_model_builder->CreateVector(output_buffers),
194                   /* metadata_buffer */ 0, /* metadatas */ 0);
195   FinishModelBuffer(*new_model_builder, new_model_offset);
196 
197   return kTfLiteOk;
198 }
199 
StripWeightsFromFlatbuffer(const absl::string_view input_flatbuffer)200 string StripWeightsFromFlatbuffer(const absl::string_view input_flatbuffer) {
201   auto input_model = FlatBufferModel::BuildFromBuffer(input_flatbuffer.data(),
202                                                       input_flatbuffer.size());
203 
204   FlatBufferBuilder builder(/*initial_size=*/10240);
205   if (StripWeightsFromFlatbuffer(input_model->GetModel(), &builder) !=
206       kTfLiteOk) {
207     return string();
208   }
209 
210   return string(reinterpret_cast<const char*>(builder.GetBufferPointer()),
211                 builder.GetSize());
212 }
213 
FlatbufferHasStrippedWeights(const Model * input_model)214 bool FlatbufferHasStrippedWeights(const Model* input_model) {
215   if (input_model->subgraphs()->size() != 1) {
216     VLOG(0) << "Only 1 subgraph supported for now";
217     return false;
218   }
219   const SubGraph* input_subgraph = (*input_model->subgraphs())[0];
220   std::unique_ptr<SubGraphT> mutable_subgraph(input_subgraph->UnPack());
221 
222   // For all tensors that have buffer > num_buffers + 1 (set to be so in
223   // strip_buffers_from_fb), create a buffer with random data & assign to them.
224   // For others, just copy over the original buffer from source model.
225   const int num_buffers = input_model->buffers()->size();
226   for (auto& tensor : mutable_subgraph->tensors) {
227     if (tensor->buffer > num_buffers + 1) {
228       return true;
229     }
230   }
231   return false;
232 }
233 
ReconstituteConstantTensorsIntoFlatbuffer(const Model * input_model,flatbuffers::FlatBufferBuilder * new_model_builder)234 TfLiteStatus ReconstituteConstantTensorsIntoFlatbuffer(
235     const Model* input_model,
236     flatbuffers::FlatBufferBuilder* new_model_builder) {
237   // TODO(b/141023954): Generalize to N subgraphs.
238   if (input_model->subgraphs()->size() != 1) {
239     VLOG(0) << "Only 1 subgraph supported for now";
240     return kTfLiteError;
241   }
242   const SubGraph* input_subgraph = (*input_model->subgraphs())[0];
243   std::unique_ptr<SubGraphT> mutable_subgraph(input_subgraph->UnPack());
244 
245   // Containers for output flatbuffer.
246   std::vector<Offset<::tflite::SubGraph>> output_subgraphs;
247   std::vector<Offset<::tflite::OperatorCode>> output_opcodes;
248   std::vector<Offset<::tflite::Buffer>> output_buffers;
249 
250   // An empty buffer, needed as a TFLite convention.
251   output_buffers.push_back(CreateBuffer(*new_model_builder));
252 
253   // For all tensors that have buffer > num_buffers + 1 (set to be so in
254   // strip_buffers_from_fb), create a buffer with random data & assign to them.
255   // For others, just copy over the original buffer from source model.
256   const int num_buffers = input_model->buffers()->size();
257   for (auto& tensor : mutable_subgraph->tensors) {
258     int buffer_id = output_buffers.size();
259     if (tensor->buffer > num_buffers + 1) {
260       // Num tensor elements.
261       int num_elements = GetNumElements(tensor->shape);
262       // Tensor type.
263       TfLiteType type;
264       if (ConvertTensorType(tensor->type, &type) != kTfLiteOk) {
265         return kTfLiteError;
266       }
267       // Generate buffer random data.
268       // Use different min/max bounds per tensor-type to ensure that random data
269       // 'appears' similar to typical values.
270       if (type == kTfLiteUInt8) {
271         std::vector<uint8_t> data;
272         GenerateRandomGaussianData(num_elements,
273                                    std::numeric_limits<uint8_t>::min(),
274                                    std::numeric_limits<uint8_t>::max(), &data);
275         auto data_buffer = new_model_builder->CreateVector(
276             reinterpret_cast<const uint8_t*>(data.data()),
277             sizeof(uint8_t) * data.size());
278         output_buffers.push_back(CreateBuffer(*new_model_builder, data_buffer));
279       } else if (type == kTfLiteInt8) {
280         std::vector<int8_t> data;
281         GenerateRandomGaussianData(num_elements,
282                                    std::numeric_limits<int8_t>::min(),
283                                    std::numeric_limits<int8_t>::max(), &data);
284         auto data_buffer = new_model_builder->CreateVector(
285             reinterpret_cast<const uint8_t*>(data.data()),
286             sizeof(int8_t) * data.size());
287         output_buffers.push_back(CreateBuffer(*new_model_builder, data_buffer));
288       } else if (type == kTfLiteFloat32) {
289         std::vector<float_t> data;
290         GenerateRandomGaussianData(num_elements, -1, 1, &data);
291         auto data_buffer = new_model_builder->CreateVector(
292             reinterpret_cast<const uint8_t*>(data.data()),
293             sizeof(float_t) * data.size());
294         output_buffers.push_back(CreateBuffer(*new_model_builder, data_buffer));
295       } else if (type == kTfLiteInt32) {
296         std::vector<int32_t> data;
297         GenerateRandomGaussianData(num_elements, 10, 10, &data);
298         auto data_buffer = new_model_builder->CreateVector(
299             reinterpret_cast<const uint8_t*>(data.data()),
300             sizeof(int32_t) * data.size());
301         output_buffers.push_back(CreateBuffer(*new_model_builder, data_buffer));
302       }
303     } else {
304       // For intermediate tensors, create a new buffer & assign the id to them.
305       // output_buffers.push_back(CreateBuffer(*new_model_builder));
306       CopyToOffsetVector<Buffer, BufferT>(
307           new_model_builder, input_model->buffers()->Get(tensor->buffer),
308           output_buffers);
309     }
310     tensor->buffer = buffer_id;
311   }
312 
313   for (const ::tflite::OperatorCode* opcode :
314        *(input_model->operator_codes())) {
315     CopyToOffsetVector<::tflite::OperatorCode, ::tflite::OperatorCodeT>(
316         new_model_builder, opcode, output_opcodes);
317   }
318 
319   flatbuffers::Offset<::tflite::SubGraph> output_subgraph =
320       ::tflite::SubGraph::Pack(*new_model_builder, mutable_subgraph.get());
321   output_subgraphs.push_back(output_subgraph);
322 
323   auto description = new_model_builder->CreateString(
324       "Generated by TFLite strip_buffers/reconstitution");
325   auto new_model_offset =
326       CreateModel(*new_model_builder, TFLITE_SCHEMA_VERSION,
327                   new_model_builder->CreateVector(output_opcodes),
328                   new_model_builder->CreateVector(output_subgraphs),
329                   description, new_model_builder->CreateVector(output_buffers),
330                   /* metadata_buffer */ 0, /* metadatas */ 0);
331   FinishModelBuffer(*new_model_builder, new_model_offset);
332 
333   return kTfLiteOk;
334 }
335 
ReconstituteConstantTensorsIntoFlatbuffer(const absl::string_view input_flatbuffer)336 string ReconstituteConstantTensorsIntoFlatbuffer(
337     const absl::string_view input_flatbuffer) {
338   auto input_model = FlatBufferModel::BuildFromBuffer(input_flatbuffer.data(),
339                                                       input_flatbuffer.size());
340 
341   FlatBufferBuilder builder(/*initial_size=*/10240);
342   if (ReconstituteConstantTensorsIntoFlatbuffer(input_model->GetModel(),
343                                                 &builder) != kTfLiteOk) {
344     return string();
345   }
346 
347   return string(reinterpret_cast<const char*>(builder.GetBufferPointer()),
348                 builder.GetSize());
349 }
350 
351 }  // namespace tflite
352