public override double Teach(double[] Input, double[] Output, double[] Expected) { if (Input is null) { throw new ArgumentNullException(nameof(Input)); } if (Output is null) { throw new ArgumentNullException(nameof(Output)); } if (Expected is null) { throw new ArgumentNullException(nameof(Expected)); } if (Expected.Length != Output.Length) { throw new InvalidOperationException("Длина вектора ожидаемого результата не совпадает с длиной вектора результата сети"); } var inertial_factor = InertialFactor; var rho = Math.Max(0, Math.Min(Rho, 1)); var layers_count = _Network.LayersCount; var outputs_count = _Network.OutputsCount; var layers = _Network._Layers; // Матрицы коэффициентов передачи слоёв var outputs = _Network._Outputs; var layer_offsets = _Network._Offsets; // Смещения слоёв var layer_offset_weights = _Network._OffsetsWeights; // Весовые коэффициенты весов слоёв <= 0 var layer_activation = _Network._Activations; // Производные активационных функций слоёв var state = _State; Process( Input, Output, layers, layer_activation, layer_offsets, layer_offset_weights, state, outputs); var dw = inertial_factor.Equals(0d) ? null : _DW; var dw_offset = inertial_factor.Equals(0d) ? null : _DWoffset; var errors = _Errors; // Массив ошибок в слоях var output_layer_error = errors[layers_count - 1]; // Ошибка выходного слоя for (var output_index = 0; output_index < outputs_count; output_index++) { output_layer_error[output_index] = (Expected[output_index] - Output[output_index]) * (layer_activation[layers_count - 1]?.DiffValue(Output[output_index]) ?? Sigmoid.DiffActivation(Output[output_index])); } // Проходим по всем слоям от выхода ко входу for (var layer_index = errors.Length - 1; layer_index >= 0; layer_index--) { var w = layers[layer_index]; // Текущий слой var layer_inputs_count = w.GetLength(1); // Количество входов текущего слоя var layer_outputs_count = w.GetLength(0); // Количество выходов (нейронов) текущего слоя var error_level = errors[layer_index]; // Ошибка текущего слоя #region Обратное распространение ошибки // Если слой не последний, то пересчитываем ошибку текущего слоя на предыдущий if (layer_index > 0) { // Количество выходов (нейронов) в предыдущем слое var prev_layer_outputs_count = layers[layer_index - 1].GetLength(0); // Создаём вектор значений для ошибки предыдущего слоя var prev_layer_error = errors[layer_index - 1]; // Извлекаем производную функции активации предыдущего слоя var prev_layer_activation = layer_activation[layer_index - 1]; var layer_state = state[layer_index]; // Вектор выхода предыдущего слоя var prev_layer_output = outputs[layer_index - 1]; // Для каждого нейрона (выхода) предыдущего слоя for (var i = 0; i < prev_layer_outputs_count; i++) { // Вычисляем ошибку как... var err = 0d; // ...как сумму произведений коэффициентов передачи связей, ведущих к данному нейрону, умноженных на ошибку соответствующего нейрона текущего слоя for (var j = 0; j < layer_outputs_count; j++) // j - номер связи с j-тым нейроном текущего слоя { err += error_level[j] * w[j, i]; // i - номер нейрона в предыдущем слое } // Ошибка по нейрону = суммарная взвешенная ошибка всех связей умноженная на значение производной функции активации для выхода нейрона switch (prev_layer_activation) { case null: prev_layer_error[i] = err * Sigmoid.DiffActivation(prev_layer_output[i]); break; case DiffSimplifiedActivationFunction activation: prev_layer_error[i] = err * activation.DiffFunc(prev_layer_output[i]); break; default: prev_layer_error[i] = err * prev_layer_activation.DiffValue(layer_state[i]); break; } } } #endregion var offset = layer_offsets[layer_index]; // Для данного слоя - смещение нейронов var w_offset = layer_offset_weights[layer_index]; // - веса смещений var layer_inputs = layer_index > 0 ? outputs[layer_index - 1] : Input; var layer_dw = dw?[layer_index]; var layer_dw_offset = dw_offset?[layer_index]; // Для всех нейронов слоя корректируем коэффициенты их входных связей и весов смещений for (var neuron = 0; neuron < layer_outputs_count; neuron++) { var error = error_level[neuron]; // Ошибка для нейрона в слое // Корректируем вес смещения var neuron_delta_w = rho * error * offset[neuron] * w_offset[neuron]; if (layer_dw_offset != null) { neuron_delta_w = inertial_factor * layer_dw_offset[neuron] + (1 - inertial_factor) * neuron_delta_w; layer_dw_offset[neuron] = neuron_delta_w; } w_offset[neuron] += neuron_delta_w; // Для каждого входа нейрона корректируем вес связи for (var input = 0; input < layer_inputs_count; input++) { var neuron_dw = rho * error * layer_inputs[input]; if (layer_dw != null) { neuron_dw = inertial_factor * layer_dw[neuron, input] + (1 - inertial_factor) * neuron_dw; layer_dw[neuron, input] = neuron_dw; } w[neuron, input] += neuron_dw; } } } var network_error = GetError(Output, Expected); if (network_error >= _LastError) { return(network_error); } _LastError = network_error; CopyArchitecture(layers, _BestVariantW, layer_offset_weights, _BestVariantOffsetW); return(network_error); }