Garantire la sicurezza della memoria nelle reti neurali
Questo rapporto parla di metodi per migliorare la sicurezza della memoria nelle reti neurali AI.
― 16 leggere min
Indice
- Reti Neurali in NeuroCodeBench
- Panoramica delle Architetture Neurali in NeuroCodeBench
- Proprietà di Sicurezza
- Modelli di Linguaggio Generativi
- Verifica del Software
- Creazione di un Dataset di Codice AI
- Risultati Empirici
- Lezioni Apprese
- Riparazione del Codice AI con Modelli di Linguaggio di Grandi Dimensioni
- Modelli di Prompt
- Esperienze Precedenti con ESBMC-AI
- Prompt Semplici
- Prompt Persona
- Limitazioni del Codice Sorgente
- Feedback dal Verificatore
- Combinazioni di Prompt
- Confronto dei Modelli
- Sintassi delle Patch LLM
- Rilevanza delle Patch LLM
- Compilazione del Codice Riparato
- Verifica del Codice Riparato
- Confronto dei Singoli Prompt
- Risultato Migliore del Prompt
- Ulteriore Ottimizzazione dei Migliori Prompt
- Lezioni Apprese dalla Riparazione Iterativa Automatica dei Programmi
- Conclusioni e Lavori Futuri
- Testare l'Efficacia dell'Utilizzo dei Backtick
- Risultati del Detector C/C++
- Risultati di Rilevanza
- Risultati di Compilazione
- Verifica dei Risultati del Codice
- Dettagli sull'Esperimento di Riparazione Iterativa
- Verifiche Riuscite per Prompt per Temperatura
- Verifiche Riuscite per Tentativo per Temperatura
- Fonte originale
- Link di riferimento
La nuova generazione di sistemi di intelligenza artificiale deve essere sicura. Questo rapporto esamina come vengono costruite le reti neurali e si concentra su problemi di sicurezza della memoria come errori di puntatore NULL, accessi fuori dai limiti, errori di doppia liberazione e perdite di memoria. L’obiettivo è individuare questi problemi e risolverli automaticamente utilizzando modelli di linguaggio di grandi dimensioni (LLM). Per raggiungere questo obiettivo, iniziamo aumentando la dimensione di NeuroCodeBench, un dataset di codice di reti neurali, a circa k programmi attraverso modifiche automatiche del codice. Poi, controlliamo la sicurezza della memoria di questi codici di rete neurale modificati utilizzando ESBMC, un moderno verificatore di software. Quando ESBMC rileva un problema, utilizziamo un modello di linguaggio di grandi dimensioni per riparare il codice. Confronteremo anche diversi modi di chiedere al modello di migliorare le sue prestazioni.
A differenza dello sviluppo software tradizionale, creare reti neurali comporta molti tentativi ed errori finché il modello non funziona bene. Questo metodo rende le reti neurali soggette a varie debolezze, come scarse prestazioni con dati non familiari, progettazione errata del modello, utilizzo di librerie rotte e bug nel codice. Purtroppo, molte di queste debolezze non sono facili da identificare fino a dopo che il sistema è in uso.
Sebbene ci siano modi per fare debug delle reti neurali, si basano principalmente su test automatici, che non possono dimostrare che il codice funzioni per ogni input. Questa mancanza di prova è preoccupante nei sistemi critici, poiché problemi comuni come errori aritmetici o accessi a memoria non validi possono portare a risultati errati, rivelare dati sensibili o danneggiare il sistema stesso.
Affrontiamo la sfida di creare implementazioni di reti neurali senza errori in diversi modi. Prima di tutto, utilizziamo verificatori di software per coprire tutti i casi possibili. È stato detto in precedenza che i verificatori hanno difficoltà con il codice delle reti neurali a causa delle sue dimensioni e complessità e di molte chiamate a librerie matematiche. Per vedere se ciò è vero, creiamo un ampio dataset di codice di reti neurali con problemi di memoria. I nostri risultati suggeriscono che i verificatori possono effettivamente trovare problemi di sicurezza della memoria nel codice delle reti neurali abbastanza facilmente, permettendoci di usarli per controllare la correttezza delle implementazioni delle reti neurali.
In secondo luogo, utilizziamo modelli di linguaggio di grandi dimensioni come strumenti efficaci per la Riparazione del codice. Negli ultimi anni, questi modelli hanno dimostrato di avere potenzialità in vari compiti legati al codice, inclusa la traduzione del codice, il completamento del codice e la riparazione automatica del codice. Tuttavia, notiamo che studi precedenti hanno mostrato che i modelli funzionano peggio su dati che differiscono da quelli su cui sono stati addestrati. I nostri risultati indicano che il codice utilizzato nelle reti neurali rientra in questa categoria ed è un ottimo caso di test per questa diminuzione delle prestazioni. I nostri risultati mostrano che i modelli di linguaggio di grandi dimensioni standard possono correggere il codice AI, ma richiedono tecniche di prompting specifiche per farlo in modo efficace.
Nel dettaglio, questo rapporto copre i seguenti argomenti:
- Il nostro metodo per generare un grande dataset di esempi di codice AI attraverso aumenti automatici del dataset NeuroCodeBench utilizzando tecniche di dati specifiche per il codice.
- I risultati dell'esecuzione dei verificatori di software ESBMC su questo dataset ampliato per identificare benchmark con problemi di sicurezza della memoria.
- Un confronto di varie tecniche per migliorare le prestazioni di riparazione dei modelli di linguaggio di grandi dimensioni. Suggeriamo anche soluzioni per sfide come uno spazio di input limitato, formattare correttamente il codice e integrare il feedback di compilatori e verificatori di software.
- Risultati iniziali su quanto bene i modelli di linguaggio di grandi dimensioni si comportano nella riparazione del codice AI. Esaminiamo il loro output lungo quattro aspetti: sintassi corretta, rilevanza rispetto al compito, se il codice si compila e riparazioni riuscite.
- L'effetto della storia di input sul processo di riparazione iterativa del codice.
La struttura di questo rapporto è la seguente: nella Sezione 1, forniamo informazioni di base su NeuroCodeBench, problemi comuni di memoria nelle reti neurali e le tecniche che utilizziamo per mutare i programmi. Nella Sezione 2, spieghiamo il nostro approccio per creare un dataset di codice AI e condividiamo i nostri risultati dall'uso di ESBMC per etichettare il dataset. Nella Sezione 3, presentiamo una varietà di tecniche di prompting avanzate, descriviamo come usarle per la riparazione del codice e confrontiamo la loro efficacia con ChatGPT.
Questo documento funge da record ufficiale per il repository di software pubblico su https://github.com/emanino/plain_c_nn_benchmark, e un repository di staging è disponibile su https://github.com/Yiannis128/plain_c_nn_benchmark.
Reti Neurali in NeuroCodeBench
NeuroCodeBench è un benchmark in C semplice per testare implementazioni di reti neurali progettato per la verifica del software. Rispetto a librerie di machine learning mainstream come PyTorch e TensorFlow, che hanno strutture complicate e sono facilmente testabili ma difficili da verificare automaticamente, NeuroCodeBench utilizza framework per micro-controllori. Qui, il codice sorgente della rete è completamente disponibile. Utilizza due strumenti esistenti che convertono specifiche di reti neurali ad alto livello in codice C autonomo: onnx2c e keras2c. Da novembre 2023, NeuroCodeBench è parte del benchmark ufficiale per la competizione internazionale di verifica del software (SV-COMP).
Panoramica delle Architetture Neurali in NeuroCodeBench
Queste architetture affrontano vari compiti di machine learning e ingegneria, come dettagliato di seguito:
- Reti di Hopfield: Reti neurali ricorrenti possono funzionare come decoder a correzione d'errore. L'idea principale è codificare una sequenza di bit in un vettore e lasciare che la rete corregga eventuali errori. Utilizziamo specificamente reti di Hopfield con pesi hebbiani.
- Reti di Approssimazione Polinomiale: Le reti neurali possono modellare le funzioni di trasferimento dei componenti elettrici. Simuliamo questo processo utilizzando un componente polinomiale ipotetico con comportamento oscillatorio.
- Reti VNN-COMP: La Competizione Internazionale di Verifica delle Reti Neurali (VNN-COMP) ha pubblicato benchmark che non includono dettagli di implementazione, concentrandosi invece su un livello di astrazione più alto. Traduciamo le reti dal formato ONNX a C con onnx2c per fornire implementazioni di riferimento.
Proprietà di Sicurezza
Inizialmente, NeuroCodeBench mirava a testare i verificatori di software con problemi di verifica impegnativi legati alle proprietà di raggiungibilità. In contrasto, il nostro dataset di codice AI testa quanto bene i modelli di linguaggio di grandi dimensioni possono riparare il codice. Poiché la maggior parte dei bug del mondo reale deriva da errori di programmazione, ci concentriamo sui problemi di sicurezza della memoria, la principale causa delle vulnerabilità del software. Qui, descriviamo brevemente diverse proprietà di sicurezza:
- Raggiungibilità: Per le reti neurali, queste proprietà definiscono le condizioni necessarie su insiemi di input e output. Ad esempio, una rete di Hopfield può ricostruire un codice in presenza di rumore se vengono soddisfatte certe condizioni.
- Sicurezza della Memoria: Questo implica controllare vulnerabilità come dereferenze di puntatore NULL, accessi fuori dai limiti, errori di doppia liberazione e perdite di memoria. Istruiremo ESBMC a controllare che tutte le operazioni di memoria siano valide e tracciate accuratamente.
Modelli di Linguaggio Generativi
L'IA è stata utilizzata per riparare automaticamente il codice. Recentemente, i modelli di linguaggio di grandi dimensioni sono diventati prominenti. Tuttavia, il codice che generano può ancora contenere debolezze. Gli LLM sono un tipo di rete neurale ricorrente che impiega una struttura encoder/decoder, spesso con un meccanismo di attenzione. A differenza dei metodi tradizionali che lavorano con sequenze di stati nascosti, gli LLM possono essere altamente parallelizzati, accelerando l'addestramento. Un progresso significativo è stato il GPT-3.5-Turbo di OpenAI, un modello closed-source con 175 miliardi di parametri, che utilizziamo per i nostri esperimenti.
Verifica del Software
Ci sono vari metodi per verificare il software, ma questo rapporto impiega il Bounded Model Checking (BMC) per trovare vulnerabilità di memoria. BMC controlla se una certa proprietà vale a una certa profondità in un sistema. Srotola il sistema e lo traduce in una condizione di verifica per valutarne la verità. ESBMC, che utilizza BMC, testa campioni di codice C AI per vulnerabilità.
Creazione di un Dataset di Codice AI
Per addestrare e valutare quanto bene i modelli di linguaggio di grandi dimensioni riparano il codice AI, abbiamo bisogno di un dataset di implementazioni di reti neurali. I dataset esistenti sono piccoli, contenendo solo poche centinaia di campioni. Pertanto, utilizziamo tecniche di aumento dei dati per espandere notevolmente il dataset, puntando a metriche di valutazione significative.
Mutazione del Programma
Per questo compito, utilizziamo Mull, uno strumento di testing di mutazione per C e C++. Mull ha due componenti principali: un plugin per il compilatore e Mull Runner. Il plugin genera mutazioni di codice durante la compilazione, iniettandole nel bitcode LLVM. Ogni mutazione altera il comportamento del programma ma è nascosta dietro flag condizionali che consentono di attivarle a runtime.
Mull Runner esegue il programma più volte con diverse mutazioni attivate per vedere come queste modifiche influenzano l'esecuzione. Ogni mutazione può essere salvata in un file patch.
Aumento dei Dati
Per aumentare il dataset di NeuroCodeBench, automatizziamo la sua generazione il più possibile. Data la grande dimensione richiesta, creare k campioni manualmente è impraticabile. Il pipeline è composto da tre fasi:
- Costruzione dei Campioni di Base: Seguiamo le istruzioni di NeuroCodeBench per creare il dataset iniziale, che contiene vari tipi di reti.
- Generazione di Patch di Mutazione: Modifichiamo il dataset di base utilizzando una suite di test di mutazione per creare variazioni di codice.
- Classificazione del Nuovo Dataset: Dopo che il dataset è stato espanso, utilizziamo ESBMC per verificare la sicurezza di ciascun campione.
Risultati Empirici
Questa sezione discute l'impostazione sperimentale e i risultati del processamento del dataset ampliato di NeuroCodeBench con ESBMC.
Risultati di Classificazione
Presentiamo classificazioni basate sui risultati di ESBMC per ciascun campione in varie categorie. Le diverse categorie mostrano variazioni nel numero di casi sicuri e non sicuri. Le reti di Hopfield tendono ad avere meno casi sicuri, probabilmente a causa della loro struttura complessa. Al contrario, le reti di approssimazione polinomiale e di rinforzo mostrano una proporzione più alta di istanze sicure e non sicure.
Tempo di Verifica
Analizziamo il tempo di verifica per un sottoinsieme di programmi sicuri e non sicuri. I nostri risultati indicano che le reti di Hopfield e le reti di approssimazione polinomiale generalmente verificano più velocemente rispetto alle altre. Tuttavia, sono necessari più dati per giungere a conclusioni chiare. Sebbene ESBMC non sia riuscito a classificare alcuni campioni, un buon numero è stato elaborato con successo in pochi minuti.
Lezioni Apprese
In generale, ESBMC verifica il codice delle reti neurali per vulnerabilità di sicurezza della memoria in modo piuttosto efficace. Questo sfida le precedenti affermazioni sulla sua difficoltà con il codice delle reti neurali. Tuttavia, il numero notevole di verdetti incerti e i lunghi tempi di verifica suggeriscono che c'è margine di miglioramento. Le strategie future potrebbero combinare strumenti di falsificazione con strumenti di verifica.
Riparazione del Codice AI con Modelli di Linguaggio di Grandi Dimensioni
Il dataset di codice AI creato in precedenza è un esempio rappresentativo di dati non familiari. Poiché il NeuroCodeBench originale è stato rilasciato di recente, probabilmente non era incluso nel set di addestramento della maggior parte dei modelli di linguaggio di grandi dimensioni avanzati. Inoltre, il nostro metodo di mutazione automatizzato garantisce che questo nuovo dataset differisca dal software tipico su cui i modelli esistenti sono stati addestrati.
In questo contesto, esploriamo se i modelli di linguaggio di grandi dimensioni possono identificare le vulnerabilità di memoria introdotte e riparare il codice.
Modelli di Prompt
Nella comunità del processamento del linguaggio naturale, si sa che le prestazioni dei modelli di linguaggio di grandi dimensioni dipendono fortemente da come vengono sollecitati. Questo ha portato a un focus su tecniche di ingegneria dei prompt, che a volte rendono superfluo il fine-tuning. Elenchiamo alcune strategie di prompt efficaci rilevanti per la generazione di codice. Il nostro obiettivo è confrontare la loro prestazione pratica.
Esperienze Precedenti con ESBMC-AI
Lavori precedenti hanno mostrato buoni risultati sollecitando modelli di linguaggio di grandi dimensioni a correggere codice vulnerabile. Includiamo il loro modello di prompt nella nostra valutazione per stabilire un baseline. Ecco una versione semplificata di quel modello:
Sei un generatore di codice sicuro che analizza codice sorgente vulnerabile e output da un programma chiamato ESBMC. Dovresti usare i risultati di ESBMC per individuare il problema e correggere il codice sorgente. Da questo punto, rispondi solo con il codice corretto.
Prompt Semplici
Il precedente modello di prompt lungo impone un ordine specifico alle informazioni fornite. Per confrontare, consideriamo anche prompt più brevi che variano l'ordine del codice e dei messaggi di errore. Includiamo diversi paia di prompt che contengono disposizioni diverse delle informazioni.
Prompt Persona
Studi recenti mostrano che i modelli di linguaggio di grandi dimensioni si comportano meglio quando vengono assegnati ruoli specifici. Questo metodo, noto come prompting per persona, può ottimizzare i risultati. Ci chiediamo se il ruolo assegnato influisca sulle prestazioni e forniamo diversi ruoli per il confronto.
Sei un ruolo. Ti verrà mostrato codice C. Riparalo e mostra il codice. Il codice è sorgente.
Limitazioni del Codice Sorgente
I modelli di linguaggio di grandi dimensioni elaborano solo una lunghezza di input limitata. Ad esempio, ChatGPT può gestire al massimo K token, il che significa che spesso è impossibile fornire grandi parti di codice. Utilizziamo due strategie per affrontare questa limitazione.
- Contestuale: Molti verificatori di software includono non solo la presenza di una vulnerabilità, ma anche la linea che l'ha causata. Dunque, selezioniamo una finestra di codice attorno alla vulnerabilità segnalata per adattarla allo spazio disponibile.
- Una Linea: Per problemi di memoria specifici, potrebbe essere sufficiente alterare la stessa riga di codice che ha causato l'errore. Eseguiamo più test in cui il modello di linguaggio di grandi dimensioni vede solo una riga.
Feedback dal Verificatore
La maggior parte dei verificatori di software fornisce un rapporto di bug che include una traccia dettagliata dello stato di errore. Investigiamo se queste informazioni giovano al modello di linguaggio di grandi dimensioni.
Combinazioni di Prompt
Riepiloghiamo le combinazioni di prompt utilizzate negli esperimenti. Ogni combinazione mira a valutare l'efficacia del modello nella risoluzione dei problemi di memoria.
Confronto dei Modelli
Confrontiamo le prestazioni di diversi modelli di prompt relativi al feedback del verificatore e alla probabilità di produrre riparazioni valide.
Sintassi delle Patch LLM
Vogliamo confermare che il modello di linguaggio di grandi dimensioni produca codice C genuino come output. Utilizziamo un detector di codice automatizzato per valutare l'output.
Rilevanza delle Patch LLM
Ci aspettiamo che poiché le vulnerabilità sono piccole modifiche di codice, le patch efficaci assomigliano da vicino al codice di input, tranne che per la riga corretta. Misuriamo la sovrapposizione dei caratteri tra input e codice di output.
Compilazione del Codice Riparato
Sebbene chiediamo al modello di linguaggio di grandi dimensioni di fornire codice C valido, non c'è garanzia che ci riesca. Quindi, controlliamo se le patch riparate si compilano correttamente.
Verifica del Codice Riparato
Infine, testiamo se il codice riparato supera i controlli per la sicurezza della memoria. Eseguiamo il codice attraverso ESBMC per contare quanti programmi ottengono una verifica di successo.
Confronto dei Singoli Prompt
Confrontiamo ora tutti i prompt eseguiti negli esperimenti, evidenziando i migliori risultati tanto per gli approcci specifici per il contesto quanto per quelli a una riga.
Risultato Migliore del Prompt
I migliori risultati sono stati ottenuti utilizzando il secondo modello di prompt con il ruolo di "Strumento di Riparazione del Codice Automatico".
Ulteriore Ottimizzazione dei Migliori Prompt
Possiamo affinare i prompt per migliorare i risultati. Ci concentriamo sui migliori due prompt e analizziamo il ruolo della formattazione e dell'elaborazione delle istruzioni per migliorare le prestazioni.
Lezioni Apprese dalla Riparazione Iterativa Automatica dei Programmi
La strategia di riparazione iterativa consente al modello di linguaggio di grandi dimensioni diverse opportunità di risolvere i problemi. I nostri esperimenti indicano che se le riparazioni non iterative hanno raggiunto un tasso di successo del 18%, l'approccio iterativo ha elevato questa percentuale al 25%.
Conclusioni e Lavori Futuri
In sintesi, abbiamo ampliato NeuroCodeBench per creare un dataset più ampio di codice C AI vulnerabile alla memoria attraverso mutazioni. Abbiamo classificato questi campioni utilizzando ESBMC per convalidare quali contenessero problemi di memoria.
Utilizzando GPT-3.5-Turbo, abbiamo esplorato varie strategie di prompt per correggere il codice modificato. Abbiamo scoperto che i modelli di prompt lunghi, in particolare il ruolo di Strumento di Riparazione del Codice Automatico, si sono dimostrati i più efficaci. Inoltre, abbiamo sviluppato strategie per aiutare a estrarre il codice problematico da grandi dataset per bypassare le limitazioni delle finestre di contesto dei modelli. Abbiamo anche testato il processo iterativo delle riparazioni automatiche dei programmi, il che ha portato a un miglioramento del 7% nei tassi di successo.
Nel lavoro futuro, prevediamo di condurre ulteriori test con modelli di linguaggio di grandi dimensioni variabili per verificare se i nostri risultati siano validi oltre GPT-3.5-Turbo. I modelli open-source potrebbero particolarmente coadiuvare la nostra ricerca poiché sono personalizzabili per compiti di riparazione dei programmi.
Testare l'Efficacia dell'Utilizzo dei Backtick
Per analizzare quanto sia efficace utilizzare backtick attorno al codice sorgente, ripeteremo gli esperimenti, variandoli per escludere i backtick. Questo ci permetterà di determinare se la loro presenza influisce sull'accuratezza delle riparazioni del codice.
Risultati del Detector C/C++
Esamineremo la distribuzione dei punteggi assegnati dal detector di codice per confrontare i risultati degli esperimenti che utilizzano backtick rispetto a quelli senza.
Risultati di Rilevanza
Successivamente, analizzeremo quanto siano simili le patch create dal modello al codice originale.
Risultati di Compilazione
Osserveremo anche quanto spesso le patch prodotte dal modello si compilano con successo in diversi setup sperimentali.
Verifica dei Risultati del Codice
Infine, valuteremo i tassi di successo della verifica di diversi prompt e come si confrontano quando utilizzano o saltano i backtick.
Dettagli sull'Esperimento di Riparazione Iterativa
Indagheremo i tassi di verifica riuscita per i prompt a varie impostazioni di temperatura, comprendendo come la temperatura influenzi le prestazioni nei diversi tentativi, focalizzandoci specificamente sulla Storia in Avanti.
Verifiche Riuscite per Prompt per Temperatura
Analizzeremo l'efficacia dei prompt a diversi livelli di temperatura per determinare come varia la capacità del modello di riparare il codice.
Verifiche Riuscite per Tentativo per Temperatura
Infine, riassumeremo come i tassi di successo cambiano con ciascun prompt attraverso le variazioni di temperatura, fornendo indicazioni sulle migliori condizioni per riparazioni riuscite.
Titolo: Automated Repair of AI Code with Large Language Models and Formal Verification
Estratto: The next generation of AI systems requires strong safety guarantees. This report looks at the software implementation of neural networks and related memory safety properties, including NULL pointer deference, out-of-bound access, double-free, and memory leaks. Our goal is to detect these vulnerabilities, and automatically repair them with the help of large language models. To this end, we first expand the size of NeuroCodeBench, an existing dataset of neural network code, to about 81k programs via an automated process of program mutation. Then, we verify the memory safety of the mutated neural network implementations with ESBMC, a state-of-the-art software verifier. Whenever ESBMC spots a vulnerability, we invoke a large language model to repair the source code. For the latest task, we compare the performance of various state-of-the-art prompt engineering techniques, and an iterative approach that repeatedly calls the large language model.
Autori: Yiannis Charalambous, Edoardo Manino, Lucas C. Cordeiro
Ultimo aggiornamento: 2024-05-14 00:00:00
Lingua: English
URL di origine: https://arxiv.org/abs/2405.08848
Fonte PDF: https://arxiv.org/pdf/2405.08848
Licenza: https://creativecommons.org/licenses/by/4.0/
Modifiche: Questa sintesi è stata creata con l'assistenza di AI e potrebbe presentare delle imprecisioni. Per informazioni accurate, consultare i documenti originali collegati qui.
Si ringrazia arxiv per l'utilizzo della sua interoperabilità ad accesso aperto.