Vetiver: Modele de monitorizare în producție

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

Această postare este cea mai recentă din seria noastră de trei părți despre MLOps cu Vetiver, după:

În acele bloguri, am introdus pachetul {vetiver} și utilizarea acestuia ca instrument pentru MLOps simplificați. Folosind setul de date {palmerpenguins} ca exemplu, am subliniat pașii antrenării unui model folosind {tidymodels}, apoi transformându-l într-un model {vetiver}. Apoi am demonstrat pașii de versiune a modelului nostru instruit și de implementare în producție.

Introducerea primului model în producție este grozav! Dar este într-adevăr doar începutul, deoarece acum va trebui să îl monitorizați cu atenție în timp pentru a vă asigura că continuă să funcționeze conform așteptărilor cu cele mai recente date. Din fericire, {vetiver} vine cu o suită de funcții pentru acest scop!

Pregătirea datelor

Un pas crucial în procesul de monitorizare este introducerea unei componente de timp. Vom urmări valorile cheie de punctare în timp pe măsură ce se colectează date noi, prin urmare analiza noastră va depinde acum de o dimensiune de timp, chiar dacă modelul nostru implementat nu are o dependență explicită de timp.

Pentru a demonstra pașii de monitorizare, vom lucra cu datele privind speranța de viață ale Organizației Mondiale a Sănătății, care urmăresc speranța medie de viață în diferite țări de-a lungul unui număr de ani. Începem prin a încărca datele:

download.file("https://www.kaggle.com/api/v1/datasets/download/kumarajarshi/life-expectancy-who",
 "archive.zip")
unzip("archive.zip")
life_expectancy = readr::read_csv("./Life Expectancy Data.csv")

Vom încerca să prezicem speranța de viață folosind procentul de cheltuieli, cheltuielile totale, populația, indicele de masă corporală (IMC) și școlarizarea. Să selectăm coloanele de interes, să punem în ordine numele variabilelor și să aruncăm toate valorile lipsă:

life_expectancy = life_expectancy |>
 janitor::clean_names(case = "snake",
 abbreviations = c("BMI")) |>
 dplyr::select("year", "life_expectancy",
 "percentage_expenditure",
 "total_expenditure", "population",
 "bmi", "schooling") |>
 tidyr::drop_na()

life_expectancy
#> # A tibble: 2,111 × 7
#> year life_expectancy percentage_expenditure total_expenditure population
#>     
#> 1 2015 65 71.3 8.16 33736494
#> 2 2014 59.9 73.5 8.18 327582
#> 3 2013 59.9 73.2 8.13 31731688
#> 4 2012 59.5 78.2 8.52 3696958
#> 5 2011 59.2 7.10 7.87 2978599
#> 6 2010 58.8 79.7 9.2 2883167
#> 7 2009 58.6 56.8 9.42 284331
#> 8 2008 58.1 25.9 8.33 2729431
#> 9 2007 57.5 10.9 6.73 26616792
#> 10 2006 57.3 17.2 7.43 2589345
#> # ℹ 2,101 more rows
#> # ℹ 2 more variables: bmi , schooling 

Datele conțin un numere year coloană care va fi utilă pentru monitorizarea performanței modelului în timp. Cu toate acestea, funcțiile de monitorizare {vetiver} vor necesita utilizarea acestei coloane
("YYYY-MM-DD") și va trebui sortat în ordine crescătoare:

life_expectancy = life_expectancy |>
 dplyr::mutate(
 year = lubridate::ymd(year, truncated = 2L)
 ) |>
 dplyr::arrange(year)

life_expectancy
#> # A tibble: 2,111 × 7
#> year life_expectancy percentage_expenditure total_expenditure
#>    
#> 1 2000-01-01 54.8 10.4 8.2 
#> 2 2000-01-01 72.6 91.7 6.26
#> 3 2000-01-01 71.3 154. 3.49
#> 4 2000-01-01 45.3 15.9 2.79
#> 5 2000-01-01 74.1 1349. 9.21
#> 6 2000-01-01 72 32.8 6.25
#> 7 2000-01-01 79.5 347. 8.8 
#> 8 2000-01-01 78.1 3557. 1.6 
#> 9 2000-01-01 66.6 35.1 4.67
#> 10 2000-01-01 65.3 3.70 2.33
#> # ℹ 2,101 more rows
#> # ℹ 3 more variables: population , bmi , schooling 

În cele din urmă, să ne imaginăm că anul este în prezent 2002, așa că datele noastre istorice de pregătire ar trebui să acopere doar anii 2000 până în 2002:

