Машинне навчання гольфу: множення


68

Я хотів би запропонувати цій громаді інший вигляд гольфу:

(Штучні) Нейронні мережі - дуже популярні моделі машинного навчання, які можуть бути розроблені та навчені наближати будь-яку задану (зазвичай невідому) функцію. Їх часто використовують для вирішення дуже складних завдань, які ми не знаємо, як алгоритмічно вирішити розпізнавання мови, певні види класифікації зображень, різні завдання в автономних системах керування, ... Для грунтовки в нейронних мережах вважайте це відмінним Стаття у Вікіпедії .

Оскільки це перше з того, що я сподіваюсь стати серією машинного навчання гольфу, я хотів би зробити все максимально простим:

Мовою та рамками на ваш вибір, спроектуйте та навчіть нейронну мережу, яка за даними обчислює їх добуток для всіх цілих чисел між (і включаючи) та .(x1,x2)x1x2x1,x21010

Мета виконання

Щоб отримати право, ваша модель може не відхилятися більш ніж на від правильного результату в будь-якому з цих записів.0.5

Правила

Ваша модель

  • повинна бути "традиційною" нейронною мережею (значення вузла обчислюється як зважена лінійна комбінація деяких вузлів попереднього шару з наступною функцією активації),
  • може використовувати лише такі стандартні функції активації:
    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
  • повинно сприймати або як тупель / вектор / список / ... цілих чисел, або плаває як єдиний вхід,(x1,x2)
  • повернути відповідь як ціле число, float (або відповідний контейнер, наприклад, вектор або список, що містить цю відповідь).

Ваша відповідь повинна містити (або посилатися на) весь код, необхідний для перевірки ваших результатів - включаючи навчені ваги вашої моделі.

Оцінка балів

Виграє нейронна мережа з найменшою кількістю ваг (включаючи зміщення ваг).

Насолоджуйтесь!


9
Ласкаво просимо на сайт! Я думаю, що цей виклик міг би отримати користь від більш чіткого визначення нейронної мережі. Тут є кілька речей 1) Було б дуже приємно, щоб ви заявили це мовою, яка вже не передбачає знання NN 2) Ви дійсно повинні перелічити функції активації у своєму дописі, а не посилатися на зовнішнє джерело ( зовнішні посилання можуть змінюватися або зникати).
Пшеничний майстер

4
Чи можемо ми повторно використовувати ваги / використовувати звивисті шари? (Я рекомендую зняти бонус, оскільки він нічого не додає до виклику і просто відволікає від головної мети.) Ваги повинні бути справжніми чи вони можуть бути складними?
недолік

4
У вашому формулюванні випливає, що вузли з шару 3 не можуть використовувати входи з шару 1. Чи варто важити, щоб вузол рівня 2 просто зробити f(x) = xдля пересилання свого введення?
Гримі

4
У правому стовпці повинно бути посилання на пісочницю, яка була створена прямо для виправлення подібних питань, перш ніж питання навіть розміщене на головному сайті. І філософія мережі полягає в тому, що краще закрити питання, виправити його і знову відкрити, ніж отримати купу відповідей, які або не матимуть сенсу після виправлення питання, або будуть жорстко обмежувати зміни, які можуть бути внесені до питання. .
Пітер Тейлор

7
Зовсім ні. Такі проблеми виявляються багаторічним досвідом бачення інших помилок. Деякі неоднозначності прослизають повз пісочницю, але багато інших потрапляють туди. І це, безумовно, було б спіймане, тому що, як зазначено в моєму першому коментарі, у нас були точно такі ж проблеми з питанням нейронної сітки два місяці тому.
Пітер Тейлор

Відповіді:


37

21 13 11 9 ваг

Це засновано на поляризаційній тотожності білінеарних форм, яка в одновимірному реальному випадку зводиться до поліноміальної ідентичності:

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

Тож y1просто обчислюємо, [x+y, x-y]використовуючи лінійне перетворення, і y3це просто абсолютне значення y1як крок попередньої обробки для наступного: Тоді "тверда" частина обчислює квадрати, які я поясню нижче, а після цього просто обчислюємо різницю та масштабування, яка знову лінійна операція.

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

Спробуйте в Інтернеті!


Я думаю, що ваш код перевірки в нижньому колонтитулі посилання TIO не вистачає програми abs. Але все одно добре.
Крістіан Сіверс

@ChristianSievers Дякую, я оновив посилання TIO!
недолік

