Dormire tranquilli.

Dormire tranquilli.

Quando scrivo bene di ChatGPT e dell’intelligenza artificiale puntualmente ricevo commenti secondo i quali i primi a perdere il posto di lavoro saranno proprio quelli come me, perche’ ChatGPT puo’ gia’ programmare e configrare cose. E anche fare design. Non perdero’ tempo a discutere quali siano i limiti di un modello di linguaggio, per quanto grande, per cui , complice una chiacchierata casuale, ho deciso di provare.

Ho quindi provato con un compito semplicissimo: una configurazione del packet filter di un BSD qualsiasi, che lasci entrare solo una porta del protocollo IPv6, e blocchi il resto.

Gli ho spiegato il problema e poi gli ho anche detto che intendevo il Packet Filter di OpenBSD.

Guardate che cosa e’ successo:

Se conoscete il firewall di OpenBSD , vi sentirete un pochino a disagio. Quello che vi sta consigliando di fare NON e’ esattamente quello che gli avevate chiesto. Decisamente no.

E cosi’: 

Ora, una cosa e’ sicura: non usate ChatGPT per configurare firewall. 


L’errore e’ grave? Quanto grave? 

Per i non avvezzi, avevo chiesto a ChatGPT di creare un firewall che lasciasse passare verso l’interno (che intendo difendere), solo una specifica porta. In breve, questo:

Quello che ha fatto e’ stato questo:

La porta aperta c’e’, ma non e’ il caso di sentirsi al sicuro. Quello che volevo era un sistema che lasciasse entrare solo chi passava da una specifica porta, ma al primo tentativo ha aperto tutto, consentendo di passare ANCHE per quella porta. 


Quando ho detto che c’era un problema, si e’ corretto. E questo significa che puo’ essere comodo per evitare di digitare tutto, se avete familiarita’ con Pf, ma se siete incompetenti usare chatGPT e fare copia-incolla e’ rischiosissimo.

Lo stesso vale per il codice dei programmatori:

Adesso voi direte: ma la soluzione e’ corretta. Ni. Nel senso che approssimera’ pigreco (in tempi finiti e’ ovvio), ma io non ho mai chiesto di limitare le iterazioni a 10000. Sapendo cosa aspettarmi, pero’, so che ha anche usato un modo implicito di dichiarare le variabili, e so che si poteva fare molto di meglio.

Con questa routine che usa normali float a 32 bit, e un limite di diecimila, il risultato si ferma cosi’: 

3.1414926535900345 (10000)

Ma il problema e’: se non 10000, qual’e’ il numero migliore? E cosa succederebbe se usassi numeri a 64 bit?

Rifacciamo:

package main

import (
    "fmt"
    "math"
)

func main() {
    // n := 10000
    // Numero di termini da utilizzare nell'espansione di McLaurin

    // Calcola il valore di π utilizzando lo sviluppo di McLaurin dell'arcotangente
    var pi float64 = 0.0
    var last float64 = 1.0
    for i := 0; last-pi != 0; i++ {
        last = pi
        sign := math.Pow(-1, float64(i))
        term := sign / (2*float64(i) + 1)
        pi += 4 * term
        fmt.Printf("Numero di iterazioni %d , valore %1.30f n", i, pi)

    }

}

Questo codice e’ migliore? Beh, se vogliamo tenere il paradigma della serie di Mc Laurin dell’arcotangente, si. Infatti, finisce cosi’:

Numero di iterazioni 27830 , valore 3.141628584745683294698892495944 
Numero di iterazioni 27831 , valore 3.141556723724897359772967320168 
Numero di iterazioni 27832 , valore 3.141628582163772609447960348916 
Numero di iterazioni 27833 , valore 3.141556726306621971644972290960 
Numero di iterazioni 27834 , valore 3.141628579582233182776462854235 
Numero di iterazioni 27835 , valore 3.141556728887975769026752459467 
Numero di iterazioni 27836 , valore 3.141628577001064570595190161839 

Perche’ finisce cosi’? Perche una serie decrescente finisce col convergere ma lo fa solo in R, mentre il computer non usa R, ma solo un insieme discreto. E quindi ha un epsilon di macchina. 

Secondo, il numero di interazioni che affronta prima di non poter distinguere due valori successivi (da quel momento andare avanti non servirebbe a nulla) e’ di 27836, il che rende arbitrario il precedente limite di 10000 iterazioni, che non ho mai chiesto.

E se osservate con occhio matematico i numeri, notate che l’errore di macchina produce caos, e questo caos ha due attrattori: {3.14155672 , 3.1416285}. Il che significa che l’algoritmo si e’ fermato “per quasi caso”. Il risultato, cioe’, orbita attorno a quei due punti, perche’ il troncamento produce caos. 


Ora, e’ vero che nemmeno il programmatore medio sa che puo’ generare caos e attrattori anche per sbaglio, scrivendo codice apparentemente corretto. La cosa non cambia se usiamo la somma dei resti, perche’ in questo caso il problema e’ il troncamento dovuto all’epsilon di macchina (ove macchina e’ il modello di numeri di golang) . Ma adesso possiamo stimare l’errore:

package main

import (
    "fmt"
    "math"
)

