AI / ИИ на Форте.

Работа с элементарной ИИ на Форте не является сложной задачей. Продемонстрируем это.

Возьмем простой пример со следующими исходными данными (обучающая выборка):

x1=0 x2=0 y_true=0
x1=0 x2=1 y_true=0
x1=1 x2=0 y_true=0
x1=1 x2=1 y_true=1

Можете заметить, что вводам x1 и x2 сопоставляется их логическое "И".

Подключим библиотеку для работы с вещественными числами.

S" lib/include/float2.f" INCLUDED

Нам понадобятся переменные w1, w2 - веса; b - смещение (сразу инициализируем все три нулями); x1, x2 - входные значения; learning_rate - шаг обучения, для определенности взят за "0.1"; y_true - соответствующее истинное значение из тестовой выборки.

FVARIABLE w1
FVARIABLE w2
FVARIABLE b
0E w1 F!
0E w2 F!
0E b F!
FVARIABLE x1 FVARIABLE x2
FVARIABLE learning_rate
0.1E learning_rate F!
FVARIABLE y_true

Напишем слово моделирующее поведение персептрона (назовем его "P"), которое просто вычисляет выход по формуле:

y_pred = x1*w1+x2*w2+b

затем сравниваем это значение с нулем, если меньше оставляем 0, иначе 1.
Таким образом мы совместили функцию активации с расчетом линейной комбинации для персептрона с двумя входами.

\ x1 w1 x2 w2 b P - формат вызова слова
: P ( F: x1 w1 x2 w2 b -> F: y_pred ) FSWAP FROT F* F+ FSWAP FROT F* F+ F0< IF 0E ELSE 1E THEN ;

Проверим работу слова на случайных данных:

1E 2E 3E 5E 1000E P F.
1.0000000 Ok

1*2+3*5+1000 = 1017, что явно больше нуля, следовательно, результат работы 1. Персептрон работает корректно.

Самое важное слово производящее тренировку персептрона назван - "T" (от слова Train). Входом служат исходные параметры x1, x2 и соответствующее им истинное значение, а вместо выхода - изменение переменных w1, w2 и b, что обозначено в комментарии стековой нотации буквой "V:" (от слова "VARIABLE").

: T ( F: x1 x2 y_true -> V: w1 w2 b ) \ x1 x2 y_true T - формат вызова слова
    y_true F! \ x1 x2 y_true -> x1 x2
    FOVER x1 F! FDUP x2 F! \ x1 x2 -> x1 x2 x1=x1 x2=x2
    w1 F@ FSWAP w2 F@ b F@ \ x1 x2 -> x1 w1 x2 w2 b
    P \ x1 w1 x2 w2 b -> y_pred
    y_true F@ FSWAP F- \ y_pred -> y_true-y_pred=error
    learning_rate F@ F* \ error -> error*learning_rate

    FDUP x1 F@ F* w1 F@ F+ w1 F! \ error*learning_rate -> error*learning_rate w1=error*learning_rate*x1+w1
    FDUP x2 F@ F* w2 F@ F+ w2 F! \ error*learning_rate -> error*learning_rate w2=error*learning_rate*x2+w2
    b F@ F+ b F! \ error*learning_rate -> b=error*learning_rate+b
    ." Обновленные веса: w1 = " w1 F@ F. ." w2 = " w2 F@ F. ." b = " b F@ F. CR
;

Первая строка - описание слова.
Вторая - сохранение значения "y_true" в одноименной переменной.
Третья, аналогично предыдущей, сохранение значений x1, x2.
Четвертая - получаем значения переменных w1, w2 и b, одновременно выстраиваем их в том порядке, которая соответствует стековой нотации слова "P".
Пятая вызываем "P" (работа которой описана выше).
В шестой считаем ошибку по формуле error = y_true-y_pred.
В седьмой умножаем на шаг обучения, чтобы в
в девятой, десятой и одиннадцатой корректируются значения переменных w1, w2 и b по формулам:

w1=error*learning_rate*x1
w2=error*learning_rate*x2
b=error*learning_rate

Двенадцатая - вывод значения обновленных весов (контрольная информация о работе слова).

Можно протестировать это слово на следующих данных:

0E 0E 0E T
0E 1E 0E T
1E 0E 0E T
1E 1E 1E T

