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
| Row | y | count |
|---|---|---|
| Cat… | Int64 | |
| 1 | begnin | 137 |
| 2 | malignant | 72 |
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
| Row | y | count |
|---|---|---|
| Cat… | Int64 | |
| 1 | begnin | 131 |
| 2 | malignant | 78 |
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 │ └─────────────────────────────────────┴─────────┘