func main() {
    // Numero di termini da utilizzare nell'espansione di McLaurin
    // n := 10000

    // Calcola il valore di π utilizzando lo sviluppo di McLaurin dell'arcotangente e i resti della serie
    pi := 0.0
    prevPi := 0.1
    remainder := 0.0
    for i := 0; pi-prevPi != 0; i++ {
        sign := math.Pow(-1, float64(i))
        term := sign / (2*float64(i) + 1)
        prevPi = pi
        pi += 4 * term
        remainder += math.Abs(pi - prevPi - 4*term)
        fmt.Printf("Numero di iterazioni %d , valore %1.30f , errore: %e n", i, pi, remainder)
    }

}

E otterremo anche la precisione:

Numero di iterazioni 24311 , valore 3.141551521638504151923143581371 , errore: 2.699995e-12 
Numero di iterazioni 24312 , valore 3.141633783849301142510057616164 , errore: 2.700070e-12 
Numero di iterazioni 24313 , valore 3.141551525021900825862530837185 , errore: 2.700169e-12 
Numero di iterazioni 24314 , valore 3.141633780466182912505246349610 , errore: 2.700301e-12 
Numero di iterazioni 24315 , valore 3.141551528404740611932766114478 , errore: 2.700311e-12 
Numero di iterazioni 24316 , valore 3.141633777083621126280377211515 , errore: 2.700392e-12 
Numero di iterazioni 24317 , valore 3.141551531787023954223059263313 , errore: 2.700464e-12 
Numero di iterazioni 24318 , valore 3.141633773701615783835450201877 , errore: 2.700521e-12 
Numero di iterazioni 24319 , valore 3.141551535168751296822620133753 , errore: 2.700608e-12 
Numero di iterazioni 24320 , valore 3.141633770320166885170465320698 , errore: 2.700724e-12 
Numero di iterazioni 24321 , valore 3.141551538549922639731448725797 , errore: 2.700912e-12 
Numero di iterazioni 24322 , valore 3.141633766939273986196212717914 , errore: 2.700990e-12 

Come vedete, usando la somma dei resti il valore arriva leggermente prima, il resto della serie sarebbe attorno ai 10 alla meno dodici, ma abbiamo ancora degli effetti caotici. 

In realta’ spero che non userete mai questo metodo perche’ fa schifo per altre ragioni (in condizioni di troncamento il resto raggiungera’ un minimo e poi continuera’ a crescere anche se lentamente), ma il problema e’ che:

  1. ha aggiunto un requisito mai richiesto (ha “sognato”, come dicono gli espertoni di AI), cioe’ 10000 iterazioni.
  2. ha usato una tecnica che genera un effetto caotico con due attrattori. 

Si poteva fare di meglio? Si, si poteva usando una produttoria. Potremmo discutere anche del suo concetto di serie di Mc Lauren, a dire il vero.

Ma il punto e’ che se sapete cosa state facendo, sapete come maneggiare gli errori che fa. Se non lo sapete, un codice “che funziona” ve lo copiaincollate, e se quello che stavate facendo era farvi un pigreco in casa, beh, avete ottenuto un requisito gratuito (che potreste non notare) e degli effetti collaterali che nel calcolo numerico sono abbastanza sgraditi.

E’ vero che la media dei programmatori non sa riconoscere i due attrattori , come ho fatto io, perche’ non ha mai fatto HPC. Quindi non sa riconoscere nemmeno di aver creato un effetto caos, e nemmeno sa che e’ il troncamento a causarlo. 

Ma il punto e’ che se copiaincollate del codice con effetti collaterali sconosciuti e requisiti caduti dal cielo, potreste scrivere cose MOLTO peggiori di questa.


Ed e’ per questo, cari signori, che:

  1. Non credo che ChatGPT mi togliera’ lavoro, ne’ lo togliera’ ai programmatori bravi.
  2. ChatGPT, essendo un modello di linguaggio, NON PUO’ evitare questi errori.
  3. Quindi, non puo’ nemmeno riprogrammare se’ stesso o “migliorarsi”. Non sa fare.

Esistono molti motivi per i quali un modello di linguaggio naturale si trovera’ a disagio nel programmare o nelle logiche molto formali. 

Ma il punto e’ molto semplice: potete dormire sonni molto tranquilli.

A perdere il lavoro saranno altri.


Ultimo appunto:

Come potete vedere, conosce una sola serie per calcolare pigreco che lui identifica sia con Mc Laurin sia con Taylor. Il guaio viene se gli faccio questa domanda:

Il codice e’ questo:

package main

import (
    "fmt"
)

func main() {
    // Numero di termini da utilizzare nella serie
    n := 10000

    // Calcola il valore di π utilizzando la serie approssimante
    pi := 0.0
    for k := 0; k < n; k++ {
        pi += 1.0/(float64(k)*8.0+1.0) - 1.0/(float64(k)*8.0+4.0) - 1.0/(float64(k)*8.0+5.0) - 1.0/(float64(k)*8.0+6.0)
    }
    pi *= 4.0

    // Stampa il valore di π calcolato
    fmt.Println("Valore di π calcolato:", pi)
}

Che all’inizio mi ha incuriosito perche’ non la conoscevo, e che sarebbe anche interessante.

Se solo quel codice calcolasse pigreco, intendo:

Valore di π calcolato: -2.6657193990759604

Adesso scusate, vado a dormire sonni tranquilli.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *