"""
参考资料:
- http://neuralnetworksanddeeplearning.com/chap2.html (反向传播)
- https://en.wikipedia.org/wiki/Sigmoid_function (Sigmoid激活函数)
- https://en.wikipedia.org/wiki/Feedforward_neural_network (前馈神经网络)
"""
import numpy
class TwoHiddenLayerNeuralNetwork:
def __init__(self, input_array: numpy.ndarray, output_array: numpy.ndarray) -> None:
"""
初始化 TwoHiddenLayerNeuralNetwork 类,为每一层分配随机权重,并将预测输出初始化为零。
input_array:用于训练神经网络的输入值(即训练数据)。
output_array:给定输入的期望输出值。
>>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float)
>>> output_val = numpy.array(([0], [0], [0]), dtype=float)
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val)
>>> nn.input_layer_and_first_hidden_layer_weights.shape[1]
4
"""
# 为训练模型提供的输入值。
self.input_array = input_array
# 分配随机初始权重,其中第一个参数是前一层中的节点数,第二个参数是下一层中的节点数。
# 分配随机初始权重。
# self.input_array.shape[1] 用于表示输入层中的节点数。
# 第一个隐藏层有4个节点。
self.input_layer_and_first_hidden_layer_weights = numpy.random.rand(
self.input_array.shape[1], 4
)
# 第一个隐藏层的随机初始值。
# 第一个隐藏层有4个节点。
# 第二个隐藏层有3个节点。
self.first_hidden_layer_and_second_hidden_layer_weights = numpy.random.rand(
4, 3
)
# 第二个隐藏层的随机初始值。
# 第二个隐藏层有3个节点。
# 输出层有1个节点。
self.second_hidden_layer_and_output_layer_weights = numpy.random.rand(3, 1)
# 提供的真实输出值。
self.output_array = output_array
# 神经网络的预测输出值。
# 预测_output 数组最初包含零。
self.predicted_output = numpy.zeros(output_array.shape)
def feedforward(self) -> numpy.ndarray:
"""
信息沿着一个方向前进,即从输入节点,通过两个隐藏节点到输出节点。
网络中没有循环或回路。
返回层在第二个隐藏层和输出层之间
(即神经网络的最后一层)。
>>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float)
>>> output_val = numpy.array(([0], [0], [0]), dtype=float)
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val)
>>> res = nn.feedforward()
>>> array_sum = numpy.sum(res)
>>> numpy.isnan(array_sum)
False
"""
# layer_between_input_and_first_hidden_layer 是连接输入节点与第一个隐藏层节点的层。
self.layer_between_input_and_first_hidden_layer = sigmoid(
numpy.dot(self.input_array, self.input_layer_and_first_hidden_layer_weights)
)
# layer_between_first_hidden_layer_and_second_hidden_layer 是连接
# 第一个隐藏节点组与第二个隐藏节点组的层。
self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid(
numpy.dot(
self.layer_between_input_and_first_hidden_layer,
self.first_hidden_layer_and_second_hidden_layer_weights,
)
)
# layer_between_second_hidden_layer_and_output 是连接
# 第二个隐藏层与输出节点的层。
self.layer_between_second_hidden_layer_and_output = sigmoid(
numpy.dot(
self.layer_between_first_hidden_layer_and_second_hidden_layer,
self.second_hidden_layer_and_output_layer_weights,
)
)
return self.layer_between_second_hidden_layer_and_output
def back_propagation(self) -> None:
"""
用于根据前一个时期(即迭代)中获得的错误率微调神经网络的权重的函数。
使用sigmoid激活函数的导数进行更新。
>>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float)
>>> output_val = numpy.array(([0], [0], [0]), dtype=float)
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val)
>>> res = nn.feedforward()
>>> nn.back_propagation()
>>> updated_weights = nn.second_hidden_layer_and_output_layer_weights
>>> (res == updated_weights).all()
False
"""
updated_second_hidden_layer_and_output_layer_weights = numpy.dot(
self.layer_between_first_hidden_layer_and_second_hidden_layer.T,
2
* (self.output_array - self.predicted_output)
* sigmoid_derivative(self.predicted_output),
)
updated_first_hidden_layer_and_second_hidden_layer_weights = numpy.dot(
self.layer_between_input_and_first_hidden_layer.T,
numpy.dot(
2
* (self.output_array - self.predicted_output)
* sigmoid_derivative(self.predicted_output),
self.second_hidden_layer_and_output_layer_weights.T,
)
* sigmoid_derivative(
self.layer_between_first_hidden_layer_and_second_hidden_layer
),
)
updated_input_layer_and_first_hidden_layer_weights = numpy.dot(
self.input_array.T,
numpy.dot(
numpy.dot(
2
* (self.output_array - self.predicted_output)
* sigmoid_derivative(self.predicted_output),
self.second_hidden_layer_and_output_layer_weights.T,
)
* sigmoid_derivative(
self.layer_between_first_hidden_layer_and_second_hidden_layer
),
self.first_hidden_layer_and_second_hidden_layer_weights.T,
)
* sigmoid_derivative(self.layer_between_input_and_first_hidden_layer),
)
self.input_layer_and_first_hidden_layer_weights += (
updated_input_layer_and_first_hidden_layer_weights
)
self.first_hidden_layer_and_second_hidden_layer_weights += (
updated_first_hidden_layer_and_second_hidden_layer_weights
)
self.second_hidden_layer_and_output_layer_weights += (
updated_second_hidden_layer_and_output_layer_weights
)
def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None:
"""
对给定次数的迭代执行前馈和反向传播过程。
每次迭代都会更新神经网络的权重。
output:给定输入的真实输出值,用于计算损失。
iterations:权重要更新的次数。
give_loss:布尔值,如果为True,则打印每次迭代的损失,如果为False,则不打印。
>>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float)
>>> output_val = numpy.array(([0], [1], [1]), dtype=float)
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val)
>>> first_iteration_weights = nn.feedforward()
>>> nn.back_propagation()
>>> updated_weights = nn.second_hidden_layer_and_output_layer_weights
>>> (first_iteration_weights == updated_weights).all()
False
"""
for iteration in range(1, iterations + 1):
self.output = self.feedforward()
self.back_propagation()
if give_loss:
loss = numpy.mean(numpy.square(output - self.feedforward()))
print(f"Iteration {iteration} Loss: {loss}")
def predict(self, input_arr: numpy.ndarray) -> int:
"""
使用训练过的神经网络为给定输入值进行预测。
模型给出的输出值在0和1之间。
如果模型值大于阈值,则predict函数返回1,否则返回0,
因为实际输出值是二进制的。
>>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float)
>>> output_val = numpy.array(([0], [1], [1]), dtype=float)
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val)
>>> nn.train(output_val, 1000, False)
>>> nn.predict([0, 1, 0]) in (0, 1)
True
"""
# 要进行预测的输入值。
self.array = input_arr
self.layer_between_input_and_first_hidden_layer = sigmoid(
numpy.dot(self.array, self.input_layer_and_first_hidden_layer_weights)
)
self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid(
numpy.dot(
self.layer_between_input_and_first_hidden_layer,
self.first_hidden_layer_and_second_hidden_layer_weights,
)
)
self.layer_between_second_hidden_layer_and_output = sigmoid(
numpy.dot(
self.layer_between_first_hidden_layer_and_second_hidden_layer,
self.second_hidden_layer_and_output_layer_weights,
)
)
return int((self.layer_between_second_hidden_layer_and_output > 0.6)[0])
def sigmoid(value: numpy.ndarray) -> numpy.ndarray:
"""
应用Sigmoid激活函数。
返回归一化值
>>> sigmoid(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64))
array([[0.73105858, 0.5 , 0.88079708],
[0.73105858, 0.5 , 0.5 ]])
"""
return 1 / (1 + numpy.exp(-value))
def sigmoid_derivative(value: numpy.ndarray) -> numpy.ndarray:
"""
提供Sigmoid函数的导数值。
返回Sigmoid值的导数
>>> sigmoid_derivative(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64))
array([[ 0., 0., -2.],
[ 0., 0., 0.]])
"""
return (value) * (1 - (value))
def example() -> int:
"""
一个关于“如何使用神经网络类并使用
所需输出的方法”的示例。
调用TwoHiddenLayerNeuralNetwork类并
为模型提供固定的输入输出值。
模型经过固定次数的训练,然后调用predict方法。
在这个例子中,输出被分为2类,即二进制分类,
这两个类别由'0'和'1'表示。
>>> example() in (0, 1)
True
"""
# 输入值。
test_input = numpy.array(
(
[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
),
dtype=numpy.float64,
)
# 给定输入的真实输出值。
output = numpy.array(([0], [1], [1], [0], [1], [0], [0], [1]), dtype=numpy.float64)
# 调用神经网络类。
neural_network = TwoHiddenLayerNeuralNetwork(
input_array=test_input, output_array=output
)
# 调用训练函数。
# 如果要查看每次迭代的损失,请将give_loss设置为True。
neural_network.train(output=output, iterations=10, give_loss=False)
return neural_network.predict(numpy.array(([1, 1, 1]), dtype=numpy.float64))
if __name__ == "__main__":
example()