blob: f8e26cabd0127c6417454417c8117f4e7379cec4 [file] [log] [blame]
Andrew Moylanff6be512018-07-03 11:05:01 +10001// Copyright 2018 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ml/machine_learning_service_impl.h"
alanlxlcb1f8562018-11-01 15:16:11 +11006#include "ml/request_metrics.h"
Andrew Moylanff6be512018-07-03 11:05:01 +10007
Michael Martisa74af932018-08-13 16:52:36 +10008#include <memory>
Andrew Moylanff6be512018-07-03 11:05:01 +10009#include <utility>
10
Michael Martisa74af932018-08-13 16:52:36 +100011#include <base/bind.h>
12#include <base/bind_helpers.h>
Honglin Yuf33dce32019-12-05 15:10:39 +110013#include <base/files/file.h>
14#include <base/files/file_util.h>
Michael Martis8783c8e2019-06-26 17:30:54 +100015#include <tensorflow/lite/model.h>
Honglin Yuf33dce32019-12-05 15:10:39 +110016#include <unicode/putil.h>
17#include <unicode/udata.h>
18#include <utils/memory/mmap.h>
Michael Martisa74af932018-08-13 16:52:36 +100019
charleszhao17777f92020-04-23 12:53:11 +100020#include "ml/handwriting.h"
21#include "ml/handwriting_recognizer_impl.h"
Michael Martisa74af932018-08-13 16:52:36 +100022#include "ml/model_impl.h"
charleszhao17777f92020-04-23 12:53:11 +100023#include "ml/mojom/handwriting_recognizer.mojom.h"
Hidehiko Abeaa488c32018-08-31 23:49:41 +090024#include "ml/mojom/model.mojom.h"
Honglin Yuf33dce32019-12-05 15:10:39 +110025#include "ml/text_classifier_impl.h"
Michael Martisa74af932018-08-13 16:52:36 +100026
Andrew Moylanff6be512018-07-03 11:05:01 +100027namespace ml {
28
Michael Martisa74af932018-08-13 16:52:36 +100029namespace {
30
Honglin Yu0ed72352019-08-27 17:42:01 +100031using ::chromeos::machine_learning::mojom::BuiltinModelId;
32using ::chromeos::machine_learning::mojom::BuiltinModelSpecPtr;
33using ::chromeos::machine_learning::mojom::FlatBufferModelSpecPtr;
Andrew Moylanb481af72020-07-09 15:22:00 +100034using ::chromeos::machine_learning::mojom::HandwritingRecognizer;
charleszhao05c5a4a2020-06-09 16:49:54 +100035using ::chromeos::machine_learning::mojom::HandwritingRecognizerSpec;
36using ::chromeos::machine_learning::mojom::HandwritingRecognizerSpecPtr;
Michael Martisa74af932018-08-13 16:52:36 +100037using ::chromeos::machine_learning::mojom::LoadModelResult;
Andrew Moylanb481af72020-07-09 15:22:00 +100038using ::chromeos::machine_learning::mojom::MachineLearningService;
39using ::chromeos::machine_learning::mojom::Model;
40using ::chromeos::machine_learning::mojom::TextClassifier;
Michael Martisa74af932018-08-13 16:52:36 +100041
42constexpr char kSystemModelDir[] = "/opt/google/chrome/ml_models/";
Andrew Moylan79b34a42020-07-08 11:13:11 +100043// Base name for UMA metrics related to model loading (`LoadBuiltinModel`,
44// `LoadFlatBufferModel`, `LoadTextClassifier` or LoadHandwritingModel).
Honglin Yu6adafcd2019-07-22 13:48:11 +100045constexpr char kMetricsRequestName[] = "LoadModelResult";
Michael Martisa74af932018-08-13 16:52:36 +100046
Honglin Yuf33dce32019-12-05 15:10:39 +110047constexpr char kTextClassifierModelFile[] =
48 "mlservice-model-text_classifier_en-v706.fb";
49
Honglin Yuc5100022020-07-09 11:54:27 +100050constexpr char kLanguageIdentificationModelFile[] =
51 "mlservice-model-language_identification-20190924.smfb";
52
Honglin Yuf33dce32019-12-05 15:10:39 +110053constexpr char kIcuDataFilePath[] = "/opt/google/chrome/icudtl.dat";
54
Michael Martisa74af932018-08-13 16:52:36 +100055} // namespace
56
Andrew Moylanff6be512018-07-03 11:05:01 +100057MachineLearningServiceImpl::MachineLearningServiceImpl(
Michael Martisa74af932018-08-13 16:52:36 +100058 mojo::ScopedMessagePipeHandle pipe,
Andrew Moylanb481af72020-07-09 15:22:00 +100059 base::Closure disconnect_handler,
Michael Martisa74af932018-08-13 16:52:36 +100060 const std::string& model_dir)
Honglin Yuf33dce32019-12-05 15:10:39 +110061 : icu_data_(nullptr),
62 text_classifier_model_filename_(kTextClassifierModelFile),
63 builtin_model_metadata_(GetBuiltinModelMetadata()),
Michael Martisa74af932018-08-13 16:52:36 +100064 model_dir_(model_dir),
Andrew Moylanb481af72020-07-09 15:22:00 +100065 receiver_(this,
66 mojo::InterfaceRequest<
67 chromeos::machine_learning::mojom::MachineLearningService>(
68 std::move(pipe))) {
69 receiver_.set_disconnect_handler(std::move(disconnect_handler));
Andrew Moylanff6be512018-07-03 11:05:01 +100070}
71
Michael Martisa74af932018-08-13 16:52:36 +100072MachineLearningServiceImpl::MachineLearningServiceImpl(
Charles Zhaod4fb7b62020-08-25 17:21:58 +100073 mojo::ScopedMessagePipeHandle pipe,
74 base::Closure disconnect_handler,
75 dbus::Bus* bus)
Andrew Moylanb481af72020-07-09 15:22:00 +100076 : MachineLearningServiceImpl(
Charles Zhaod4fb7b62020-08-25 17:21:58 +100077 std::move(pipe), std::move(disconnect_handler), kSystemModelDir) {
78 if (bus) {
79 dlcservice_client_ = std::make_unique<DlcserviceClient>(bus);
80 }
81}
Michael Martisa74af932018-08-13 16:52:36 +100082
Honglin Yuf33dce32019-12-05 15:10:39 +110083void MachineLearningServiceImpl::SetTextClassifierModelFilenameForTesting(
84 const std::string& filename) {
85 text_classifier_model_filename_ = filename;
86}
87
Andrew Moylanb481af72020-07-09 15:22:00 +100088void MachineLearningServiceImpl::Clone(
89 mojo::PendingReceiver<MachineLearningService> receiver) {
90 clone_receivers_.Add(this, std::move(receiver));
Andrew Moylan2fb80af2020-07-08 10:52:08 +100091}
92
Honglin Yu0ed72352019-08-27 17:42:01 +100093void MachineLearningServiceImpl::LoadBuiltinModel(
94 BuiltinModelSpecPtr spec,
Andrew Moylanb481af72020-07-09 15:22:00 +100095 mojo::PendingReceiver<Model> receiver,
Qijiang Fan5d381a02020-04-19 23:42:37 +090096 LoadBuiltinModelCallback callback) {
Honglin Yu0ed72352019-08-27 17:42:01 +100097 // Unsupported models do not have metadata entries.
98 const auto metadata_lookup = builtin_model_metadata_.find(spec->id);
99 if (metadata_lookup == builtin_model_metadata_.end()) {
Honglin Yua81145a2019-09-23 15:20:13 +1000100 LOG(WARNING) << "LoadBuiltinModel requested for unsupported model ID "
101 << spec->id << ".";
Qijiang Fan5d381a02020-04-19 23:42:37 +0900102 std::move(callback).Run(LoadModelResult::MODEL_SPEC_ERROR);
Honglin Yu0ed72352019-08-27 17:42:01 +1000103 RecordModelSpecificationErrorEvent();
104 return;
105 }
106
107 const BuiltinModelMetadata& metadata = metadata_lookup->second;
108
109 DCHECK(!metadata.metrics_model_name.empty());
110
charleszhao5a7050e2020-07-14 15:21:41 +1000111 RequestMetrics request_metrics(metadata.metrics_model_name,
112 kMetricsRequestName);
Honglin Yu0ed72352019-08-27 17:42:01 +1000113 request_metrics.StartRecordingPerformanceMetrics();
114
115 // Attempt to load model.
116 const std::string model_path = model_dir_ + metadata.model_file;
117 std::unique_ptr<tflite::FlatBufferModel> model =
118 tflite::FlatBufferModel::BuildFromFile(model_path.c_str());
119 if (model == nullptr) {
120 LOG(ERROR) << "Failed to load model file '" << model_path << "'.";
Qijiang Fan5d381a02020-04-19 23:42:37 +0900121 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
Honglin Yu0ed72352019-08-27 17:42:01 +1000122 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
123 return;
124 }
125
Honglin Yuc0cef102020-01-17 15:26:01 +1100126 ModelImpl::Create(metadata.required_inputs, metadata.required_outputs,
Andrew Moylanb481af72020-07-09 15:22:00 +1000127 std::move(model), std::move(receiver),
Honglin Yuc0cef102020-01-17 15:26:01 +1100128 metadata.metrics_model_name);
Honglin Yu0ed72352019-08-27 17:42:01 +1000129
Qijiang Fan5d381a02020-04-19 23:42:37 +0900130 std::move(callback).Run(LoadModelResult::OK);
Honglin Yu0ed72352019-08-27 17:42:01 +1000131
132 request_metrics.FinishRecordingPerformanceMetrics();
133 request_metrics.RecordRequestEvent(LoadModelResult::OK);
134}
135
136void MachineLearningServiceImpl::LoadFlatBufferModel(
137 FlatBufferModelSpecPtr spec,
Andrew Moylanb481af72020-07-09 15:22:00 +1000138 mojo::PendingReceiver<Model> receiver,
Qijiang Fan5d381a02020-04-19 23:42:37 +0900139 LoadFlatBufferModelCallback callback) {
Honglin Yu0ed72352019-08-27 17:42:01 +1000140 DCHECK(!spec->metrics_model_name.empty());
141
charleszhao5a7050e2020-07-14 15:21:41 +1000142 RequestMetrics request_metrics(spec->metrics_model_name, kMetricsRequestName);
Honglin Yu0ed72352019-08-27 17:42:01 +1000143 request_metrics.StartRecordingPerformanceMetrics();
144
Andrew Moylan79b34a42020-07-08 11:13:11 +1000145 // Take the ownership of the content of `model_string` because `ModelImpl` has
Honglin Yu0ed72352019-08-27 17:42:01 +1000146 // to hold the memory.
147 auto model_string_impl =
148 std::make_unique<std::string>(std::move(spec->model_string));
149
150 std::unique_ptr<tflite::FlatBufferModel> model =
151 tflite::FlatBufferModel::BuildFromBuffer(model_string_impl->c_str(),
152 model_string_impl->length());
153 if (model == nullptr) {
154 LOG(ERROR) << "Failed to load model string of metric name: "
155 << spec->metrics_model_name << "'.";
Qijiang Fan5d381a02020-04-19 23:42:37 +0900156 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
Honglin Yu0ed72352019-08-27 17:42:01 +1000157 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
158 return;
159 }
160
Honglin Yuc0cef102020-01-17 15:26:01 +1100161 ModelImpl::Create(
Honglin Yu0ed72352019-08-27 17:42:01 +1000162 std::map<std::string, int>(spec->inputs.begin(), spec->inputs.end()),
163 std::map<std::string, int>(spec->outputs.begin(), spec->outputs.end()),
Andrew Moylanb481af72020-07-09 15:22:00 +1000164 std::move(model), std::move(model_string_impl), std::move(receiver),
Honglin Yu0ed72352019-08-27 17:42:01 +1000165 spec->metrics_model_name);
166
Qijiang Fan5d381a02020-04-19 23:42:37 +0900167 std::move(callback).Run(LoadModelResult::OK);
Honglin Yu0ed72352019-08-27 17:42:01 +1000168
169 request_metrics.FinishRecordingPerformanceMetrics();
170 request_metrics.RecordRequestEvent(LoadModelResult::OK);
171}
172
Honglin Yuf33dce32019-12-05 15:10:39 +1100173void MachineLearningServiceImpl::LoadTextClassifier(
Andrew Moylanb481af72020-07-09 15:22:00 +1000174 mojo::PendingReceiver<TextClassifier> receiver,
Honglin Yuf33dce32019-12-05 15:10:39 +1100175 LoadTextClassifierCallback callback) {
charleszhao5a7050e2020-07-14 15:21:41 +1000176 RequestMetrics request_metrics("TextClassifier", kMetricsRequestName);
Honglin Yuf33dce32019-12-05 15:10:39 +1100177 request_metrics.StartRecordingPerformanceMetrics();
178
179 // Attempt to load model.
180 std::string model_path = model_dir_ + text_classifier_model_filename_;
181 auto scoped_mmap =
182 std::make_unique<libtextclassifier3::ScopedMmap>(model_path);
183 if (!scoped_mmap->handle().ok()) {
184 LOG(ERROR) << "Failed to load the text classifier model file '"
185 << model_path << "'.";
186 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
187 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
188 return;
189 }
190
191 // Create the TextClassifier.
Honglin Yuc5100022020-07-09 11:54:27 +1000192 if (!TextClassifierImpl::Create(&scoped_mmap,
193 model_dir_ + kLanguageIdentificationModelFile,
Andrew Moylanb481af72020-07-09 15:22:00 +1000194 std::move(receiver))) {
Honglin Yuf33dce32019-12-05 15:10:39 +1100195 LOG(ERROR) << "Failed to create TextClassifierImpl object.";
196 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
197 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
198 return;
199 }
200
201 // initialize the icu library.
202 InitIcuIfNeeded();
203
204 std::move(callback).Run(LoadModelResult::OK);
205
206 request_metrics.FinishRecordingPerformanceMetrics();
207 request_metrics.RecordRequestEvent(LoadModelResult::OK);
208}
209
Charles Zhaoc882eb02020-07-27 10:02:35 +1000210void MachineLearningServiceImpl::RemovedFunction_3() {
211 NOTREACHED() << "RemovedFunction_3 should not be called";
charleszhao05c5a4a2020-06-09 16:49:54 +1000212}
213
214void MachineLearningServiceImpl::LoadHandwritingModelWithSpec(
215 HandwritingRecognizerSpecPtr spec,
Andrew Moylanb481af72020-07-09 15:22:00 +1000216 mojo::PendingReceiver<HandwritingRecognizer> receiver,
Charles Zhaoc882eb02020-07-27 10:02:35 +1000217 LoadHandwritingModelWithSpecCallback callback) {
charleszhao5a7050e2020-07-14 15:21:41 +1000218 RequestMetrics request_metrics("HandwritingModel", kMetricsRequestName);
charleszhao17777f92020-04-23 12:53:11 +1000219 request_metrics.StartRecordingPerformanceMetrics();
220
221 // Load HandwritingLibrary.
222 auto* const hwr_library = ml::HandwritingLibrary::GetInstance();
223
224 if (hwr_library->GetStatus() ==
225 ml::HandwritingLibrary::Status::kNotSupported) {
226 LOG(ERROR) << "Initialize ml::HandwritingLibrary with error "
227 << static_cast<int>(hwr_library->GetStatus());
228
229 std::move(callback).Run(LoadModelResult::FEATURE_NOT_SUPPORTED_ERROR);
230 request_metrics.RecordRequestEvent(
231 LoadModelResult::FEATURE_NOT_SUPPORTED_ERROR);
232 return;
233 }
234
235 if (hwr_library->GetStatus() != ml::HandwritingLibrary::Status::kOk) {
236 LOG(ERROR) << "Initialize ml::HandwritingLibrary with error "
237 << static_cast<int>(hwr_library->GetStatus());
238
239 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
240 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
241 return;
242 }
243
244 // Create HandwritingRecognizer.
Andrew Moylanb481af72020-07-09 15:22:00 +1000245 if (!HandwritingRecognizerImpl::Create(std::move(spec),
246 std::move(receiver))) {
charleszhao17777f92020-04-23 12:53:11 +1000247 LOG(ERROR) << "LoadHandwritingRecognizer returned false.";
248 std::move(callback).Run(LoadModelResult::LOAD_MODEL_ERROR);
249 request_metrics.RecordRequestEvent(LoadModelResult::LOAD_MODEL_ERROR);
250 return;
251 }
252
253 std::move(callback).Run(LoadModelResult::OK);
254 request_metrics.FinishRecordingPerformanceMetrics();
255 request_metrics.RecordRequestEvent(LoadModelResult::OK);
256}
257
Honglin Yuf33dce32019-12-05 15:10:39 +1100258void MachineLearningServiceImpl::InitIcuIfNeeded() {
259 if (icu_data_ == nullptr) {
260 // Need to load the data file again.
261 int64_t file_size;
262 const base::FilePath icu_data_file_path(kIcuDataFilePath);
263 CHECK(base::GetFileSize(icu_data_file_path, &file_size));
264 icu_data_ = new char[file_size];
265 CHECK(base::ReadFile(icu_data_file_path, icu_data_,
266 static_cast<int>(file_size)) == file_size);
267 // Init the Icu library.
268 UErrorCode err = U_ZERO_ERROR;
269 udata_setCommonData(reinterpret_cast<void*>(icu_data_), &err);
270 DCHECK(err == U_ZERO_ERROR);
271 // Never try to load Icu data from files.
272 udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
273 }
274}
275
Andrew Moylanff6be512018-07-03 11:05:01 +1000276} // namespace ml