Я не фахівець з НН, з цікавості, як робиться підрахунок ваги? y0потребує 4, y1потребує 2, y3потребує 2, y4потребує 1, y5потребує 1 і y6потребує 2. Це 12?
Маргарет Блум

3
@MargaretBloom Так, це дійсно трохи незвично, але в коментарях ОП сказали, що ми можемо повторно використовувати ваги і тільки коли-небудь доводиться їх рахувати один раз, навіть якщо ми використовуємо одну і ту ж вагу кілька разів. Отже всі ваги, які я використовую, визначені в першій частині функції.
недолік

31

7 ваг

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

Спробуйте в Інтернеті!

ϵex1+x+x22

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

ϵepsc


1
Не впевнений, що це вважається "традиційною нейронною мережею" (правило №1), але очевидно, що її можна переформатувати в одну, тому я не бачу з цим проблеми. Приємне рішення!
Стефан Мескен

1
Ви можете визначити C = -B(1 вага), а потім мати [e_s, e_d] = conv([A,B,C], [eps, eps])(2 ваги), щоб зберегти одну вагу :) (BTW: Дуже розумний підхід!)
недолік

(Я забув додати exp)
flawr

4
Ви можете навіть набагато знизитись, повторно використовуючи ваги - вам не доведеться рахувати одну і ту ж вагу кілька разів.
недолік

2
@flawr Це приємний трюк, але я думаю, що припущення щодо згортання та повторного використання ваг у коментарях роблять це настільки іншим завданням, що я буду тримати цю відповідь такою, якою є.
xnor

22

33 31 ваг

# 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";

Спробуйте в Інтернеті!

Це робить довге множення в (сорти) двійковій формі, і таким чином повертає точний результат. Слід скористатись вікном помилки 0,5 для гольфу це ще декілька, але я не знаю як.

Шари 1 - 6 розкладають перший вхід на 5 "біт". З міркувань гольфу ми не використовуємо фактичні бінарні дані. Найбільш значущий "біт" має вагу -15 замість 16, а коли вхід 0, всі "біти" становлять 0,5 (що все одно чудово виходить, оскільки він зберігає ідентичність inputA = -15*a15 + 8*a8 + 4*a4 + 2*a2 + 1*a1).


1
Я очікував, що хтось придумає жорстко закодований алгоритм множення, визначений ANN. Але я не думав, що це буде перша відповідь. Молодці! (Мені також хочеться дізнатися, чи зможете ви вирішити щось подібне за допомогою набору даних MNIST чи якоїсь іншої, більш відносної проблеми ML: D).
Стефан Мескен

14

43 ваги

Два рішення, опубліковані до цього часу, були дуже розумними, але їх підходи, швидше за все, не спрацюють з більш традиційними завданнями машинного навчання (наприклад, OCR). Отже, я хотів би подати «загальне» (без розумних хитрощів) рішення цього завдання, яке, сподіваємось, надихає інших людей на вдосконалення та всмоктування у світ машинного навчання:

Моя модель - це дуже проста нейронна мережа з двома прихованими шарами, вбудованими в TensorFlow 2.0 (але будь-який інший фреймворк також може працювати):

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')
])

Як ви бачите, всі шари щільні (що, звичайно, не є оптимальним), функція активації є tanh (що насправді може бути нормально для цього завдання), за винятком вихідного шару, який, в силу характеру цього завдання, має лінійну функцію активації.

Є 43 ваги:

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

1010

Далі я їх детально налаштував - оптимізуючи максимальне відхилення для будь-якої з цілих задач на множення. На жаль, мої нотатки не показують дуже тонкої настройки, що я закінчив, але це було дуже незначно. По сусідству 100 епох на цих 441 навчальних зразках, розмір партії - 441.

Це ваги, з якими я закінчив:

[<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

Мою модель можна знайти тут, і ви також можете спробувати її в Інтернеті! в середовищі Google Colab.


6

2 ваги

ϵ>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. Як я вже згадував у коментарі вище, кожна нейронна сітка з вагами з машинною точністю може бути зібрана в гольф (величезну!) Нейронну сітку лише з двома різними вагами. Я застосував цю процедуру для написання наступного коду MATLAB:

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}

Як втекти лише 1 вагою (!)

{±0.1}0.10.1

0.1x=wwx,

w100.110.5

{±10k}10k

(Можливо, ми повинні модифікувати те, як оцінюються повторно використані ваги в майбутніх завданнях з нейрової сітки з гольфу.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.