13-层的实现
7.4.2 层的实现
本章的神经网络中的层对象需要维护3种状态数据:所含神经元、其上一层和输出缓存。输出缓存类似于神经元的缓存,但高一个级别。它缓存了层中每一个神经元在调用激活函数之后的输出。
在创建时,层对象的主要职责是初始化其内部的神经元。因此, Layer 类的 __init__() 方法需要知道应该初始化多少个神经元,它们的激活函数是什么,以及它们的学习率为多少。在本章这个简单的神经网络中,层中的每个神经元都有相同的激活函数和学习率。具体代码如代码清单7-4所示。
代码清单7-4 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] = []
# the following could all be one large list comprehension
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 必须让每个神经元都对其进行处理。请记住,层中的每个神经元都会接收到上一层中每个神经元传入的信号。 outputs() 正是如此处理的。 outputs() 还会返回处理后的结果(以便经由网络传递到下一层)并将输出缓存一份。如果不存在上一层,则表示本层为输入层,只要将信号向前传递给下一层即可。具体代码如代码清单7-5所示。
代码清单7-5 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
在反向传播时需要计算两种不同类型的delta:输出层中神经元的delta和隐藏层中神经元的delta。图7-4和图7-5中分别给出了公式的描述,代码清单7-6中的两个方法只是机械地将公式转换成了代码。稍后在反向传播过程中神经网络对象将会调用这两个方法。
代码清单7-6 layer.py(续)
# should only be called on output layer
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])
# should not be called on output layer
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