blob: 2c17365d48bafe55f462e9a937bd81f184a126b3 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2022 The Chromium Authors
Min Qin75bee1b2022-02-05 03:57:342// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
6
7#include "base/bit_cast.h"
8#include "base/metrics/field_trial_params.h"
Min Qin7a9f38902023-05-15 16:27:469#include "base/rand_util.h"
Min Qin75bee1b2022-02-05 03:57:3410#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_split.h"
Min Qincae984cb2022-05-20 03:08:3412#include "base/time/clock.h"
Salvador Guerrero5783fd62023-05-13 00:28:3213#include "base/time/time.h"
Min Qincae984cb2022-05-20 03:08:3414#include "components/segmentation_platform/internal/constants.h"
Min Qin642ab222022-05-19 21:54:5315#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
Min Qin75bee1b2022-02-05 03:57:3416#include "components/segmentation_platform/internal/stats.h"
17#include "components/segmentation_platform/public/config.h"
18#include "components/segmentation_platform/public/features.h"
Min Qincae984cb2022-05-20 03:08:3419#include "components/segmentation_platform/public/local_state_helper.h"
Min Qin75bee1b2022-02-05 03:57:3420#include "services/metrics/public/cpp/ukm_builders.h"
21#include "services/metrics/public/cpp/ukm_recorder.h"
22
23#define CALL_MEMBER_FN(obj, func) ((obj).*(func))
24#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x)[0])
25
Min Qin1eeb6432023-02-23 23:41:5926using segmentation_platform::SegmentationUkmHelper;
ssid8386fc72022-05-21 00:34:1727using segmentation_platform::proto::SegmentId;
Min Qin75bee1b2022-02-05 03:57:3428using ukm::builders::Segmentation_ModelExecution;
29
ssid7a2fe7012023-07-05 23:16:0930namespace segmentation_platform {
31
Min Qin75bee1b2022-02-05 03:57:3432namespace {
33using UkmMemberFn =
34 Segmentation_ModelExecution& (Segmentation_ModelExecution::*)(int64_t);
35
Xing Liu282dd0d2022-02-24 00:20:3936const UkmMemberFn kSegmentationUkmInputMethods[] = {
Min Qin75bee1b2022-02-05 03:57:3437 &Segmentation_ModelExecution::SetInput0,
38 &Segmentation_ModelExecution::SetInput1,
39 &Segmentation_ModelExecution::SetInput2,
40 &Segmentation_ModelExecution::SetInput3,
41 &Segmentation_ModelExecution::SetInput4,
42 &Segmentation_ModelExecution::SetInput5,
43 &Segmentation_ModelExecution::SetInput6,
44 &Segmentation_ModelExecution::SetInput7,
45 &Segmentation_ModelExecution::SetInput8,
46 &Segmentation_ModelExecution::SetInput9,
47 &Segmentation_ModelExecution::SetInput10,
48 &Segmentation_ModelExecution::SetInput11,
49 &Segmentation_ModelExecution::SetInput12,
50 &Segmentation_ModelExecution::SetInput13,
51 &Segmentation_ModelExecution::SetInput14,
52 &Segmentation_ModelExecution::SetInput15,
53 &Segmentation_ModelExecution::SetInput16,
54 &Segmentation_ModelExecution::SetInput17,
55 &Segmentation_ModelExecution::SetInput18,
56 &Segmentation_ModelExecution::SetInput19,
57 &Segmentation_ModelExecution::SetInput20,
58 &Segmentation_ModelExecution::SetInput21,
59 &Segmentation_ModelExecution::SetInput22,
60 &Segmentation_ModelExecution::SetInput23,
61 &Segmentation_ModelExecution::SetInput24,
62 &Segmentation_ModelExecution::SetInput25,
63 &Segmentation_ModelExecution::SetInput26,
64 &Segmentation_ModelExecution::SetInput27,
65 &Segmentation_ModelExecution::SetInput28,
Min Qin81e6bda2022-12-07 06:44:2766 &Segmentation_ModelExecution::SetInput29,
67 &Segmentation_ModelExecution::SetInput30,
68 &Segmentation_ModelExecution::SetInput31,
69 &Segmentation_ModelExecution::SetInput32,
70 &Segmentation_ModelExecution::SetInput33,
71 &Segmentation_ModelExecution::SetInput34,
72 &Segmentation_ModelExecution::SetInput35,
73 &Segmentation_ModelExecution::SetInput36,
74 &Segmentation_ModelExecution::SetInput37,
75 &Segmentation_ModelExecution::SetInput38,
76 &Segmentation_ModelExecution::SetInput39,
77 &Segmentation_ModelExecution::SetInput40,
78 &Segmentation_ModelExecution::SetInput41,
79 &Segmentation_ModelExecution::SetInput42,
80 &Segmentation_ModelExecution::SetInput43,
81 &Segmentation_ModelExecution::SetInput44,
82 &Segmentation_ModelExecution::SetInput45,
83 &Segmentation_ModelExecution::SetInput46,
84 &Segmentation_ModelExecution::SetInput47,
85 &Segmentation_ModelExecution::SetInput48,
86 &Segmentation_ModelExecution::SetInput49};
Min Qin75bee1b2022-02-05 03:57:3487
Min Qin1eeb6432023-02-23 23:41:5988const UkmMemberFn kSegmentationUkmPredictionResultMethods[] = {
89 &Segmentation_ModelExecution::SetPredictionResult1,
90 &Segmentation_ModelExecution::SetPredictionResult2,
91 &Segmentation_ModelExecution::SetPredictionResult3,
92 &Segmentation_ModelExecution::SetPredictionResult4,
93 &Segmentation_ModelExecution::SetPredictionResult5,
94 &Segmentation_ModelExecution::SetPredictionResult6,
95 &Segmentation_ModelExecution::SetPredictionResult7,
96 &Segmentation_ModelExecution::SetPredictionResult8,
97 &Segmentation_ModelExecution::SetPredictionResult9,
98 &Segmentation_ModelExecution::SetPredictionResult10};
99
Xing Liu282dd0d2022-02-24 00:20:39100const UkmMemberFn kSegmentationUkmOutputMethods[] = {
101 &Segmentation_ModelExecution::SetActualResult,
102 &Segmentation_ModelExecution::SetActualResult2,
103 &Segmentation_ModelExecution::SetActualResult3,
104 &Segmentation_ModelExecution::SetActualResult4,
105 &Segmentation_ModelExecution::SetActualResult5,
106 &Segmentation_ModelExecution::SetActualResult6};
Min Qin7a9f38902023-05-15 16:27:46107
108// 1 out of 100 model execution will be reported.
109const int kDefaultModelExecutionSamplingRate = 100;
110
111int GetModelExecutionSamplingRate() {
112 return base::GetFieldTrialParamByFeatureAsInt(
113 segmentation_platform::features::
114 kSegmentationPlatformModelExecutionSampling,
115 segmentation_platform::kModelExecutionSamplingRateKey,
116 kDefaultModelExecutionSamplingRate);
117}
Min Qin75bee1b2022-02-05 03:57:34118
Min Qin1eeb6432023-02-23 23:41:59119// Helper method to add model prediction results to UKM log.
120void AddPredictionResultToUkmModelExecution(
121 ukm::builders::Segmentation_ModelExecution* model_execution,
122 const std::vector<float>& results) {
123 CHECK_LE(results.size(), ARRAY_SIZE(kSegmentationUkmPredictionResultMethods));
124 for (size_t i = 0; i < results.size(); ++i) {
125 CALL_MEMBER_FN(*model_execution, kSegmentationUkmPredictionResultMethods[i])
126 (SegmentationUkmHelper::FloatToInt64(results[i]));
127 }
128}
129
ssid7a2fe7012023-07-05 23:16:09130std::string GetDebugString(const ModelProvider::Request& input_tensor,
131 const ModelProvider::Response& outputs) {
132 std::stringstream out;
133 out << "Inputs: ";
134 int j = 0;
135 for (const auto& i : input_tensor) {
136 out << j++ << ":" << i << " ";
137 }
138 out << " Outputs: ";
139 j = 0;
140 for (const auto& i : outputs) {
141 out << j++ << ":" << i << " ";
142 }
143 return out.str();
144}
145
146} // namespace
Min Qin75bee1b2022-02-05 03:57:34147
148SegmentationUkmHelper::SegmentationUkmHelper() {
149 Initialize();
150}
151
152SegmentationUkmHelper::~SegmentationUkmHelper() = default;
153
Min Qin75bee1b2022-02-05 03:57:34154// static
155SegmentationUkmHelper* SegmentationUkmHelper::GetInstance() {
156 static base::NoDestructor<SegmentationUkmHelper> helper;
157 return helper.get();
158}
159
Min Qin67dc6b9c2023-02-01 21:00:20160void SegmentationUkmHelper::Initialize() {
161 // TODO(crbug.com/1406404): Migrate models for these segments to use
162 // `upload_tensors`.
163 allowed_segment_ids_.clear();
164 if (base::FeatureList::IsEnabled(segmentation_platform::features::
165 kSegmentationDefaultReportingSegments)) {
166 allowed_segment_ids_ = base::flat_set<SegmentId>{
167 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
168 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
169 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
170 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY,
171 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
172 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES,
Min Qin67dc6b9c2023-02-01 21:00:20173 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID_V2};
174 }
Min Qin7a9f38902023-05-15 16:27:46175 sampling_rate_ = GetModelExecutionSamplingRate();
176 DCHECK_GE(sampling_rate_, 0);
Min Qin67dc6b9c2023-02-01 21:00:20177}
178
Min Qin75bee1b2022-02-05 03:57:34179ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
ssid8386fc72022-05-21 00:34:17180 SegmentId segment_id,
Min Qin75bee1b2022-02-05 03:57:34181 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04182 const ModelProvider::Request& input_tensor,
Min Qin1eeb6432023-02-23 23:41:59183 const std::vector<float>& results) {
Min Qin75bee1b2022-02-05 03:57:34184 ukm::SourceId source_id = ukm::NoURLSourceId();
Min Qin7a9f38902023-05-15 16:27:46185 // Do some sampling before sending out UKM.
186 if (sampling_rate_ == 0) {
187 return source_id;
188 }
189
190 if (base::RandInt(1, sampling_rate_) > 1) {
191 return source_id;
192 }
Min Qin75bee1b2022-02-05 03:57:34193 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
194
Xing Liu282dd0d2022-02-24 00:20:39195 // Add inputs to ukm message.
196 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
Min Qinf254aa92022-08-24 18:48:42197 input_tensor)) {
Xing Liu282dd0d2022-02-24 00:20:39198 return ukm::kInvalidSourceId;
Min Qinf254aa92022-08-24 18:48:42199 }
Min Qin75bee1b2022-02-05 03:57:34200
Min Qin1eeb6432023-02-23 23:41:59201 AddPredictionResultToUkmModelExecution(&execution_result, results);
202 execution_result.Record(ukm::UkmRecorder::Get());
Min Qin75bee1b2022-02-05 03:57:34203 return source_id;
204}
205
Xing Liu282dd0d2022-02-24 00:20:39206ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
ssid8386fc72022-05-21 00:34:17207 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39208 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04209 const ModelProvider::Request& input_tensor,
210 const ModelProvider::Response& outputs,
Min Qind52d150c2022-04-22 05:32:13211 const std::vector<int>& output_indexes,
Min Qin642ab222022-05-19 21:54:53212 absl::optional<proto::PredictionResult> prediction_result,
213 absl::optional<SelectedSegment> selected_segment) {
Xing Liu282dd0d2022-02-24 00:20:39214 ukm::SourceId source_id = ukm::NoURLSourceId();
215 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
216 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
217 input_tensor)) {
218 return ukm::kInvalidSourceId;
219 }
220
221 if (!AddOutputsToUkm(&execution_result, outputs, output_indexes)) {
222 return ukm::kInvalidSourceId;
223 }
224
ritikagupeaf525c2022-11-11 00:53:24225 if (prediction_result.has_value() && prediction_result->result_size() > 0) {
Min Qin1eeb6432023-02-23 23:41:59226 std::vector<float> results(prediction_result->result().begin(),
227 prediction_result->result().end());
228 AddPredictionResultToUkmModelExecution(&execution_result, results);
Salvador Guerrero5783fd62023-05-13 00:28:32229 base::Time prediction_time = base::Time::FromDeltaSinceWindowsEpoch(
230 base::Microseconds(prediction_result->timestamp_us()));
231 execution_result.SetOutputDelaySec(
232 (base::Time::Now() - prediction_time).InSeconds());
Min Qind52d150c2022-04-22 05:32:13233 }
Min Qin642ab222022-05-19 21:54:53234 if (selected_segment.has_value()) {
235 execution_result.SetSelectionResult(selected_segment->segment_id);
236 execution_result.SetOutputDelaySec(
237 (base::Time::Now() - selected_segment->selection_time).InSeconds());
238 }
239
ssid7a2fe7012023-07-05 23:16:09240 VLOG(1) << "Recording training data " << proto::SegmentId_Name(segment_id)
241 << " " << GetDebugString(input_tensor, outputs);
242
Xing Liu282dd0d2022-02-24 00:20:39243 execution_result.Record(ukm::UkmRecorder::Get());
244 return source_id;
245}
246
247bool SegmentationUkmHelper::AddInputsToUkm(
248 ukm::builders::Segmentation_ModelExecution* ukm_builder,
ssid8386fc72022-05-21 00:34:17249 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39250 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04251 const ModelProvider::Request& input_tensor) {
Xing Liu282dd0d2022-02-24 00:20:39252 if (input_tensor.size() > ARRAY_SIZE(kSegmentationUkmInputMethods)) {
253 // Don't record UKM if there are too many tensors.
254 stats::RecordTooManyInputTensors(input_tensor.size());
255 return false;
256 }
257
258 ukm_builder->SetOptimizationTarget(segment_id).SetModelVersion(model_version);
259 for (size_t i = 0; i < input_tensor.size(); ++i) {
260 CALL_MEMBER_FN(*ukm_builder, kSegmentationUkmInputMethods[i])
261 (FloatToInt64(input_tensor[i]));
262 }
263 return true;
264}
265
266bool SegmentationUkmHelper::AddOutputsToUkm(
267 ukm::builders::Segmentation_ModelExecution* ukm_builder,
Jan Turecek9deb2252022-11-23 23:25:04268 const ModelProvider::Response& outputs,
Xing Liu282dd0d2022-02-24 00:20:39269 const std::vector<int>& output_indexes) {
270 DCHECK(!outputs.empty());
271 if (outputs.size() != output_indexes.size())
272 return false;
273
274 const int output_methods_size = ARRAY_SIZE(kSegmentationUkmOutputMethods);
275 if (outputs.size() > output_methods_size)
276 return false;
277
278 for (size_t i = 0; i < outputs.size(); ++i) {
279 if (output_indexes[i] >= output_methods_size)
280 return false;
281 CALL_MEMBER_FN(*ukm_builder,
282 kSegmentationUkmOutputMethods[output_indexes[i]])
283 (FloatToInt64(outputs[i]));
284 }
285
286 return true;
287}
288
ssid6036f472023-07-11 00:40:45289bool SegmentationUkmHelper::IsUploadRequested(
Min Qinf254aa92022-08-24 18:48:42290 const proto::SegmentInfo& segment_info) const {
Min Qinf254aa92022-08-24 18:48:42291 return segment_info.model_metadata().upload_tensors() ||
Min Qin67dc6b9c2023-02-01 21:00:20292 allowed_segment_ids_.contains(segment_info.segment_id());
Min Qinf254aa92022-08-24 18:48:42293}
294
Min Qin75bee1b2022-02-05 03:57:34295// static
296int64_t SegmentationUkmHelper::FloatToInt64(float f) {
297 // Encode the float number in IEEE754 double precision.
Peter Kastingcc88ac052022-05-03 09:58:01298 return base::bit_cast<int64_t>(static_cast<double>(f));
Min Qin75bee1b2022-02-05 03:57:34299}
300
Min Qincae984cb2022-05-20 03:08:34301// static
302bool SegmentationUkmHelper::AllowedToUploadData(
303 base::TimeDelta signal_storage_length,
304 base::Clock* clock) {
Min Qin0b78c782022-05-21 01:42:13305 base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
306 kSegmentationUkmMostRecentAllowedTimeKey);
307 // If the local state is never set, return false.
308 if (most_recent_allowed.is_null() ||
309 most_recent_allowed == base::Time::Max()) {
Salvador Guerrero6ca4da82023-07-11 16:29:01310 VLOG(1) << "UKM consent not granted";
Min Qin0b78c782022-05-21 01:42:13311 return false;
312 }
Salvador Guerrero6ca4da82023-07-11 16:29:01313
314 if (most_recent_allowed + signal_storage_length < clock->Now()) {
315 return true;
316 } else {
317 VLOG(1) << "UKM consent granted on: " << most_recent_allowed
318 << ". Waiting for the model's storage period ("
319 << most_recent_allowed + signal_storage_length
320 << ") to avoid uploading data collected pre-consent";
321 return false;
322 }
Min Qincae984cb2022-05-20 03:08:34323}
324
Min Qin75bee1b2022-02-05 03:57:34325} // namespace segmentation_platform