Maschinelles Lernen Golf: Multiplikation


68

Ich möchte dieser Community eine andere Art von Golfherausforderung vorschlagen:

(Künstliche) Neuronale Netze sind sehr beliebte Modelle des maschinellen Lernens, die so entworfen und trainiert werden können, dass sie sich einer bestimmten (normalerweise unbekannten) Funktion annähern. Sie werden oft verwendet, um hochkomplexe Probleme zu lösen, die wir nicht algorithmisch lösen können, z. B. Spracherkennung, bestimmte Arten von Bildklassifizierungen, verschiedene Aufgaben in autonomen Fahrsystemen, ... Für eine Einführung in neuronale Netze ist dies hervorragend Wikipedia-Artikel .

Da dies die erste in meiner Hoffnung ist, eine Reihe maschineller Golfherausforderungen zu werden, möchte ich die Dinge so einfach wie möglich halten:

Entwerfen und trainieren Sie in der Sprache und dem Rahmen Ihrer Wahl ein neuronales Netzwerk, das unter Berücksichtigung von (x1,x2) sein Produkt x1x2 für alle ganzen Zahlen x1,x2 zwischen (und einschließlich) 10 und 10 berechnet .

Leistungsziel

Um sich zu qualifizieren, darf Ihr Modell bei keinem dieser Einträge um mehr als 0.5 vom korrekten Ergebnis abweichen .

Regeln

