Exemple #1
0
    def test_parse_discrete_space(self, ffnn_config):
        input_space = spaces.Discrete(n=self.input_n)
        output_space = spaces.Discrete(n=self.output_n)

        dummy_individual = np.zeros((FeedForwardNumPy.get_individual_size(
            ffnn_config, input_space=input_space, output_space=output_space)))

        brain = FeedForwardPyTorch(input_space=input_space, output_space=output_space, individual=dummy_individual,
                                   config=ffnn_config)

        # Test input space
        sample_input = 1
        assert input_space.contains(sample_input)
        parsed_input = brain.parse_brain_input(sample_input)

        # Parsed Input should be one-hot encoded
        assert len(parsed_input.shape) == 1
        assert len(parsed_input) == input_space.n
        assert all(x == 0 for x in parsed_input[:sample_input])
        assert parsed_input[sample_input] == 1
        assert all(x == 0 for x in parsed_input[sample_input + 1:])

        # Test output space
        brain_output = brain.step(sample_input)

        # This should be an Integer in [0, output_space.n - 1]
        assert isinstance(brain_output, np.integer)
        assert 0 <= brain_output < output_space.n
Exemple #2
0
    def test_parse_tuple_space(self, ffnn_config):
        # Prepare some spaces to test the Tuple space
        one_dimensional_box = spaces.Box(low=self.low, high=self.high, shape=self.one_dimensional_shape)
        multi_dimensional_box = spaces.Box(low=self.low, high=self.high, shape=self.multi_dimensional_shape)
        first_discrete = spaces.Discrete(n=self.input_n)
        second_discrete = spaces.Discrete(n=self.output_n)

        input_space = spaces.Tuple([one_dimensional_box, first_discrete, multi_dimensional_box, second_discrete])
        output_space = spaces.Tuple([second_discrete, multi_dimensional_box, one_dimensional_box, first_discrete])

        dummy_individual = np.zeros((FeedForwardNumPy.get_individual_size(
            ffnn_config, input_space=input_space, output_space=output_space)))

        brain = FeedForwardPyTorch(input_space=input_space, output_space=output_space, individual=dummy_individual,
                                   config=ffnn_config)

        # Test input space
        sample_input = (np.ones(self.one_dimensional_shape), 1, np.ones(self.multi_dimensional_shape) * 2, 2)
        assert input_space.contains(sample_input)
        parsed_input = brain.parse_brain_input(sample_input)
        assert len(parsed_input.shape) == 1
        assert spaces.flatdim(input_space) == len(parsed_input)

        # Test output space
        brain_output = brain.step(sample_input)

        # 4 nested spaces inside the input space
        assert len(brain_output) == 4

        # First nested space is second_discrete
        nested_output = brain_output[0]
        assert isinstance(nested_output, np.integer)

        # Second nested space is multi_dimensional_box
        nested_output = brain_output[1]
        assert len(nested_output.shape) == len(self.multi_dimensional_shape)
        assert all([x == y for (x, y) in zip(nested_output.shape, self.multi_dimensional_shape)])

        # Third nested space is one_dimensional_box
        nested_output = brain_output[2]
        assert len(nested_output.shape) == len(self.one_dimensional_shape)
        assert all([x == y for (x, y) in zip(nested_output.shape, self.one_dimensional_shape)])

        # Fourth nested space is first_discrete
        nested_output = brain_output[3]
        assert isinstance(nested_output, np.integer)
Exemple #3
0
    def test_parse_rgb_input(self, ffnn_config):
        # Environments with RGB observations usually use spaces.Box as their observation space, which means it is
        # the input space for the brains
        input_space = spaces.Box(low=self.low, high=self.high, shape=self.rgb_shape)
        output_space = spaces.Box(low=self.low, high=self.high, shape=self.one_dimensional_shape)

        # First test the input space
        # Not used but needed to create a Brain
        dummy_individual = np.zeros((FeedForwardNumPy.get_individual_size(
            ffnn_config, input_space=input_space, output_space=output_space)))

        brain = FeedForwardPyTorch(input_space=input_space, output_space=output_space, individual=dummy_individual,
                                   config=ffnn_config)

        sample_input = np.ones(input_space.shape)
        assert input_space.contains(sample_input)
        # Assume now we have RGB input space, in our flatten function this should return a multidimensional NumPy array
        parsed_input = brain.parse_brain_input(sample_input)

        # When input_space == spaces.Box the input is not transformed
        assert np.array_equal(sample_input, parsed_input)
        assert (isinstance(parsed_input, np.ndarray) and len(
            parsed_input.shape) == 3 and parsed_input.shape == sample_input.shape)
