Сделал простую нейронную сеть на C#. Все работает нормально с маленьким примером, а если я создаю 10 входных нейронов да еще скрытых слоев 2. То у меня до идеального ответа пройдет эпох 700. И каждый раз разное значение.
Я пытался изменять гиперпараметры, но безуспешно.
Пожалуйста посмотрите мой код и подскажите что я сделал не так.
И если не сложно может кто нибудь объяснить где у меня ошибка в поиске ошибки нейронной сети. Я все вроде делал по статье, но не получается.
P.s вообще идея была что на вход подается 9 значений (это 0 или 1 в зависимости от того закрашена клетка или нет). И если в поле 3 на 3 палка вертикальная, то нейронная сеть говорит что палка вертикальная ну или горизонтальная.
Вот ссылка на статью: https://m.habr.com/ru/post/312450/
А вот сам код:
static Random r;
static void Main(string[] args)
{
//Входные данные
double[,] inputData = new double[,]
{
{1,0,0,1,0,0,1,0,0},
{1,1,1,0,0,0,0,0,0},
{0,1,0,0,1,0,0,1,0},
{0,0,0,1,1,1,0,0,0},
{0,0,1,0,0,1,0,0,1},
{0,0,0,0,0,0,1,1,1}
};
/*inputData = new double[,]
{
{0,1,1}, {0,0,1},{1,0,1},{0,0,0},{1,0,0},{0,1,0},{1,1,0},{1,1,1}
};*/
//Идеальные значения
double[] outputData = new double[] { 1, 0, 1, 0, 1, 0 };
//outputData = new double[] { 1, 1, 0, 0, 0, 0, 0, 1 };
//Значение момента
double a = 0.8;
//Скорость обучения
double E = 0.8;
//Все нейроны
double[][,] allNeurons = new double[][,]
{
new double[10,1], new double[6,2], new double[4,2], new double[1,2]
//new double[4, 1], new double[1, 2]
};
//Все веса
double[][,,] allWeights = new double[][,,]
{
new double[10,5,2], new double[6,3,2], new double[4,1,2]
//new double[4,1,2]
};
r = new Random();
//Инициализация весов в промежутке от -0.5 до 0.5 случайным образом
for (int i = 0; i < allWeights.GetLength(0); i++)
for (int j = 0; j < allWeights[i].GetLength(0); j++)
for (int k = 0; k < allWeights[i].GetLength(1); k++)
allWeights[i][j, k, 0] = r.NextDouble() - 0.5;
double error = 0;
for (int epoch = 0; epoch < 1000; epoch++)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(epoch);
Console.ResetColor();
for (int stepTrain = 0; stepTrain < inputData.GetLength(0); stepTrain++)
{
//Инициализация входных нейронов
for (int i = 0; i < allNeurons[0].GetLength(0) - 1; i++)
allNeurons[0][i, 0] = inputData[stepTrain, i];
//Инициализация нейронов смещения
for (int i = 0; i < allNeurons.GetLength(0) - 1; i++)
allNeurons[i][allNeurons[i].GetLength(0) - 1, 0] = 1;
//Прямое прохождение по всем слоям
for (int i = 1; i < allNeurons.GetLength(0); i++)
{
int lastLayer = 1;
if (i == allNeurons.GetLength(0) - 1)
lastLayer = 0;
for (int k = 0; k < allNeurons[i].GetLength(0) - lastLayer; k++)
{
for (int j = 0; j < allNeurons[i - 1].GetLength(0); j++)
allNeurons[i][k, 0] += allWeights[i - 1][j, k, 0] * allNeurons[i - 1][j, 0];
allNeurons[i][k, 0] = Sigmoid(allNeurons[i][k, 0]);
}
}
//Нахождение ошибки у выходных нейронов
int index = allNeurons.GetLength(0) - 1;
for (int i = 0; i < allNeurons[index].GetLength(0); i++)
allNeurons[index][i, 1] = FixOutError(outputData, allNeurons[index][i, 0], stepTrain);
//Нахождение ошибок для скрытых нейронов
for (int i = allNeurons.GetLength(0) - 2; i >= 1; i--)
{
int lastLayer = 1;
if (i + 1 == allNeurons.GetLength(0) - 1)
lastLayer = 0;
for (int j = 0; j < allNeurons[i].GetLength(0); j++)
{
for (int k = 0; k < allNeurons[i + 1].GetLength(0) - lastLayer; k++)
allNeurons[i][j, 1] += allWeights[i][j, k, 0] * allNeurons[i + 1][k, 1];
allNeurons[i][j, 1] = allNeurons[i][j, 1] * (1 - allNeurons[i][j, 0]) * allNeurons[i][j, 0];
}
}
//Корректировка весов
for (int i = 1; i < allNeurons.GetLength(0); i++)
{
int lastLayer = 1;
if (i == allNeurons.GetLength(0) - 1)
lastLayer = 0;
for (int k = 0; k < allNeurons[i - 1].GetLength(0); k++)
for (int j = 0; j < allNeurons[i].GetLength(0) - lastLayer; j++)
{
allWeights[i - 1][k, j, 1] = E * allNeurons[i - 1][k, 0] * allNeurons[i][j, 1] + a * allWeights[i - 1][k, j, 1];
allWeights[i - 1][k, j, 0] += allWeights[i - 1][k, j, 1];
}
}
error += Math.Pow(Math.Atan(outputData[stepTrain] - allNeurons[3][0, 1]), 2);
Console.WriteLine(allNeurons[3][0, 0]);
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(error / outputData.Length);
error = 0;
Console.ResetColor();
//Thread.Sleep(300);
}
Console.ReadKey();
}
static double FixOutError(double[] outputData, double outputValue, int index) => (outputData[index] - outputValue) * (1 - outputValue) * outputValue;
static double Sigmoid(double outputData) => 1 / (1 + Math.Exp(-(outputData)));
Дополнение:
Я изменил поиск ошибки тоесть строку
error += Math.Pow(Math.Atan(outputData[stepTrain] - allNeurons[3][0, 1]), 2);
на строку
error += Math.Abs(outputData[stepTrain] - allNeurons[3][0, 0]);
И в конце эпохи я просто делю error на количество элементов в массиве идеальных значений.
И заметил что первые эпох 200 значение error стоит 0.6 примерно и почти не изменяется. После 200 эпох ошибка начинает спадать до 0.04 и дальше медленно уменьшается.
Как исправить это ошибку когда 200 эпох error почти не изменяется.
Дополнение:
Я изменил случайную генерацию весов с -0.5 до 0.5 на с -1 до 0 и нейронная сеть начала обучаться за 100 эпох а не за 700 или 1400 как было.
Но не знаю правильно ли я сделал
В общем посидев и поигравшись с нейронной сетью.
1) Изменил количество скрытых слоев до 1 слоя в котором 5 нейронов (1 нейрон смещения)
2) Оставил генерацию весов -0.5 до 0.5 Особо роли не сыграло. Только в начале, эпох 8-10 нейронка немного тупит. (Хотя бы не 200 эпох как раньше)
3) Если останавливать обучение на error <= 0.05 то это займет эпох 80 (минимальное которое я получил) если нужно более точнее ответы (хотя они и так уже достаточно точны) то при error <= 0.02 нейронка будет учиться 400 эпох. Потому что примерно на 0.3-0.4 ошибка очень медленно спадает.
4) Гиперпараметры изменил так:
Значение момента (a) = 0.9
Скорость обучения (E) ставил на 1, но решил поставить на 0.9 просто где то слышал что лучше не ставить гиперпараметры на 1 (особенно значение момента).
В итоге с 800-1400 эпох обучения я снизил до 80-400 (зависит от четкости ответа который вам нужен).
Исправьте пожалуйста если я где-то допустил ошибку.
Просто нейронными сетями занимаю неделю всего.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как отображать картинки(массив байтов) в вьюшке(<img>)?