Dein Modell

  • muss ein "traditionelles" neuronales Netzwerk sein (der Wert eines Knotens wird als gewichtete lineare Kombination einiger Knoten in einer vorherigen Schicht berechnet, gefolgt von einer Aktivierungsfunktion),
  • darf nur die folgenden Standard-Aktivierungsfunktionen verwenden:
    1. linear(x)=x ,
    2. softmax(x)i=exijexj ,
    3. seluα,β(x)={βx, if x>0αβ(ex1), otherwise ,
    4. softplus(x)=ln(ex+1) ,
    5. leaky-reluα(x)={x, if x<0αx, otherwise ,
    6. tanh(x) ,
    7. sigmoid(x)=exex+1 ,
    8. hard-sigmoid(x)={0, if x<2.51, if x>2.50.2x+0.5, otherwise ,
    9. ex
  • muss (x1,x2) entweder als Tupel / Vektor / Liste / ... von ganzen Zahlen oder als Gleitkommazahl als einzige Eingabe nehmen,
  • Geben Sie die Antwort als Ganzzahl, Float (oder einen geeigneten Container, z. B. einen Vektor oder eine Liste, die diese Antwort enthält) zurück.

Ihre Antwort muss den gesamten Code enthalten (oder mit diesem verknüpft), der zur Überprüfung Ihrer Ergebnisse erforderlich ist - einschließlich der trainierten Gewichte Ihres Modells.

Wertung

Das neuronale Netz mit der geringsten Anzahl von Gewichten (einschließlich Bias-Gewichten) gewinnt.

Genießen!


9
Willkommen auf der Seite! Ich denke, diese Herausforderung könnte in hohem Maße von einer stabileren Definition eines neuronalen Netzwerks profitieren. Hier gibt es ein paar Dinge: 1) Es wäre sehr nett, wenn Sie dies in einer Sprache ausdrücken würden, die noch keine Kenntnis der NNs impliziert. 2) Sie sollten die Aktivierungsfunktionen in Ihrem Beitrag auflisten, anstatt auf eine externe Quelle zu verlinken. ( externe Links können sich ändern oder verschwinden).
Weizen-Assistent

4
Können wir Gewichte wiederverwenden / Faltungsschichten verwenden? (Ich empfehle, den Bonus zu streichen, da er der Herausforderung nichts hinzufügt und nur vom Hauptziel ablenkt.) Sollen die Gewichte echt sein oder können sie komplex sein?
Fehler

4
Ihre Formulierung impliziert, dass Knoten aus Schicht 3 keine Eingaben aus Schicht 1 verwenden können. Kostet es eine Gewichtung, wenn ein Knoten aus Schicht 2 nur f(x) = xdie Eingabe weiterleitet?
Grimy

4
In der rechten Spalte sollte sich ein Link zur Sandbox befinden, der speziell zur Behebung derartiger Probleme erstellt wurde, bevor die Frage überhaupt auf der Hauptseite veröffentlicht wird. Und die Netzwerkphilosophie ist, dass es besser ist, eine Frage zu schließen, zu beheben und erneut zu öffnen, als eine Reihe von Antworten zu erhalten, die entweder nach der Behebung der Frage keinen Sinn ergeben oder die Änderungen, die an der Frage vorgenommen werden können, stark einschränken .
Peter Taylor

7
Überhaupt nicht. Diese Art von Problemen wird durch die langjährige Erfahrung erkannt, andere Menschen zu sehen, die die gleiche Art von Fehlern machen. Einige Unklarheiten entziehen sich dem Sandkasten, aber viele weitere werden dort gefangen. Und das wäre definitiv aufgefangen worden, denn wie in meinem ersten Kommentar erwähnt, hatten wir vor zwei Monaten genau die gleichen Probleme mit einer neuronalen Netzfrage.
Peter Taylor

Antworten:


37

21 13 11 9 gewichte

Dies basiert auf der Polarisationsidentität bilinearer Formen, die sich im eindimensionalen Realfall auf die polynomiale Identität reduziert:

xy=(x+y)2(xy)24

y1Berechne also einfach [x+y, x-y]mit einer linearen Transformation und y3ist nur der absolute Wert von y1als Vorverarbeitungsschritt für den nächsten: Dann berechnet der "harte" Teil die Quadrate, die ich unten erläutere, und danach berechnet er einfach einen Unterschied und eine Skalierung, die ist wieder eine lineare Operation.

s{0,1,2,,20}0.5

approx_square(x)=i=02wiexp(0.0001ix)

W2=(wi)i0.02

function p = net(x)
% 9 weights
one = 1; 
mone =-1;
zero = 0;
fourth = 0.25;
W1 = [1e-4, 2e-4];
W2  = [-199400468.100687;99700353.6313757];
b2 = 99700114.4299316;
leaky_relu = @(a,x)max(a*x,x); 


% Linear
y0 = [one, one; one, mone] * x;

% Linear + ReLU
y1 = mone * y0;
y2 = [leaky_relu(zero, y0), leaky_relu(zero, y1)];

% Linear
y3 = y2 * [one; one];

% Linear + exp
y4 = exp(y3 * W1); 

% Linear + Bias
y5 =  y4 * W2 + b2;

% Linear
y6 = [one, mone]*y5;
p = y6 * fourth;

end

Probieren Sie es online!


Ich denke, dass Ihr Prüfcode in der Fußzeile des TIO-Links eine Anwendung von verpasst abs. Aber trotzdem ist alles in Ordnung.
Christian Sievers

@ChristianSievers Danke, ich habe den TIO-Link aktualisiert!
Fehler

Ich bin kein Experte für NN, aus Neugier, wie wird die Gewichtszählung durchgeführt? y0braucht 4, y1braucht 2, y3braucht 2, y4braucht 1, y5braucht 1 und y6braucht 2. Das ist 12?
Margaret Bloom

3
@MargaretBloom Ja, das ist in der Tat ein bisschen ungewöhnlich, aber das OP sagte in den Kommentaren, dass wir Gewichte wiederverwenden können und sie immer nur einmal zählen müssen, selbst wenn wir dasselbe Gewicht mehrmals verwenden. Alle von mir verwendeten Gewichte werden im ersten Teil der Funktion definiert.
Fehler

31

7 Gewichte

eps = 1e-6
c = 1 / (2 * eps * eps)

def f(A, B):
	e_s = exp(eps * A + eps * B)  # 2 weights, exp activation
	e_d = exp(eps * A - eps * B)  # 2 weights, exp activation
	return c * e_s + (-c) * e_d + (-1 / eps) * B  # 3 weights, linear activation

Probieren Sie es online!

ϵex1+x+x22

ABeϵA+ϵBeϵAϵB2ϵ2Bϵ

ϵepsc


1
Ich bin mir nicht sicher, ob dies als "traditionelles neuronales Netzwerk" gilt (Regel 1), aber es ist offensichtlich, dass es in eines umformatiert werden kann, daher sehe ich kein Problem damit. Schöne lösung!
Stefan Mesken

1
Sie könnten definieren C = -B(1 Gewicht) und dann haben [e_s, e_d] = conv([A,B,C], [eps, eps])(2 Gewichte), um ein Gewicht zu sparen :) (Übrigens: Sehr kluger Ansatz!)
Fehler

