суботу, 23 вересня 2017 р.

Геометричний погляд на перцептрони

Двопогоровий нейрон

  • Спочатку обчислити зважену суму входових даних від інших нейронів (плюс зсув).
  • Тоді видати 1, якщо зважена сума більше нуля.
\begin{equation}\label{eq:z_def} z = b + \sum_i x_i w_i \end{equation} \begin{equation}\label{eq:y_def} y = \begin{cases} 1, \mbox{якщо}\ z \ge 0\\ 0, \mbox{інакше} \end{cases} \end{equation} Тут ми не зважатимемо на зсув, бо ми можемо розглядати зсув як вагу для ще одного входового елементу, що постійно дає 1.

Простір-ваг.

  • Цей простір має один вимір на кожну вагу.
  • Вектор (точка) в цьому просторі представляє якийсь набір ваг.
Припускаючи, що ми не зважаємо на зсуви, ми можемо розглядати кожен тренувальний випадок як гіперплощину крізь початок координат. Ваги мусять лежати з одного боку цієї гіперплощини, щоб відповідь була правильною.
Погляньмо, що ж це означає матиематично. припустимо, що у нас на вході є вектор з координатами $\vec{v} = (a, b)$. Щоб цей вектор повертав правильну відповідь, згідно з \ref{eq:z_def} і \ref{eq:y_def} нам потрібно, щоб $\vec{v}\cdot\vec{w} \ge 0$. Тут $\vec{w}$ - це вектор ваг з координатами $(x ,y)$. Тобто, нам треба, щоб $ax+by \ge 0$. Якщо переписати нерівність як $y = -\frac{a}{b} x$, то стає зрозуміло, що підхожі набори ваг лежать з боку цієї гіперплощини куди вказує входовий вектор.

Конус допустимих розв'язків

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

Процедура навчання

Вибираємо тренувальні випадки за алгоритмом згідно з яким зрештою ми переберемо усі тренувальні випадки.
  • Якщо вихід правильний, залишаємо ваги без змін.
  • Якщо результат - це помилковий 0, додаємо входовий вектор до ваг.
  • Якщо результат - це помилкова 1, віднімаємо входовий вектор від ваг.
Ця процедура гарантує знаходження набору ваг, який видає правильний резльтат для всіх тренувальних випадків, якщо такий набір існує.

Чому процедура працює

Розглянемо піднесену до квадрату відстань $d_a^2+d_b^2$ між будь-яким допустимим вектором ваг і поточним вектором ваг. Нам би хотілось щоб кожного разу коли перцептрон помиляється ми наближали поточний вектор ваг до всіх допустимих векторів ваг. Але це не так. Тому ми розглянемо щедро допустимі вектори ваг, що лежать в допустимомій області із відступом не менше ніж довжина входового вектора, який визначає гіперплощину-обмеження.

Кілька слів про доведення

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

Приклад програми, що реалізує цей алгоритм навчання.

Тут я подаю приклад програми, яка вчиться розрізняти лінійно роздільну множину. Насправді більшість коду - це виведення графіки в gif-файл.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
ds = [1 1; 2 -2; -1 -1.5; -2 -1; -2 1; 3 -1];
class = [1 -1 -1 -1 1 1];

ds_neg = ds(class==-1,:);
ds_pos = ds(class==1,:);

w = [0 1 0.5]';
eta = 0.2;

count = size(ds, 1);

y = @(w, x) -(w(2)*x+w(1))/w(3);

h = figure;
epoch = 0;
% тут ми припускаємо, що множини лінійно роздільні, тому
% ми зупинимось лише коли не буде помилок
stillMistakes = true;
while stillMistakes
    for i = 1:count 
        x = [1 ds(i, :)];
        if class(i) * x * w < 0
            w = w + class(i) * eta * x';
        end

        hold off
        plot([-5, 5], [y(w, -5), y(w, 5)]);
        title(strcat('епоха', {' '}, num2str(epoch)));
        hold on
        scatter(ds_neg(:,1), ds_neg(:,2), 'o');
        scatter(ds_pos(:,1), ds_pos(:,2), '+');
        plot(x(2),x(3), 'o', 'MarkerSize',15);
        axis([-5, 5, -5, 5]);

        drawnow
        frame = getframe(1);
        im{epoch * count + i} = frame2im(frame);
    end
    epoch = epoch + 1;
    
    % як множину для перевірки ми використовуємо усю тренувальну множину
    stillMistakes = false;
    for i = 1:count
        x = [1 ds(i, :)];
        if class(i) * x * w < 0
            stillMistakes = true;
            break
        end
    end
end
close;

fn = 'linearlySeparable.gif';
for idx = 1:(epoch * count)
    [A,map] = rgb2ind(im{idx},256);
    if idx == 1
        imwrite(A,map,fn,'gif','LoopCount',Inf,'DelayTime',1);
    else
        imwrite(A,map,fn,'gif','WriteMode','append','DelayTime',1);
    end
end

Немає коментарів:

Дописати коментар