xref: /freebsd/contrib/llvm-project/llvm/lib/Analysis/TFLiteUtils.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1*bdd1243dSDimitry Andric //===- TFUtils.cpp - tensorflow evaluation utilities ----------------------===//
2*bdd1243dSDimitry Andric //
3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bdd1243dSDimitry Andric //
7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8*bdd1243dSDimitry Andric //
9*bdd1243dSDimitry Andric // This file implements utilities for interfacing with tensorflow C APIs.
10*bdd1243dSDimitry Andric //
11*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
12*bdd1243dSDimitry Andric #include "llvm/Config/config.h"
13*bdd1243dSDimitry Andric #if defined(LLVM_HAVE_TFLITE)
14*bdd1243dSDimitry Andric 
15*bdd1243dSDimitry Andric #include "llvm/ADT/Twine.h"
16*bdd1243dSDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h"
17*bdd1243dSDimitry Andric #include "llvm/Support/Base64.h"
18*bdd1243dSDimitry Andric #include "llvm/Support/CommandLine.h"
19*bdd1243dSDimitry Andric #include "llvm/Support/Debug.h"
20*bdd1243dSDimitry Andric #include "llvm/Support/JSON.h"
21*bdd1243dSDimitry Andric #include "llvm/Support/MemoryBuffer.h"
22*bdd1243dSDimitry Andric #include "llvm/Support/Path.h"
23*bdd1243dSDimitry Andric #include "llvm/Support/raw_ostream.h"
24*bdd1243dSDimitry Andric 
25*bdd1243dSDimitry Andric #include "tensorflow/lite/interpreter.h"
26*bdd1243dSDimitry Andric #include "tensorflow/lite/kernels/register.h"
27*bdd1243dSDimitry Andric #include "tensorflow/lite/model.h"
28*bdd1243dSDimitry Andric #include "tensorflow/lite/model_builder.h"
29*bdd1243dSDimitry Andric #include "tensorflow/lite/op_resolver.h"
30*bdd1243dSDimitry Andric #include "tensorflow/lite/logger.h"
31*bdd1243dSDimitry Andric 
32*bdd1243dSDimitry Andric #include <cassert>
33*bdd1243dSDimitry Andric #include <numeric>
34*bdd1243dSDimitry Andric #include <optional>
35*bdd1243dSDimitry Andric 
36*bdd1243dSDimitry Andric using namespace llvm;
37*bdd1243dSDimitry Andric 
38*bdd1243dSDimitry Andric namespace llvm {
39*bdd1243dSDimitry Andric class EvaluationResultImpl {
40*bdd1243dSDimitry Andric public:
41*bdd1243dSDimitry Andric   EvaluationResultImpl(const std::vector<const TfLiteTensor *> &Outputs)
42*bdd1243dSDimitry Andric       : Outputs(Outputs){};
43*bdd1243dSDimitry Andric 
44*bdd1243dSDimitry Andric   const TfLiteTensor *getOutput(size_t I) { return Outputs[I]; }
45*bdd1243dSDimitry Andric 
46*bdd1243dSDimitry Andric   EvaluationResultImpl(const EvaluationResultImpl &) = delete;
47*bdd1243dSDimitry Andric   EvaluationResultImpl(EvaluationResultImpl &&Other) = delete;
48*bdd1243dSDimitry Andric 
49*bdd1243dSDimitry Andric private:
50*bdd1243dSDimitry Andric   const std::vector<const TfLiteTensor *> Outputs;
51*bdd1243dSDimitry Andric };
52*bdd1243dSDimitry Andric 
53*bdd1243dSDimitry Andric class TFModelEvaluatorImpl {
54*bdd1243dSDimitry Andric public:
55*bdd1243dSDimitry Andric   TFModelEvaluatorImpl(StringRef SavedModelPath,
56*bdd1243dSDimitry Andric                        const std::vector<TensorSpec> &InputSpecs,
57*bdd1243dSDimitry Andric                        const std::vector<TensorSpec> &OutputSpecs,
58*bdd1243dSDimitry Andric                        const char *Tags);
59*bdd1243dSDimitry Andric 
60*bdd1243dSDimitry Andric   bool isValid() const { return IsValid; }
61*bdd1243dSDimitry Andric   size_t outputSize() const { return Output.size(); }
62*bdd1243dSDimitry Andric 
63*bdd1243dSDimitry Andric   std::unique_ptr<EvaluationResultImpl> evaluate() {
64*bdd1243dSDimitry Andric     Interpreter->Invoke();
65*bdd1243dSDimitry Andric     return std::make_unique<EvaluationResultImpl>(Output);
66*bdd1243dSDimitry Andric   }
67*bdd1243dSDimitry Andric 
68*bdd1243dSDimitry Andric   const std::vector<TfLiteTensor *> &getInput() const { return Input; }
69*bdd1243dSDimitry Andric 
70*bdd1243dSDimitry Andric   ~TFModelEvaluatorImpl();
71*bdd1243dSDimitry Andric 
72*bdd1243dSDimitry Andric private:
73*bdd1243dSDimitry Andric   std::unique_ptr<tflite::FlatBufferModel> Model;
74*bdd1243dSDimitry Andric 
75*bdd1243dSDimitry Andric   /// The objects necessary for carrying out an evaluation of the SavedModel.
76*bdd1243dSDimitry Andric   /// They are expensive to set up, and we maintain them accross all the
77*bdd1243dSDimitry Andric   /// evaluations of the model.
78*bdd1243dSDimitry Andric   std::unique_ptr<tflite::Interpreter> Interpreter;
79*bdd1243dSDimitry Andric 
80*bdd1243dSDimitry Andric   /// The input tensors. We set up the tensors once and just mutate theirs
81*bdd1243dSDimitry Andric   /// scalars before each evaluation. The input tensors keep their value after
82*bdd1243dSDimitry Andric   /// an evaluation.
83*bdd1243dSDimitry Andric   std::vector<TfLiteTensor *> Input;
84*bdd1243dSDimitry Andric 
85*bdd1243dSDimitry Andric   /// The output nodes.
86*bdd1243dSDimitry Andric   std::vector<const TfLiteTensor *> Output;
87*bdd1243dSDimitry Andric 
88*bdd1243dSDimitry Andric   void invalidate() { IsValid = false; }
89*bdd1243dSDimitry Andric 
90*bdd1243dSDimitry Andric   bool IsValid = true;
91*bdd1243dSDimitry Andric 
92*bdd1243dSDimitry Andric   /// Reusable utility for ensuring we can bind the requested Name to a node in
93*bdd1243dSDimitry Andric   /// the SavedModel Graph.
94*bdd1243dSDimitry Andric   bool checkReportAndInvalidate(const TfLiteTensor *Tensor,
95*bdd1243dSDimitry Andric                                 const TensorSpec &Spec);
96*bdd1243dSDimitry Andric };
97*bdd1243dSDimitry Andric 
98*bdd1243dSDimitry Andric } // namespace llvm
99*bdd1243dSDimitry Andric 
100*bdd1243dSDimitry Andric TFModelEvaluatorImpl::TFModelEvaluatorImpl(
101*bdd1243dSDimitry Andric     StringRef SavedModelPath, const std::vector<TensorSpec> &InputSpecs,
102*bdd1243dSDimitry Andric     const std::vector<TensorSpec> &OutputSpecs, const char *Tags = "serve")
103*bdd1243dSDimitry Andric     : Input(InputSpecs.size()), Output(OutputSpecs.size()) {
104*bdd1243dSDimitry Andric   // INFO and DEBUG messages could be numerous and not particularly interesting
105*bdd1243dSDimitry Andric   tflite::LoggerOptions::SetMinimumLogSeverity(tflite::TFLITE_LOG_WARNING);
106*bdd1243dSDimitry Andric   // FIXME: make ErrorReporter a member (may also need subclassing
107*bdd1243dSDimitry Andric   // StatefulErrorReporter) to easily get the latest error status, for
108*bdd1243dSDimitry Andric   // debugging.
109*bdd1243dSDimitry Andric   tflite::StderrReporter ErrorReporter;
110*bdd1243dSDimitry Andric   SmallVector<char, 128> TFLitePathBuff;
111*bdd1243dSDimitry Andric   llvm::sys::path::append(TFLitePathBuff, SavedModelPath, "model.tflite");
112*bdd1243dSDimitry Andric   StringRef TFLitePath(TFLitePathBuff.data(), TFLitePathBuff.size());
113*bdd1243dSDimitry Andric   Model = tflite::FlatBufferModel::BuildFromFile(TFLitePath.str().c_str(),
114*bdd1243dSDimitry Andric                                                  &ErrorReporter);
115*bdd1243dSDimitry Andric   if (!Model) {
116*bdd1243dSDimitry Andric     invalidate();
117*bdd1243dSDimitry Andric     return;
118*bdd1243dSDimitry Andric   }
119*bdd1243dSDimitry Andric 
120*bdd1243dSDimitry Andric   tflite::ops::builtin::BuiltinOpResolver Resolver;
121*bdd1243dSDimitry Andric   tflite::InterpreterBuilder Builder(*Model, Resolver);
122*bdd1243dSDimitry Andric   Builder(&Interpreter);
123*bdd1243dSDimitry Andric 
124*bdd1243dSDimitry Andric   if (!Interpreter) {
125*bdd1243dSDimitry Andric     invalidate();
126*bdd1243dSDimitry Andric     return;
127*bdd1243dSDimitry Andric   }
128*bdd1243dSDimitry Andric 
129*bdd1243dSDimitry Andric   // We assume the input buffers are valid for the lifetime of the interpreter.
130*bdd1243dSDimitry Andric   // By default, tflite allocates memory in an arena and will periodically take
131*bdd1243dSDimitry Andric   // away memory and reallocate it in a different location after evaluations in
132*bdd1243dSDimitry Andric   // order to improve utilization of the buffers owned in the arena. So, we
133*bdd1243dSDimitry Andric   // explicitly mark our input buffers as persistent to avoid this behavior.
134*bdd1243dSDimitry Andric   for (size_t I = 0; I < Interpreter->inputs().size(); ++I)
135*bdd1243dSDimitry Andric     Interpreter->tensor(I)->allocation_type =
136*bdd1243dSDimitry Andric         TfLiteAllocationType::kTfLiteArenaRwPersistent;
137*bdd1243dSDimitry Andric 
138*bdd1243dSDimitry Andric   if (Interpreter->AllocateTensors() != TfLiteStatus::kTfLiteOk) {
139*bdd1243dSDimitry Andric     invalidate();
140*bdd1243dSDimitry Andric     return;
141*bdd1243dSDimitry Andric   }
142*bdd1243dSDimitry Andric   // Known inputs and outputs
143*bdd1243dSDimitry Andric   StringMap<int> InputsMap;
144*bdd1243dSDimitry Andric   StringMap<int> OutputsMap;
145*bdd1243dSDimitry Andric   for (size_t I = 0; I < Interpreter->inputs().size(); ++I)
146*bdd1243dSDimitry Andric     InputsMap[Interpreter->GetInputName(I)] = I;
147*bdd1243dSDimitry Andric   for (size_t I = 0; I < Interpreter->outputs().size(); ++I)
148*bdd1243dSDimitry Andric     OutputsMap[Interpreter->GetOutputName(I)] = I;
149*bdd1243dSDimitry Andric 
150*bdd1243dSDimitry Andric   size_t NumberFeaturesPassed = 0;
151*bdd1243dSDimitry Andric   for (size_t I = 0; I < InputSpecs.size(); ++I) {
152*bdd1243dSDimitry Andric     auto &InputSpec = InputSpecs[I];
153*bdd1243dSDimitry Andric     auto MapI = InputsMap.find(InputSpec.name() + ":" +
154*bdd1243dSDimitry Andric                                std::to_string(InputSpec.port()));
155*bdd1243dSDimitry Andric     if (MapI == InputsMap.end()) {
156*bdd1243dSDimitry Andric       Input[I] = nullptr;
157*bdd1243dSDimitry Andric       continue;
158*bdd1243dSDimitry Andric     }
159*bdd1243dSDimitry Andric     Input[I] = Interpreter->tensor(MapI->second);
160*bdd1243dSDimitry Andric     if (!checkReportAndInvalidate(Input[I], InputSpec))
161*bdd1243dSDimitry Andric       return;
162*bdd1243dSDimitry Andric     std::memset(Input[I]->data.data, 0,
163*bdd1243dSDimitry Andric                 InputSpecs[I].getTotalTensorBufferSize());
164*bdd1243dSDimitry Andric     ++NumberFeaturesPassed;
165*bdd1243dSDimitry Andric   }
166*bdd1243dSDimitry Andric 
167*bdd1243dSDimitry Andric   if (NumberFeaturesPassed < Interpreter->inputs().size()) {
168*bdd1243dSDimitry Andric     // we haven't passed all the required features to the model, throw an error.
169*bdd1243dSDimitry Andric     errs() << "Required feature(s) have not been passed to the ML model";
170*bdd1243dSDimitry Andric     invalidate();
171*bdd1243dSDimitry Andric     return;
172*bdd1243dSDimitry Andric   }
173*bdd1243dSDimitry Andric 
174*bdd1243dSDimitry Andric   for (size_t I = 0; I < OutputSpecs.size(); ++I) {
175*bdd1243dSDimitry Andric     const auto &OutputSpec = OutputSpecs[I];
176*bdd1243dSDimitry Andric     Output[I] = Interpreter->output_tensor(
177*bdd1243dSDimitry Andric         OutputsMap[OutputSpec.name() + ":" +
178*bdd1243dSDimitry Andric                    std::to_string(OutputSpec.port())]);
179*bdd1243dSDimitry Andric     if (!checkReportAndInvalidate(Output[I], OutputSpec))
180*bdd1243dSDimitry Andric       return;
181*bdd1243dSDimitry Andric   }
182*bdd1243dSDimitry Andric }
183*bdd1243dSDimitry Andric 
184*bdd1243dSDimitry Andric TFModelEvaluator::TFModelEvaluator(StringRef SavedModelPath,
185*bdd1243dSDimitry Andric                                    const std::vector<TensorSpec> &InputSpecs,
186*bdd1243dSDimitry Andric                                    const std::vector<TensorSpec> &OutputSpecs,
187*bdd1243dSDimitry Andric                                    const char *Tags)
188*bdd1243dSDimitry Andric     : Impl(new TFModelEvaluatorImpl(SavedModelPath, InputSpecs, OutputSpecs,
189*bdd1243dSDimitry Andric                                     Tags)) {
190*bdd1243dSDimitry Andric   if (!Impl->isValid())
191*bdd1243dSDimitry Andric     Impl.reset();
192*bdd1243dSDimitry Andric }
193*bdd1243dSDimitry Andric 
194*bdd1243dSDimitry Andric TFModelEvaluatorImpl::~TFModelEvaluatorImpl() {}
195*bdd1243dSDimitry Andric 
196*bdd1243dSDimitry Andric bool TFModelEvaluatorImpl::checkReportAndInvalidate(const TfLiteTensor *Tensor,
197*bdd1243dSDimitry Andric                                                     const TensorSpec &Spec) {
198*bdd1243dSDimitry Andric   if (!Tensor) {
199*bdd1243dSDimitry Andric     errs() << "Could not find TF_Output named: " + Spec.name();
200*bdd1243dSDimitry Andric     IsValid = false;
201*bdd1243dSDimitry Andric   }
202*bdd1243dSDimitry Andric   if (Spec.getTotalTensorBufferSize() != Tensor->bytes)
203*bdd1243dSDimitry Andric     IsValid = false;
204*bdd1243dSDimitry Andric 
205*bdd1243dSDimitry Andric   // If the total sizes match, there could still be a mismatch in the shape.
206*bdd1243dSDimitry Andric   // We ignore that for now.
207*bdd1243dSDimitry Andric 
208*bdd1243dSDimitry Andric   return IsValid;
209*bdd1243dSDimitry Andric }
210*bdd1243dSDimitry Andric 
211*bdd1243dSDimitry Andric std::optional<TFModelEvaluator::EvaluationResult> TFModelEvaluator::evaluate() {
212*bdd1243dSDimitry Andric   if (!isValid())
213*bdd1243dSDimitry Andric     return std::nullopt;
214*bdd1243dSDimitry Andric   return EvaluationResult(Impl->evaluate());
215*bdd1243dSDimitry Andric }
216*bdd1243dSDimitry Andric 
217*bdd1243dSDimitry Andric void *TFModelEvaluator::getUntypedInput(size_t Index) {
218*bdd1243dSDimitry Andric   TfLiteTensor *T = Impl->getInput()[Index];
219*bdd1243dSDimitry Andric   if (!T)
220*bdd1243dSDimitry Andric     return nullptr;
221*bdd1243dSDimitry Andric   return T->data.data;
222*bdd1243dSDimitry Andric }
223*bdd1243dSDimitry Andric 
224*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult::EvaluationResult(
225*bdd1243dSDimitry Andric     std::unique_ptr<EvaluationResultImpl> Impl)
226*bdd1243dSDimitry Andric     : Impl(std::move(Impl)) {}
227*bdd1243dSDimitry Andric 
228*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult::EvaluationResult(EvaluationResult &&Other)
229*bdd1243dSDimitry Andric     : Impl(std::move(Other.Impl)) {}
230*bdd1243dSDimitry Andric 
231*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult &
232*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult::operator=(EvaluationResult &&Other) {
233*bdd1243dSDimitry Andric   Impl = std::move(Other.Impl);
234*bdd1243dSDimitry Andric   return *this;
235*bdd1243dSDimitry Andric }
236*bdd1243dSDimitry Andric 
237*bdd1243dSDimitry Andric void *TFModelEvaluator::EvaluationResult::getUntypedTensorValue(size_t Index) {
238*bdd1243dSDimitry Andric   return Impl->getOutput(Index)->data.data;
239*bdd1243dSDimitry Andric }
240*bdd1243dSDimitry Andric 
241*bdd1243dSDimitry Andric const void *
242*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult::getUntypedTensorValue(size_t Index) const {
243*bdd1243dSDimitry Andric   return Impl->getOutput(Index)->data.data;
244*bdd1243dSDimitry Andric }
245*bdd1243dSDimitry Andric 
246*bdd1243dSDimitry Andric TFModelEvaluator::EvaluationResult::~EvaluationResult() {}
247*bdd1243dSDimitry Andric TFModelEvaluator::~TFModelEvaluator() {}
248*bdd1243dSDimitry Andric 
249*bdd1243dSDimitry Andric #endif // defined(LLVM_HAVE_TFLITE)
250