Шаг 91.
Задачи ComputerScience на Python.
Простейшие нейронные сети. Построение сети. Реализация слоев

    На этом шаге мы рассмотрим особенности этой реализации.

    Слой в нашей сети должен поддерживать три элемента состояния:

Выходной кэш похож на кэш нейрона, но на один слой выше. Он кэширует выходные данные каждого нейрона в данном слое после применения функций активации.

    В момент создания слоя основной задачей является инициализация его нейронов. Поэтому методу __init__() класса Layer нужно знать, сколько нейронов требуется инициализировать, какими должны быть их функции активации и какова скорость обучения. В нашей простой сети у всех нейронов слоя функция активации и скорость обучения одинаковы (файл layer.py).

from __future__ import annotations
from typing import List, Callable, Optional
from random import random
from neuron import Neuron
from util import dot_product


class Layer:
    def __init__(self, previous_layer: Optional[Layer], num_neurons: int,
                 learning_rate: float, activation_function: Callable[[float], float],
                 derivative_activation_function: Callable[[float], float]) -> None:
        self.previous_layer: Optional[Layer] = previous_layer
        self.neurons: List[Neuron] = []
        # дальше может идти одно большое списковое включение
        for i in range(num_neurons):
            if previous_layer is None:
                random_weights: List[float] = []
            else:
                random_weights = [random() for _ in range(len(previous_layer.neurons))]
            neuron: Neuron = Neuron(random_weights, learning_rate,
                                    activation_function, derivative_activation_function)
            self.neurons.append(neuron)
        self.output_cache: List[float] = [0.0 for _ in range(num_neurons)]

    По мере того как сигналы передаются через сеть, их должен обрабатывать каждый нейрон Layer. (Помните, что каждый нейрон в слое получает сигналы от каждого нейрона предыдущего слоя.) Именно это делает метод output(). Он также возвращает результат обработки для передачи по сети на следующий слой и кэширует выходные данные. Если предыдущего слоя нет, значит, данный слой является входным и он просто передает сигналы на следующий слой (файл layer.py).

    def outputs(self, inputs: List[float]) -> List[float]:
        if self.previous_layer is None:
            self.output_cache = inputs
        else:
            self.output_cache = [n.output(inputs) for n in self.neurons]
        return self.output_cache

    Существует два типа дельт для вычисления в обратном распространении:

Их формулы показаны на рисунках 1 и 2 84 шага, и следующие два метода представляют собой механические переводы этих формул на Python (файл layer.py). Впоследствии эти методы будут вызываться сетью во время обратного распространения.
    # вызывается только для выходного слоя
    def calculate_deltas_for_output_layer(self, expected: List[float]) -> None:
        for n in range(len(self.neurons)):
            self.neurons[n].delta = \
                self.neurons[n].derivative_activation_function(self.neurons[n].output_cache) * \
                (expected[n] - self.output_cache[n])

    # не вызывается для выходного слоя
    def calculate_deltas_for_hidden_layer(self, next_layer: Layer) -> None:
        for index, neuron in enumerate(self.neurons):
            next_weights: List[float] = [n.weights[index] for n in next_layer.neurons]
            next_deltas: List[float] = [n.delta for n in next_layer.neurons]
            sum_weights_and_deltas: float = dot_product(next_weights, next_deltas)
            neuron.delta = neuron.derivative_activation_function(neuron.output_cache) * \
                           sum_weights_and_deltas
Архив с файлом можно взять здесь.

    На следующем шаге мы рассмотрим реализацию сети.




Предыдущий шаг Содержание Следующий шаг