Начиная знакомство с нейронными сетями и нейронами в частности, зачастую трудно представить как «оно» работает прочитав сухую теорию. Поэтому я предлагаю вам рассмотреть нейрон решающей простую, но очень наглядную задачу.
Задача: Определить четность или нечетность числа по его двоичному представлению.
Собственно говоря это задача классификации объектов с которой нейронные сети справляются достаточно хорошо (нейронные сети могут решать ограниченный спектр задач). Приступим.
Нейрон — составная часть любой нейронной сети. В состав нейрона входят умножители (входы), сумматор и нелинейный преобразователь. Входы осуществляют связь между нейронами и умножают входной сигнал на число, характеризующее силу связи, весовой коэффициент входа. Сумматор выполняет сложение сигналов, поступающих по входным связям от других нейронов, и внешних входных сигналов. Нелинейный преобразователь реализует нелинейную функцию одного аргумента — выхода сумматора. Эта функция называется «функция активации» или «передаточная функция» нейрона. Нейрон в целом реализует скалярную функцию векторного аргумента.
Синтез сети является в общем случае сложной задачей. В нашем случае для решения этой задачи нам хватит сети из одного нейрона. На входы нейрона будем подавать соответствующие разряды числа в двоичном виде (для простоты ограничимся диапазоном от 0 до 255). Т.е. наш нейрон будет иметь восемь входов и один выход.
Теперь необходимо определиться с передаточной функцией и видом весовых коэффициентов входов. Веса могут быть различными: 1 или 0, целочисленными, либо вещественными (с различными ограничениями). Мы будем использовать вещественные не отрицательные веса. В качестве передаточной функции будем использовать пороговую функцию с порогом равному среднему весу в нейроне. Т.е. если сумма входов умноженных на веса больше среднего веса то нейрон вернет единицу, в противном случае ноль.
Теперь определим как мы будем модифицировать весовые коэффициенты во время работы нейрона. Если ответ нейрона был верным необходимо увеличить веса входов которые были активированы, и уменьшить в противном случае. К исходному весу нейрона мы будем прибавлять (вычитать) произведение соответствующего входа на исходный вес и на коэффициент обучения и все это деленное на сумму весов нейрона. Коэффициент обучения влияет на скорость обучения, но его увеличение может привести к ошибочной работе нейрона.
Реализация
Класс на C# реализующий наш нейрон.
Обучение нейрона организовать очень просто, нужно альтернативным способом проверить четность (нечетность) числа и сказать ему верный или нет был его ответ. В итоге у правильного обученного нейрона все веса кроме веса при первом разряде (собственно если он равен 1 число нечетное, если 0 то четное) станут крайне малыми, что говорит о том что они не как не влияют на четность (нечетность), а этот единственный вес будет расти, что говорит о его значимости. Наш нейрон безошибочно будет работать в выбранном диапазоне чисел уже после пары тысяч обучающих прогонов, а вот если, например, веса поменять на 1 и 0, и изменять соответственно на единицу то нейрон обучить можно за десяток прогонов и у обученного нейрона все веса кроме веса при первом разряде будут равны 0, а он будет равен 1.
Задача: Определить четность или нечетность числа по его двоичному представлению.
Собственно говоря это задача классификации объектов с которой нейронные сети справляются достаточно хорошо (нейронные сети могут решать ограниченный спектр задач). Приступим.
Немного теории
Нейрон — составная часть любой нейронной сети. В состав нейрона входят умножители (входы), сумматор и нелинейный преобразователь. Входы осуществляют связь между нейронами и умножают входной сигнал на число, характеризующее силу связи, весовой коэффициент входа. Сумматор выполняет сложение сигналов, поступающих по входным связям от других нейронов, и внешних входных сигналов. Нелинейный преобразователь реализует нелинейную функцию одного аргумента — выхода сумматора. Эта функция называется «функция активации» или «передаточная функция» нейрона. Нейрон в целом реализует скалярную функцию векторного аргумента.
Синтез сети
Синтез сети является в общем случае сложной задачей. В нашем случае для решения этой задачи нам хватит сети из одного нейрона. На входы нейрона будем подавать соответствующие разряды числа в двоичном виде (для простоты ограничимся диапазоном от 0 до 255). Т.е. наш нейрон будет иметь восемь входов и один выход.
Теперь необходимо определиться с передаточной функцией и видом весовых коэффициентов входов. Веса могут быть различными: 1 или 0, целочисленными, либо вещественными (с различными ограничениями). Мы будем использовать вещественные не отрицательные веса. В качестве передаточной функции будем использовать пороговую функцию с порогом равному среднему весу в нейроне. Т.е. если сумма входов умноженных на веса больше среднего веса то нейрон вернет единицу, в противном случае ноль.
Теперь определим как мы будем модифицировать весовые коэффициенты во время работы нейрона. Если ответ нейрона был верным необходимо увеличить веса входов которые были активированы, и уменьшить в противном случае. К исходному весу нейрона мы будем прибавлять (вычитать) произведение соответствующего входа на исходный вес и на коэффициент обучения и все это деленное на сумму весов нейрона. Коэффициент обучения влияет на скорость обучения, но его увеличение может привести к ошибочной работе нейрона.
Реализация
Класс на C# реализующий наш нейрон.
- public class Neuron
- {
- int[] input = new int[8]; //входы
- double[] weight = new double[8]; //веса входов
-
- double mx = 0; //средний вес
- double sum = 0; //сумма входов умноженных на вес
- int k = 1; //коэфициент обучения
-
- public Neuron()
- {
- for (int i = 0; i < 8; i++)
- {
- weight[i] = 1; //начальные веса можно задать любые
- //согласно с принятыми ограничениями
- }
- }
- public byte Analyze(byte b)
- {
- for (int i = 0; i < 8; i++)
- {
- input[i] = 0;
- }
- for (int i = 0; i < 8; i++) //кривенькое преобразование в двоичный вид
- {
- if ((b / 2) != 0)
- {
- input[i] = b % 2;
- b = Convert.ToByte(b / 2);
- }
- else
- {
- input[i] = b % 2;
- break;
- }
- }
-
- sum = 0;
- mx = weight.Sum() / 8; //считаем средний вес
-
- for (int i = 0; i < 8; i++)
- {
- if (weight[i] > 0) //считаем сумму
- sum += weight[i] * input[i];
- }
-
- if (sum > mx) //определяем выход нейрона
- return 1;
- else
- return 0;
- }
- public void Yes() //обучение при верном ответе
- {
- double s = weight.Sum();
- for (int i = 0; i < 8; i++)
- {
- weight[i] += input[i] * (weight[i] / s) * k;
- }
- this.Normalize();
- }
- public void No() //обучение при неверном ответе
- {
- double s = weight.Sum();
- for (int i = 0; i < 8; i++)
- {
- weight[i] -= input[i] * (weight[i] / s) * k;
- }
- this.Normalize();
- }
- private void Normalize() //чтобы коэфициенты не зашкаливали и для наглядности
- { //оставляем в них не более двух разрядов до запятой
- double min = weight.Min();
- double max = weight.Max();
- int pow = 0;
- double b = max / 10;
- while (b > 1)
- {
- b = b / 10;
- pow++;
- }
-
- for (int i = 0; i < 8; i++)
- {
- weight[i] = weight[i] / Math.Pow(10, pow - 1);
- }
- }
- public void Reset() //сброс весов
- {
- for (int i = 0; i < 8; i++)
- {
- weight[i] = 1;
- }
- }
- //свойства по вкусу
- public string Bytes
- {
- get
- {
- string str = "";
- for (int i = 0; i < 8; i++)
- {
- str += input[i].ToString();
- }
- return str;
- }
- }
- public string Weights
- {
- get
- {
- string str = "";
- for (int i = 0; i < 8; i++)
- {
- str += weight[i].ToString();
- str += " ";
- }
- return str;
- }
- }
- public string Mx
- {
- get
- {
- return mx.ToString();
- }
- }
- public string Sum
- {
- get
- {
- return sum.ToString();
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Замечания
Обучение нейрона организовать очень просто, нужно альтернативным способом проверить четность (нечетность) числа и сказать ему верный или нет был его ответ. В итоге у правильного обученного нейрона все веса кроме веса при первом разряде (собственно если он равен 1 число нечетное, если 0 то четное) станут крайне малыми, что говорит о том что они не как не влияют на четность (нечетность), а этот единственный вес будет расти, что говорит о его значимости. Наш нейрон безошибочно будет работать в выбранном диапазоне чисел уже после пары тысяч обучающих прогонов, а вот если, например, веса поменять на 1 и 0, и изменять соответственно на единицу то нейрон обучить можно за десяток прогонов и у обученного нейрона все веса кроме веса при первом разряде будут равны 0, а он будет равен 1.