Начитавшись тут всякой всячины решил дать то что многие хотят видеть. По крайней мере я так предполагаю, ну да ладно.
Ниже код, который вы наверное не воспримите в серьёз, но я попробую пояснить что это значит, а заодно его раскоментирую.
Это основная функция:
void work(unsigned char *, unsigned char *, unsigned char *, _Bool) |
|
[/code]
Как видите она ничего не возвращает, в этом нет необходимости. Что за параметры она принимает:
1 - Указатель на однобайтовую беззнаковую переменную, это вход нейросети. Вся функция это обработчик так сказать "мозгового модуля", надеюсь вы знакомы с тем, что он из себя представляет. Входное значение передаётся через этот указатель и получается для всех элементов входного буфера нужно вызвать эту функцию, так как каждый элемент обрабатывает один модуль.
2 - Указатель на буфер памяти(массив однобайтовых беззнаковых переменных размером в 255 элементов, можно больше, если будете использовать переменные размером более байта(значения в байте от 0 до 255)). Тоесть веса связей нейронов, это выходы. Почему только веса и почему выходы - нейрон это абстрация, это ячейка памяти со значением, более сложная реализация предполагает расчёт значения внутри нейрона по силе сигнала, направлению связи и куче факторов, которые я опустил. Все знают что в перцептроне есть пороговая функция? А кто знает что такое порог в нейроне? Так вот, порог это точность совпадения входящего сигнала с запомненным значением. Проще говоря нейрон это цифра и вход это цифра и если они равны, нейрон выстреливает сигнал по аксону. Это может заинтересовать того человека, что хочет сделать автомат с целеполаганием и конечным числом обрабатываемых ситуаций. Есть 2 пути - сложный с использованием памяти и простой с использованием вычислительных ресурсов.
3 - указатель на элемент буфера выходов(массива однобайтовых беззнаковых переменных). Как я писал, каждый нейрон имеет веса, это веса связей с выходами и только с выходами, почему только с выходами - соединения нейронов между собой реализует то что я назвал запоминанием значения. Это одна из оптимизаций расходования памяти, чтобы небыло в ней лишнего. Каждый модуль обрабатывает только один выход, чтобы получить несколько выходных значений следует соединять модули паралельно(о чём я напишу далее) и передавать указатели на разные элементы массива выходов.
4 - trm - тормозной сигнал (булева переменная длиной в байт), принимает значение 0 и 1. Почему только 0 и 1 - опять же для простоты. От неё зависят веса связей.
Ну параметры я описал, теперь другие параметры, неочевидные.
Нейрон - абстрактное число, что не хранится в памяти а является значением переменной используемой в цикле. Нейрон имеет в буфере памяти вес связи с выходом. Он реализован с одним аксоном(без коллатералей) в пределах модуля. Модули можно соединять последовательно и паралельно.
Поясняю - последовательное соединение означает, что выход предыдущего модуля передаётся на вход следующего, при этом обрабатывается только один выход(это количество памяти), пралельно - это когда несколько модулей имеют один вход, но обрабатывают много выходов(это качество памяти).
Первый параметр вход, принимает одно значение, один вход это вершина одного мозгового модуля, так же как выход - его окончание.
Мозговой модуль обрабатывает 255 состояний входа, тоесть по одному значению входа и одному весу на нейрон.
Выходы вычисляются средним арифметическим веса связи и значения элемента выходного буфера, опять же для простоты, не выходим за байт, не перешагиваем от 255 сразу к 0 при сложении и корректируем значение аккуратно.
В вычислении участвуют только вес связи нейрона который совпал с входным значением.
Это означает - одна ситуация, один ответ. Чтобы получить множество ответов на одну ситуацию, следует продлить цепочку модулей, соединив их паралельно.
Чтобы обрабатывать массивы, например изображения, на каждую RGB составляющую, тоесть на каждый байт следует создать по одному модулю.
Коррекция весов происходит в реальном времени через значение тормозного сигнала. Тоесть можно вставить функцию обратного вызова перед вычислением весов, изменяющую значение тормозного сигнала в соответствии с ситуацией.
Совокупность мозговых модулей можно назвать ядром. Ядро обрабатывает какой либо тип информации и также ядра могут быть соединены последовательно и паралельно. Программная прослойка между ядрами создаст зависимость тормозного сигнала одних ядер от выходов других или иначе говоря создаст ситуацию коррекции весов в ядре(поиск выхода из сложившейся ситуации).
Ядра объединяются в извилины, извилины в полушария, ну а полушария в мозг по той же схеме.
Теоретически, каждый модуль можно вычислять паралельно без негативных последствий. По этому думаю каждому модулю можно выделить собственный буфер памяти.
Минусы - нет идеального конечного числа состояний вычисляемых единой формулой, нет плавного перехода от одного выхода к другому, нет подавления входного сигнала в зависимости от его важности(но можно подавить выходы).
Конечно нет предела совершенству и я буду это дело переделывать ещё не один раз, но я надеюсь что вы уловили суть моего понимания функционирования интеллекта, мозга и нейронных сетей. Это наиболее простая версия кода, я её не проверял, набросал для наглядности.
void net_work(unsigned char *val, unsigned char *mem, unsigned char *out, _Bool trm)
{
unsigned char in;//переменная нейрона
for(in = 0; in < 255; in++)
{
if(in / 255 != 0)//проверка деления на ноль
{
if(*val / (in / 255) == 255)//вычисление степени сходства(аналогично с вычислением процентного сходства)
{
if(trm == 0 && mem[in] < 255)//
mem[in]++;
else if(trm == 1 && mem[in] > 0)
mem[in]--;
*out = (*out + mem[in]) / 2;
break;
}
}
}
}[/code]