Skip to content

Commit

Permalink
[merge-103][segmentation] Add Feed user segment
Browse files Browse the repository at this point in the history
It is useful to anlyze the impact of start surface split by the Feed
user segments. Also useful to identify which users would like different
modules.

BUG=1325414

(cherry picked from commit 72e7d84)

Change-Id: I8175a7a75e2fe673b348c47c694c8702ae509c97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3630696
Reviewed-by: Shakti Sahu <shaktisahu@chromium.org>
Reviewed-by: Sophie Chang <sophiechang@chromium.org>
Commit-Queue: Siddhartha S <ssid@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1003883}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3657486
Auto-Submit: Siddhartha S <ssid@chromium.org>
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Reviewed-by: Min Qin <qinmin@chromium.org>
Cr-Commit-Position: refs/branch-heads/5060@{#189}
Cr-Branched-From: b83393d-refs/heads/main@{#1002911}
  • Loading branch information
ssiddhartha authored and Chromium LUCI CQ committed May 23, 2022
1 parent e319b46 commit 1cd2bda
Show file tree
Hide file tree
Showing 14 changed files with 358 additions and 2 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,8 @@ static_library("browser") {
"security_events/security_event_sync_bridge_impl.h",
"segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.cc",
"segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.h",
"segmentation_platform/default_model/feed_user_segment.cc",
"segmentation_platform/default_model/feed_user_segment.h",
"segmentation_platform/default_model/low_user_engagement_model.cc",
"segmentation_platform/default_model/low_user_engagement_model.h",
"segmentation_platform/default_model/metadata_writer.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/segmentation_platform/default_model/feed_user_segment.h"

#include "base/metrics/field_trial_params.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/public/model_provider.h"

namespace segmentation_platform {

namespace {
using optimization_guide::proto::OptimizationTarget;

// Default parameters for Chrome Start model.
constexpr OptimizationTarget kFeedUserOptimizationTarget =
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
constexpr proto::TimeUnit kFeedUserTimeUnit = proto::TimeUnit::DAY;
constexpr uint64_t kFeedUserBucketDuration = 1;
constexpr int64_t kFeedUserSignalStorageLength = 28;
constexpr int64_t kFeedUserMinSignalCollectionLength = 7;
constexpr int64_t kFeedUserResultTTL = 1;

// Discrete mapping parameters.
constexpr char kFeedUserDiscreteMappingKey[] = "feed_user_segment";
constexpr float kFeedUserDiscreteMappingMinResult = 1;
constexpr int64_t kFeedUserDiscreteMappingRank = 1;
constexpr std::pair<float, int> kDiscreteMappings[] = {
{kFeedUserDiscreteMappingMinResult, kFeedUserDiscreteMappingRank}};

// InputFeatures.
constexpr MetadataWriter::UMAFeature kFeedUserUMAFeatures[] = {
MetadataWriter::UMAFeature{
.signal_type = proto::SignalType::USER_ACTION,
.name = "ContentSuggestions.Feed.CardAction.Open",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{
.signal_type = proto::SignalType::USER_ACTION,
.name = "ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{
.signal_type = proto::SignalType::USER_ACTION,
.name = "ContentSuggestions.Feed.CardAction.OpenInNewTab",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "MobileNTPMostVisited",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "MobileNewTabOpened",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "Home",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "MobileMenuRecentTabs",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "MobileMenuHistory",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0},
MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
.name = "MobileTabReturnedToCurrentTab",
.bucket_count = 14,
.tensor_length = 1,
.aggregation = proto::Aggregation::COUNT,
.enum_ids_size = 0}};

#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))

} // namespace

FeedUserSegment::FeedUserSegment()
: ModelProvider(kFeedUserOptimizationTarget) {}

void FeedUserSegment::InitAndFetchModel(
const ModelUpdatedCallback& model_updated_callback) {
proto::SegmentationModelMetadata chrome_start_metadata;
MetadataWriter writer(&chrome_start_metadata);
writer.SetSegmentationMetadataConfig(
kFeedUserTimeUnit, kFeedUserBucketDuration, kFeedUserSignalStorageLength,
kFeedUserMinSignalCollectionLength, kFeedUserResultTTL);

// Set discrete mapping.
writer.AddDiscreteMappingEntries(kFeedUserDiscreteMappingKey,
kDiscreteMappings, 1);

// Set features.
writer.AddUmaFeatures(kFeedUserUMAFeatures, ARRAY_SIZE(kFeedUserUMAFeatures));

constexpr int kModelVersion = 1;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindRepeating(model_updated_callback, kFeedUserOptimizationTarget,
std::move(chrome_start_metadata), kModelVersion));
}

void FeedUserSegment::ExecuteModelWithInput(const std::vector<float>& inputs,
ExecutionCallback callback) {
// Invalid inputs.
if (inputs.size() != ARRAY_SIZE(kFeedUserUMAFeatures)) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
return;
}

float result = 0;
const bool feed_opened = (inputs[0] + inputs[1] + inputs[2]) >= 2;
const bool mv_tiles_used = inputs[3] >= 2;
const bool home_or_ntp_used = (inputs[4] + inputs[5]) >= 4;

if (feed_opened) {
result = 1;
} else if (mv_tiles_used) {
result = 0.75;
} else if (home_or_ntp_used) {
result = 0.5;
}

base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}

bool FeedUserSegment::ModelAvailable() {
return true;
}

} // namespace segmentation_platform
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_FEED_USER_SEGMENT_H_
#define CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_FEED_USER_SEGMENT_H_

#include "components/segmentation_platform/public/model_provider.h"

namespace segmentation_platform {

// Segmentation Chrome Feed user model provider. Provides a default model and
// metadata for the Feed user optimization target.
class FeedUserSegment : public ModelProvider {
public:
FeedUserSegment();
~FeedUserSegment() override = default;

FeedUserSegment(FeedUserSegment&) = delete;
FeedUserSegment& operator=(FeedUserSegment&) = delete;

// ModelProvider implementation.
void InitAndFetchModel(
const ModelUpdatedCallback& model_updated_callback) override;
void ExecuteModelWithInput(const std::vector<float>& inputs,
ExecutionCallback callback) override;
bool ModelAvailable() override;
};

} // namespace segmentation_platform

#endif // CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_FEED_USER_SEGMENT_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/segmentation_platform/default_model/feed_user_segment.h"

#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace segmentation_platform {

class FeedUserModelTest : public testing::Test {
public:
FeedUserModelTest() = default;
~FeedUserModelTest() override = default;

void SetUp() override {
feed_user_model_ = std::make_unique<FeedUserSegment>();
}

void TearDown() override {
feed_user_model_.reset();
RunUntilIdle();
}

void RunUntilIdle() { task_environment_.RunUntilIdle(); }

void ExpectInitAndFetchModel() {
base::RunLoop loop;
feed_user_model_->InitAndFetchModel(
base::BindRepeating(&FeedUserModelTest::OnInitFinishedCallback,
base::Unretained(this), loop.QuitClosure()));
loop.Run();
}

void OnInitFinishedCallback(
base::RepeatingClosure closure,
optimization_guide::proto::OptimizationTarget target,
proto::SegmentationModelMetadata metadata,
int64_t) {
EXPECT_EQ(metadata_utils::ValidateMetadataAndFeatures(metadata),
metadata_utils::ValidationResult::kValidationSuccess);
std::move(closure).Run();
}

void ExpectExecutionWithInput(const std::vector<float>& inputs,
bool expected_error,
float expected_result) {
base::RunLoop loop;
feed_user_model_->ExecuteModelWithInput(
inputs, base::BindOnce(&FeedUserModelTest::OnExecutionFinishedCallback,
base::Unretained(this), loop.QuitClosure(),
expected_error, expected_result));
loop.Run();
}

void OnExecutionFinishedCallback(base::RepeatingClosure closure,
bool expected_error,
float expected_result,
const absl::optional<float>& result) {
if (expected_error) {
EXPECT_FALSE(result.has_value());
} else {
EXPECT_TRUE(result.has_value());
EXPECT_EQ(result.value(), expected_result);
}
std::move(closure).Run();
}

protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<FeedUserSegment> feed_user_model_;
};

TEST_F(FeedUserModelTest, InitAndFetchModel) {
ExpectInitAndFetchModel();
}

TEST_F(FeedUserModelTest, ExecuteModelWithInput) {
std::vector<float> input(9, 0);

ExpectExecutionWithInput(input, false, 0);

input[4] = 3;
input[5] = 2;
ExpectExecutionWithInput(input, false, 0.5);

input[3] = 3;
ExpectExecutionWithInput(input, false, 0.75);

input[0] = 1;
input[2] = 2;
ExpectExecutionWithInput(input, false, 1);

ExpectExecutionWithInput({}, true, 0);
ExpectExecutionWithInput({1, 2}, true, 0);
}

} // namespace segmentation_platform
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/segmentation_platform/default_model/feed_user_segment.h"
#include "chrome/browser/segmentation_platform/default_model/low_user_engagement_model.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/public/config.h"
Expand Down Expand Up @@ -41,6 +42,9 @@ constexpr int kDummyFeatureSelectionTTLDays = 1;