Exemple #4
0
    def test_parse_box_space(self, ffnn_config):
        input_space = spaces.Box(low=self.low, high=self.high, shape=self.one_dimensional_shape)
        output_space = spaces.Box(low=self.low, high=self.high, shape=self.multi_dimensional_shape)

        # First test the input space
        # Not used but needed to create a Brain
        dummy_individual = np.zeros((FeedForwardNumPy.get_individual_size(
            ffnn_config, input_space=input_space, output_space=output_space)))

        brain = FeedForwardPyTorch(input_space=input_space, output_space=output_space, individual=dummy_individual,
                                   config=ffnn_config)

        sample_input = np.ones(input_space.shape)
        assert input_space.contains(sample_input)
        parsed_input = brain.parse_brain_input(sample_input)

        # When input_space == spaces.Box the input is not transformed
        assert np.array_equal(sample_input, parsed_input)

        # Now test the output space
        brain_output = brain.step(sample_input)

        assert brain_output.shape == output_space.shape
Exemple #5
0
    def test_lstm_output(self, ffnn_config: FeedForwardCfg):

        input_size = 28
        output_size = 8

        number_of_inputs = 10000

        input_space = gym.spaces.Box(-1, 1, (input_size, ))
        output_space = gym.spaces.Box(-1, 1, (output_size, ))

        individual_size = FeedForwardPyTorch.get_individual_size(
            ffnn_config, input_space, output_space)

        # Make two copies to avoid possible errors due to PyTorch reusing data from the same memory address
        individual_pytorch = np.random.randn(individual_size).astype(
            np.float32)
        individual_numpy = np.copy(individual_pytorch)

        with torch.no_grad():
            ffnn_pytorch = FeedForwardPyTorch(input_space, output_space,
                                              individual_pytorch, ffnn_config)

        ffnn_numpy = FeedForwardNumPy(input_space, output_space,
                                      individual_numpy, ffnn_config)

        with torch.no_grad():
            # Hidden layers are in a stacked list, so unpack first
            hidden_layers = ffnn_config.hidden_layers[0]
            current_input = input_size

            for i, hidden_layer in enumerate(hidden_layers):
                weight_array_pytorch: np.ndarray = ffnn_pytorch.layers[
                    i].weight.data.numpy()
                weight_array_numpy: np.ndarray = ffnn_numpy.weights[i]

                assert np.array_equal(weight_array_pytorch, weight_array_numpy)
                assert weight_array_pytorch.shape == (hidden_layer,
                                                      current_input)

                if ffnn_config.use_bias:
                    bias_array_pytorch: np.ndarray = ffnn_pytorch.layers[
                        i].bias.data.numpy()
                    bias_array_numpy: np.ndarray = ffnn_numpy.bias[i]

                    assert np.array_equal(bias_array_pytorch, bias_array_numpy)
                    assert bias_array_pytorch.shape == (hidden_layer, )

                current_input = hidden_layer

            assert weight_array_pytorch.shape == (current_input, output_size)

        inputs = np.random.randn(number_of_inputs,
                                 input_size).astype(np.float32)

        ffnn_pytorch_outputs = []
        ffnn_numpy_outputs = []

        ffnn_pytorch_times = []
        ffnn_numpy_times = []

        # Collect predictions of PyTorch and NumPy implementations and collect time data
        for i in inputs:
            with torch.no_grad():
                time_s = time.time()
                ffnn_pytorch_output = ffnn_pytorch.step(i)
                ffnn_pytorch_times.append(time.time() - time_s)
                ffnn_pytorch_outputs.append(ffnn_pytorch_output)

            time_s = time.time()
            ffnn_numpy_output = ffnn_numpy.step(i)
            ffnn_numpy_times.append(time.time() - time_s)
            ffnn_numpy_outputs.append(ffnn_numpy_output)

        ffnn_pytorch_outputs = np.array(ffnn_pytorch_outputs)
        ffnn_numpy_outputs = np.array(ffnn_numpy_outputs)

        ffnn_pytorch_times = np.array(ffnn_pytorch_times)
        ffnn_numpy_times = np.array(ffnn_numpy_times)

        assert len(ffnn_pytorch_outputs) == len(ffnn_numpy_outputs)
        assert ffnn_pytorch_outputs.size == ffnn_numpy_outputs.size

        print(
            "\nPyTorch Mean Prediction Time {}s | NumPy Mean Prediction Time {}s"
            .format(np.mean(ffnn_pytorch_times), np.mean(ffnn_numpy_times)))

        print(
            "PyTorch Stddev Prediction Time {}s | NumPy Stddev Prediction Time {}s"
            .format(np.std(ffnn_pytorch_times), np.std(ffnn_numpy_times)))

        print(
            "PyTorch Max Prediction Time {}s | NumPy Max Prediction Time {}s".
            format(np.max(ffnn_pytorch_times), np.max(ffnn_numpy_times)))

        print(
            "PyTorch Min Prediction Time {}s | NumPy Min Prediction Time {}s".
            format(np.min(ffnn_pytorch_times), np.min(ffnn_numpy_times)))

        # Use percentage instead of np.allclose() because some results exceed the rtol value, but it is a low percentage
        close_percentage = np.count_nonzero(
            np.isclose(ffnn_pytorch_outputs,
                       ffnn_numpy_outputs)) / ffnn_pytorch_outputs.size

        assert close_percentage > 0.98

        print(
            "Equal predictions between PyTorch and NumPy",
            "Implementation of FFNN: {}% of {} predictions".format(
                close_percentage * 100, number_of_inputs))
