Trova la radice più grande di un polinomio con una rete neurale


11

La sfida

Trova la più piccola rete neurale feedforward tale che, dato qualsiasi vettore di input tridimensionale (a,b,c) con voci intere in , la rete emette la radice più grande (cioè "più positiva") della polinomiale con errore strettamente inferiore a .[10,10]x3+ax2+bx+c0.1

ammissibilità

La nozione di ammissibilità nella mia precedente sfida sul golf della rete neurale sembrava un po 'restrittiva, quindi per questa sfida, stiamo usando una definizione più liberale di rete neurale feedforward:

Un neurone è una funzione che è specificata da un vettore di pesi , una tendenza e una funzione di attivazione nel modo seguente:ν:RnRwRn bR f:RR

ν(x):=f(wx+b),xRn.

Una rete neurale feedforward con nodi di input è una funzione di che può essere costruita da una sequenza di neuroni, dove ogni riceve input da e genera un scalare . Dato un determinato set S \ subseteq \ {1, \ ldots, N \} dei nodi di output , l'output della rete neurale è il vettore (x_k) _ {k \ in S} .{1,,n}(x1,,xn)Rn(νk)k=n+1Nνk:Rk1R(x1,,xk1)xkS{1,,N}(xk)kS

Poiché le funzioni di attivazione possono essere regolate per qualsiasi attività, è necessario limitare la classe di funzioni di attivazione per mantenere interessante questa sfida. Sono consentite le seguenti funzioni di attivazione:

  • Identità. f(t)=t

  • Relu. f(t)=max(t,0)

  • SoftPlus. f(t)=ln(et+1)

  • Sigma. f(t)=etet+1

  • Sinusoid. f(t)=sint

Nel complesso, una rete neurale ammissibile è specificata da nodi di input, una sequenza di neuroni e nodi di output, mentre ogni neurone è specificato da un vettore di pesi, una distorsione e una funzione di attivazione dall'elenco precedente. Ad esempio, la seguente rete neurale è ammissibile, sebbene non soddisfi l'obiettivo prestazionale di questa sfida:

  • Nodi di input: {1,2}

  • Neuroni: perνk(x1,,xk1):=xk2+xk1k{3,,10}

  • Nodi di output: {5,9,10}

Questa rete è composta da 8 neuroni, ciascuno con zero pregiudizi e attivazione dell'identità. In parole, questa rete calcola la sequenza di Fibonacci generalizzata generata da e e quindi emette i numeri 5, 9 e 10 da questa sequenza, in quell'ordine.x1x2

punteggio

Dato un numero reale con l'espansione decimale finale, lascia che sia il numero intero non negativo più piccolo per il quale e che sia il numero intero non negativo più piccolo per quale è intero. Quindi diciamo che è la precisione di .xp(x)p10p|x|<1q(x)q10qxp(x)+q(x)x

Ad esempio, ha una precisione di , mentre ha una precisione di .x=1.0014x=00

Il tuo punteggio è la somma delle precisioni dei pesi e dei pregiudizi nella tua rete neurale.

