blob: a41a83f3dbc55092418f98995b725bb57a99f9c5 [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,
Hailey Wang182b7632023-10-31 20:35:54171 SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES};
Min Qin67dc6b9c2023-02-01 21:00:20172 }
Min Qin7a9f38902023-05-15 16:27:46173 sampling_rate_ = GetModelExecutionSamplingRate();
174 DCHECK_GE(sampling_rate_, 0);
Min Qin67dc6b9c2023-02-01 21:00:20175}
176
Min Qin75bee1b2022-02-05 03:57:34177ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
ssid8386fc72022-05-21 00:34:17178 SegmentId segment_id,
Min Qin75bee1b2022-02-05 03:57:34179 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04180 const ModelProvider::Request& input_tensor,
Min Qin1eeb6432023-02-23 23:41:59181 const std::vector<float>& results) {
Min Qin75bee1b2022-02-05 03:57:34182 ukm::SourceId source_id = ukm::NoURLSourceId();
Min Qin7a9f38902023-05-15 16:27:46183 // Do some sampling before sending out UKM.
184 if (sampling_rate_ == 0) {
185 return source_id;
186 }
187
188 if (base::RandInt(1, sampling_rate_) > 1) {
189 return source_id;
190 }
Min Qin75bee1b2022-02-05 03:57:34191 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
192
Xing Liu282dd0d2022-02-24 00:20:39193 // Add inputs to ukm message.
194 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
Min Qinf254aa92022-08-24 18:48:42195 input_tensor)) {
Xing Liu282dd0d2022-02-24 00:20:39196 return ukm::kInvalidSourceId;
Min Qinf254aa92022-08-24 18:48:42197 }
Min Qin75bee1b2022-02-05 03:57:34198
Min Qin1eeb6432023-02-23 23:41:59199 AddPredictionResultToUkmModelExecution(&execution_result, results);
200 execution_result.Record(ukm::UkmRecorder::Get());
Min Qin75bee1b2022-02-05 03:57:34201 return source_id;
202}
203
Xing Liu282dd0d2022-02-24 00:20:39204ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
ssid8386fc72022-05-21 00:34:17205 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39206 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04207 const ModelProvider::Request& input_tensor,
208 const ModelProvider::Response& outputs,
Min Qind52d150c2022-04-22 05:32:13209 const std::vector<int>& output_indexes,
Min Qin642ab222022-05-19 21:54:53210 absl::optional<proto::PredictionResult> prediction_result,
211 absl::optional<SelectedSegment> selected_segment) {
Xing Liu282dd0d2022-02-24 00:20:39212 ukm::SourceId source_id = ukm::NoURLSourceId();
213 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
214 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
215 input_tensor)) {
216 return ukm::kInvalidSourceId;
217 }
218
219 if (!AddOutputsToUkm(&execution_result, outputs, output_indexes)) {
220 return ukm::kInvalidSourceId;
221 }
222
ritikagupeaf525c2022-11-11 00:53:24223 if (prediction_result.has_value() && prediction_result->result_size() > 0) {
Min Qin1eeb6432023-02-23 23:41:59224 std::vector<float> results(prediction_result->result().begin(),
225 prediction_result->result().end());
226 AddPredictionResultToUkmModelExecution(&execution_result, results);
Salvador Guerrero5783fd62023-05-13 00:28:32227 base::Time prediction_time = base::Time::FromDeltaSinceWindowsEpoch(
228 base::Microseconds(prediction_result->timestamp_us()));
229 execution_result.SetOutputDelaySec(
230 (base::Time::Now() - prediction_time).InSeconds());
Min Qind52d150c2022-04-22 05:32:13231 }
Min Qin642ab222022-05-19 21:54:53232 if (selected_segment.has_value()) {
233 execution_result.SetSelectionResult(selected_segment->segment_id);
234 execution_result.SetOutputDelaySec(
235 (base::Time::Now() - selected_segment->selection_time).InSeconds());
236 }
237
ssid7a2fe7012023-07-05 23:16:09238 VLOG(1) << "Recording training data " << proto::SegmentId_Name(segment_id)
239 << " " << GetDebugString(input_tensor, outputs);
240
Xing Liu282dd0d2022-02-24 00:20:39241 execution_result.Record(ukm::UkmRecorder::Get());
242 return source_id;
243}
244
245bool SegmentationUkmHelper::AddInputsToUkm(
246 ukm::builders::Segmentation_ModelExecution* ukm_builder,
ssid8386fc72022-05-21 00:34:17247 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39248 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04249 const ModelProvider::Request& input_tensor) {
Xing Liu282dd0d2022-02-24 00:20:39250 if (input_tensor.size() > ARRAY_SIZE(kSegmentationUkmInputMethods)) {
251 // Don't record UKM if there are too many tensors.
252 stats::RecordTooManyInputTensors(input_tensor.size());
253 return false;
254 }
255
256 ukm_builder->SetOptimizationTarget(segment_id).SetModelVersion(model_version);
257 for (size_t i = 0; i < input_tensor.size(); ++i) {
258 CALL_MEMBER_FN(*ukm_builder, kSegmentationUkmInputMethods[i])
259 (FloatToInt64(input_tensor[i]));
260 }
261 return true;
262}
263
264bool SegmentationUkmHelper::AddOutputsToUkm(
265 ukm::builders::Segmentation_ModelExecution* ukm_builder,
Jan Turecek9deb2252022-11-23 23:25:04266 const ModelProvider::Response& outputs,
Xing Liu282dd0d2022-02-24 00:20:39267 const std::vector<int>& output_indexes) {
268 DCHECK(!outputs.empty());
269 if (outputs.size() != output_indexes.size())
270 return false;
271
272 const int output_methods_size = ARRAY_SIZE(kSegmentationUkmOutputMethods);
273 if (outputs.size() > output_methods_size)
274 return false;
275
276 for (size_t i = 0; i < outputs.size(); ++i) {
277 if (output_indexes[i] >= output_methods_size)
278 return false;
279 CALL_MEMBER_FN(*ukm_builder,
280 kSegmentationUkmOutputMethods[output_indexes[i]])
281 (FloatToInt64(outputs[i]));
282 }
283
284 return true;
285}
286
ssid6036f472023-07-11 00:40:45287bool SegmentationUkmHelper::IsUploadRequested(
Min Qinf254aa92022-08-24 18:48:42288 const proto::SegmentInfo& segment_info) const {
Min Qinf254aa92022-08-24 18:48:42289 return segment_info.model_metadata().upload_tensors() ||
Min Qin67dc6b9c2023-02-01 21:00:20290 allowed_segment_ids_.contains(segment_info.segment_id());
Min Qinf254aa92022-08-24 18:48:42291}
292
Min Qin75bee1b2022-02-05 03:57:34293// static
294int64_t SegmentationUkmHelper::FloatToInt64(float f) {
295 // Encode the float number in IEEE754 double precision.
Peter Kastingcc88ac052022-05-03 09:58:01296 return base::bit_cast<int64_t>(static_cast<double>(f));
Min Qin75bee1b2022-02-05 03:57:34297}
298
Min Qincae984cb2022-05-20 03:08:34299// static
300bool SegmentationUkmHelper::AllowedToUploadData(
301 base::TimeDelta signal_storage_length,
302 base::Clock* clock) {
Min Qin0b78c782022-05-21 01:42:13303 base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
304 kSegmentationUkmMostRecentAllowedTimeKey);
305 // If the local state is never set, return false.
306 if (most_recent_allowed.is_null() ||
307 most_recent_allowed == base::Time::Max()) {
Salvador Guerrero6ca4da82023-07-11 16:29:01308 VLOG(1) << "UKM consent not granted";
Min Qin0b78c782022-05-21 01:42:13309 return false;
310 }
Salvador Guerrero6ca4da82023-07-11 16:29:01311
312 if (most_recent_allowed + signal_storage_length < clock->Now()) {
313 return true;
314 } else {
315 VLOG(1) << "UKM consent granted on: " << most_recent_allowed
316 << ". Waiting for the model's storage period ("
317 << most_recent_allowed + signal_storage_length
318 << ") to avoid uploading data collected pre-consent";
319 return false;
320 }
Min Qincae984cb2022-05-20 03:08:34321}
322
Min Qin75bee1b2022-02-05 03:57:34323} // namespace segmentation_platform