В конечном счете обычный градиент всех победил на большой выборке.
- Код: Выделить всё
function get_weight_delta(lesson, neuron, expRes, rate, outFilter)
{
let sz = (neuron.length-1)|0, j = 0|0;
let x = 0.0; // без сегмоиды
for( j = 0; j<sz; ++j )
x += lesson[j]*neuron[j];
x += neuron[sz];
let seg_x = outFilter.segmoid(x);
let trueError = (expRes - seg_x)*outFilter.deriv(x);
let error = trueError/(1+trueError*trueError);
let weight_delta =error*rate;
return weight_delta;
}
Особенностей почти нет. Пожалуй, только такая штука: trueError/(1+trueError*trueError);
Это борьба с расколбасом. На среднее значение практически не влияет, около процента, но улучшает худшие. Был вариант чуть получше, но он требовал подсчета среднего квадрата весов нейрона.
Мои эксперименты с заменой производной в среднем чуть похуже, где-то на 4%, и лишь изредка давали удачные решения. Но нас интересуют не удачные, а худшие или средние.
Несколько раз сменил архитектуру и теперь обнаружил, что вычисление нейрона по выборке сломано. Функции там уже довольно большие, с полпинка не восстановлю.
Заметно дала нормализация стартового шума. Лучший вариант дала такая:
просто вычитаем из весов среднее и масштабируем до +-0.7 Можно до 1.0, среднее чуть лучше, но появляются ужасно плохие худшие, с ошибкой 32% на моем тесте, в то время как я экспериментирую в районе 14% Аддидивный член масштабирую +-0.1
Если увеличивать веса, средняя ошибка улучшается, но растет максимальная, все чаще появляются неустойчивые конфигурации.
После нормализации дала прибавку ортогонализация. Победила такая версия: на каждом слое, для каждого нейрона, ищу два ближайших нейрона по среднеквадратичному отклонению. И их покомпонентно растаскиваю:
e = (w0i - w1i)*0.08
w0i += e
w1i -= e
Всю процедуру повторяю 20 раз.
Встряска (shake) при заходе в локальный минимум ничего не даёт. Либо в ней бага, либо применяю криво. Хотя раньше я только ей и оптимизировал сеть. До того как пришел к вычислению глобального минимума.