(Ich habe vergessen, das hinzuzufügen exp)
Fehler

4
Wenn Sie die Gewichte wiederverwenden, können Sie sogar viel niedriger werden - Sie müssen nicht mehrmals dasselbe Gewicht zählen.
Fehler

2
@flawr Das ist ein netter Trick, aber ich denke, dass die Berücksichtigung von Faltung und Wiederverwendung von Gewichten in den Kommentaren dies so sehr zu einer anderen Herausforderung macht, dass ich diese Antwort so belassen werde, wie sie ist.
4.

22

33 31 gewichte

# Activation functions
sub hard { $_[0] < -2.5 ? 0 : $_[0] > 2.5 ? 1 : 0.2 * $_[0] + 0.5 }
sub linear { $_[0] }

# Layer 0
sub inputA() { $a }
sub inputB() { $b }

# Layer 1
sub a15() { hard(5*inputA) }

# Layer 2
sub a8()  { hard(-5*inputA + 75*a15 - 37.5) }

# Layer 3
sub aa()  { linear(-5*inputA + 75*a15 - 40*a8) }

# Layer 4
sub a4()  { hard(aa - 17.5) }

# Layer 5
sub a2()  { hard(aa - 20*a4 - 7.5) }

# Layer 6
sub a1()  { linear(0.2*aa - 4*a4 - 2*a2) }

# Layer 7
sub b15() { hard(0.25*inputB - 5*a15) }
sub b8()  { hard(0.25*inputB - 5*a8) }
sub b4()  { hard(0.25*inputB - 5*a4) }
sub b2()  { hard(0.25*inputB - 5*a2) }
sub b1()  { hard(0.25*inputB - 5*a1) }

# Layer 8
sub output() { linear(-300*b15 + 160*b8 + 80*b4 + 40*b2 + 20*b1 - 10*inputA) }

# Test
for $a (-10..10) {
        for $b (-10..10) {
                die if abs($a * $b - output) >= 0.5;
        }
}

print "All OK";

Probieren Sie es online!

Dies führt eine lange Multiplikation in (sorta) binär durch und gibt somit das genaue Ergebnis zurück. Es sollte möglich sein, das 0,5-Fehlerfenster zu nutzen, um dies noch weiter zu verbessern, aber ich bin mir nicht sicher, wie.

Die Schichten 1 bis 6 zerlegen die erste Eingabe in 5 "Bits". Aus Golfgründen verwenden wir keine eigentlichen Binärdateien. Das höchstwertige "Bit" hat eine Gewichtung von -15 anstelle von 16, und wenn die Eingabe 0 ist, sind alle "Bits" 0,5 (was immer noch gut funktioniert, da es die Identität beibehält inputA = -15*a15 + 8*a8 + 4*a4 + 2*a2 + 1*a1).


1
Ich hatte erwartet, dass jemand einen hartcodierten, ANN-fähigen Multiplikationsalgorithmus entwickelt. Aber ich hätte nicht gedacht, dass es die erste Antwort sein würde. Gut gemacht! (Ich bin auch gespannt, ob Sie so etwas mit dem MNIST-Datensatz oder einem anderen, relastischeren ML-Problem schaffen können: D.)
Stefan Mesken,

14

43 Gewichte

Die beiden bisher veröffentlichten Lösungen waren sehr clever, aber ihre Ansätze funktionieren wahrscheinlich nicht für traditionellere Aufgaben im maschinellen Lernen (wie OCR). Daher möchte ich für diese Aufgabe eine "generische" (keine cleveren Tricks) Lösung vorschlagen, die hoffentlich andere Menschen dazu inspiriert, sie zu verbessern und in die Welt des maschinellen Lernens einzutauchen:

Mein Modell ist ein sehr einfaches neuronales Netzwerk mit 2 versteckten Schichten, die in TensorFlow 2.0 erstellt wurden (aber jedes andere Framework würde auch funktionieren):

model = tf.keras.models.Sequential([
tf.keras.layers.Dense(6, activation='tanh', input_shape=(2,)),
tf.keras.layers.Dense(3, activation='tanh'),
tf.keras.layers.Dense(1, activation='linear')
])

