Suppongo che stia svolgendo una missione: quella di moderare l’ammirazione che la gente sembra provare nei confronti dell’intelligenza artificiale.
Poiché mi appassiona l’ottica, scrivo un sacco di codice, di ogni tipo, e ovviamente sono interessato a passare il minor tempo possibile a farlo.
Prima di aggiungere al mio sistema operativo la funzionalità di elaborazione Zernike tramite il pipeline OpenGL, ho chiesto a ChatGPT di crearne una per me. Questo è successo 18 mesi fa.
Naturalmente, ho ritenuto il risultato inutile. Perché? Ancora una volta, come nel mio precedente post, il codice sembrava in buone condizioni a vista, ma nei dettagli più profondi era inutile. Vi mostrerò cosa intendo.
Calcolo dei polinomi zernike
Nel calcolare i polinomi di Zernike è necessario calcolare il polinomio radiale (che tiene conto della distanza dal centro per una coordinata data da x e y come parametri) e sia il coseno che l’sinus dell’angolo. Quindi, permettetemi di chiedervi, gli esseri umani, che non sono in grado di competere con l’IA, se avete già calcolato la distanza (la hypotenusa o un triangolo) utilizzando le coordinate x e y, come procedete per calcolare il coseno e l’sinus dell’angolo?
Penso che 100% di voi che avete letto fino a qui sappiate dividere x e y per l'ipotenusa. Cosa significa questo? L'intelligenza artificiale più recente e più avanzata si fa? Prende le coordinate e calcola l'angolo usando la tangente inversa e, come avreste potuto intuire, poi utilizza l'angolo per calcolare il seno e il coseno.
Tutta quella energia per cosa?
Questo è il risultato che ho visto 18 mesi fa, e 300 GWh (solo per le GPU, poi aggiungiamo il raffreddamento e la rete di comunicazione), o 90 giorni di apprendimento con rinforzo e abbiamo esattamente lo stesso risultato.
Non c'è quasi alcuna intelligenza in questo lavoro. È solo una traduzione puramente testuale di alcuni contenuti del libro di testo in un altro tipo di testo.
Ho cercato di mettere in luce il rapporto logaritmico tra le prestazioni dell’IA e la potenza di calcolo. Vedendo questo, preferisco usare il logaritmo ogni volta perché quello che vedo non è un miglioramento.
Ovviamente, ho chiesto alla DeepSeek, che mi ha affascinato, la stessa domanda e, in realtà (sto scherzando), il risultato è stato ancora peggiore. Non di molto, perché è difficile produrre un codice peggiore di questo.
Se volete verificarlo voi stessi, ecco la mia domanda:
È possibile scrivere un shader OpenGL che generi una mappa di densità bidimensionale basata su un vettore di coefficienti zernike? Il risultato è mostrato di seguito.
uniform vec2 resolution; // Screen resolution
uniform float zernikeCoeffs[15]; // Array of Zernike coefficients (adjust size as needed)
uniform float scale; // Scaling factor for the density map
// Convert cartesian (x,y) to polar (rho, theta)
vec2 cartesianToPolar(vec2 xy) {
float rho = length(xy);
float theta = atan(xy.y, xy.x);
return vec2(rho, theta);
}
// Zernike radial polynomial function
float radialPoly(int n, int m, float rho) {
float result = 0.0;
m = abs(m);
if ((n - m) % 2 != 0) return 0.0;
for (int k = 0; k <= (n - m) / 2; k++) {
float coef = 1.0;
// Calculate binomial coefficient
for (int i = 1; i <= k; i++) {
coef *= float(n - 2 * k + i) / float(i);
}
for (int i = 1; i <= (n - m) / 2 - k; i++) {
coef *= float(m + i) / float(i);
}
result += pow(-1.0, float(k)) * coef * pow(rho, float(n - 2 * k));
}
return result;
}
// Zernike polynomial evaluation
float zernike(int index, vec2 polar) {
float rho = polar.x;
float theta = polar.y;
// Zernike polynomial ordering (n,m): (0,0), (1,-1), (1,1), (2,-2), (2,0), etc.
int n, m;
if (index == 0) { n = 0; m = 0; } // Z0,0
else if (index == 1) { n = 1; m = -1; } // Z1,-1
else if (index == 2) { n = 1; m = 1; } // Z1,1
else if (index == 3) { n = 2; m = -2; } // Z2,-2
else if (index == 4) { n = 2; m = 0; } // Z2,0
else if (index == 5) { n = 2; m = 2; } // Z2,2
else if (index == 6) { n = 3; m = -3; } // Z3,-3
else if (index == 7) { n = 3; m = -1; } // Z3,-1
else if (index == 8) { n = 3; m = 1; } // Z3,1
else if (index == 9) { n = 3; m = 3; } // Z3,3
else return 0.0;
if (rho > 1.0) return 0.0; // Zernike defined only within unit disk
float radial = radialPoly(n, m, rho);
if (m < 0) return radial * sin(float(-m) * theta);
else return radial * cos(float(m) * theta);
}
void main() {
// Normalize coordinates to [-1, 1] range centered at (0,0)
vec2 uv = (gl_FragCoord.xy - resolution.xy * 0.5) / (resolution.y * 0.5);
vec2 polar = cartesianToPolar(uv);
// Calculate wavefront aberration by summing Zernike terms
float density = 0.0;
for (int i = 0; i < 15; i++) {
density += zernikeCoeffs[i] * zernike(i, polar);
}
// Scale and convert to visible range
density *= scale;
float value = (density + 1.0) * 0.5; // Map [-1,1] to [0,1]
value = clamp(value, 0.0, 1.0);
// Output as grayscale density map
FragColor = vec4(vec3(value), 1.0);
}