var indeх = 1;
var rows = File.ReadAllLines(FILE_PATH).Skip(1).Take(TRAIN_ROWS_COUNT).ToList();
foreach (var row in rows)
{
Console.WriteLine("Итерация {0} из {1}", indeх++, TRAIN_ROWS_COUNT);
var values = row.Split(',');
for (int i = 1; i < values.Length; i++)
{
var value = byte.Parse(values[i]); //var value = (values[i] == "0") ? 0 : 1;
layerAssotiations[
byte.Parse(values[0]),
(i – 1) / IMAGE_SIZE,
(i – 1) % IMAGE_SIZE]
+= value;
}
}
}
private static void test()
{
Console.WriteLine("Начало тестирования нейросети");
var indeх = 1;
var rows = File.ReadAllLines(FILE_PATH).Skip(1 + TRAIN_ROWS_COUNT).Take(TEST_ROWS_COUNT).ToList();
foreach (var row in rows)
{
Console.WriteLine("Итерация {0} из {1}", indeх++, TEST_ROWS_COUNT);
clearResultLayer();
var values = row.Split(',');
for (int i = 1; i < values.Length; i++)
{
var value = byte.Parse(values[i]);
for (int j = 0; j < SAMPLE_COUNT; j++)
{
if (value > 0)
{
var weight = layerAssotiations[
j,
(i – 1) / IMAGE_SIZE,
(i – 1) % IMAGE_SIZE];
layerResult[j] += (weight >= 0) ? weight : -INCORRECT_PENALTY;
}
}
}
calculateStatistics(byte.Parse(values[0]));
}
}
private static void clearResultLayer()
{
layerResult = new Dictionary
for (int i = 0; i < SAMPLE_COUNT; i++) layerResult[i] = 0;
}
private static void calculateStatistics(byte correctNumber)
{
var proposalNumber = layerResult.OrderByDescending(p => p.Value).First().Key;
Console.WriteLine("Число {0} определено как {1} {2}", correctNumber, proposalNumber,
proposalNumber == correctNumber ? "УСПЕХ" : "НЕУДАЧА");
if (proposalNumber == correctNumber) correctResults++;
}
}
}
5 Нейросеть Matt Mazur
Сделал, наконец, рабочий пример нейросети (НС). Сначала прочитал теорию – объяснение механизма back propagation из русской вики. Вроде все понятно. За вечер написал программу для анализа цифр 0..9 из базы MNIST. Программа запускается, веса обновляются, работа кипит. У любого программиста есть вера в чудо. Что программа, которая компилируется при первом запуске сразу сработает корректно. Да, но нет, девочки. На выходе – 10% распознанных цифр. Ерунда, короче. Как если бы цифра определялась случайным образом.
Стал разбираться. Выяснилось, что в русской вики алгоритм описан сложно, неточно, замылено и недостаточно понятно для реализации без PhD в математике:
1) При изменении весов ребер НС их нужно умножать на коэффициент обучения. Иначе, метод градиентного спуска может застрять в локальном минимуме, так и не попав в глобальный минимум. Но не сказано, как подбирать этот коэффициент.
2) Не сказано, что после подсчета дельт для изменения весов на каждом слое, обновлять веса ребер нужно один раз для всех слоев сразу, а не сразу после подсчета каждой дельты.
3) Для скрытых слоев в правиле подсчета дельт сказано, что нужно умножить на веса исходящих ребер на их дельты. Это не так. Не дельты, а 3 из 4-х множителей в дельте. Т.е. из дельт нужно исключить сигнал на предыдущем узле. Т.е. умножить на: (Tj-Oj)*Oj*(1-Oj), а не на (Tj-Oj)*Oj*(1-Oj)*On, где n=j-1
4) Ничего не сказано про архитектуру слоев и ребер. Должны ли они быть Full-connected [FC] (каждый узел соединен с каждым следующим) и как изменить алгоритм если у нас не FC-слои.
5) Пишут про функцию ошибки НС, но никак в финальных расчетах алгоритма ее не используют (хотя из нее через производные выводят формулы подсчета).
6) Полно туманных разъяснений вида "ну, тут очевидно – надо взять частную производную dE/dw". Ага, очевидно. Целыми днями только этим и занимаюсь.
7) Нет нормального примера простой НС с проставленными числами для каждого сигнала, веса, узла и расчета как это считается хотя бы на одной итерации.