Как минимизировать ошибку округления и повысить точность в python, используя Numpy и метод конечных разностей для частных производных? - PullRequest
0 голосов
/ 19 января 2020

Я пытаюсь создать библиотеку нейронных сетей с нуля (ie не использует Tensorflow), и у меня возникают некоторые проблемы из-за ошибки округления. Код для нейронной сети настроен так, чтобы я мог вводить глубину и высоту нейронной сети. Из-за этого для меня нецелесообразно находить аналитический c градиент для моей функции стоимости. Чтобы обойти это, я выполняю метод конечных разностей, чтобы вычислить частную производную моей функции стоимости по каждому весу и смещению. Тем не менее, большинство из этих производных оцениваются в ноль, что означает, что моя нейронная сеть действительно не меняется в процессе обучения (ну, это так, но не после нескольких сотен эпох или около того).

Мне интересно если есть способ гарантировать более высокую точность для этих типов вычислений, или если есть лучший способ вычислить эти градиенты, которые не приведут к такой низкой точности.

Кроме того, могут быть проблемы с числовыми точность в другом месте в коде (особенно в методе feed_forward), но сейчас я думаю, что мои главные проблемы в градиенте l ie в методе обучения.

Ниже приведены важные части моего кода:

# This is a general purpose neural network library. It will start simple, but features will be added over time.

import numpy as np

np.random.seed(42)


class NeuralNetwork:

    def __init__(self, number_of_inputs, number_of_outputs, number_of_hidden_layers=0, height=5,
                 purpose="classification"):
        self.number_of_inputs = number_of_inputs
        self.number_of_outputs = number_of_outputs
        self.number_of_hidden_layers = number_of_hidden_layers
        self.height = height
        self.purpose = purpose

        # Weights will be entered as an array of matrices (np.arrays). The nth matrix will correspond to the nth layer
        # (including the first layer). Thus, there will be number_of_hidden_layers + 1 matrices. The nth row of each
        # matrix will correspond to the nth neuron of the next layer.

        for i in range(0, number_of_hidden_layers + 1):
            if i == 0 and number_of_hidden_layers != 0:
                self.weights = [np.random.rand(number_of_inputs, height)]
            elif i < number_of_hidden_layers:
                self.weights.append(np.random.rand(height, height))
            elif number_of_hidden_layers == 0:
                self.weights = [np.random.rand(number_of_inputs, number_of_outputs)]
            else:
                self.weights.append(np.random.rand(height, number_of_outputs))

        self.bias = np.random.rand(1, number_of_hidden_layers + 1)

    def feed_forward(self, data, weights, bias):
        output = self.sigmoid(np.dot(data, weights[0]) + bias[0][0])

        for i in range(1, self.number_of_hidden_layers + 1):
            output = self.sigmoid(np.dot(output, weights[i]) + bias[0][i])

        return output

    def train(self, inputs, outputs, epochs, training_rate, derivative_step_size=10**(-5):
        new_bias = self.bias.copy()
        new_weights = self.weights.copy()
        for iteration in range(epochs):

            for step in range(len(inputs)):

                for entry in range(len(self.weights)):
                    for i in range(len(self.weights[entry])):
                        for j in range(len(self.weights[entry][i])):
                            temp_weights1 = self.weights.copy()
                            temp_weights2 = self.weights.copy()
                            temp_weights1[entry][i][j] = self.weights[entry][i][j] + derivative_step_size
                            temp_weights2[entry][i][j] = self.weights[entry][i][j] - derivative_step_size
                            derivative = ((self.error(np.array(inputs[step]), outputs, temp_weights1, self.bias) -
                                           self.error(np.array(inputs[step]), outputs, temp_weights2,
                                                      self.bias)))/(2*derivative_step_size)
                            new_weights[entry][i][j] -= training_rate*derivative[step]

                for entry in range(len(self.bias[0])):
                    temp_bias1 = self.bias.copy()
                    temp_bias2 = self.bias.copy()
                    temp_bias1[0][entry] = self.bias[0][entry] + derivative_step_size
                    temp_bias2[0][entry] = self.bias[0][entry] - derivative_step_size
                    derivative = ((self.error(np.array(inputs[step]), outputs, self.weights, temp_bias1) -
                                   self.error(np.array(inputs[step]), outputs, self.weights, temp_bias2))
                                  / (2*derivative_step_size))

                    new_bias[0][entry] -= training_rate*derivative[step]

                self.weights = new_weights.copy()
                self.bias = new_bias.copy()

    def error(self, inputs, outputs, weights, bias):
        return (self.feed_forward(inputs, weights, bias) - outputs)**2

    def sigmoid(self, x):
        return 1/(1+np.exp(-x))

    def relu(self, x):
        temp = x
        for i in range(len(x)):
            if i > 0:
                temp[i][0] = x[i][0]
            else:
                temp[i][0] = 0

        return temp


example = NeuralNetwork(3, 1, 1)

# This is the training data
inputs = np.array([[0, 0, 1], [0, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]])
outputs = np.array([[1], [0], [0], [1], [1]])


example.train(inputs, outputs, 1000, 0.05)

print(np.average(example.error(inputs, outputs, example.weights, example.bias)))
...