-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtidymodels_build_new_model.qmd
276 lines (207 loc) · 7.13 KB
/
tidymodels_build_new_model.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
---
title: "Construire un modèle parsnip"
format: html
lang: fr
---
## Introduction
Notre objectif ici est de construire un nouveau modèle `{parsnip}` à partir d'une implémentation existante dans un package R.
Intérêts : éliminer des dépendances, du code dupliqué, pouvoir profiter du cadre `{tidymodels}`.
### Ressources
- <https://www.tidymodels.org/learn/develop/models/>
- [Tuto tidymodels - Rencontres R 2023](https://abichat.github.io/rr23-tuto-tidymodels)
### Un modèle `{parsnip}`
Il se définit par
- un `mode` (régression linéaire, logistique, ...),
- un `type` (régression, classification),
- un moteur de calcul `engine` (`lm`, `stan`, ...).
Qunad on ajoute un modèle, on doit donc spécifier son `mode` et son `engine`. On peut aussi ajouter un nouveau modèle différent d'un pré-existant seulement par son `type` (ie même combinaison `engine` et `mode`).
Il est possible de voir l'ensemble des modèles déjà disponibles [ici](https://www.tidymodels.org/find/parsnip/).
## Intégration de la régression PLN
```{r, set-up}
#| warning: false
library(PLNmodels)
library(tidymodels)
library(poissonreg)
```
### Données
Nous utiliserons le jeu de données de la [vignette de PLN](https://pln-team.github.io/PLNmodels/articles/PLN.html).
```{r}
data(trichoptera)
```
::: {.callout-note title="Remarque"}
Il existe une fonction `prepare_data` dans `{parsnip}` également.
:::
### Différentes étapes pour intégrer `PLNmodels::PLN` dans parsnip
#### Etape 1 : Spécification du modèle et de ses arguments
On va ajouter la fonction `PLNmodels::PLN` à la liste des fonctions utilisables pour faire de la régression de Poisson.
```{r}
show_model_info("poisson_reg")
```
```{r, set-model}
set_model_mode(model = "poisson_reg", mode = "regression")
set_model_engine(
"poisson_reg",
mode = "regression",
eng = "PLN"
)
set_dependency("poisson_reg", eng = "PLN", pkg = "PLNmodels")
```
On peut vérifier ce qu'on a ajouté.
```{r}
show_model_info("poisson_reg")
```
Si notre modèle a des arguments supplémentaires que ceux existants, on peut les ajouter.
#### Etape 2 : Créer la fonction principale associée au modèle le cas échéant
Ici comme nous avons seulement ajouté un moteur à un modèle préexistant ce n'est pas nécessaire.
todo: regarder comment ajouter l'information possible d'utilisation de poids
#### Etape 3 : Préciser le module d'ajustement (`fit`)
- L'argument `interface`, peut prendre les valeurs `formula`, `data` ou `matrices`.
- `protect` est une liste optionnelle d'arguments qui ne devraient pas être modifié par l'utilisateur.
- `func` est un vecteur précisant le package et la fonction qui sera appelé pour l'ajustement.
- `defaults` est une liste optionnelle d'arguments que l'utilisateur peut modifier mais pour laquelle on peut spécifier des valeurs par défaut.
```{r, set-fit}
set_fit(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
value = list(
interface = "formula",
protect = c("formula", "data"),
func = c(pkg = "PLNmodels", fun = "PLN"),
defaults = list(control = PLN_param(), weights = NULL, subset = NULL)
)
)
```
On peut ajouter des traitements par défaut pour les variables explicatives, tels que calculs de variables indicatrices, calcul d'une constante etc...
```{r, set_encoding}
set_encoding(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
options = list(
predictor_indicators = "traditional",
compute_intercept = TRUE,
remove_intercept = TRUE,
allow_sparse_x = FALSE
)
)
```
#### Etape 4 : Ajouter un module pour la prédiction (`predict`)
```{r, set-pred}
set_pred(
model = "poisson_reg",
eng = "PLN",
mode = "regression",
type = "numeric",
value = list(
pre = NULL,
post = NULL,
func = c(fun = "predict"),
args =
list(
object = expr(object$fit),
newdata = expr(new_data),
type = "response"
)
)
)
```
### Application sur les données trichoptera
::: panel-tabset
#### PLN dans PLNmodels
```{r}
prep_trichoptera <- PLNmodels::prepare_data(trichoptera$Abundance, trichoptera$Covariate)
myPLN <- PLN(Abundance ~ 1 , data = prep_trichoptera)
myPLN
```
#### PLN dans parsnip
```{r}
resPLN <- poisson_reg() %>%
set_engine("PLN") %>%
fit(Abundance ~ 1 , data = prep_trichoptera)
resPLN
```
:::
```{r}
summary(resPLN)
resPLN$spec
# Pour recuperer les coefficients
coef(resPLN$fit)
all.equal(coef(myPLN), coef(resPLN$fit))
# Pour faire de la prediction
pred_pln <- predict(myPLN, newdata = prep_trichoptera, type = "response")
pred_pln %>% head(n=3)
# On peut appliquer les methodes
#pred_parsnipfit <- predict(resPLN$fit, newdata = prep_trichoptera)
pred_parsnip <- predict(resPLN, new_data = trichoptera$Abundance)
pred_parsnip %>% head(n=3)
```
### Test d'un workflow
```{r}
#| eval: false
# separation du jeu de donnees en test et apprentissage
set.seed(123)
tri_split <- initial_split(data = prep_trichoptera, prop = 0.9)
#tri_train <- training(tri_split)
#tri_test <- testing(tri_split)
p_recipe <-
recipe(Abundance ~ 1 + Temperature, data = training(tri_split))
pln_model <-
poisson_reg()%>%
set_engine("PLN") %>%
set_mode("regression")
pln_workflow <- workflow() %>%
add_recipe(p_recipe) %>%
add_model(pln_model)
fitted_workflow <- pln_workflow %>%
fit(training(tri_split))
# Predicition sur le jeu d'entrainement
test_pred <- fitted_workflow %>% predict(new_data = testing(tri_split))
```
Pour la suite du workflow, calcul de performance sur un rééchantillonnage etc., ce serait possible mais nécessite un peu de code. Il faudrait ici que la sortie de prédiction soit en format plus 'tidy' ou réécrire le calcul de métrique sur des matrices.
### Autre jeu de données, exemple en régression univariée
```{r}
p <- read.csv("https://stats.idre.ucla.edu/stat/data/poisson_sim.csv")
p <- within(p, {
prog <- factor(prog, levels=1:3, labels=c("General", "Academic",
"Vocational"))
id <- factor(id)
})
```
On essaie maintenant en utilisant `{parsnip}`.
::: panel-tabset
#### glm de base
```{r glm-p}
summary(m1 <- glm(num_awards ~ prog + math, family="poisson", data=p))
```
#### glm dans parsnip
```{r glm-parsnip}
poisson_reg() %>%
set_engine("glm") %>%
set_mode("regression") %>%
fit(num_awards ~ prog + math, data = p) %>%
predict(p)
```
#### pln
```{r PLN}
mod_pln <- PLN(as.matrix(num_awards) ~ prog + math, data = p)
mod_pln %>%
predict(p, type = "response") %>%
head(n = 10)
```
#### PLN dans parsnip
```{r PLN-parsnip}
poisson_reg() %>%
set_engine("PLN") %>%
set_mode("regression") %>%
fit(as.matrix(num_awards) ~ prog + math, data = p) %>%
predict(p)
```
:::
On peut vérifier la commande associée au modèle programmé.
```{r}
poisson_reg() %>% translate(engine = "PLN")
```
## Pour aller plus loin
Dans le cadre `tidymodels`, tout est assez modulaire et personnalisable : on peut ajouter de nouvelles recettes de pré-traitement, personnaliser son rééchantillonnage, le calcul de métrique etc.
Un peu de documentation sur le site de `{tidymodels]` dans la [section *Develop custom modeling tools*](https://www.tidymodels.org/learn/).