2 隐藏层神经网络

"""
参考资料:
    - 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()

 

posted @ 2024-02-01 16:53  mlhello-world  阅读(25)  评论(0编辑  收藏  举报