$d) if ($d == $id) return $token; return null; } //Trasforma un testo in una lista di tokens. In italiano approssimiamo alle parole della lingua. function tokenize($text) { $text=strtolower($text); $replace=Array("?|qst.","!|exc.",",| e ",";| pev",":| duep ","à|a","è|e","é|è","ì|i","ò|o","ù|u"," | ", "1| uno ","2| due ","3| tre ","4| quattro ","5| cinque ","6| sei ","7| sette ","8| otto ","9| nove ", "0| zero "); $rp=true; while ($rp) { $rp=false; foreach ($replace as $x) { $x=explode("|",$x); $text = str_replace($x[0], $x[1], $text, $count); $rp=$rp | $count>0; }; }; $text = preg_replace('/[^\x00-\x7F]/', '', $text); $text=explode(".", $text); $tb=Array(); foreach ($text as $t) if (trim($t)!="") $tb[]=explode(" ", trim($t)); return $tb; }; //Secondo passo: ho una "scatola" che riceve N tokens e deve cercare di indovinare il successivo. //Entrata: una lista di token, uscita: un valore di probabilità per ciascuno dei token memorizzati. // poi si sceglie il piu probabile. //Possibile variante a base statistica: //function get_next_token($tlist) -> Richiederebbe una tipizzazione.. o un riferimento fonetico.. mah. //Metodo attuale: function prodotto($v, $p) //prodotto tra vocabolario e token embedded { $r = 0; for ($i=0; $i $x) { $e = exp($x); $exp[$i] = $e; $somma += $e; } foreach ($exp as $i => $x) $exp[$i] = $x / $somma; return $exp; } function loss($probabilita, $token_corretto) { return -log($probabilita[$token_corretto]); } //Per prima cosa inizializziamo i pesi, o lo leggiamo da disco se ho gia fatto apprendimenti function setPesi() { global $pesi, $dizionario; for ($id=1; $id<=count($dizionario); $id++) if (!isset($pesi[$id])) $pesi[$id] = get_vector(); } function grad_pesi($grad_logit, $embedding) { $grad = []; foreach ($grad_logit as $i => $g) { foreach ($embedding as $j => $x) $grad[$i][$j] = $g * $x; } return $grad; } function trasforma($x, $W) { $r = []; foreach ($W as $i => $riga) $r[$i] = prodotto($x, $riga); return $r; } function train($token, $token_corretto) { global $embedding, $pesi, $learning_rate; $id = tok_num($token); $target = tok_num($token_corretto); $x = $embedding[$id]; // vettore di ingresso $logit = []; // forward: embedding -> logits foreach ($pesi as $i => $riga) $logit[$i] = prodotto($x, $riga); $p = softmax($logit); // logits -> probabilità $loss = -log($p[$target]); // errore, utile anche per vedere se stiamo imparando // backward: softmax + cross entropy $grad_logit = []; foreach ($p as $i => $prob) { $grad_logit[$i] = $prob; if ($i == $target) $grad_logit[$i] -= 1; }; // gradienti PRIMA di modificare i pesi $grad_W = grad_pesi($grad_logit, $x); $grad_x = grad_embedding($pesi, $grad_logit, VECTOR_SIZE); // aggiorna matrice W foreach ($pesi as $i => $riga) { for ($j=0; $j $token) { $id = tok_num($token); for ($j=0; $j $peso) for ($j=0; $j<$vs; $j++) $r[$j] += $peso * $v[$n][$j]; return $r; } function attention($q, $k, $v) { $out = []; $N = count($q); $size = count($q[0]); for ($n=0; $n<$N; $n++) { $score = []; for ($i=0; $i<$N; $i++) { if ($i <= $n) $score[$i] = prodotto($q[$n], $k[$i]) / sqrt($size); else $score[$i] = -INF; } $a = softmax($score); $out[$n] = array_fill(0, $size, 0); for ($i=0; $i<$N; $i++) for ($j=0; $j<$size; $j++) $out[$n][$j] += $a[$i] * $v[$i][$j]; } return $out; } function somma_vettori($a, $b) { $r = []; for ($i=0; $i $token) { $q[$n] = trasforma($token, $Wq[$h]); $k[$n] = trasforma($token, $Wk[$h]); $v[$n] = trasforma($token, $Wv[$h]); } $outputs[$h] = attention($q, $k, $v); } // riuniamo le teste token per token $risultato = []; foreach ($x as $n => $token) { $unito = []; for ($h = 0; $h < HEADS; $h++) $unito = array_merge($unito, $outputs[$h][$n]); // rimescola insieme il risultato delle teste $risultato[$n] = trasforma($unito, $Wo); } return $risultato; } function get_matrix($righe, $colonne) { $m = []; for ($i=0; $i<$righe; $i++) $m[] = get_vector($colonne); return $m; } function forward($frase) { global $Wq, $Wk, $Wv, $Wo, $W1, $W2, $pesi; $x = prepara_frase($frase); setPesi(); $a = multihead_attention($x, $Wq, $Wk, $Wv, $Wo); foreach ($x as $n => $vettore) { $x[$n] = somma_vettori($vettore, $a[$n]); $x[$n] = layernorm($x[$n]); } foreach ($x as $n => $vettore) { $f = ffn($vettore, $W1, $W2); $x[$n] = somma_vettori($vettore, $f); $x[$n] = layernorm($x[$n]); } $ultimo = $x[count($x)-1]; //echo implode(" ", $ultimo)."\n"; $logit = []; foreach ($pesi as $id => $riga) $logit[$id] = prodotto($ultimo, $riga); return softmax($logit); } function errore($frase, $token_corretto) { $target = tok_num($token_corretto); // il target deve esistere nel vocabolario $p = forward($frase); // esegue tutto il Transformer return -log(max($p[$target], 1e-12)); // errore dell'intero modello } function gradiente_matrice(&$W, $frase, $target) { $epsilon = 0.001; $gradienti = []; foreach ($W as $i => &$riga) foreach ($riga as $j => &$peso) { $originale = $peso; $peso = $originale + $epsilon; // prova verso destra $e1 = errore($frase, $target); $peso = $originale - $epsilon; // prova verso sinistra $e2 = errore($frase, $target); $gradiente = ($e1-$e2)/(2*$epsilon); // pendenza in questo punto $peso = $originale; // il modello resta invariato $gradienti[$i][$j] = $gradiente; } return $gradienti; } function gradiente_vettore(&$v, $frase, $target) { $epsilon = 0.001; $gradienti = []; foreach ($v as $i => &$peso) { $originale = $peso; $peso = $originale+$epsilon; // errore poco più avanti $e1 = errore($frase, $target); $peso = $originale-$epsilon; // errore poco più indietro $e2 = errore($frase, $target); $gradiente = ($e1-$e2)/(2*$epsilon); // pendenza $peso = $originale; // il modello resta invariato $gradienti[$i] = $gradiente; } return $gradienti; } function applica_gradiente_matrice(&$W, $gradienti) { global $learning_rate; foreach ($gradienti as $i => $riga) foreach ($riga as $j => $gradiente) $W[$i][$j] -= $learning_rate*$gradiente; } function applica_gradiente_vettore(&$v, $gradienti) { global $learning_rate; foreach ($gradienti as $i => $gradiente) $v[$i] -= $learning_rate*$gradiente; } function learn($frase, $target) { global $Wq, $Wk, $Wv, $Wo, $W1, $W2, $pesi, $embedding, $posizione; tok_num($target); // vocabolario completo prima delle prove forward($frase); // crea eventuali strutture mancanti $grad_Wq = []; $grad_Wk = []; $grad_Wv = []; for ($h=0; $h $token) { $id = tok_num($token); if (!isset($grad_embedding[$id])) $grad_embedding[$id] = gradiente_vettore($embedding[$id], $frase, $target); $grad_posizione[$n] = gradiente_vettore($posizione[$n], $frase, $target); } // Tutti i gradienti precedenti sono stati misurati sullo stesso modello. for ($h=0; $h $gradiente) applica_gradiente_vettore($embedding[$id], $gradiente); foreach ($grad_posizione as $n => $gradiente) applica_gradiente_vettore($posizione[$n], $gradiente); } /* function learn($frase, $target) //Temporanea per tests { global $pesi; tok_num($target); forward($frase); impara_matrice($pesi, $frase, $target); } */ /*function learn_frase($frase, $giri=20) { for ($n=1; $n $prob) echo " ".num_tok($id)." ".$prob."\n"; } //Codice di avvio $posizione = []; $pesi = []; $learning_rate = 0.01; for ($h=0; $h modifica i pesi per imparare la frase // LLM read testo -> prova a imparare l'intero testo frase per frase // LLM query frase -> tenta autocompletamento /* for ($i=0; $i<100; $i++) { learn(["il","gatto","mangia"], "il"); learn(["il","cane","mangia"], "la"); } query_test("il gatto mangia"); query_test("il cane mangia"); */ read_text("il gatto mangia il topo. il cane mangia la carne.", 100); query_test("il gatto"); query_test("il cane"); query_test("il gatto mangia"); query_test("il cane mangia");