Настройка начальных весов при проекции

В библиотеке Synaptic управление весами нейронной сети играет ключевую роль в скорости обучения и качестве получаемых результатов. Начальные веса определяют, как сеть начнёт обучение, и влияют на возможность сети избежать локальных минимумов функции потерь.

Базовые концепции весов в Synaptic

В Synaptic каждый нейрон имеет входящие синаптические связи, каждая из которых содержит вес (weight). Вес определяет силу влияния входного сигнала на нейрон. По умолчанию при создании сети веса инициализируются случайными числами в диапазоне [-1, 1], что обеспечивает базовую вариативность сигналов и предотвращает симметричность обучения.

Пример создания простого нейрона с произвольными весами:

const synaptic = require('synaptic');
const Neuron = synaptic.Neuron;

let neuron = new Neuron();
console.log(neuron.connections.inputs); // пустой объект перед соединениями

После соединения с другими нейронами веса можно установить вручную или модифицировать автоматически.

Проекция и её роль

Проекция (Projection) в Synaptic представляет собой набор синаптических соединений между слоями. При создании соединения между двумя слоями (Layer) автоматически генерируются веса для каждой связи. Настройка этих весов важна для:

  • ускорения сходимости сети,
  • предотвращения затухания или взрыва градиентов,
  • управления распределением сигналов на начальном этапе обучения.

Пример создания проекции между слоями:

const Layer = synaptic.Layer;

let inputLayer = new Layer(3);
let hiddenLayer = new Layer(4);

inputLayer.project(hiddenLayer); // создаёт проекцию с начальной инициализацией весов

По умолчанию project создаёт рандомизированные веса в диапазоне [-1, 1], но библиотека позволяет задавать собственные начальные веса.

Ручная инициализация весов

Для точного контроля над весами используется метод connect, позволяющий задавать сразу все веса проекции.

Пример:

let weights = [
    [0.2, -0.5, 0.1, 0.0],
    [0.7, 0.1, -0.3, 0.4],
    [-0.4, 0.9, 0.2, -0.1]
];

for (let i = 0; i < inputLayer.list.length; i++) {
    for (let j = 0; j < hiddenLayer.list.length; j++) {
        inputLayer.list[i].project(hiddenLayer.list[j], weights[i][j]);
    }
}

Здесь каждый входной нейрон получает индивидуально заданный вес для каждого соединения, что позволяет моделировать специфическую начальную конфигурацию сети.

Инициализация с распределением

Для больших сетей часто используются статистические распределения для начальных весов: нормальное (Гаусса) или равномерное. В JavaScript можно реализовать это с помощью функции генерации случайных чисел с требуемыми параметрами:

function randomNormal(mean = 0, stdDev = 1) {
    let u = 0, v = 0;
    while(u === 0) u = Math.random();
    while(v === 0) v = Math.random();
    let num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
    return num * stdDev + mean;
}

// Применение к весам проекции
inputLayer.list.forEach(inputNeuron => {
    inputNeuron.connections.projected.forEach(conn => {
        conn.weight = randomNormal(0, 0.1);
    });
});

Такой подход обеспечивает стабильное обучение и уменьшает вероятность сильных перепадов сигналов в начальной фазе.

Масштабирование и нормализация весов

Важной практикой является масштабирование начальных весов в зависимости от количества входов нейрона. Для слоев с большим числом нейронов слишком большие веса могут привести к насыщению активации, а слишком маленькие — к затуханию градиента. Рекомендуемая формула для нормализации:

[ w_i = ]

Применение в Synaptic:

inputLayer.list.forEach(neuron => {
    neuron.connections.projected.forEach(conn => {
        conn.weight = (Math.random() * 2 - 1) / Math.sqrt(inputLayer.list.length);
    });
});

Программная модификация весов после проекции

Весы можно изменять в любой момент обучения, что позволяет экспериментировать с адаптивной инициализацией или динамическим пересемплированием:

hiddenLayer.list.forEach(neuron => {
    neuron.connections.inputs.forEach(conn => {
        conn.weight *= 0.9; // лёгкое уменьшение веса
    });
});

Это часто используется для регуляризации и предотвращения переобучения.

Особенности весов в рекуррентных и сложных сетях

В рекуррентных сетях, создаваемых через Synaptic, веса связей между временными шагами также подлежат инициализации. Рекомендуется применять симметричное распределение с малой дисперсией, чтобы сигналы не взрывались при распространении через временные слои.

// Пример для рекуррентного слоя
const Layer = synaptic.Layer;
let recurrentLayer = new Layer(5);
recurrentLayer.list.forEach(neuron => {
    neuron.connections.projected.forEach(conn => {
        conn.weight = (Math.random() * 2 - 1) * 0.05;
    });
});

Резюме принципов настройки весов

  • Использовать малые случайные веса для предотвращения насыщения активации.
  • Применять масштабирование по количеству входов для стабилизации обучения.
  • Возможна ручная установка весов для специализированных сетей или тестирования гипотез.
  • Для больших сетей предпочтительно применять нормальное распределение с малой дисперсией.
  • В рекуррентных сетях крайне важно ограничивать диапазон весов для предотвращения взрыва градиентов.

Эти методы обеспечивают гибкость и контроль над процессом обучения, делая Synaptic мощным инструментом для экспериментирования с различными архитектурами нейронных сетей.