Commit 7189d272 authored by Jia's avatar Jia Committed by Commit Bot

[On-device adaptive brightness] Allow training to start immediately.

This cl changes when training should start.
1. Previously, training will only start after user has been inactive for a
certain amount of time (the training delay), and also if there are at least
certain number of training points. A training point is created when a user
makes a brightness adjustment.

2. Now, training can still be delayed, but if the delay is 0, we will start
training immediately after a training point is received. We no longer need
to accumulate data points before training.
- We run the training job and also disk-writing job (for writing trained curves)
in a non-UI thread, hence it's non-blocking.

Bug: 881215
Change-Id: Ifcb0714bd74d278722d1c718f57ca82dd16aed1b
Reviewed-on: https://chromium-review.googlesource.com/c/1350020Reviewed-by: default avatarAndrew Moylan <amoylan@chromium.org>
Commit-Queue: Jia Meng <jiameng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611067}
parent 3b49cce0
...@@ -116,9 +116,6 @@ bool SetInitialCurves(Trainer* trainer, ...@@ -116,9 +116,6 @@ bool SetInitialCurves(Trainer* trainer,
} // namespace } // namespace
constexpr base::TimeDelta ModellerImpl::kTrainingDelay;
constexpr size_t ModellerImpl::kMaxTrainingDataPoints;
constexpr size_t ModellerImpl::kMinTrainingDataPoints;
constexpr int ModellerImpl::kAmbientLightHorizonSeconds; constexpr int ModellerImpl::kAmbientLightHorizonSeconds;
constexpr base::TimeDelta ModellerImpl::kAmbientLightHorizon; constexpr base::TimeDelta ModellerImpl::kAmbientLightHorizon;
constexpr int ModellerImpl::kNumberAmbientValuesToTrack; constexpr int ModellerImpl::kNumberAmbientValuesToTrack;
...@@ -200,10 +197,7 @@ void ModellerImpl::OnUserBrightnessChanged(double old_brightness_percent, ...@@ -200,10 +197,7 @@ void ModellerImpl::OnUserBrightnessChanged(double old_brightness_percent,
ConvertToLog(average_ambient_lux), ConvertToLog(average_ambient_lux),
tick_clock_->NowTicks()}); tick_clock_->NowTicks()});
if (data_cache_.size() == kMaxTrainingDataPoints) { ScheduleTrainerStart();
model_timer_.Stop();
StartTraining();
}
} }
void ModellerImpl::OnUserBrightnessChangeRequested() {} void ModellerImpl::OnUserBrightnessChangeRequested() {}
...@@ -244,6 +238,14 @@ MonotoneCubicSpline ModellerImpl::GetGlobalCurveForTesting() const { ...@@ -244,6 +238,14 @@ MonotoneCubicSpline ModellerImpl::GetGlobalCurveForTesting() const {
return global_curve_; return global_curve_;
} }
size_t ModellerImpl::GetMaxTrainingDataPointsForTesting() const {
return max_training_data_points_;
}
base::TimeDelta ModellerImpl::GetTrainingDelayForTesting() const {
return training_delay_;
}
base::FilePath ModellerImpl::GetCurvePathFromProfile(const Profile* profile) { base::FilePath ModellerImpl::GetCurvePathFromProfile(const Profile* profile) {
DCHECK(profile); DCHECK(profile);
const base::FilePath empty_path; const base::FilePath empty_path;
...@@ -305,6 +307,19 @@ ModellerImpl::ModellerImpl( ...@@ -305,6 +307,19 @@ ModellerImpl::ModellerImpl(
als_reader_observer_.Add(als_reader); als_reader_observer_.Add(als_reader);
brightness_monitor_observer_.Add(brightness_monitor); brightness_monitor_observer_.Add(brightness_monitor);
user_activity_observer_.Add(user_activity_detector); user_activity_observer_.Add(user_activity_detector);
const int max_training_data_points = GetFieldTrialParamByFeatureAsInt(
features::kAutoScreenBrightness, "max_training_data_points", -1);
if (max_training_data_points > 0) {
max_training_data_points_ = max_training_data_points;
}
const int training_delay_in_seconds = GetFieldTrialParamByFeatureAsInt(
features::kAutoScreenBrightness, "training_delay_in_seconds",
training_delay_.InSeconds());
if (training_delay_in_seconds >= 0) {
training_delay_ = base::TimeDelta::FromSeconds(training_delay_in_seconds);
}
} }
...@@ -398,20 +413,31 @@ void ModellerImpl::OnSetInitialCurves( ...@@ -398,20 +413,31 @@ void ModellerImpl::OnSetInitialCurves(
is_modeller_enabled_ = true; is_modeller_enabled_ = true;
OnInitializationComplete(); OnInitializationComplete();
// We may have received a brightness change as a training example before the
// model is set up. Call |ScheduleTrainerStart| to prepare training.
ScheduleTrainerStart(); ScheduleTrainerStart();
} }
void ModellerImpl::ScheduleTrainerStart() { void ModellerImpl::ScheduleTrainerStart() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!is_modeller_enabled_.has_value() || !*is_modeller_enabled_)
return;
if (data_cache_.size() >= max_training_data_points_ ||
training_delay_.is_zero()) {
model_timer_.Stop();
StartTraining();
return;
}
// Reset the timer if it's already running. // Reset the timer if it's already running.
model_timer_.Start(FROM_HERE, kTrainingDelay, this, model_timer_.Start(FROM_HERE, training_delay_, this,
&ModellerImpl::StartTraining); &ModellerImpl::StartTraining);
} }
void ModellerImpl::StartTraining() { void ModellerImpl::StartTraining() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (data_cache_.size() < kMinTrainingDataPoints) { if (data_cache_.empty()) {
ScheduleTrainerStart();
return; return;
} }
...@@ -434,8 +460,6 @@ void ModellerImpl::OnTrainingFinished(const MonotoneCubicSpline& curve) { ...@@ -434,8 +460,6 @@ void ModellerImpl::OnTrainingFinished(const MonotoneCubicSpline& curve) {
base::BindOnce(&SaveCurveToDisk, curve_path_, curve, is_testing_), base::BindOnce(&SaveCurveToDisk, curve_path_, curve, is_testing_),
base::BindOnce(&ModellerImpl::OnCurveSavedToDisk, base::BindOnce(&ModellerImpl::OnCurveSavedToDisk,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
ScheduleTrainerStart();
} }
} // namespace auto_screen_brightness } // namespace auto_screen_brightness
......
...@@ -41,19 +41,6 @@ class ModellerImpl : public Modeller, ...@@ -41,19 +41,6 @@ class ModellerImpl : public Modeller,
public BrightnessMonitor::Observer, public BrightnessMonitor::Observer,
public ui::UserActivityObserver { public ui::UserActivityObserver {
public: public:
// Once user remains idle for |kTrainingDelay|, we start training the model.
static constexpr base::TimeDelta kTrainingDelay =
base::TimeDelta::FromSeconds(60);
// If number of recorded training data has reached |kMaxTrainingDataPoints| we
// start training immediately, without waiting for user to become idle for
// |kTrainingDelay|.
static constexpr size_t kMaxTrainingDataPoints = 100;
// Only train when there are at least |kMinTrainingDataPoints| training data
// points.
static constexpr size_t kMinTrainingDataPoints = 10;
// TODO(jiameng): we currently use past 10 seconds of ambient values to // TODO(jiameng): we currently use past 10 seconds of ambient values to
// calculate average. May revise. // calculate average. May revise.
static constexpr int kAmbientLightHorizonSeconds = 10; static constexpr int kAmbientLightHorizonSeconds = 10;
...@@ -109,8 +96,14 @@ class ModellerImpl : public Modeller, ...@@ -109,8 +96,14 @@ class ModellerImpl : public Modeller,
// training. // training.
size_t NumberTrainingDataPointsForTesting() const; size_t NumberTrainingDataPointsForTesting() const;
// Returns |global_curve_| for unit tests.
MonotoneCubicSpline GetGlobalCurveForTesting() const; MonotoneCubicSpline GetGlobalCurveForTesting() const;
// Returns |max_training_data_points_| for unit tests.
size_t GetMaxTrainingDataPointsForTesting() const;
base::TimeDelta GetTrainingDelayForTesting() const;
// Returns the path that will be used to store curves. It also creates // Returns the path that will be used to store curves. It also creates
// intermediate directories if they do not exist. Returns an empty path on // intermediate directories if they do not exist. Returns an empty path on
// failures. // failures.
...@@ -164,7 +157,13 @@ class ModellerImpl : public Modeller, ...@@ -164,7 +157,13 @@ class ModellerImpl : public Modeller,
const base::Optional<MonotoneCubicSpline>& loaded_curve, const base::Optional<MonotoneCubicSpline>& loaded_curve,
bool is_personal_curve_valid); bool is_personal_curve_valid);
// Starts |model_timer_| to start training after certain inactivity period. // Either starts training immediately or delays it for |training_delay_|.
// Training starts immediately if |training_delay_| is 0 or number of training
// points reached |max_training_data_points_|.
// This function is called after a user brightness change signal is received
// (that will be used as an example), and when a user activity is detected.
// It's also called after initial curves are set.
// Nothing will happen if model is not enabled.
void ScheduleTrainerStart(); void ScheduleTrainerStart();
// Starts model training and runs it in non UI thread. Also clears // Starts model training and runs it in non UI thread. Also clears
...@@ -178,6 +177,17 @@ class ModellerImpl : public Modeller, ...@@ -178,6 +177,17 @@ class ModellerImpl : public Modeller,
// are running on non-UI thread. // are running on non-UI thread.
const bool is_testing_ = false; const bool is_testing_ = false;
// If number of recorded training data has reached |max_training_data_points_|
// we start training immediately, without waiting for user to become idle for
// |training_delay_|. This can be overridden by experiment flag
// "max_training_data_points".
size_t max_training_data_points_ = 100;
// Once user remains idle for |training_delay_|, we start training the model.
// If this value is 0, we will not need to wait for user to remain inactive.
// This can be overridden by experiment flag "training_delay_in_seconds".
base::TimeDelta training_delay_ = base::TimeDelta::FromSeconds(60);
ScopedObserver<AlsReader, AlsReader::Observer> als_reader_observer_; ScopedObserver<AlsReader, AlsReader::Observer> als_reader_observer_;
ScopedObserver<BrightnessMonitor, BrightnessMonitor::Observer> ScopedObserver<BrightnessMonitor, BrightnessMonitor::Observer>
......
...@@ -101,7 +101,14 @@ class FakeTrainer : public Trainer { ...@@ -101,7 +101,14 @@ class FakeTrainer : public Trainer {
const std::vector<TrainingDataPoint>& data) override { const std::vector<TrainingDataPoint>& data) override {
DCHECK(is_configured_); DCHECK(is_configured_);
DCHECK(current_curve_); DCHECK(current_curve_);
current_curve_.emplace(CreateTestCurveFromTrainingData(data)); std::vector<TrainingDataPoint> used_data = data;
// We need at least 2 points to create a MonotoneCubicSpline. Hence we
// insert another one if |data| has only 1 point.
if (data.size() == 1) {
used_data.push_back(data[0]);
}
current_curve_.emplace(CreateTestCurveFromTrainingData(used_data));
return *current_curve_; return *current_curve_;
} }
...@@ -408,8 +415,8 @@ TEST_F(ModellerImplTest, OnAmbientLightUpdated) { ...@@ -408,8 +415,8 @@ TEST_F(ModellerImplTest, OnAmbientLightUpdated) {
} }
// User brightness changes are received, training example cache reaches // User brightness changes are received, training example cache reaches
// |kMaxTrainingDataPoints| to trigger early training. This all happens within a // |max_training_data_points_| to trigger early training. This all happens
// small window shorter than |kTrainingDelay|. // within a small window shorter than |training_delay_|.
TEST_F(ModellerImplTest, OnUserBrightnessChanged) { TEST_F(ModellerImplTest, OnUserBrightnessChanged) {
Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess); Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
...@@ -418,7 +425,9 @@ TEST_F(ModellerImplTest, OnUserBrightnessChanged) { ...@@ -418,7 +425,9 @@ TEST_F(ModellerImplTest, OnUserBrightnessChanged) {
base::nullopt /* personal_curve */); base::nullopt /* personal_curve */);
std::vector<TrainingDataPoint> expected_data; std::vector<TrainingDataPoint> expected_data;
for (size_t i = 0; i < ModellerImpl::kMaxTrainingDataPoints - 1; ++i) {
for (size_t i = 0; i < modeller_->GetMaxTrainingDataPointsForTesting() - 1;
++i) {
EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting()); EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting());
scoped_task_environment_.FastForwardBy( scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromMilliseconds(1)); base::TimeDelta::FromMilliseconds(1));
...@@ -435,7 +444,7 @@ TEST_F(ModellerImplTest, OnUserBrightnessChanged) { ...@@ -435,7 +444,7 @@ TEST_F(ModellerImplTest, OnUserBrightnessChanged) {
} }
// Training should not have started. // Training should not have started.
EXPECT_EQ(ModellerImpl::kMaxTrainingDataPoints - 1, EXPECT_EQ(modeller_->GetMaxTrainingDataPointsForTesting() - 1,
modeller_->NumberTrainingDataPointsForTesting()); modeller_->NumberTrainingDataPointsForTesting());
// Add one more data point to trigger the training early. // Add one more data point to trigger the training early.
...@@ -471,7 +480,7 @@ TEST_F(ModellerImplTest, MultipleUserActivities) { ...@@ -471,7 +480,7 @@ TEST_F(ModellerImplTest, MultipleUserActivities) {
fake_als_reader_.ReportAmbientLightUpdate(30); fake_als_reader_.ReportAmbientLightUpdate(30);
std::vector<TrainingDataPoint> expected_data; std::vector<TrainingDataPoint> expected_data;
for (size_t i = 0; i < ModellerImpl::kMinTrainingDataPoints; ++i) { for (size_t i = 0; i < 10; ++i) {
EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting()); EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting());
scoped_task_environment_.FastForwardBy( scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromMilliseconds(1)); base::TimeDelta::FromMilliseconds(1));
...@@ -487,33 +496,31 @@ TEST_F(ModellerImplTest, MultipleUserActivities) { ...@@ -487,33 +496,31 @@ TEST_F(ModellerImplTest, MultipleUserActivities) {
ConvertToLog(modeller_->AverageAmbientForTesting()), now}); ConvertToLog(modeller_->AverageAmbientForTesting()), now});
} }
EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints, EXPECT_EQ(modeller_->NumberTrainingDataPointsForTesting(), 10u);
modeller_->NumberTrainingDataPointsForTesting());
scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay - scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromSeconds(10)); modeller_->GetTrainingDelayForTesting() / 2);
// A user activity is received, timer should be reset. // A user activity is received, timer should be reset.
const ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(0, 0), const ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(0, 0),
gfx::Point(0, 0), base::TimeTicks(), 0, 0); gfx::Point(0, 0), base::TimeTicks(), 0, 0);
modeller_->OnUserActivity(&mouse_event); modeller_->OnUserActivity(&mouse_event);
scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay - scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromSeconds(2)); modeller_->GetTrainingDelayForTesting() / 3);
EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints, EXPECT_EQ(modeller_->NumberTrainingDataPointsForTesting(), 10u);
modeller_->NumberTrainingDataPointsForTesting());
// Another user event is received. // Another user event is received.
modeller_->OnUserActivity(&mouse_event); modeller_->OnUserActivity(&mouse_event);
// After |kTrainingDelay| - 2 seconds, no training has started. // After |training_delay_|/2, no training has started.
scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay - scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromSeconds(2)); modeller_->GetTrainingDelayForTesting() / 2);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints, EXPECT_EQ(modeller_->NumberTrainingDataPointsForTesting(), 10u);
modeller_->NumberTrainingDataPointsForTesting());
// After another 2 seconds, training is scheduled. // After another |training_delay_|/2, training is scheduled.
scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2)); scoped_task_environment_.FastForwardBy(
modeller_->GetTrainingDelayForTesting() / 2);
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(0u, modeller_->NumberTrainingDataPointsForTesting()); EXPECT_EQ(0u, modeller_->NumberTrainingDataPointsForTesting());
...@@ -526,27 +533,6 @@ TEST_F(ModellerImplTest, MultipleUserActivities) { ...@@ -526,27 +533,6 @@ TEST_F(ModellerImplTest, MultipleUserActivities) {
EXPECT_EQ(expected_curve, *result_curve); EXPECT_EQ(expected_curve, *result_curve);
} }
// No training is done because number of training data points is less than
// |kMinTrainingDataPoints|.
TEST_F(ModellerImplTest, MinTrainingDataPointsRequired) {
Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
test_observer_->CheckStatus(true /* is_model_initialized */,
modeller_->GetGlobalCurveForTesting(),
base::nullopt /* personal_curve */);
fake_als_reader_.ReportAmbientLightUpdate(30);
modeller_->OnUserBrightnessChanged(10, 20);
// No training is done because we have too few training data points.
scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay +
base::TimeDelta::FromSeconds(10));
scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(1u, modeller_->NumberTrainingDataPointsForTesting());
EXPECT_FALSE(test_observer_->trained_curve_received());
}
// Global curve specified by valid experiment parameter. // Global curve specified by valid experiment parameter.
TEST_F(ModellerImplTest, GlobaCurveFromValidExperimentParam) { TEST_F(ModellerImplTest, GlobaCurveFromValidExperimentParam) {
const std::string global_curve_spec("1,10\n2,20\n3,30"); const std::string global_curve_spec("1,10\n2,20\n3,30");
...@@ -585,6 +571,32 @@ TEST_F(ModellerImplTest, GlobaCurveFromInvalidExperimentParam) { ...@@ -585,6 +571,32 @@ TEST_F(ModellerImplTest, GlobaCurveFromInvalidExperimentParam) {
base::nullopt /* personal_curve */); base::nullopt /* personal_curve */);
} }
// Training delay is 0, hence we train as soon as we have 1 data point.
TEST_F(ModellerImplTest, ZeroTrainingDelay) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kAutoScreenBrightness, {
{"training_delay_in_seconds", "0"},
});
Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
test_observer_->CheckStatus(true /* is_model_initialized */,
modeller_->GetGlobalCurveForTesting(),
base::nullopt /* personal_curve */);
fake_als_reader_.ReportAmbientLightUpdate(30);
const ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(0, 0),
gfx::Point(0, 0), base::TimeTicks(), 0, 0);
modeller_->OnUserActivity(&mouse_event);
modeller_->OnUserBrightnessChanged(10, 20);
scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(0u, modeller_->NumberTrainingDataPointsForTesting());
EXPECT_TRUE(test_observer_->trained_curve_received());
}
} // namespace auto_screen_brightness } // namespace auto_screen_brightness
} // namespace power } // namespace power
} // namespace chromeos } // namespace chromeos
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment