Environnement - Packages¶

In [28]:
# activer l'environnement
using Pkg
Pkg.activate("env_julia_mlj")
  Activating project at `c:\Users\ricco\Desktop\demo\env_julia_mlj`
In [29]:
# liste des packages installés
Pkg.status()
Status `C:\Users\ricco\Desktop\demo\env_julia_mlj\Project.toml`
  [324d7699] CategoricalArrays v1.1.0
  [a93c6f00] DataFrames v1.8.2
  [7073ff75] IJulia v1.34.4
  [add582a8] MLJ v0.23.2
  [6ee0df7b] MLJLinearModels v0.10.1
  [30f210dd] ScientificTypesBase v3.1.0
  [fdbf4ff8] XLSX v0.11.3

Importation et préparation des données¶

Importation - Types des variables¶

In [30]:
# packages
import DataFrames as DFR
import XLSX

# lecture des données
df = DFR.DataFrame(XLSX.readtable("./breast.xlsx"))

# premières lignes
println(DFR.first(df,10))
10×10 DataFrame
 Row │ clump  ucellsize  ucellshape  mgadhesion  sepics  bnuclei  bchromatin  normnucl  mitoses  classe    
     │ Int64  Int64      Int64       Int64       Int64   Int64    Int64       Int64     Int64    String    
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │     4          2           2           1       2        1           2         1        1  begnin
   2 │     1          1           1           1       2        1           2         1        1  begnin
   3 │     2          1           1           1       2        1           2         1        1  begnin
   4 │    10          6           6           2       4       10           9         7        1  malignant
   5 │     4          1           1           1       2        1           2         1        1  begnin
   6 │     1          1           1           1       2        1           1         1        1  begnin
   7 │     1          1           1           1       2        1           2         1        1  begnin
   8 │     5          1           1           1       2        1           2         1        1  begnin
   9 │     3          1           1           1       2        1           2         1        1  begnin
  10 │     1          1           1           1       2        4           2         1        1  begnin
In [31]:
# vérifier le schéma de la base
import MLJ
MLJ.schema(df)
┌────────────┬──────────┬────────┐
│ names      │ scitypes │ types  │
├────────────┼──────────┼────────┤
│ clump      │ Count    │ Int64  │
│ ucellsize  │ Count    │ Int64  │
│ ucellshape │ Count    │ Int64  │
│ mgadhesion │ Count    │ Int64  │
│ sepics     │ Count    │ Int64  │
│ bnuclei    │ Count    │ Int64  │
│ bchromatin │ Count    │ Int64  │
│ normnucl   │ Count    │ Int64  │
│ mitoses    │ Count    │ Int64  │
│ classe     │ Textual  │ String │
└────────────┴──────────┴────────┘

Préparation des structures¶

In [32]:
# isoler y et X dans des structures distinctes
y, X = MLJ.unpack(df,==(:classe))

# dimensions
println("Dim. de y = $(DFR.size(y))")
println("Dim. de X = $(DFR.size(X))")
Dim. de y = (699,)
Dim. de X = (699, 9)

Ajustement du type des variables¶

In [33]:
# convertir y en variable catégorielle pour la rég. logistique
# équivalent du type factor sous R
# utilisation du package CategoricalArrays
import CategoricalArrays as CA
y = CA.categorical(y)

# vérification des modalités
println(eltype(y))
CategoricalArrays.CategoricalValue{String, UInt32}
In [34]:
# transformer les X en variables continues
# parce qu'utilisation de la régression logistique
# utilisation du package ScientificTypesBase
import ScientificTypesBase as STB
X = MLJ.coerce(X,STB.Count => STB.Continuous)

# schéma
MLJ.schema(X)
┌────────────┬────────────┬─────────┐
│ names      │ scitypes   │ types   │
├────────────┼────────────┼─────────┤
│ clump      │ Continuous │ Float64 │
│ ucellsize  │ Continuous │ Float64 │
│ ucellshape │ Continuous │ Float64 │
│ mgadhesion │ Continuous │ Float64 │
│ sepics     │ Continuous │ Float64 │
│ bnuclei    │ Continuous │ Float64 │
│ bchromatin │ Continuous │ Float64 │
│ normnucl   │ Continuous │ Float64 │
│ mitoses    │ Continuous │ Float64 │
└────────────┴────────────┴─────────┘

Partition en TRAIN/TEST¶

In [35]:
# indices pour partition en train et test
# stratify est possible parce y est catégorielle maintenant (et non plus string)
idTrain, idTest = MLJ.partition(1:DFR.nrow(X),0.7,shuffle=true,rng=42,stratify=y)

# structures y et X pour train/test
# par indexation avec les indices
yTrain, yTest = y[idTrain], y[idTest]
XTrain, XTest = X[idTrain,:], X[idTest,:]

# afficher les dimensions pour vérifications
println("Dim. de y = $(DFR.size(yTrain)) et $(DFR.size(yTest))")
println("Dim. de X = $(DFR.size(XTrain)) et $(DFR.size(XTest))")
Dim. de y = (490,) et (209,)
Dim. de X = (490, 9) et (209, 9)

Régression logistique avec MLJ¶

Importation de la classe¶

In [36]:
# importer la régression logistique
# à partir du module MLJLinearModels
LogisticClassifier = @MLJ.load LogisticClassifier pkg=MLJLinearModels
import MLJLinearModels ✔
┌ Info: For silent loading, specify `verbosity=0`. 
└ @ Main C:\Users\ricco\.julia\packages\MLJModels\9LbNu\src\loading.jl:159
MLJLinearModels.LogisticClassifier

Instanciation, initialisation, entraînement¶

In [37]:
# instancier le modèle avec les paramètres par défaut
# c'est ici qu'il faudrait passer les paramètres de l'algo
# le cas échéant (cf. la doc)
lr = LogisticClassifier()

# préparation de l'objet pour l'entraînement
# avec la méthode machine() de MLJ
mach = MLJ.machine(lr,XTrain,yTrain)

# lancer l'entraînement -> l'objet mach est màj directement avec "!"
MLJ.fit!(mach)
┌ Info: Training machine(LogisticClassifier(lambda = 2.220446049250313e-16, …), …).
└ @ MLJBase C:\Users\ricco\.julia\packages\MLJBase\krfwA\src\machines.jl:499
┌ Info: Solver: MLJLinearModels.LBFGS{Optim.Options{Float64, Nothing}, @NamedTuple{}}
│   optim_options: Optim.Options{Float64, Nothing}
│   lbfgs_options: @NamedTuple{} NamedTuple()
└ @ MLJLinearModels C:\Users\ricco\.julia\packages\MLJLinearModels\s9vSj\src\mlj\interface.jl:72
trained Machine; caches model-specific representations of data
  model: LogisticClassifier(lambda = 2.220446049250313e-16, …)
  args: 
    1:	Source @785 ⏎ ScientificTypesBase.Table{AbstractVector{ScientificTypesBase.Continuous}}
    2:	Source @343 ⏎ AbstractVector{ScientificTypesBase.Multiclass{2}}

Coefficients estimés¶

In [38]:
# coefficients
fp = MLJ.fitted_params(mach)
fp.coefs
9-element Vector{Pair{Symbol, Float64}}:
      :clump => 0.5761811400769583
  :ucellsize => 0.11603135710231566
 :ucellshape => 0.4696520533428362
 :mgadhesion => 0.23912920145868183
     :sepics => 0.038655433716668595
    :bnuclei => 0.46705412226561094
 :bchromatin => 0.39596636659133017
   :normnucl => 0.0473490389190752
    :mitoses => 0.430029264625534
In [39]:
# intercept
fp.intercept
-9.808847714828486

Prédiction et évaluation en test¶

Probabilité d'appartenance¶

In [40]:
# predict sur l'échantillon test -> proba d'appartenance
# via la machine qui a été entraînée
proba_pred = MLJ.predict(mach,XTest)

# premières valeurs
DFR.first(proba_pred,10)
10-element CategoricalDistributions.UnivariateFiniteVector{ScientificTypesBase.Multiclass{2}, String, UInt32, Float64}:
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.000328, malignant=>1.0)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.999, malignant=>0.00137)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.996, malignant=>0.00358)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>7.19e-5, malignant=>1.0)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.993, malignant=>0.00656)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.97, malignant=>0.0296)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.00105, malignant=>0.999)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.00161, malignant=>0.998)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>0.0754, malignant=>0.925)
 UnivariateFinite{ScientificTypesBase.Multiclass{2}}(begnin=>7.29e-5, malignant=>1.0)

Affectation aux classes¶

In [41]:
# transformer en prediction via le mode
y_pred = MLJ.mode.(proba_pred)
y_pred[1:10]
10-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
 "malignant"
 "begnin"
 "begnin"
 "malignant"
 "begnin"
 "begnin"
 "malignant"
 "malignant"
 "malignant"
 "malignant"

Accuracy¶

In [42]:
# calcul de l'accuracy
println("Accuracy = $(MLJ.accuracy(yTest,y_pred))")
Accuracy = 0.9712918660287081

Matrice de confusion (transposée / Scikit-Learn)¶

In [43]:
# ou la matrice de confusion
# attention l'ordre des vecteurs passés à la fonction
# attention à la présentation de la matrice
mc = MLJ.confusion_matrix(y_pred,yTest)
mc
          ┌───────────────────┐
          │   Ground Truth    │
┌─────────┼─────────┬─────────┤
│Predicted│ begnin  │malignant│
├─────────┼─────────┼─────────┤
│ begnin  │   131   │    0    │
├─────────┼─────────┼─────────┤
│malignant│    6    │   72    │
└─────────┴─────────┴─────────┘
In [44]:
# vérification des effectifs -> yTest observé
DFR.combine(DFR.groupby(DFR.DataFrame(y=yTest), :y), DFR.nrow => :count)
2×2 DataFrame
Rowycount
Cat…Int64
1begnin137
2malignant72
In [45]:
# vérification des effectifs -> prédictions en test
DFR.combine(DFR.groupby(DFR.DataFrame(y=y_pred), :y), DFR.nrow => :count)
2×2 DataFrame
Rowycount
Cat…Int64
1begnin131
2malignant78

Validation croisée¶

Instancier l'objet validation croisée¶

In [46]:
# instanciation - validation croisée stratifiée
# 5 folds
cv = MLJ.StratifiedCV(nfolds=5,shuffle=true)
StratifiedCV(
  nfolds = 5, 
  shuffle = true, 
  rng = Random.TaskLocalRNG())

Exécution¶

In [47]:
# avec l'objet utilisée pour l'entraînement
MLJ.evaluate!(mach,
                resampling = cv,
                measure = [MLJ.accuracy])
PerformanceEvaluation object with these fields:
  model, tag, measure, operation,
  measurement, uncertainty_radius_95, per_fold, per_observation,
  fitted_params_per_fold, report_per_fold,
  train_test_rows, resampling, repeats
Tag: LogisticClassifier-955
Extract:
┌────────────┬──────────────┬─────────────┐
│ measure    │ operation    │ measurement │
├────────────┼──────────────┼─────────────┤
│ Accuracy() │ predict_mode │ 0.957       │
└────────────┴──────────────┴─────────────┘
┌─────────────────────────────────────┬─────────┐
│ per_fold                            │ 1.96*SE │
├─────────────────────────────────────┼─────────┤
│ [0.949, 0.969, 0.959, 0.939, 0.969] │ 0.013   │
└─────────────────────────────────────┴─────────┘