blob: 7d1af6396bcb54aeff76f77ab0c3fb1d1530f7e4 [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,
173 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT,
174 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID_V2};
175 }
Min Qin7a9f38902023-05-15 16:27:46176 sampling_rate_ = GetModelExecutionSamplingRate();
177 DCHECK_GE(sampling_rate_, 0);
Min Qin67dc6b9c2023-02-01 21:00:20178}
179
Min Qin75bee1b2022-02-05 03:57:34180ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
ssid8386fc72022-05-21 00:34:17181 SegmentId segment_id,
Min Qin75bee1b2022-02-05 03:57:34182 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04183 const ModelProvider::Request& input_tensor,
Min Qin1eeb6432023-02-23 23:41:59184 const std::vector<float>& results) {
Min Qin75bee1b2022-02-05 03:57:34185 ukm::SourceId source_id = ukm::NoURLSourceId();
Min Qin7a9f38902023-05-15 16:27:46186 // Do some sampling before sending out UKM.
187 if (sampling_rate_ == 0) {
188 return source_id;
189 }
190
191 if (base::RandInt(1, sampling_rate_) > 1) {
192 return source_id;
193 }
Min Qin75bee1b2022-02-05 03:57:34194 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
195
Xing Liu282dd0d2022-02-24 00:20:39196 // Add inputs to ukm message.
197 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
Min Qinf254aa92022-08-24 18:48:42198 input_tensor)) {
Xing Liu282dd0d2022-02-24 00:20:39199 return ukm::kInvalidSourceId;
Min Qinf254aa92022-08-24 18:48:42200 }
Min Qin75bee1b2022-02-05 03:57:34201
Min Qin1eeb6432023-02-23 23:41:59202 AddPredictionResultToUkmModelExecution(&execution_result, results);
203 execution_result.Record(ukm::UkmRecorder::Get());
Min Qin75bee1b2022-02-05 03:57:34204 return source_id;
205}
206
Xing Liu282dd0d2022-02-24 00:20:39207ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
ssid8386fc72022-05-21 00:34:17208 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39209 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04210 const ModelProvider::Request& input_tensor,
211 const ModelProvider::Response& outputs,
Min Qind52d150c2022-04-22 05:32:13212 const std::vector<int>& output_indexes,
Min Qin642ab222022-05-19 21:54:53213 absl::optional<proto::PredictionResult> prediction_result,
214 absl::optional<SelectedSegment> selected_segment) {
Xing Liu282dd0d2022-02-24 00:20:39215 ukm::SourceId source_id = ukm::NoURLSourceId();
216 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
217 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
218 input_tensor)) {
219 return ukm::kInvalidSourceId;
220 }
221
222 if (!AddOutputsToUkm(&execution_result, outputs, output_indexes)) {
223 return ukm::kInvalidSourceId;
224 }
225
ritikagupeaf525c2022-11-11 00:53:24226 if (prediction_result.has_value() && prediction_result->result_size() > 0) {
Min Qin1eeb6432023-02-23 23:41:59227 std::vector<float> results(prediction_result->result().begin(),
228 prediction_result->result().end());
229 AddPredictionResultToUkmModelExecution(&execution_result, results);
Salvador Guerrero5783fd62023-05-13 00:28:32230 base::Time prediction_time = base::Time::FromDeltaSinceWindowsEpoch(
231 base::Microseconds(prediction_result->timestamp_us()));
232 execution_result.SetOutputDelaySec(
233 (base::Time::Now() - prediction_time).InSeconds());
Min Qind52d150c2022-04-22 05:32:13234 }
Min Qin642ab222022-05-19 21:54:53235 if (selected_segment.has_value()) {
236 execution_result.SetSelectionResult(selected_segment->segment_id);
237 execution_result.SetOutputDelaySec(
238 (base::Time::Now() - selected_segment->selection_time).InSeconds());
239 }
240
ssid7a2fe7012023-07-05 23:16:09241 VLOG(1) << "Recording training data " << proto::SegmentId_Name(segment_id)
242 << " " << GetDebugString(input_tensor, outputs);
243
Xing Liu282dd0d2022-02-24 00:20:39244 execution_result.Record(ukm::UkmRecorder::Get());
245 return source_id;
246}
247
248bool SegmentationUkmHelper::AddInputsToUkm(
249 ukm::builders::Segmentation_ModelExecution* ukm_builder,
ssid8386fc72022-05-21 00:34:17250 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39251 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04252 const ModelProvider::Request& input_tensor) {
Xing Liu282dd0d2022-02-24 00:20:39253 if (input_tensor.size() > ARRAY_SIZE(kSegmentationUkmInputMethods)) {
254 // Don't record UKM if there are too many tensors.
255 stats::RecordTooManyInputTensors(input_tensor.size());
256 return false;
257 }
258
259 ukm_builder->SetOptimizationTarget(segment_id).SetModelVersion(model_version);
260 for (size_t i = 0; i < input_tensor.size(); ++i) {
261 CALL_MEMBER_FN(*ukm_builder, kSegmentationUkmInputMethods[i])
262 (FloatToInt64(input_tensor[i]));
263 }
264 return true;
265}
266
267bool SegmentationUkmHelper::AddOutputsToUkm(
268 ukm::builders::Segmentation_ModelExecution* ukm_builder,
Jan Turecek9deb2252022-11-23 23:25:04269 const ModelProvider::Response& outputs,
Xing Liu282dd0d2022-02-24 00:20:39270 const std::vector<int>& output_indexes) {
271 DCHECK(!outputs.empty());
272 if (outputs.size() != output_indexes.size())
273 return false;
274
275 const int output_methods_size = ARRAY_SIZE(kSegmentationUkmOutputMethods);
276 if (outputs.size() > output_methods_size)
277 return false;
278
279 for (size_t i = 0; i < outputs.size(); ++i) {
280 if (output_indexes[i] >= output_methods_size)
281 return false;
282 CALL_MEMBER_FN(*ukm_builder,
283 kSegmentationUkmOutputMethods[output_indexes[i]])
284 (FloatToInt64(outputs[i]));
285 }
286
287 return true;
288}
289
ssid6036f472023-07-11 00:40:45290bool SegmentationUkmHelper::IsUploadRequested(
Min Qinf254aa92022-08-24 18:48:42291 const proto::SegmentInfo& segment_info) const {
Min Qinf254aa92022-08-24 18:48:42292 return segment_info.model_metadata().upload_tensors() ||
Min Qin67dc6b9c2023-02-01 21:00:20293 allowed_segment_ids_.contains(segment_info.segment_id());
Min Qinf254aa92022-08-24 18:48:42294}
295
Min Qin75bee1b2022-02-05 03:57:34296// static
297int64_t SegmentationUkmHelper::FloatToInt64(float f) {
298 // Encode the float number in IEEE754 double precision.
Peter Kastingcc88ac052022-05-03 09:58:01299 return base::bit_cast<int64_t>(static_cast<double>(f));
Min Qin75bee1b2022-02-05 03:57:34300}
301
Min Qincae984cb2022-05-20 03:08:34302// static
303bool SegmentationUkmHelper::AllowedToUploadData(
304 base::TimeDelta signal_storage_length,
305 base::Clock* clock) {
Min Qin0b78c782022-05-21 01:42:13306 base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
307 kSegmentationUkmMostRecentAllowedTimeKey);
308 // If the local state is never set, return false.
309 if (most_recent_allowed.is_null() ||
310 most_recent_allowed == base::Time::Max()) {
Salvador Guerrero6ca4da82023-07-11 16:29:01311 VLOG(1) << "UKM consent not granted";
Min Qin0b78c782022-05-21 01:42:13312 return false;
313 }
Salvador Guerrero6ca4da82023-07-11 16:29:01314
315 if (most_recent_allowed + signal_storage_length < clock->Now()) {
316 return true;
317 } else {
318 VLOG(1) << "UKM consent granted on: " << most_recent_allowed
319 << ". Waiting for the model's storage period ("
320 << most_recent_allowed + signal_storage_length
321 << ") to avoid uploading data collected pre-consent";
322 return false;
323 }
Min Qincae984cb2022-05-20 03:08:34324}
325
Min Qin75bee1b2022-02-05 03:57:34326} // namespace segmentation_platform