Skip to content

Sort and Filter Model Candidates on Experiment Page

What does this MR do and why?

Allows users to search for candidates in the Experiment Page. For now, only search by name works.

Screenshots or screen recordings

Before After
image image

Database

by_name+order_by

SELECT
    "ml_candidates".*
FROM
    "ml_candidates"
WHERE
    "ml_candidates"."experiment_id" = 13
    AND (ml_candidates.name LIKE '%\_9%')
ORDER BY
    name DESC

Execution Plan:

https://postgres.ai/console/gitlab/gitlab-production-tunnel-pg12/sessions/14488/commands/50977

 Sort  (cost=3.17..3.17 rows=1 width=106) (actual time=0.160..0.162 rows=0 loops=1)
   Sort Key: ml_candidates.name DESC
   Sort Method: quicksort  Memory: 25kB
   Buffers: shared hit=7
   I/O Timings: read=0.000 write=0.000
   ->  Index Scan using index_ml_candidates_on_experiment_id_and_iid on public.ml_candidates  (cost=0.14..3.16 rows=1 width=106) (actual time=0.026..0.026 rows=0 loops=1)
         Index Cond: (ml_candidates.experiment_id = 13)
         Filter: (ml_candidates.name ~~ '%\_9%'::text)
         Rows Removed by Filter: 0
         Buffers: shared hit=4
         I/O Timings: read=0.000 write=0.000

by_name+order_by_metric

SELECT
    "ml_candidates".*
FROM
    "ml_candidates"
    INNER JOIN ( SELECT DISTINCT ON (candidate_id, name)
            *
        FROM
            "ml_candidate_metrics"
        WHERE
            "ml_candidate_metrics"."name" = 'auc'
        ORDER BY
            candidate_id,
            name,
            id DESC) latest ON latest.candidate_id = ml_candidates.id
WHERE
    "ml_candidates"."experiment_id" = 13
    AND (name LIKE '%\_9%')
ORDER BY
    latest.value DESC,
    ml_candidates.id DESC

Execution Plan:

https://postgres.ai/console/gitlab/gitlab-production-tunnel-pg12/sessions/14488/commands/50976

 Sort  (cost=12.43..12.43 rows=1 width=114) (actual time=1.770..1.772 rows=0 loops=1)
   Sort Key: ml_candidate_metrics.value DESC, ml_candidates.id DESC
   Sort Method: quicksort  Memory: 25kB
   Buffers: shared hit=9 read=1
   I/O Timings: read=1.682 write=0.000
   ->  Nested Loop  (cost=9.26..12.42 rows=1 width=114) (actual time=1.722..1.723 rows=0 loops=1)
         Buffers: shared hit=3 read=1
         I/O Timings: read=1.682 write=0.000
         ->  Index Scan using index_ml_candidates_on_experiment_id_and_iid on public.ml_candidates  (cost=0.14..3.16 rows=1 width=106) (actual time=1.720..1.720 rows=0 loops=1)
               Index Cond: (ml_candidates.experiment_id = 131)
               Filter: (ml_candidates.name ~~ '%a%'::text)
               Rows Removed by Filter: 0
               Buffers: shared hit=3 read=1
               I/O Timings: read=1.682 write=0.000
         ->  Unique  (cost=9.12..9.15 rows=5 width=90) (actual time=0.000..0.000 rows=0 loops=0)
               I/O Timings: read=0.000 write=0.000
               ->  Sort  (cost=9.12..9.13 rows=5 width=90) (actual time=0.000..0.000 rows=0 loops=0)
                     Sort Key: ml_candidate_metrics.candidate_id, ml_candidate_metrics.id DESC
                     I/O Timings: read=0.000 write=0.000
                     ->  Seq Scan on public.ml_candidate_metrics  (cost=0.00..9.06 rows=5 width=90) (actual time=0.000..0.000 rows=0 loops=0)
                           Filter: (ml_candidate_metrics.name = 'auc'::text)
                           Rows Removed by Filter: 0
                           I/O Timings: read=0.000 write=0.000

How to set up and validate locally

  1. Enable the feature flag

    echo "Feature.enable(:ml_experiment_tracking)" | bundle exec rails c
  2. Create an Experiment and 10 candidates

    # Added variables
    user_id = 1
    project_id = 1
    
    exp = Ml::Experiment.create!(name: "Awesome Gitlab Experiment", user_id: user_id, project_id: project_id)
    10.times.each { |i| exp.candidates.create!(user_id: user_id, start_time: 0, name: "candidate_#{i}") }
    exp.candidates.each_with_index { |c, i| c.metrics.create!(name: "auc", value: i*0.1 , tracked_at: Time.zone.now, step: 1)} 
    exp.candidates.each_with_index { |c, i| c.metrics.create!(name: "accuracy", value: i* 0.1+0.1 , tracked_at: Time.zone.now, step: 1)} 
    exp.candidates.each { |c| c.params.create!(name: "algorithm", value: ["LogisticRegression", "DecisionTree"].sample )}
  3. Navigate to <your project>/-/ml/experiments and click on "Awesome Gitlab Experiment"

  4. Verify search works, changing the order sets the correct parameters and reloads the page

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #383984 (closed)

Edited by Eduardo Bonet

Merge request reports