(Ad esempio, l'esempio sopra ha un punteggio di 16.)

Verifica

Mentre le radici possono essere espresse in termini di formula cubica , la radice più grande è forse più facilmente accessibile con mezzi numerici. Seguendo il suggerimento di @ xnor, ho calcolato la radice più grande per ogni scelta di numeri interi , e i risultati possono essere trovati qui . Ogni riga di questo file di testo è nel modulo . Ad esempio, la prima riga riporta che la radice più grande di è circa .a,b,c[10,10]x 3 - 10 x 2 - 10 x - 10 10.99247140445449a,b,c,rootx310x210x1010.99247140445449

Modifica: il file originale che ho pubblicato conteneva errori nei casi in cui il polinomio presentava una radice multipla. La versione corrente dovrebbe essere priva di tali errori.


3
Cosa succede nel polinomio di input non ha radici reali, come quando a=0e il quadratico ha due radici complesse?
xnor

Penso che la soluzione più pulita sarebbe dire che l'input sarà adiverso da zero, o anche solo 1. Inoltre, consiglierei di inserire alcuni casi di test, dando le radici ad alta precisione in modo da poter verificare che i nostri siano entro 0,1. Sarebbe anche bello avere degli output per tutti gli input possibili, probabilmente in un link dato che è molto per il post.
xnor

1
Mi piacciono le nuove regole di ammissibilità. Sembra che la nuova funzione sinusoidale sia estremamente sfruttabile. Ho una prova imprecisa che una funzione del modulo x -> a * sin(b * softplus(x) + c)può essere adatta a qualsiasi numero finito di punti dati con numero intero xa precisione arbitraria usando una frequenza estremamente grande e precisa.
xnor

1
Non sono sicuro di quanto sarebbe utile (per le sfide future): nella teoria dei numeri usiamo le funzioni di altezza per misurare la complessità di un numero. Ad esempio l'altezza ingenua di una frazione (ridotta) è data da (e ci sono molte generalizzazioni). Forse questo potrebbe essere usato come misura alternativa. h = log max { | p | , | q | }p/qh=logmax{|p|,|q|}
flawr

1
@ DustinG.Mixon Non sono sicuro che tu sia a conoscenza, ma abbiamo una sandbox per pubblicare bozze e discutere i dettagli di una sfida e una chat .
flawr

Risposte:


6

14.674.000.667 5.436.050 5.403.448 10.385 5.994 4.447
3.806 di precisione totale

Per una linea di base, ho studiato il seguente approccio: selezionare tale che se campioniamo il polinomio atM,δ,ϵ>0p(x)=x3+ax2+bx+c

S:={M,M+δ,M+2δ,,M},

quindi il punto di campionamento più grande soddisfa esiste necessariamente e risiede necessariamente entro dalla radice più grande di . Si può dimostrare che per la nostra raccolta di polinomi, si può prendere , e .sSp(s)<ϵ0.1pM=11δ=0.1ϵ=104

Per progettare una rete neurale che implementa questa logica, si inizia con uno strato di neuroni che il polinomio campione su . Per ogni , prendiamoSSS

X1,S=S2un'+SB+1c+S3.

Successivamente, identifichiamo quali di questi sono inferiori a . Si scopre che per , sostiene che solo se . Pertanto, possiamo usare le attivazioni relu per identificare esattamente i nostri campioni:ε=10-4SSp(S)<10-4p(S)0

relu(10-4-t)-relu(-t)10-4={1Se t00Se t10-4.

Lo implementiamo con alcuni strati di neuroni:

X2,S=relu(-1X1,S+10-4),X3,S=relu(-1X1,S),X4,S=104X2,S-104X3,S.

A questo punto, abbiamo quando , e altrimenti . Ricordiamo che cerchiamo la più grande per cui . A tal fine, etichettiamo come (per comodità notazionale) e per ogni , definiamo iterativamenteX4,S=1p(S)<10-4X4,S=0SX4,S=1X4,MX5,MK1

X5,M-Kδ=1X4,M-Kδ+2X5,M-(K-1)δ=Σj=0K2K-jX4,M-jδ.

Grazie a questa trasformazione, ogni è un numero intero non negativo e è la unica per la quale . Ora possiamo identificare con un'altra applicazione delle attivazioni relu:X5,SSsx5,s=1s

relu(t2)2relu(t1)+t={1if t=10if tZ0{1}.

In modo esplicito, definiamo i neuroni per

x6,s=relu(1x5,s2),x7,s=relu(1x5,s1),x8,s=1x6,s2x7,s+1x5s.

Quindi se e altrimenti . Le combiniamo linearmente per produrre il nostro nodo di output:x8,s=1s=sx8,s=0

x9=sSsx8,s=s.

Per il punteggio, ogni strato ha neuroni con diversi livelli di precisione: (1) , (2) , (3) , (4) , (5) , (6) , (7) , (8) , (9). Inoltre, tutti gli strati tranne due hanno neuroni; lo strato 5 ha neuroni e lo strato 9 ha neurone.6+3+1+9=191+4=515+5=101+1=21+1=21+1=21+1+1=33|S||S|=221|S|-11

Modifica: Miglioramenti: (1) Possiamo campionare il polinomio in modo molto più efficiente usando differenze finite. (2) Possiamo bypassare i livelli da 2 a 4 usando invece un'attivazione sigmoid. (3) I problemi di overflow nello strato 5 possono essere evitati (e gli strati successivi possono essere combinati) applicando più attentamente le attivazioni relu. (4) La somma finale è più economica con la somma per parti .

Quello che segue è il codice MATLAB. Per essere chiari, precè una funzione (che si trova qui ) che calcola la precisione di un vettore di pesi o distorsioni.

function sstar = findsstar2(a,b,c)

relu = @(x) x .* (x>0);

totprec = 0;

% x1 samples the polynomial on -11:0.1:11
x1=[];
for s = -11:0.1:11
    if length(x1) < 5
        w1 = [s^2 s 1];
        b1 = s^3;
        x1(end+1,:) = w1 * [a; b; c] + b1;
        totprec = totprec + prec(w1) + prec(b1);
    else
        w1 = [-1 4 -6 4];
        x1(end+1,:) = w1 * x1(end-3:end,:);
        totprec = totprec + prec(w1);
    end
end

% x4 indicates whether the polynomial is nonpositive
w4 = -6e5;
b4 = 60;
x4=[];
for ii=1:length(x1)
    x4(end+1) = sigmf(w4 * x1(ii) + b4, [1,0]);
    totprec = totprec + prec(w4) + prec(b4);
end

% x6 indicates which entries are less than or equal to sstar
x5 = zeros(size(x1));
x6 = zeros(size(x1));
x5(end) = 0;
x6(end) = 0;
for ii = 1:length(x5)-1
    w5 = [-1 -1];
    b5 = 1;
    x5(end-ii) = relu(w5 * [x4(end-ii); x6(end-ii+1)] + b5);
    totprec = totprec + prec(w5) + prec(b5);
    w6 = -1;
    b6 = 1;
    x6(end-ii) = w6 * x5(end-ii) + b6;
    totprec = totprec + prec(w6) + prec(b6);
end

% a linear combination produces sstar
w7 = 0.1*ones(1,length(x1));
w7(1) = -11;
sstar = w7 * x6;

%disp(totprec) % uncomment to display score

end

2

53.268 29.596 29.306 precisione totale

La comunicazione privata con @ A.Rex ha portato a questa soluzione, in cui costruiamo una rete neurale che memorizza le risposte. L'idea principale è che ogni funzione su un set finito gode della decomposizionef:SRS

f(X)=ΣSSf(S){1Se X=S0altro}.

Come tale, si può costruire un'implementazione di rete neurale di trasformando prima l'input in una funzione indicatore dell'input, e quindi combinando linearmente usando gli output desiderati come pesi. Inoltre, le attivazioni relu rendono possibile questa trasformazione:f

relu(t-1)-2relu(t)+relu(t+1)={1Se t=00Se tZ{0}.

Ciò che segue è un'implementazione MATLAB di questo approccio. Per essere chiari, roots.txtè il file root pubblicato sopra (trovato qui ) ed precè una funzione (trovata qui ) che calcola la precisione totale di un vettore di pesi o distorsioni.

Modifica 1: Due miglioramenti rispetto all'originale: (1) Ho preso in considerazione alcuni neuroni dai loop for. (2) Ho implementato " integrazione di Lebesgue " nella somma finale combinando prima i termini dello stesso livello impostato. In questo modo, pago per il valore di precisione superiore di un'uscita solo una volta ogni livello impostato. Inoltre, è sicuro arrotondare le uscite al quinto più vicino con il teorema della radice razionale .

Modifica 2: miglioramenti minori aggiuntivi: (1) ho preso in considerazione più neuroni da un ciclo for. (2) Non mi preoccupo di calcolare il termine nella somma finale per cui l'output è già zero.

function r = approxroot(a,b,c)

relu = @(x)x .* (x>0);

totalprec=0;

% x4 indicates which entry of (-10:10) is a
w1 = ones(21,1);   b1 = -(-10:10)'-1;    x1 = relu(w1 * a + b1);
w2 = ones(21,1);   b2 = -(-10:10)';      x2 = relu(w2 * a + b2);
w3 = ones(21,1);   b3 = -(-10:10)'+1;    x3 = relu(w3 * a + b3);
w4p1 = ones(21,1); w4p2 = -2*ones(21,1); w4p3 = ones(21,1);
x4 = w4p1 .* x1 + w4p2 .* x2 + w4p3 .* x3;
totalprec = totalprec + prec(w1) + prec(w2) + prec(w3) + prec(b1) + prec(b2) + prec(b3) + prec(w4p1) + prec(w4p2) + prec(w4p3);

% x8 indicates which entry of (-10:10) is b
w5 = ones(21,1);   b5 = -(-10:10)'-1;    x5 = relu(w5 * b + b5);
w6 = ones(21,1);   b6 = -(-10:10)';      x6 = relu(w6 * b + b6);
w7 = ones(21,1);   b7 = -(-10:10)'+1;    x7 = relu(w7 * b + b7);
w8p1 = ones(21,1); w8p2 = -2*ones(21,1); w8p3 = ones(21,1);
x8 = w8p1 .* x5 + w8p2 .* x6 + w8p3 .* x7;
totalprec = totalprec + prec(w5) + prec(w6) + prec(w7) + prec(b5) + prec(b6) + prec(b7) + prec(w8p1) + prec(w8p2) + prec(w8p3);

% x12 indicates which entry of (-10:10) is c
w9 = ones(21,1);    b9 = -(-10:10)'-1;     x9 = relu(w9 * c + b9);
w10 = ones(21,1);   b10 = -(-10:10)';      x10 = relu(w10 * c + b10);
w11 = ones(21,1);   b11 = -(-10:10)'+1;    x11 = relu(w11 * c + b11);
w12p1 = ones(21,1); w12p2 = -2*ones(21,1); w12p3 = ones(21,1);
x12 = w12p1 .* x9 + w12p2 .* x10 + w12p3 .* x11;
totalprec = totalprec + prec(w9) + prec(w10) + prec(w11) + prec(b9) + prec(b10) + prec(b11) + prec(w12p1) + prec(w12p2) + prec(w12p3);

% x15 indicates which row of the roots file is relevant
x15=[];
for aa=-10:10
    w13 = 1;
    b13 = -2;
    x13 = w13 * x4(aa+11) + b13;
    totalprec = totalprec + prec(w13) + prec(b13);
    for bb=-10:10
        w14p1 = 1;
        w14p2 = 1;
        x14 = w14p1 * x13 + w14p2 * x8(bb+11);
        totalprec = totalprec + prec(w14p1) + prec(w14p2);
        for cc=-10:10
            w15p1 = 1;
            w15p2 = 1;
            x15(end+1,1) = relu(w15p1 * x14 + w15p2 * x12(cc+11));
            totalprec = totalprec + prec(w15p1) + prec(w15p2);
        end
    end
end

% r is the desired root, rounded to the nearest fifth
A = importdata('roots.txt');
outputs = 0.2 * round(5 * A(:,4)');
uniqueoutputs = unique(outputs);
x16 = [];
for rr = uniqueoutputs
    if rr == 0
        x16(end+1,:) = 0;
    else
        lvlset = find(outputs == rr);
        w16 = ones(1,length(lvlset));
        x16(end+1,:) = w16 * x15(lvlset);
        totalprec = totalprec + prec(w16);
    end
end
w17 = uniqueoutputs;
r = w17 * x16;
totalprec = totalprec + prec(w17);

%disp(totalprec) % uncomment to display score

end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.