Окончательно можем написать слово "epochs", которая проведет обучение на тестовой выборке.

: epochs ( N -> )
    0 DO
    ." Эпоха " I . CR
    0E 0E 0E T
    0E 1E 0E T
    1E 0E 0E T
    1E 1E 1E T
    CR
    LOOP ;

Она просто прогоняет N раз (количество эпох) обучение на тестовых данных с выводом результатов о проделанной работе.

Запустим его с параметром 10.

10 epochs
Эпоха 0
Обновленные веса: w1 = 0.1000000 w2 = 0.1000000 b = -0.1000000
Обновленные веса: w1 = 0.1000000 w2 = 0.0000000 b = -0.2000000
Обновленные веса: w1 = 0.1000000 w2 = 0.0000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.1000000

Эпоха 1
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.1000000
Обновленные веса: w1 = 0.2000000 w2 = 0.0000000 b = -0.2000000
Обновленные веса: w1 = 0.1000000 w2 = 0.0000000 b = -0.3000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 2
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 3
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 4
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 5
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 6
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 7
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 8
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Эпоха 9
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000
Обновленные веса: w1 = 0.2000000 w2 = 0.1000000 b = -0.2000000

Ok

Обратим внимание, что, начиная с Эпохи 2, веса w1 и w2, а также b не меняются (если предварительно не тестировать работу слова "T", то с Эпохи 3).

Проверим "натренированность" весов и смещения, для чего напишем последнее слово "PREDICT", которая будет предсказывать выход "y".

: PREDICT ( x1 x2 -> y. )
    w1 F@ FSWAP w2 F@ b F@ P F.
;

Работа которого заключается в том, чтобы подготовить параметры для слова "P". Проверим качество тренировки:

0E 0E PREDICT
0.0000000 Ok

0E 1E PREDICT
0.0000000 Ok

1E 0E PREDICT
0.0000000 Ok

1E 1E PREDICT
1.0000000 Ok

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

В слове "T" добавляем три строки преобразующие "x1 x2 y_true" из целочисленного в вещественный формат

: T ( x1 x2 y_true -> V: w1 w2 b ) \ x1 x2 y_true T - формат вызова слова
    ROT S>D D>F
    SWAP S>D D>F
    S>D D>F
    y_true F! \ x1 x2 y_true -> x1 x2
    FOVER x1 F! FDUP x2 F! \ x1 x2 -> x1 x2 x1=x1 x2=x2
    w1 F@ FSWAP w2 F@ b F@ \ x1 x2 -> x1 w1 x2 w2 b
    P \ x1 w1 x2 w2 b -> y_pred
    y_true F@ FSWAP F- \ y_pred -> y_true-y_pred=error
    learning_rate F@ F* \ error -> error*learning_rate

    FDUP x1 F@ F* w1 F@ F+ w1 F! \ error*learning_rate -> error*learning_rate w1=error*learning_rate*x1+w1
    FDUP x2 F@ F* w2 F@ F+ w2 F! \ error*learning_rate -> error*learning_rate w2=error*learning_rate*x2+w2
    b F@ F+ b F! \ error*learning_rate -> b=error*learning_rate+b
    ." Обновленные веса: w1 = " w1 F@ F. ." w2 = " w2 F@ F. ." b = " b F@ F. CR
;

Также изменится "epochs" которое вызывает уже исправленное "T".

: epochs ( N -> )
    0 DO
    ." Эпоха " I . CR
    0 0 0 T
    0 1 0 T
    1 0 0 T
    1 1 1 T
    CR
    LOOP ;

И наконец, в "PREDICT" преобразуем вход "x1 x2" в вещественный, а результат "y." из вещественного (точка в конце обозначает тот факт, что он печатается на экран, а не остается на стеке).

: PREDICT ( x1 x2 -> y. )
    SWAP S>D D>F
    S>D D>F
    w1 F@ FSWAP w2 F@ b F@ P F>D D>S .
;

результат тренировки будет выглядеть так:

0 0 PREDICT
0 Ok
0 1 PREDICT
0 Ok
1 0 PREDICT
0 Ok
1 1 PREDICT
1 Ok

 

 

Логическое "ИЛИ"

Возьмем другую серию данных. Теперь вводам сопоставим их логическое "ИЛИ".

