Move chapter 2 to chapter 3

This commit is contained in:
Raffaele Mignone 2021-06-14 15:43:02 +02:00
parent 7ece08dba9
commit bd39451614
Signed by: norangebit
GPG Key ID: F5255658CB220573
1 changed files with 104 additions and 127 deletions

View File

@ -1,160 +1,137 @@
# Analisi
# Metodologia {#sec:methodology}
## RQ1: come il ML e' distribuito sull'architettura dei progetti? {#sec:rq1}
## Research Questions
In questa prima analisi si è andato a verificare l'esistenza di una differenza nei files e nelle directories modificate in base al tipo di cambiamento.
Per poter svolgere questa analisi è stato necessario individuare il numero totale di file modificati per *fix* generici e per i *fix* specifici del \acl{ML}.
A tal fine i commit sono stati raggruppati rispetto al progetto e al tipo di cambiamento (\ac{ML}, no \ac{ML}) e per ogni istanza di questo raggruppamento si è eseguito l'union set dei files modificati.
Come output di questa fase si è generato per ogni progetto:
Questo studio ha l'obiettivo di dare una risposta a queste cinque domande:
- l'insieme dei file modificati per *fix* di \ac{ML}
- l'insieme dei file modificati per fix generici
- **RQ1**: *come il \ac{ML} e' distribuito sull'architettura dei progetti?*
Infine eseguendo l'union set tra questi due insiemi si è ottenere l'insieme totale dei files modificati durante i *fix*.
In questo modo è stato possibile andare a valutare la percentuale di files modificati in relazione al tipo di cambiamento.
Attraverso la funzione di libreria Python `os.path.dirname` sono stati ottenuti i tre insiemi sopra citati anche per quanto riguarda le directories.
In questa *RQ* si vuole investigare l'architettura dei progetti.
In particolare l'attenzione viene concentratala sui files e sulle directories modificate durante interventi di *issues fixing*.
Obbiettivo di questa domanda è anche individuare la percentuale di files che utilizzano import riconducibili a librerie e framework di \acl{ML}.
- **RQ2**: *come sono distribuiti i bug sulle diverse fasi di \ac{ML}?*
Dalla @fig:files-directories si può notare che i cambiamenti generici vanno ad impattare su una superficie maggiore del sistema, sia che l'analisi sia svolta al livello di files che di directories.
Un'ulteriore aspetto interessante riguarda la varianza delle distribuzioni, infatti, indipendentemente dalla granularità dell'analisi, il dato riguardante i cambiamenti di \acl{ML} è caratterizzato da una maggiore varianza.
Il workflow tipico per lo sviluppo di un'applicazione di \acl{ML} si compone di più fasi.
L'obiettivo di questa *RQ* è quello di individuare le fasi più critiche per quanto riguarda l'introduzione di difetti all'interno del prodotto software.
- **RQ3**: *esiste una differenza di entropy tra \ac{ML} bug e altri bug?*
![Percentuale di files e directories modificate in base al tipo di cambiamento](figures/files-and-directories.pdf){#fig:files-directories width=80%}
A partire dai lavori precedenti svolti sull'entropia di un cambiamento, si vuole capire se esiste una differenza in termini di entropia generata tra le correzioni dei difetti ascrivibili al \acl{ML} e gli altri difetti.
- **RQ4**: *come varia il livello di discussione tra \ac{ML} bug e altri bug?*
Un'ulteriore analisi rispetto all'architettura dei progetti è stata svolta mediante gli import.
Attraverso uno script sono stati estratti, per ogni file, gli import utilizzati all'interno del file stesso.
A questo punto sono stati individuati i files di \acl{ML} in base agli import utilizzati.
La classificazione è avvenuta utilizzando due livelli di severità; in un primo (severità *strict*) caso sono stati considerati come import di \acl{ML} solo delle librerie strettamente di \ac{ML} come ad esempio `keras`, `TernsorFlow`, `PyTorch`, ecc.
Mentre in un secondo caso (severità *base*) sono state incluse anche librerie utilizzate spesso in ambito \ac{ML}, ma anche in altri ambiti, come ad esempio `pandas`, `numpy` e `scipy`.
Questa *RQ* riguarda il livello di discussione dei *bug*.
In particolare si vuole capire se, all'interno dei progetti di \acl{ML}, i bug generici sono discussi con lo stesso livello di approfondimento di quelli specifici del \ac{ML}.
- **RQ5**: *come varia il time-to-fix tra \ac{ML} bug e altri bug?*
![Percentuale di file che utilizzano librerie di ML](figures/imports.pdf){#fig:imports width=70%}
Un altro aspetto caratteristico di un *fix* è il tempo necessario per poter essere attuato.
Questa *RQ* ha lo scopo di verificare l'esistenza di differenze tra i *bug* generici e quelli di \acl{ML}.
Dal boxplot riportato in @fig:imports si può notare che, indipendentemente dalla severità dell'analisi, la percentuale di file che utilizzano librerie di \acl{ML} è caratterizzata da una forte varianza.
Ciò indica che i progetti inclusi all'interno dello studio sono di varia natura e che alcuni sono più incentrati sul \ac{ML} rispetto ad altri.
Inoltre, considerando l'analisi *strict*, è possibile osservare come solo un $25\%$ dei progetti abbia una percentuale di files di \ac{ML} superiore al $45\%$.
## Selezione dei progetti
## RQ2: come sono distribuiti i bug sulle diverse fasi di ML? {#sec:rq2}
L'individuazione dei progetti da analizzare è avvenuta mediate l'ausilio dell'\ac{API} messa a disposizione da GitHub.
In particolare è stata eseguita una query per ottenere una lista di repository che fanno uso di librerie e framework di \ac{ML} come `TensorFlow`, `Pytorch` e `scikit-learn`.
In questo modo è stato possibile ottenere una lista di $26758$ repository che è stata successivamente filtrata per individuare solo i progetti d'interesse per il seguente studio.
Come illustrato nella @sec:classificazione-commit per poter determinare la natura di un *issue fix* si è fatto ricorso alla classificazione delle *issues* ad esso associate.
La maggior parte delle *issues* è stata classificata automaticamente, ma è stato comunque necessario classificarne una porzione in modo manuale per poter avere un train/test set.
Come detto precedentemente, nel caso delle *issues* classificate a mano, oltre all'individuazione della tipologia (\ac{ML}, non \ac{ML}) è stata individuata anche la fase in cui il problema si palesava (si veda @sec:classificazione-issues).
Questo dato aggiuntivo presente su alcune issues è stato *proiettato* anche sulla classificazione dei commit di *fix* per andare a valutare come questi sono distribuiti sulle varie fasi.
I risultati di questa analisi sono riportati in @fig:count-fix-phases.
L'operazione di filtraggio è avvenuta attraverso due fasi; una prima automatica e una seconda manuale.
La prima fase ha avuto l'obiettivo di selezionare unicamente i repository *popolari*.
Nella maggior parte dei casi viene utilizzato il numero di stelle come indice della popolarità di un progetto [@borges2016understandingfactorsthat], ma per questo lavoro si è preferito dare maggiore rilevanza ad altri aspetti, come il numero di fork, il numero di *contributors* e il numero di issues chiuse.
Questa scelta è stata dettata dall'esigenza di selezionare non solo repository popolari, ma anche caratterizzati da una forte partecipazione della community.
![Istanze dei fix in base alla fase](figures/count-fix-phases.pdf){#fig:count-fix-phases width=70%}
I progetti che hanno superato questa prima selezione dovevano:
Rispetto alla distribuzione sulle issues (@fig:labeling-phases) è possibile notare la scomparsa della fase *data collection*, inoltre è evidente anche la riduzione delle occorrenze di *model training* e una crescita d'importanza per quanto riguarda le fasi di *model requirements* e *model deployment*.
Sfortunatamente i dati disponibili per questa analisi sono molto limitati (è stato possibile ricavare la fase solo per quaranta *fix*), per cui non è stato possibile effettuare delle analisi più approfondite.
- essere lavori originali, per cui sono stati esclusi tutti i fork.
- avere almeno cento issues chiuse.
- avere almeno dieci contributors.
- avere almeno venticinque fork.
## RQ3: esiste una differenza di entropy tra ML bug e altri bug? {#sec:rq3}
Alla fine di questa prima selezione il numero di repository si è ridotto a sessantasei e sono stati analizzati manualmente per rimuovere listati associati a libri e/o tutorial, progetti non in lingua inglese e librerie.
Alla fine di questa seconda fase il numero di progetti è sceso a trenta.
La successiva analisi avevo lo scopo di verificare l'esistenza di una differenza tra l'entropia del *fix* rispetto alla natura di questi.
L'analisi è stata svolta sia a livello di file, sia a livello di linee, quindi per ogni commit del dataset è stato necessario individuare sia il numero di file che hanno subito delle modifiche, sia il numero di linee alterate, considerando in questo modo sia le aggiunte che le rimozioni.
## Fetch di issues e commit
Inoltre per poter valutare l'entità del cambiamento è stato necessario conoscere anche il numero totale di file e di linee di ogni progetto.
Questi valori sono stati calcolati attraverso la storia `git` del branch `master`[^branch-master].
Per ogni commit sono stati individuati i file aggiunti ($+1$) e rimossi ($-1$) in modo tale da poter calcolare il delta-cambiamento del commit.
Eseguendo la somma di questo delta su tutti i commit si è ottenuto il numero totale di file del progetto.
In modo analogo si è proceduto anche per quanto riguarda le linee.
Una volta individuati i progetti da analizzare si è reso necessario recuperare l'intera storia dei progetti e le issues ad essi associate.
Per entrambe le operazioni è stato utilizzato il tool *perceval*[@duenas2018percevalsoftwareproject].
Nel caso delle issues, essendo queste informazioni non direttamente contenute all'interno del repository `git`, è stato necessario utilizzare nuovamente l'\ac{API} di GitHub.
Poiché le chiamate associate ad un singolo *token* sono limitate nel tempo si è scelto di configurare *perseval* in modo tale da introdurre in automatico uno ritardo ogni qualvolta veniva raggiunto il limite.
Inoltre il codice è stato dispiegato su un \ac{VPS} in modo da poter eseguire il fetch senza che fosse necessario mantenere attiva una macchina fisica.
[^branch-master]: Oltre al branch `master` è stato considerato anche il branch `main` diventato molto comune dopo le proteste del movimento Black Lives Matter e il branch `master-V2` unico branch utilizzato da un progetto.
Con il processo precedentemente illustrato è stato possibile recuperare:
Una volta note queste informazioni preliminari è stato possibile calcolare l'entropia dei *fix* che è stata riportata nei boxplot[^boxplot-entropy] in @fig:entropy.
Dal boxplot in @fig:files-entropy è possibile notare una distribuzione equivalente per le due tipologie di fix.
Una situazione analoga si riscontra anche nell'analisi sulle linee (@fig:lines-entropy) anche se in questo caso è possibile notare che i valori di entropia associati ai fix di \ac{ML} sono shiftati leggermente verso l'alto.
- $34180$ commit.
- $15267$ tra issues e pull request.
[^boxplot-entropy]: Per ragioni di visualizzazione è stato scelto il $95$-$esimo$ quantile come limite superiore di entrambi i grafici.
## Classificazione dei dati
### Classificazione delle issues {#sec:classificazione-issues}
Al fine di poter eseguire un confronto tra i *fix* di \ac{ML} e quelli *generici* è stato necessario classificare sia le issues che i commit.
Per quanto riguarda i primi si è scelto di attuare una classificazione basata sul testo, in particolare considerando il titolo e il corpo della issue, ma escludendo i commenti di risposta in modo da non rendere i dati troppo rumorosi.
Il numero elevato di elementi non rende praticabile una classificazione manuale per cui si è optato per una classificazione automatica.
A tal fine sono stati implementati ed analizzati due classificatori, uno supervisionato e uno non supervisionato.
I due modelli considerati sono:
- un classificatore statico basato su una lista di vocaboli tipici del \ac{ML}.
- un modello *naïve Bayes* [@2021naivebayesclassifier; @harrington2012machinelearningaction].
La classificazione mediate il classificatore statico non necessita di un *labeling* manuale dei dati, ma richiede la definizione dei vocaboli tipici del \ac{ML}.
Lista dei termini caratteristici del \acl{ML} non è stata costruita da zero, ma è basata su lavori precedenti[@humbatova-2019-taxonomyrealfaults].
In questo modo tutte le issues che utilizzavano almeno un vocabolo tipico del \acl{ML} sono state classificate come issues di \ac{ML}.
Nel caso del modello *naïve Bayes*, essendo questo un algoritmo di apprendimento supervisionato, si è resa necessaria una classificazione manuale delle issues.
A tal scopo è stato eseguito un campionamento stratificato in base al progetto di provenienza di $376$ issues che sono state divise tra due lettori e labellate.
Durante il labeling si scelto di classificare ulteriormente le issue di \ac{ML} al fine di individuare anche la fase in cui il problema si è palesato.
La definizioni delle varie fasi è avvenuta partendo da un lavoro di *Microsoft*[@amershi-2019-softwareengineeringmachine].
Le fasi considerate sono:
- *Model Requirements*: questa fase comprende tutte le discussioni rispetto all'individuazione del modello più adatto, le funzionalità che questo deve esporre e come adattare un modello esistente per eseguire una diversa funzionalità.
- *Data Collection*: comprende le operazioni volte alla definizione di un dataset.
Rientrano in questa fase sia la ricerca di dataset già esistenti che la costruzione di nuovi dataset.
- *Data Labeling*: questa fase si rende necessaria ogni qual volta si opera con modelli basati su apprendimento supervisionato.
- *Data cleaning*: in questa fase non rientrano soltanto le operazioni strettamente di pulizia dei dati come ad esempio rimozione di record rumorosi o incompleti, ma tutte le trasformazioni eseguite sui dati, quindi anche operazioni di standardizzazione, flip di immagini ecc.
- *Feature Engineering*: questa fase serve per identificare le trasformazioni da attuare sui dati e le migliori configurazioni degli *hyperparametri* al fine di migliorare il modello.
- *Model Training*: questa fase racchiude il training vero e proprio del modello.
- *Model Evaluation*: in questa fase vengono valutate le performance del modello utilizzando metriche standard come *precision* e *recall*, ma anche andando a confrontare i risultati ottenuti rispetto a quelli generati da altri modelli o rispetto all'esperienza[^esperienza].
- *Model Deployment*: questa fase riguarda il dispiegamento del modello sul dispositivo target.
- *Model Monitoring*: una volta dispiegato il modello deve essere continuamente monitora al fini di assicurasi un corretto comportamento anche sui dati reali.
[^esperienza]: Non sempre è possibile valutare un modello in modo oggettivo, ci sono determinati contesti, come ad esempio la generazione di *deep fakes*, in cui è comunque necessaria una valutazione umana per determinare la qualità del risultato.
A partire dal dataset *labellato* è stato possibile costruire un training e un test set, mediante i quali è stato possibile allenare e valutare le performance del modello bayesiano.
Mentre le performance del primo modello sono state valutate sull'intero dataset.
\begin{figure}[!ht]
\subfloat[Entropia calcolata sui files\label{fig:files-entropy}]{%
\includegraphics[width=0.45\textwidth]{src/figures/files-entropy.pdf}
\subfloat[Numero di issues rispetto al tipo\label{fig:labeling-type}]{%
\includegraphics[width=0.45\textwidth]{src/figures/count-type.pdf}
}
\hfill
\subfloat[Entropia calcolata sulle linee\label{fig:lines-entropy}]{%
\includegraphics[width=0.45\textwidth]{src/figures/lines-entropy.pdf}
\subfloat[Numero di issues rispetto alla fase\label{fig:labeling-phases}]{%
\includegraphics[width=0.45\textwidth]{src/figures/count-phases.pdf}
}
\caption{Entropia in base al tipo di fix}
\label{fig:entropy}
\caption{Risultati della classificazione manuale delle issues}
\label{fig:labeling}
\end{figure}
Per verificare la rilevanza statistica di questa diversità sono stati svolti il *ranksum* test e il *Cliff's delta* i cui risultati sono riportati nella @tbl:test-entropy.
Nel caso dell'entropia sui file possiamo dire che la differenza è marginale poiché il *p-value* è prossimo a $0.05$, mentre nel caso dell'entropia calcolato sulle linee la differenza viene confermata dal test.
In entrambi i casi, l'*effect size* è trascurabile.
Al fine di poter confrontare i due modelli sono state utilizzate le metriche di precision e recall.
Com'è possibile notare dai valori riportati in @tbl:confronto-modelli-classificazione-issues, il modello basato sulla lista di vocaboli è leggermente più preciso del modello bayesiano, ma presenta una recall decisamente più bassa.
Dalla @fig:labeling-type si evince la natura minoritaria delle issues di \ac{ML} rispetto alle issues generiche, per questo motivo si è scelto di preferire il modello naïve Bayes in modo da perdere quante meno istanze possibili anche a costo di sacrificare leggermente la precisione.
| | ranksum p-values | Cliff's delta |
|--------------|:----------------:|:-------------:|
| file entropy | 0.059 | 0.044 |
| line entropy | 5.932e-06 | 0.105 |
| | Classificatore statico | naïve Bayes |
|-----------|------------------------|-------------|
| precision | 0.46 | 0.41 |
| recall | 0.74 | 0.94 |
: Risultati dei test statistici per quanto riguarda l'entropia {#tbl:test-entropy}
: Confronto dei due modelli per la classificazione delle issues. {#tbl:confronto-modelli-classificazione-issues}
## RQ4: come varia il livello di discussione tra ML bug e altri bug? {#sec:rq4}
### Classificazione dei commit {#sec:classificazione-commit}
Per rispondere a questa domanda è stato necessario andare a valutare il numero di commenti presenti all'interno di ogni issues.
Poiché un singolo commit può far riferimento a più issues è stato considerato il numero di commenti medi.
I risultati ottenuti sono stati riportati nel boxplot[^boxplot-discussion] in @fig:discussion-comments.
Prima di poter classificare i commit si è reso necessaria un'ulteriore fase di filtraggio in modo da poter separare i commit di *issue fixing* da quelli generici.
Sono stati considerati come commit di *fix* tutti quei commit al cui interno veniva fatto riferimento a delle issues attraverso la notazione *"#"*.
Questa operazione ha ridotto il dataset dei commit a $3321$ unità la cui distribuzione in base al tipo è riportata in @fig:count-commit.
In questo caso si evince una differenza molto più marcata tra le due distribuzioni.
In particolare è possibile notare che le *issue fix* di \ac{ML} presentano una maggiore discussione e anche una maggiore varianza.
Se consideriamo la differenza interquartile, in modo da escludere completamente eventuali outlier, possiamo osservare che nei *fix* generici questa varia tra zero e uno.
Ciò vuol dire che il $50\%$ interno delle issues o non presenta commenti o ne presenta uno solo.
Mentre la differenza interquartile dei *fix* di \acl{ML} è compreso tra uno e cinque quindi nel $50\%$ interno tutte le issues hanno almeno un commento di risposta.
A questo punto è stato possibile separare i *fix* di \acl{ML} da quelli generici.
La classificazione è avvenuta attraverso la lista delle issues citate all'interno del *commit message* e sono stati considerati come commit di \ac{ML} tutti quei commit che facevano riferimento ad almeno una issue di \ac{ML}.
[^boxplot-discussion]: In questo caso il limite superiore è pari al $97$-$esimo$ quantile.
\newpage
\begin{figure}[!ht]
\subfloat[Numero di commenti medi\label{fig:discussion-comments}]{%
\includegraphics[width=0.45\textwidth]{src/figures/comments.pdf}
}
\hfill
\subfloat[Numero di parole medie per commento\label{fig:discussion-words}]{%
\includegraphics[width=0.45\textwidth]{src/figures/words.pdf}
}
\caption{Livello di discussione in base al tipo}
\label{fig:discussion}
\end{figure}
A questo punto si è cercato di capire se al maggior numero di commenti è associata effettivamente una maggiore quantità di informazioni scambiate.
Per svolgere questa analisi si è partiti dal presupposto che la quantità di informazioni scambiate sia proporzionale al numero di parole utilizzate nel commento.
Quindi per ogni *issue* è stato calcolato il numero medio di parole presenti all'interno di un commento.
I risultati di quest'ulteriore analisi sono riportati in @fig:discussion-words.
Anche in questo caso si può vedere che nel caso di \ac{ML} *fix* la distribuzione presenta valori più elevati e maggiore varianza.
Per cui non solo nei *fix* di \acl{ML} c'è maggiore discussione, ma la discussione è anche più *densa*.
Anche in questo caso sono stati svolti i test statistici.
In @tbl:test-discussion è possibile vedere come per entrambe le metriche considerate il *p-value* sia abbondantemente inferiore alla soglia di $0.05$ quindi abbiamo una conferma della diversità delle due distribuzioni riscontrata dal boxplot.
Inoltre, per entrambe le metriche, abbiamo un *effect size* medio.
| | ranksum p-values | Cliff's delta |
|---------------------|:----------------:|:-------------:|
| commenti medi | 9.053e-75 | 0.425 |
| parole per commento | 2.889e-59 | 0.377 |
: Risultati dei test statistici per quanto riguarda il livello di discussione {#tbl:test-discussion}
## RQ5: come varia il time-to-fix tra ML bug e altri bug? {#sec:rq5}
In quest'ultima analisi si vuole andare a valutare se c'è differenza nel tempo necessario per eseguire il *fix*.
Per valutare questo parametro è stato necessario estrarre da ogni *issue* la data di apertura e di chiusura e calcolare i giorni che intercorrono tra queste.
I risultati così ottenuti sono stati riportati in @fig:day-to-fix.
![Giorni necessari per il fix](figures/day-to-fix.pdf){#fig:day-to-fix width=70%}
Anche in questo caso è possibile notare una netta differenza tra i *fix* di \ac{ML} e gli altri.
In particolare i bug di \acl{ML} necessitano, mediamente, di maggior tempo per essere risolti e sono caratterizzati da una varianza maggiore.
Inoltre è possibile vedere come la mediana non sia centrata, bensì spostata verso il basso.
Questo vuol dire che il $50\%$ basso dei *bug* di \ac{ML} viene comunque risolto in tempi brevi (due giorni circa), mentre l'altro $50\%$ può richiedere una quantità di tempo decisamente superiore.
Un'ulteriore testimonianza del maggior tempo necessario per risolvere le problematiche legate al \ac{ML} ci viene data dagli outlier.
Nel caso di un problema generico, questo, viene considerato come *anomalo* se per essere risolto necessita di un tempo superiore ai cinque giorni.
Mentre nel caso dei *fix* di \acl{ML} per essere considerato outlier un *issue*, necessaria di un *time-to-fix* superiore ai trentacinque giorni.
Il maggior tempo necessario ad attuare la correzione indica che i *bug* di \ac{ML} sono più difficili da individuare e correggere rispetto a quelli generici.
Inoltre questo risultato contribuisce a spiegare il dato emerso dalla sezione precedente, in quanto per individuare la fonte del problema sembrerebbe essere necessaria una discussione più approfondita.
Anche per quest'ultima *RQ* sono stati svolti i test statistici illustrati precedentemente.
Dai risultati riportati in @tbl:test-time-to-fix è possibile notare un *p-value* inferiore a $0.05$ e un *effect size* medio.
| | ranksum p-values | Cliff's delta |
|------------|:----------------:|:-------------:|
| day-to-fix | 7.354e-53 | 0.355 |
: Risultati dei test statistici per quanto riguarda il time-to-fix {#tbl:test-time-to-fix}
![Risultato della classificazione dei commit](figures/count-commit.pdf){#fig:count-commit width=80%}