TensorFlow Serving C++ API Documentation
regressor.cc
1 /* Copyright 2017 Google Inc. 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_serving/servables/tensorflow/regressor.h"
17 
18 #include <stddef.h>
19 
20 #include <algorithm>
21 #include <functional>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "tensorflow/cc/saved_model/signature_constants.h"
28 #include "tensorflow/core/example/example.pb.h"
29 #include "tensorflow/core/framework/tensor.h"
30 #include "tensorflow/core/lib/core/errors.h"
31 #include "tensorflow/core/lib/core/notification.h"
32 #include "tensorflow/core/lib/core/status.h"
33 #include "tensorflow/core/platform/threadpool_options.h"
34 #include "tensorflow/core/platform/tracing.h"
35 #include "tensorflow/core/platform/types.h"
36 #include "tensorflow_serving/apis/input.pb.h"
37 #include "tensorflow_serving/apis/model.pb.h"
38 #include "tensorflow_serving/apis/regression.pb.h"
39 #include "tensorflow_serving/apis/regressor.h"
40 #include "tensorflow_serving/servables/tensorflow/util.h"
41 
42 namespace tensorflow {
43 namespace serving {
44 namespace {
45 
46 // Implementation of the RegressorInterface using SavedModel.
47 class SavedModelTensorFlowRegressor : public RegressorInterface {
48  public:
49  explicit SavedModelTensorFlowRegressor(
50  const RunOptions& run_options, Session* session,
51  const SignatureDef* const signature,
52  const thread::ThreadPoolOptions& thread_pool_options =
53  thread::ThreadPoolOptions())
54  : run_options_(run_options),
55  session_(session),
56  signature_(signature),
57  thread_pool_options_(thread_pool_options) {}
58 
59  ~SavedModelTensorFlowRegressor() override = default;
60 
61  Status Regress(const RegressionRequest& request,
62  RegressionResult* result) override {
63  TRACELITERAL("SavedModelTensorFlowRegressor::Regress");
64 
65  string input_tensor_name;
66  std::vector<string> output_tensor_names;
67  TF_RETURN_IF_ERROR(PreProcessRegression(*signature_, &input_tensor_name,
68  &output_tensor_names));
69 
70  std::vector<Tensor> outputs;
71  int num_examples;
72  int64_t runtime_latency;
73  TF_RETURN_IF_ERROR(PerformOneShotTensorComputation(
74  run_options_, request.input(), input_tensor_name, output_tensor_names,
75  session_, &outputs, &num_examples, thread_pool_options_,
76  &runtime_latency));
77  RecordRuntimeLatency(request.model_spec().name(), /*api=*/"Regress",
78  /*runtime=*/"TF1", runtime_latency);
79 
80  TRACELITERAL("ConvertToRegressionResult");
81  return PostProcessRegressionResult(*signature_, num_examples,
82  output_tensor_names, outputs, result);
83  }
84 
85  private:
86  const RunOptions run_options_;
87  Session* const session_;
88  const SignatureDef* const signature_;
89  const thread::ThreadPoolOptions thread_pool_options_;
90 
91  TF_DISALLOW_COPY_AND_ASSIGN(SavedModelTensorFlowRegressor);
92 };
93 
94 class SavedModelRegressor : public RegressorInterface {
95  public:
96  SavedModelRegressor(const RunOptions& run_options,
97  std::unique_ptr<SavedModelBundle> bundle)
98  : run_options_(run_options), bundle_(std::move(bundle)) {}
99 
100  ~SavedModelRegressor() override = default;
101 
102  Status Regress(const RegressionRequest& request,
103  RegressionResult* result) override {
104  SignatureDef signature;
105  TF_RETURN_IF_ERROR(GetRegressionSignatureDef(
106  request.model_spec(), bundle_->meta_graph_def, &signature));
107  SavedModelTensorFlowRegressor regressor(run_options_,
108  bundle_->session.get(), &signature);
109  return regressor.Regress(request, result);
110  }
111 
112  private:
113  const RunOptions run_options_;
114  std::unique_ptr<SavedModelBundle> bundle_;
115 
116  TF_DISALLOW_COPY_AND_ASSIGN(SavedModelRegressor);
117 };
118 
119 } // namespace
120 
121 Status CreateRegressorFromSavedModelBundle(
122  const RunOptions& run_options, std::unique_ptr<SavedModelBundle> bundle,
123  std::unique_ptr<RegressorInterface>* service) {
124  service->reset(new SavedModelRegressor(run_options, std::move(bundle)));
125  return absl::OkStatus();
126 }
127 
128 Status CreateFlyweightTensorFlowRegressor(
129  const RunOptions& run_options, Session* session,
130  const SignatureDef* signature,
131  std::unique_ptr<RegressorInterface>* service) {
132  return CreateFlyweightTensorFlowRegressor(
133  run_options, session, signature, thread::ThreadPoolOptions(), service);
134 }
135 
136 Status CreateFlyweightTensorFlowRegressor(
137  const RunOptions& run_options, Session* session,
138  const SignatureDef* signature,
139  const thread::ThreadPoolOptions& thread_pool_options,
140  std::unique_ptr<RegressorInterface>* service) {
141  service->reset(new SavedModelTensorFlowRegressor(
142  run_options, session, signature, thread_pool_options));
143  return absl::OkStatus();
144 }
145 
146 Status GetRegressionSignatureDef(const ModelSpec& model_spec,
147  const MetaGraphDef& meta_graph_def,
148  SignatureDef* signature) {
149  const string signature_name = model_spec.signature_name().empty()
150  ? kDefaultServingSignatureDefKey
151  : model_spec.signature_name();
152  auto iter = meta_graph_def.signature_def().find(signature_name);
153  if (iter == meta_graph_def.signature_def().end()) {
154  return errors::InvalidArgument(strings::StrCat(
155  "No signature was found with the name: ", signature_name));
156  }
157  if (GetSignatureMethodNameCheckFeature()) {
158  if (iter->second.method_name() != kRegressMethodName) {
159  return errors::InvalidArgument(strings::StrCat(
160  "Expected regression signature method_name to be ",
161  kRegressMethodName, ". Was: ", iter->second.method_name()));
162  }
163  } else {
164  TF_RETURN_IF_ERROR(PreProcessRegression(iter->second, nullptr, nullptr));
165  }
166  *signature = iter->second;
167  return absl::OkStatus();
168 }
169 
170 Status PreProcessRegression(const SignatureDef& signature,
171  string* input_tensor_name,
172  std::vector<string>* output_tensor_names) {
173  if (GetSignatureMethodNameCheckFeature() &&
174  signature.method_name() != kRegressMethodName) {
175  return errors::InvalidArgument(strings::StrCat(
176  "Expected regression signature method_name to be ", kRegressMethodName,
177  ". Was: ", signature.method_name()));
178  }
179  if (signature.inputs().size() != 1) {
180  return errors::InvalidArgument(
181  strings::StrCat("Expected one input Tensor."));
182  }
183  if (signature.outputs().size() != 1) {
184  return errors::InvalidArgument(
185  strings::StrCat("Expected one output Tensor."));
186  }
187 
188  auto input_iter = signature.inputs().find(kRegressInputs);
189  if (input_iter == signature.inputs().end()) {
190  return errors::InvalidArgument(
191  "No regression inputs found in SignatureDef: ",
192  signature.DebugString());
193  }
194  if (input_tensor_name != nullptr) {
195  *input_tensor_name = input_iter->second.name();
196  }
197 
198  auto output_iter = signature.outputs().find(kRegressOutputs);
199  if (output_iter == signature.outputs().end()) {
200  return errors::InvalidArgument(
201  "No regression outputs found in SignatureDef: ",
202  signature.DebugString());
203  }
204  if (output_tensor_names != nullptr) {
205  output_tensor_names->push_back(output_iter->second.name());
206  }
207  return absl::OkStatus();
208 }
209 
210 Status PostProcessRegressionResult(
211  const SignatureDef& signature, int num_examples,
212  const std::vector<string>& output_tensor_names,
213  const std::vector<Tensor>& output_tensors, RegressionResult* result) {
214  if (output_tensors.size() != output_tensor_names.size()) {
215  return errors::InvalidArgument(
216  "Expected output_tensors and output_tensor_names to have the same "
217  "size.");
218  }
219 
220  auto output_iter = signature.outputs().find(kRegressOutputs);
221  if (output_iter == signature.outputs().end()) {
222  return errors::FailedPrecondition(
223  "No regression outputs found in SignatureDef: ",
224  signature.DebugString());
225  }
226  const string output_tensor_name = output_iter->second.name();
227  const Tensor* output_tensor = nullptr;
228  for (int i = 0; i < output_tensor_names.size(); ++i) {
229  if (output_tensor_names[i] == output_tensor_name) {
230  output_tensor = &output_tensors[i];
231  break;
232  }
233  }
234 
235  // Ensure the regression score output is shaped how we expect.
236  if (output_tensor == nullptr) {
237  return errors::InvalidArgument(strings::StrCat(
238  "Could not find output tensor '", output_tensor_name, "'"));
239  }
240  if (!(output_tensor->dims() == 1 ||
241  (output_tensor->dims() == 2 && output_tensor->dim_size(1) == 1))) {
242  return errors::InvalidArgument(
243  "Expected output Tensor shape to be either [batch_size] or ",
244  "[batch_size, 1] but got ", output_tensor->shape().DebugString());
245  }
246  if (num_examples != output_tensor->dim_size(0)) {
247  return errors::InvalidArgument(strings::StrCat(
248  "Input batch size did not match output batch size: ", num_examples,
249  " vs. ", output_tensor->dim_size(0)));
250  }
251  if (output_tensor->dtype() != DT_FLOAT) {
252  return errors::InvalidArgument("Expected output Tensor of DT_FLOAT. Got: ",
253  DataType_Name(output_tensor->dtype()));
254  }
255 
256  if (output_tensor->NumElements() != num_examples) {
257  return errors::InvalidArgument("Expected output batch size to be ",
258  num_examples,
259  ". Got: ", output_tensor->NumElements());
260  }
261 
262  const auto& output_tensor_flat = output_tensor->flat<float>();
263  for (int i = 0; i < num_examples; ++i) {
264  result->add_regressions()->set_value(output_tensor_flat(i));
265  }
266  return absl::OkStatus();
267 }
268 
269 Status RunRegress(const RunOptions& run_options,
270  const MetaGraphDef& meta_graph_def,
271  const absl::optional<int64_t>& servable_version,
272  Session* session, const RegressionRequest& request,
273  RegressionResponse* response,
274  const thread::ThreadPoolOptions& thread_pool_options) {
275  SignatureDef signature;
276  TF_RETURN_IF_ERROR(GetRegressionSignatureDef(request.model_spec(),
277  meta_graph_def, &signature));
278 
279  std::unique_ptr<RegressorInterface> regressor_interface;
280  TF_RETURN_IF_ERROR(CreateFlyweightTensorFlowRegressor(
281  run_options, session, &signature, thread_pool_options,
282  &regressor_interface));
283 
284  MakeModelSpec(request.model_spec().name(),
285  request.model_spec().signature_name(), servable_version,
286  response->mutable_model_spec());
287 
288  // Run regression
289  return regressor_interface->Regress(request, response->mutable_result());
290 }
291 
292 } // namespace serving
293 } // namespace tensorflow