x1=0 x2=0 y_true=0
x1=0 x2=1 y_true=1
x1=1 x2=0 y_true=1
x1=1 x2=1 y_true=1

в коде предыдущего примера изменится только слово:

: epochs ( N -> )
    0 DO
    ." Эпоха " I . CR
    0E 0E 0E T
    0E 1E 1E T
    1E 0E 1E T
    1E 1E 1E T
    CR
    LOOP ;

После обучения, слово PREDICT выдаст следующие результаты:

0E 0E PREDICT
0.0000000 Ok

0E 1E PREDICT
1.0000000 Ok

1E 0E PREDICT
1.0000000 Ok

1E 1E PREDICT
1.0000000 Ok

Проверьте это самостоятельно. Заметим, что с Эпохи 2 коэффициенты перестают меняться.

 


Операция "Исключающее ИЛИ"

Теперь исходные данные будут выглядеть так:

x1=0 x2=0 y_true=0
x1=0 x2=1 y_true=1
x1=1 x2=0 y_true=1
x1=1 x2=1 y_true=0

Аналогично изменится только слово "epochs":

: epochs ( N -> )
    0 DO
    ." Эпоха " I . CR
    0E 0E 0E T
    0E 1E 1E T
    1E 0E 1E T
    1E 1E 0E T
    CR
    LOOP ;

После обучения (тоже 10 эпох), получим:

0E 0E PREDICT
1.0000000 Ok

0E 1E PREDICT
1.0000000 Ok

1E 0E PREDICT
0.0000000 Ok

1E 1E PREDICT
0.0000000 Ok

Что не является правильным результатом. Даже после Эпохи 9 коэффициенты меняются. Можете по экспериментировать самостоятельно с гораздо большим количеством эпох. Результат аналогичный даже после 1000 эпох ("1000 epochs"). Задача "Исключающее ИЛИ" не разрешима на персептроне, необходимо более сложные модели для такой простой операции.

Чтобы получить корректные результаты для каждого примера вводите код заново в новое окно консоли, или инициализируйте переменные заново:

0E w1 F!
0E w2 F!
0E b F!

 


Пример 1 - логическое "И" (окончательный код, можно "копи пастить")

S" lib/include/float2.f" INCLUDED

FVARIABLE w1
FVARIABLE w2
FVARIABLE b
0E w1 F!
0E w2 F!
0E b F!
FVARIABLE x1 FVARIABLE x2
FVARIABLE learning_rate
0.1E learning_rate F!
FVARIABLE y_true

: P ( F: x1 w1 x2 w2 b -> F: y_pred ) FSWAP FROT F* F+ FSWAP FROT F* F+ F0< IF 0E ELSE 1E THEN ;

: T ( F: x1 x2 y_true -> V: w1 w2 b ) \ x1 x2 y_true T - формат вызова слова
y_true F! \ x1 x2 y_true -> x1 x2
FOVER x1 F! FDUP x2 F! \ x1 x2 -> x1 x2 x1=x1 x2=x2
w1 F@ FSWAP w2 F@ b F@ \ x1 x2 -> x1 w1 x2 w2 b
P \ x1 w1 x2 w2 b -> y_pred
y_true F@ FSWAP F- \ y_pred -> y_true-y_pred=error
learning_rate F@ F* \ error -> error*learning_rate

FDUP x1 F@ F* w1 F@ F+ w1 F! \ error*learning_rate -> error*learning_rate w1=error*learning_rate*x1+w1
FDUP x2 F@ F* w2 F@ F+ w2 F! \ error*learning_rate -> error*learning_rate w2=error*learning_rate*x2+w2
b F@ F+ b F! \ error*learning_rate -> b=error*learning_rate+b
." Обновленные веса: w1 = " w1 F@ F. ." w2 = " w2 F@ F. ." b = " b F@ F. CR
;

: epochs ( N -> )
0 DO
." Эпоха " I . CR
0E 0E 0E T
0E 1E 0E T
1E 0E 0E T
1E 1E 1E T
CR
LOOP ;

10 epochs

: PREDICT ( x1 x2 -> y. )
w1 F@ FSWAP w2 F@ b F@ P F.
;

0E 0E PREDICT

0E 1E PREDICT

1E 0E PREDICT

1E 1E PREDICT

 


Пример 2 - логическое "ИЛИ"