Exemple #6
0
    def test_ffnn_predict_speedtest(self):
        def predict_with_maximum_speed(u, A, B, C, D, E, a, b, c, d, e):
            x = np.matmul(A, u)
            x = np.add(x, a)
            x = np.maximum(0, x)

            x = np.matmul(B, x)
            x = np.add(x, b)
            x = np.maximum(0, x)

            x = np.matmul(C, x)
            x = np.add(x, c)
            x = np.maximum(0, x)

            x = np.matmul(D, x)
            x = np.add(x, d)
            x = np.maximum(0, x)

            x = np.matmul(E, x)
            x = np.add(x, e)

            return x

        input_size = 6
        output_size = 1

        number_of_inputs = 3000000

        input_space = gym.spaces.Box(-1, 1, (input_size, ))
        output_space = gym.spaces.Box(-1, 1, (output_size, ))

        ffnn_config = FeedForwardCfg(type="FeedForward_Numpy",
                                     use_bias=True,
                                     hidden_layers=[[16, 32, 16, 8]],
                                     neuron_activation="relu",
                                     neuron_activation_output="linear")

        individual_size = FeedForwardPyTorch.get_individual_size(
            ffnn_config, input_space, output_space)
        individual = np.random.rand(individual_size).astype(np.float32)

        ffnn_numpy = FeedForwardNumPy(input_space, output_space, individual,
                                      ffnn_config)

        # Weight Matrizes
        A = ffnn_numpy.weights_hidden_layers[0].copy()
        B = ffnn_numpy.weights_hidden_layers[1].copy()
        C = ffnn_numpy.weights_hidden_layers[2].copy()
        D = ffnn_numpy.weights_hidden_layers[3].copy()
        E = ffnn_numpy.weights_output_layer.copy()

        # Biases
        a = ffnn_numpy.biases_hidden_layers[0].copy()
        b = ffnn_numpy.biases_hidden_layers[1].copy()
        c = ffnn_numpy.biases_hidden_layers[2].copy()
        d = ffnn_numpy.biases_hidden_layers[3].copy()
        e = ffnn_numpy.biases_output_layer.copy()

        inputs = np.random.rand(number_of_inputs, input_size,
                                1).astype(np.float32)

        t_start = time.time()
        output_ffnn_class = ffnn_numpy.predict(inputs)
        time_ffnn_class = time.time() - t_start
        print(time_ffnn_class)

        t_start = time.time()
        output_maximum_speed = predict_with_maximum_speed(
            inputs, A, B, C, D, E, a, b, c, d, e)
        time_maximum_speed = time.time() - t_start
        print(time_maximum_speed)

        relative_speed_tolerance = time_ffnn_class / time_maximum_speed - 1

        print(relative_speed_tolerance)

        print(
            np.count_nonzero(
                np.isclose(output_ffnn_class, output_maximum_speed)))
        assert number_of_inputs == np.count_nonzero(
            np.isclose(output_ffnn_class, output_maximum_speed))