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