historic_life_expectancy = life_expectancy |>
 dplyr::filter(year <= "2002-01-01")

Mai târziu în această postare vom verifica modul în care modelul nostru funcționează pe datele mai recente pentru a ilustra efectele derivei modelului.

Antrenamentul modelului nostru

Înainte de a începe să ne antrenăm modelul, ar trebui să împărțim datele în seturi de „antrenare” și „testare”:

library("tidymodels")

data_split = rsample::initial_split(
 historic_life_expectancy,
 prop = 0.7
)
train_data = rsample::training(data_split)
test_data = rsample::testing(data_split)

Setul de testare reprezintă 30% din datele originale și va fi folosit pentru a nota modelul pe date nevăzute după antrenament.

Celula de cod de mai jos se ocupă de pașii de configurare a unui model antrenat în {vetiver} și versiunea acestuia folosind {pins}. Pentru o explicație mai detaliată a ceea ce face acest cod, trimitem cititorul înapoi la partea 1.

Vom folosi din nou un element de bază K-modelul cel mai apropiat vecin, deși de data aceasta am configurat fluxul de lucru ca model de regresie, deoarece prezicem o cantitate continuă. Rețineți că acest lucru necesită instalarea pachetului {kknn}.

# Train the model with {tidymodels}
model = recipe(
 life_expectancy ~ percentage_expenditure +
 total_expenditure + population + bmi + schooling,
 data = train_data
) |>
 workflow(nearest_neighbor(mode = "regression")) |>
 fit(train_data)

# Convert to a {vetiver} model
v_model = vetiver::vetiver_model(
 model,
 model_name = "k-nn",
 description = "life-expectancy"
)

# Store the model using {pins}
model_board = pins::board_temp(versioned = TRUE)
vetiver::vetiver_pin_write(model_board, v_model)

Aici modelul de bord {pins} este creat folosind pins::board_temp() care generează un folder local temporar.

În acest moment, ar trebui să verificăm modul în care modelul nostru funcționează pe datele de testare nevăzute. Eroarea maximă absolută (mae), eroare pătratică medie (rmse) și R2 (rsq) poate fi calculată pe o anumită perioadă de timp folosind vetiver::vetiver_compute_metrics():

metrics = augment(v_model, new_data = test_data) |>
 vetiver::vetiver_compute_metrics(
 date_var = year,
 period = "year",
 truth = life_expectancy,
 estimate = .pred
 )

metrics
#> # A tibble: 9 × 5
#> .index .n .metric .estimator .estimate
#>     
#> 1 2000-01-01 46 rmse standard 4.06 
#> 2 2000-01-01 46 rsq standard 0.836
#> 3 2000-01-01 46 mae standard 3.05 
#> 4 2001-01-01 44 rmse standard 4.61 
#> 5 2001-01-01 44 rsq standard 0.844
#> 6 2001-01-01 44 mae standard 3.43 
#> 7 2002-01-01 36 rmse standard 4.14 
#> 8 2002-01-01 36 rsq standard 0.853
#> 9 2002-01-01 36 mae standard 3.04

Prima linie de cod aici trimite date noi (în acest caz, datele de testare nevăzute) modelului nostru și generează un .pred coloana care conține predicțiile modelului. Această ieșire este apoi conectată la
vetiver::vetiver_compute_metrics() care include următoarele argumente:

  • date_var: numele coloanei de dată de utilizat pentru monitorizarea performanței modelului în timp.
  • period: perioada ("hour", "day", "week"etc) peste care ar trebui să fie calculate valorile de scor. Suntem restricționați de datele noastre la utilizare "year"; pentru date mai granulare, poate fi mai sensibil să se monitorizeze modelul pe perioade de timp mai scurte.
  • truth: valorile reale ale variabilei țintă (în exemplul nostru acesta este life_expectancy coloana datelor de testare).
  • estimate: predicțiile variabilei țintă pentru a compara valorile reale cu (în exemplul nostru acesta este .pred coloană calculată în pasul precedent).

Vom reveni la aceste valori mai târziu în această postare, așa că deocamdată să le stocăm împreună cu modelul nostru folosind {pins}:

pins::pin_write(model_board, metrics, "k-nn")

Vom sări peste detaliile despre implementarea modelului nostru, deoarece acest lucru este deja tratat în partea 2.

Monitorizarea modelului nostru

În timp, putem observa că modelul nostru începe derivăunde predicțiile sale se îndepărtează treptat de adevăr pe măsură ce datele evoluează. Există două cauze comune pentru aceasta:

  • Derivarea datelor: distribuția statistică a unei variabile de intrare se modifică.
  • Derivarea conceptului: se modifică relația dintre țintă și o variabilă de intrare.