constexpr int kChromeLowUserEngagementSelectionTTLDays = 30;

constexpr int kFeedUserSegmentSelectionTTLDays = 14;
constexpr int kFeedUserSegmentUnknownSelectionTTLDays = 14;

#if BUILDFLAG(IS_ANDROID)

constexpr int kAdaptiveToolbarDefaultSelectionTTLDays = 28;
Expand Down Expand Up @@ -194,6 +198,33 @@ std::unique_ptr<Config> GetConfigForChromeLowUserEngagement() {
return config;
}

std::unique_ptr<Config> GetConfigForFeedSegments() {
auto config = std::make_unique<Config>();
config->segmentation_key = kFeedUserSegmentationKey;
config->segment_ids = {
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER,
};
config->segment_selection_ttl =
base::Days(base::GetFieldTrialParamByFeatureAsInt(
features::kSegmentationPlatformFeedSegmentFeature,
"segment_selection_ttl_days", kFeedUserSegmentSelectionTTLDays));
config->unknown_selection_ttl =
base::Days(base::GetFieldTrialParamByFeatureAsInt(
features::kSegmentationPlatformFeedSegmentFeature,
"unknown_selection_ttl_days",
kFeedUserSegmentUnknownSelectionTTLDays));
return config;
}

std::unique_ptr<ModelProvider> GetFeedUserSegmentDefautlModel() {
if (!base::GetFieldTrialParamByFeatureAsBool(
features::kSegmentationPlatformFeedSegmentFeature,
kDefaultModelEnabledParam, false)) {
return nullptr;
}
return std::make_unique<FeedUserSegment>();
}

} // namespace