Wie Sie sehen, sind alle Ebenen dicht (was mit Sicherheit nicht optimal ist). Die Aktivierungsfunktion ist hellbraun (was für diese Aufgabe möglicherweise in Ordnung ist), mit Ausnahme der Ausgabeebene, die aufgrund der Art dieser Aufgabe hat eine lineare Aktivierungsfunktion.

Es gibt 43 Gewichte:

  • (2+1)6=18
  • (6+1)3=21
  • (3+1)1=4

1010

Als nächstes habe ich sie verfeinert und für die maximale Abweichung bei einer der ganzzahligen Multiplikationsaufgaben optimiert. Leider zeigen meine Notizen nicht viel Feinabstimmung, die ich am Ende gemacht habe, aber es war sehr gering. In der Nähe von 100 Epochen auf diesen 441 Trainingsmustern mit einer Chargengröße von 441.

Dies sind die Gewichte, mit denen ich gelandet bin:

[<tf.Variable 'dense/kernel:0' shape=(2, 6) dtype=float32, numpy=
 array([[ 0.10697944,  0.05394982,  0.05479664, -0.04538541,  0.05369904,
         -0.0728976 ],
        [ 0.10571832,  0.05576797, -0.04670485, -0.04466859, -0.05855528,
         -0.07390639]], dtype=float32)>,
 <tf.Variable 'dense/bias:0' shape=(6,) dtype=float32, numpy=
 array([-3.4242163, -0.8875816, -1.7694025, -1.9409281,  1.7825342,
         1.1364107], dtype=float32)>,
 <tf.Variable 'dense_1/kernel:0' shape=(6, 3) dtype=float32, numpy=
 array([[-3.0665843 ,  0.64912266,  3.7107112 ],
        [ 0.4914808 ,  2.1569328 ,  0.65417236],
        [ 3.461693  ,  1.2072319 , -4.181983  ],
        [-2.8746269 , -4.9959164 ,  4.505049  ],
        [-2.920127  , -0.0665407 ,  4.1409926 ],
        [ 1.3777553 , -3.3750365 , -0.10507642]], dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(3,) dtype=float32, numpy=array([-1.376577  ,  2.8885336 ,  0.19852689], dtype=float32)>,
 <tf.Variable 'dense_2/kernel:0' shape=(3, 1) dtype=float32, numpy=
 array([[-78.7569  ],
        [-23.602606],
        [ 84.29587 ]], dtype=float32)>,
 <tf.Variable 'dense_2/bias:0' shape=(1,) dtype=float32, numpy=array([8.521169], dtype=float32)>]

0.44350433910=90.443504

Mein Modell finden Sie hier und Sie können es auch online ausprobieren! in einer Google Colab-Umgebung.


6

2 Gewichte

ϵ>0

xyeϵx+ϵy+eϵxϵyeϵxϵyeϵx+ϵy4ϵ2.

ϵ=0.01

{±ϵ,±(4ϵ2)1}{±ϵ,(4ϵ3)1}±(4ϵ2)1=±ϵ(4ϵ3)1. Wie ich oben in einem Kommentar erwähnt habe, kann jedes neuronale Netz mit Gewichten in Maschinengenauigkeit zu einem (riesigen!) Neuronalen Netz mit nur zwei unterschiedlichen Gewichten golfen werden. Ich habe dieses Verfahren angewendet, um den folgenden MATLAB-Code zu schreiben:

function z=approxmultgolfed(x,y)

w1 = 0.1;   % first weight
w2 = -w1;   % second weight

k  = 250000;
v1 = w1*ones(k,1);
v2 = w2*ones(k,1);

L1 = w1*eye(2);
L2 = [ w1 w1; w2 w2; w1 w2; w2 w1 ];
L3 = [ v1 v1 v2 v2 ];
L4 = v1';

z = L4 * L3 * exp( L2 * L1 * [ x; y ] );

{±0.1}

Mit nur 1 Gewicht (!) Durchkommen

{±0.1}0.10.1

0.1x=wwx,

w100.110.5

{±10k}10k

(Vielleicht sollten wir ändern, wie wiederverwendete Gewichte in zukünftigen Herausforderungen des Golfsports mit neuronalen Netzen gewertet werden.)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.