Luând exemplul datelor privind speranța de viață:

  • Cheltuielile unei țări sunt de așteptat să varieze în timp din cauza schimbărilor în politica guvernamentală și a evenimentelor neașteptate, cum ar fi pandemiile și prăbușirile economice. Aceasta este deriva de date.
  • Progresele în medicină pot însemna că speranța de viață se poate îmbunătăți chiar dacă IMC rămâne neschimbat. Aceasta este deriva conceptului.

Revenind la modelul nostru, care a fost antrenat folosind date din 2000 până în 2002, să verificăm acum cum va funcționa pe datele „viitoare” până în 2010:

# Generate "new" data from 2003 to 2010
new_life_expectancy = life_expectancy |>
 dplyr::filter(year > "2002-01-01" &
 year <= "2010-01-01")

# Score the model performance on the new data
new_metrics = augment(v_model, new_data = new_life_expectancy) |>
 vetiver::vetiver_compute_metrics(
 date_var = year,
 period = "year",
 truth = life_expectancy,
 estimate = .pred
 )

new_metrics
#> # A tibble: 24 × 5
#> .index .n .metric .estimator .estimate
#>     
#> 1 2003-01-01 141 rmse standard 5.21 
#> 2 2003-01-01 141 rsq standard 0.760
#> 3 2003-01-01 141 mae standard 3.64 
#> 4 2004-01-01 141 rmse standard 5.14 
#> 5 2004-01-01 141 rsq standard 0.761
#> 6 2004-01-01 141 mae standard 3.60 
#> 7 2005-01-01 141 rmse standard 5.83 
#> 8 2005-01-01 141 rsq standard 0.684
#> 9 2005-01-01 141 mae standard 4.19 
#> 10 2006-01-01 141 rmse standard 6.23 
#> # ℹ 14 more rows

Să stocăm acum noile valori în tabloul model {pins} (împreună cu valorile originale):

vetiver::vetiver_pin_metrics(
 model_board,
 new_metrics,
 "k-nn"
)

Acum putem încărca atât valorile originale, cât și cele noi, apoi le putem vizualiza cu acestea vetiver::vetiver_plot_metrics():

# Load the metrics
monitoring_metrics = pins::pin_read(model_board, "k-nn")

# Plot the metrics
vetiver::vetiver_plot_metrics(monitoring_metrics) +
 scale_size(name = "Number ofnobservations", range = c(2, 4)) +
 theme_minimal()

Un grafic cu linii care arată evoluția erorii absolute maxime, a erorii pătrate medii și a metricii R pătrate a modelului de speranță de viață antrenat în timp între anii 2000 și 2010. Ambele măsurători ale erorii cresc în timp, în timp ce R pătratul metric scade.Un grafic cu linii care arată evoluția erorii absolute maxime, a erorii pătrate medii și a metricii R pătrate a modelului de speranță de viață antrenat în timp între anii 2000 și 2010. Ambele măsurători ale erorii cresc în timp, în timp ce R pătratul metric scade.

Mărimea punctelor de date reprezintă numărul de observații utilizate pentru a calcula valorile în fiecare perioadă. Până în 2002, folosim datele de testare nevăzute pentru a nota modelul nostru; după aceasta, folosim setul complet de date disponibil.

Observăm o eroare de model în creștere în timp, sugerând că modelul implementat ar trebui antrenat numai folosind cele mai recente date. Pentru acest set de date particular, ar fi rezonabil să se recalifice și să redistribuie modelul anual.

Rezumat

În acest blog am introdus ideea de a monitoriza modelele în producție folosind cadrul Vetiver. Folosind ca exemplu datele privind speranța de viață de la Organizația Mondială a Sănătății, am subliniat cum să urmărim valorile cheie ale modelului în timp și să identificăm deviația modelului.

Pe măsură ce începeți să vă retrageți modelele vechi și să le înlocuiți cu modele noi instruite pe cele mai recente date, asigurați-vă că păstrați TOATE modelele dvs. (vechi și noi) versiuni și stocate. În acest fel, puteți prelua orice model istoric și puteți stabili de ce a dat o anumită predicție la o anumită dată.

Cadrul {vetiver} include, de asemenea, un șablon R Markdown pentru crearea unui tablou de bord de monitorizare a modelului. Pentru mai multe despre aceasta, consultați documentația {vetiver}.

Următoarea postare din seria noastră Vetiver va oferi o schiță a cadrului Python. Rămâneți pe fază pentru asta cândva în noul an!

Pentru actualizări și revizuiri ale acestui articol, consultați postarea inițială

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.