- Сформулировать конкретную гипотезу относительно данной закономерности. Я решил, что лучше всего подойдёт предположение, что наш мозг использует систему счисления, основанную на разложении чисел на степени золотого сечения, так как некоторые её особенности очень близки работе примитивных нейросетей: дело в том, что степени золотого сечения более высокого порядка можно разложить бесконечным числом способов в суммы степеней менее высокого порядка и даже отрицательных степеней. Таким образом, более высокая степень как бы «возбуждается» от нескольких низших степеней, тем самым проявляя то самое сходство с нейросетью.
- Описать конкретный способ её проверки: я выбрал мат. моделирование эволюции мозга посредством случайных изменений в простейшей возможной нейросети — матрице линейного оператора.
- Составить критерии подтверждения гипотезы. Моим критерием было то, что система счисления, основанная на золотом сечении, реализуется на нейросетевом движке при тех же объёмах информации с меньшим числом ошибок, чем двоичная.
Так как речь идёт о программировании, опишу поподробнее второй и третий пункты.
Для моделирования случайных изменений в мозге в процессе эволюции я использовал функцию rand_s(), так как она криптографически устойчива, и, соответственно, даст «более случайный» результат. Также я использовал в качестве критерия того, что нейросеть достигла при обучении наименьшего числа ошибок то, что при отклонении матрицы в случайные стороны на небольшие значения её произведение на вектор меняется на примерно одинаковый модуль.
Что же касается кодировки данных в самом векторе, то я использовал 28-мерный вектор для двух 14-значных бинарных чисел и их суммы (после первых 14 знаков в сумме идёт просто 14 нулей для заполнения) и 40-мерный вектор для двух чисел в системе с золотым сечением.
Входной файл же имеет следующий формат.
Первая строка — два целых числа, разделённых пробелом, размерность вектора и количество элементов в обучающей выборке.
Все последующие строки: первая строка — вход нейросети, вторая — правильный результат обработки.
Вот фрагмент кода нейросети, отвечающий за её обучение на выборке из входных данных и соответствующих им правильным результатам:
while (((d-mu)*(d-mu)>0.01)||(q<10)) //Нейросеть обучается, пока отклонение результата от правильного не станет "топтаться" на ровном месте
{
s=0; //Инициализирую переменную для хранения суммы квадратов разностей результата нейросети с правильным
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
(*(z+k*n+i))=0;
}
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
(*(z+k*n+i))=(*(z+k*n+i))+(*(a+i*n+j))*(*(x+k*n+j));
}
} // Вычисляем результат умножения матрицы нейросети на вектор
for (i=0;i<n;i++)
{
s=s+((*(z+k*n+i))-(*(y+k*n+i)))*((*(z+k*n+i))-(*(y+k*n+i)));
} //Вычисляем сумму квадратов разностей между результатом нейросети и правильным
s1=s+1;
}
while (s<s1) //Нейросеть пробует изменять матрицу в разные стороны, пока сумма квадратов не будет меньше, чем была раньше
{
s1=0; //Инициализируем переменную для новой суммы квадратов
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
(*(z+k*n+i))=0;
}
}//Инициализируем массив (вектор) для хранения результата умножения матрицы
rand_s(&p);
k1 = (int) (p/((int) (UINT_MAX/n)));
rand_s(&p);
k2 = (int) (p/((int) (UINT_MAX/n)));
//Генерируем координаты изменяемой "связи" между нейронами, то есть элемента матрицы
rand_s(&p);
h=((double) p/UINT_MAX)-0.5; //Генерируем шаг
h1=1;
rand_s(&p);
l=((int) ((double) p/UINT_MAX)*20);
for (i=0;i<l;i++)
{
h1=h1/10;
}
h=h*h1;
//Делаем, чтобы шаг более равномерно пробегал различные порядки
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
if ((i==k1)&&(j==k2))
(*(z+k*n+i))=(*(z+k*n+i))+(*(a+i*n+j))*(*(x+k*n+j))+h*(*(x+k*n+j));
else
(*(z+k*n+i))=(*(z+k*n+i))+(*(a+i*n+j))*(*(x+k*n+j));
}
}
//Вычисляем результат умножения изменённой матрицы на вектор
for (i=0;i<n;i++)
{
s1=s1+((*(z+k*n+i))-(*(y+k*n+i)))*((*(z+k*n+i))-(*(y+k*n+i)));
}//Вычисляем сумму квадратов разностей между результатом изменённой нейросети и правильным результатом
}
}
(*(a+k1*n+k2))=(*(a+k1*n+k2))+h;
//Изменяем нейросеть после нахождения успешного варианта с меньшей ошибкой в результате
s1=0;
d=0;
for (k1=0;k1<n;k1++)
{
for (k2=0;k2<n;k2++)
{
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
(*(z+k*n+i))=0;
}
}
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
if ((i==k1)&&(j==k2))
(*(z+k*n+i))=(*(z+k*n+i))+((*(a+i*n+j))+0.1)*(*(x+k*n+j));
else
(*(z+k*n+i))=(*(z+k*n+i))+(*(a+i*n+j))*(*(x+k*n+j));
}
}
}
s1=0;
for (k=0;k<m;k++)
{
for (i=0;i<n;i++)
{
s1=s1+((*(z+k*n+i))-(*(y+k*n+i)))*((*(z+k*n+i))-(*(y+k*n+i)));
}
}
d=d+(s1-s)*(s1-s)/(n*m);// Вычисляем средний квадрат изменения результата при изменении элемента матрицы
}
}
mu=mu*((double) q/(q+1))+((double) d/(q+1));//Вычисляем среднее значение квадрата изменения за несколько прошедших циклов
q=q+1;
printf("%lf \n",mu);//Выводим на экран это самое среднее для отладки кода
}
Входные данные я также генерировал случайным образом, это были вещественные числа от нуля до единицы. Также, кроме обучающей выборки я сгенерировал ещё и тестовую выборку, на которой испытал свою нейросеть. Кроме того, я для каждого полученного нейросетью результата вычислил среднеквадратическую ошибку, то есть корень из среднего квадрата разности между элементами вектора, полученного нейросетью и вектора, содержащего правильный результат.
В результате у меня получилось по 1000 средних ошибок для результата работы нейросети со сложением в двоичной и основанной на золотом сечении системах счисления. Размерность вектора я подобрал таким образом, чтобы в них хранилось примерно одинаковое число информации как внутри системы счисления, так и между ними.
Я сравнил ошибки в разных системах счисления парными t-тестами и вот, что у меня получилось.
Сравнение: Золотое сечение — двоичная система
Гипотеза: Ошибка при золотом сечении в среднем меньше.
Результаты:
t = -22.033
df = 999
p<0.001
Cohen's d = -0.697 (При золотом сечении ошибка меньше)
99% доверительный интервал для Cohen's d:
от -inf до -0.615
Тест на нормальность распределения Шапиро — Уилка:
W = 0.998 p=0.382 (распределения примерно соответствуют нормальному)
Дескриптивная статистика:
Золотое сечение:
Среднее арифметическое: 0.365
Стандартное отклонение: 0.044
Двоичная система:
Среднее арифметическое: 0.414
Стандартное отклонение: 0.055
Все данные, использованные в данном небольшом кустарном исследовании, я решил оставить пока у себя как доказательства того, что я не нарисовал цифры с потолка. Кто попросит, тому могу отправить.
Теперь к выводам. Так как нейросети, обучение которых построено на случайном изменении связей между нейронами и отборе наилучших из них (как во время эволюции), в данном случае показали, что они значимо лучше справляются с золотым сечением, чем с двойкой в качестве основания системы счисления при одинаковом количестве информации, то можно предположить, что и эволюция мозга животных, и, в частности, человека, шла по похожему пути.
UPD. С момента публикации автор провёл новое исследование, в котором учёл поправку на количество измерений и влияние основания системы счисления отдельно от его расстояния до золотого сечения при помощи линейной регрессии. Результат оказался неутешительным: близость основания к золотому сечению скорее увеличивает ошибку, чем уменьшает её, так что сенсация, как всегда, сорвалась.
Комментариев нет:
Отправить комментарий