std::vector<std::unique_ptr<Config>> GetSegmentationPlatformConfig() {
Expand All @@ -218,6 +249,11 @@ std::vector<std::unique_ptr<Config>> GetSegmentationPlatformConfig() {
if (IsLowEngagementFeatureEnabled()) {
configs.emplace_back(GetConfigForChromeLowUserEngagement());
}

if (base::FeatureList::IsEnabled(
features::kSegmentationPlatformFeedSegmentFeature)) {
configs.emplace_back(GetConfigForFeedSegments());
}
return configs;
}

Expand Down Expand Up @@ -252,6 +288,10 @@ std::unique_ptr<ModelProvider> DefaultModelsRegister::GetModelProvider(
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT) {
return GetLowEngagementDefaultModel();
}
if (target ==
optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
return GetFeedUserSegmentDefautlModel();
}
return nullptr;
}

Expand Down
1 change: 1 addition & 0 deletions chrome/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -5250,6 +5250,7 @@ test("unit_tests") {
"../browser/search_engines/template_url_parser_unittest.cc",
"../browser/search_engines/template_url_service_sync_unittest.cc",
"../browser/search_engines/template_url_service_unittest.cc",
"../browser/segmentation_platform/default_model/feed_user_segment_unittest.cc",
"../browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc",
"../browser/services_unittest.cc",
"../browser/sessions/chrome_serialized_navigation_driver_unittest.cc",
Expand Down
2 changes: 2 additions & 0 deletions components/optimization_guide/core/model_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ std::string GetStringNameForOptimizationTarget(
return "PageTopicsV2";
case proto::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
return "SegmentationChromeLowUserEngagement";
case proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
return "SegmentationFeedUser";
// Whenever a new value is added, make sure to add it to the OptTarget
// variant list in
// //tools/metrics/histograms/metadata/optimization/histograms.xml.
Expand Down
2 changes: 2 additions & 0 deletions components/optimization_guide/proto/models.proto
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ enum OptimizationTarget {
OPTIMIZATION_TARGET_PAGE_TOPICS_V2 = 15;
// Target for segmentation: Determine users with low engagement with chrome.
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
// Target for segmentation: Determine users who prefer to use Feed.
OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
}

// The model engine versions that can be used to do model inference.
Expand Down

0 comments on commit 1cd2bda

Please sign in to comment.