S" lib/include/float2.f" INCLUDED

FVARIABLE w1
FVARIABLE w2
FVARIABLE b
0E w1 F!
0E w2 F!
0E b F!
FVARIABLE x1 FVARIABLE x2
FVARIABLE learning_rate
0.1E learning_rate F!
FVARIABLE y_true

: P ( F: x1 w1 x2 w2 b -> F: y_pred ) FSWAP FROT F* F+ FSWAP FROT F* F+ F0< IF 0E ELSE 1E THEN ;

: T ( F: x1 x2 y_true -> V: w1 w2 b ) \ x1 x2 y_true T - формат вызова слова
y_true F! \ x1 x2 y_true -> x1 x2
FOVER x1 F! FDUP x2 F! \ x1 x2 -> x1 x2 x1=x1 x2=x2
w1 F@ FSWAP w2 F@ b F@ \ x1 x2 -> x1 w1 x2 w2 b
P \ x1 w1 x2 w2 b -> y_pred
y_true F@ FSWAP F- \ y_pred -> y_true-y_pred=error
learning_rate F@ F* \ error -> error*learning_rate

FDUP x1 F@ F* w1 F@ F+ w1 F! \ error*learning_rate -> error*learning_rate w1=error*learning_rate*x1+w1
FDUP x2 F@ F* w2 F@ F+ w2 F! \ error*learning_rate -> error*learning_rate w2=error*learning_rate*x2+w2
b F@ F+ b F! \ error*learning_rate -> b=error*learning_rate+b
." Обновленные веса: w1 = " w1 F@ F. ." w2 = " w2 F@ F. ." b = " b F@ F. CR
;

: epochs ( N -> )
0 DO
." Эпоха " I . CR
0E 0E 0E T
0E 1E 1E T
1E 0E 1E T
1E 1E 1E T
CR
LOOP ;

10 epochs

: PREDICT ( x1 x2 -> y. )
w1 F@ FSWAP w2 F@ b F@ P F.
;

0E 0E PREDICT

0E 1E PREDICT

1E 0E PREDICT

1E 1E PREDICT

 


Пример 3 - "Исключающее ИЛИ"

S" lib/include/float2.f" INCLUDED

FVARIABLE w1
FVARIABLE w2
FVARIABLE b
0E w1 F!
0E w2 F!
0E b F!
FVARIABLE x1 FVARIABLE x2
FVARIABLE learning_rate
0.1E learning_rate F!
FVARIABLE y_true

: P ( F: x1 w1 x2 w2 b -> F: y_pred ) FSWAP FROT F* F+ FSWAP FROT F* F+ F0< IF 0E ELSE 1E THEN ;

: T ( F: x1 x2 y_true -> V: w1 w2 b ) \ x1 x2 y_true T - формат вызова слова
y_true F! \ x1 x2 y_true -> x1 x2
FOVER x1 F! FDUP x2 F! \ x1 x2 -> x1 x2 x1=x1 x2=x2
w1 F@ FSWAP w2 F@ b F@ \ x1 x2 -> x1 w1 x2 w2 b
P \ x1 w1 x2 w2 b -> y_pred
y_true F@ FSWAP F- \ y_pred -> y_true-y_pred=error
learning_rate F@ F* \ error -> error*learning_rate

FDUP x1 F@ F* w1 F@ F+ w1 F! \ error*learning_rate -> error*learning_rate w1=error*learning_rate*x1+w1
FDUP x2 F@ F* w2 F@ F+ w2 F! \ error*learning_rate -> error*learning_rate w2=error*learning_rate*x2+w2
b F@ F+ b F! \ error*learning_rate -> b=error*learning_rate+b
." Обновленные веса: w1 = " w1 F@ F. ." w2 = " w2 F@ F. ." b = " b F@ F. CR
;

: epochs ( N -> )
0 DO
." Эпоха " I . CR
0E 0E 0E T
0E 1E 1E T
1E 0E 1E T
1E 1E 0E T
CR
LOOP ;

10 epochs

: PREDICT ( x1 x2 -> y. )
w1 F@ FSWAP w2 F@ b F@ P F.
;

0E 0E PREDICT

0E 1E PREDICT

1E 0E PREDICT

1E 1E PREDICT


Как и было сказано ранее отличие всех трех примеров только в коде слова "epochs".