Базовый персептрон для шлюза AND в PHP, я правильно делаю? Странные результаты - PullRequest
0 голосов
/ 29 июня 2018

Я хотел бы узнать о нейронных сетях, начиная с самого основного алгоритма персептрона. Итак, я реализовал один в PHP и получаю странные результаты после обучения. Все 4 возможных комбинации ввода возвращают либо неправильные, либо правильные результаты (чаще всего неправильные).

1) Что-то не так с моей реализацией или результаты, которые я получаю, нормальные?

2) Может ли этот тип реализации работать с более чем двумя входами?

3) Каким будет следующий (самый простой) шаг в изучении нейронных сетей после этого? Может быть, добавление нейронов, изменение функции активации или ...?

P.S. Я довольно плохо разбираюсь в математике и не обязательно понимаю математику за перцептроном на 100%, по крайней мере, не в обучающей части.

Перцептрон Класс

<?php

namespace Perceptron;

class Perceptron
{
    // Number of inputs
    protected $n;

    protected $weights = [];

    protected $bias;

    public function __construct(int $n)
    {
        $this->n = $n;

        // Generate random weights for each input
        for ($i = 0; $i < $n; $i++) {
            $w = mt_rand(-100, 100) / 100;

            array_push($this->weights, $w);
        }

        // Generate a random bias
        $this->bias = mt_rand(-100, 100) / 100;
    }

    public function sum(array $inputs)
    {
        $sum = 0;

        for ($i = 0; $i < $this->n; $i++) {
            $sum += ($inputs[$i] * $this->weights[$i]);
        }

        return $sum + $this->bias;
    }

    public function activationFunction(float $sum)
    {
        return $sum < 0.0 ? 0 : 1;
    }

    public function predict(array $inputs)
    {
        $sum = $this->sum($inputs);

        return $this->activationFunction($sum);
    }

    public function train(array $trainingSet, float $learningRate)
    {
        foreach ($trainingSet as $row) {
            $inputs = array_slice($row, 0, $this->n);
            $correctOutput = $row[$this->n];

            $output = $this->predict($inputs);
            $error = $correctOutput - $output;

            // Adjusting the weights
            $this->weights[0] = $this->weights[0] + ($learningRate * $error);
            for ($i = 0; $i < $this->n - 1; $i++) {
                $this->weights[$i + 1] =
                    $this->weights[$i] + ($learningRate * $inputs[$i] * $error);
            }
        }

        // Adjusting the bias
        $this->bias += ($learningRate * $error);
    }
}

Основной файл

<?php

require_once 'vendor/autoload.php';

use Perceptron\Perceptron;

// Create a new perceptron with 2 inputs
$perceptron = new Perceptron(2);

// Test the perceptron
echo "Before training:\n";

$output = $perceptron->predict([0, 0]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([0, 1]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([1, 0]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([1, 1]);
echo "{$output} - " . ($output == 1 ? 'correct' : 'nope') . "\n";

// Train the perceptron
$trainingSet = [
    // The 3rd column is the correct output
    [0, 0, 0],
    [0, 1, 0],
    [1, 0, 0],
    [1, 1, 1],
];

for ($i = 0; $i < 1000; $i++) {
    $perceptron->train($trainingSet, 0.1);
}

// Test the perceptron again - now the results should be correct
echo "\nAfter training:\n";

$output = $perceptron->predict([0, 0]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([0, 1]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([1, 0]);
echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n";

$output = $perceptron->predict([1, 1]);
echo "{$output} - " . ($output == 1 ? 'correct' : 'nope') . "\n";

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Обнаружил свою глупую ошибку: я не корректировал смещение для каждого ряда тренировочного набора, так как случайно поместил его вне цикла foreach. Вот как должен выглядеть train() метод:

public function train(array $trainingSet, float $learningRate)
{
    foreach ($trainingSet as $row) {
        $inputs = array_slice($row, 0, $this->n);
        $correctOutput = $row[$this->n];

        $output = $this->predict($inputs);
        $error = $correctOutput - $output;

        // Adjusting the weights
        for ($i = 0; $i < $this->n; $i++) {
            $this->weights[$i] += ($learningRate * $inputs[$i] * $error);
        }

        // Adjusting the bias
        $this->bias += ($learningRate * $error);
    }
}

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

0 голосов
/ 29 июня 2018

Я должен поблагодарить вас за публикацию этого вопроса, я хотел получить шанс немного глубже погрузиться в нейронные сети. Во всяком случае, до дела. После возни с подробными сведениями о том, что происходит, в итоге потребовалось всего лишь 1 изменение символа для работы, как и предполагалось:

public function sum(array $inputs)
{
    ...
    //instead of multiplying the input by the weight, we should be adding the weight
    $sum += ($inputs[$i] + $this->weights[$i]);
    ...
}

С этим изменением 1000 итераций обучения становятся излишними. Один бит кода сбивал с толку, разные настройки весов:

public function train(array $trainingSet, float $learningRate)
{
    foreach ($trainingSet as $row) {
        ...
        $this->weights[0] = $this->weights[0] + ($learningRate * $error);
        for ($i = 0; $i < $this->n - 1; $i++) {
            $this->weights[$i + 1] =
                $this->weights[$i] + ($learningRate * $inputs[$i] * $error);
        }
}

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

for ($i = 0; $i < $this->n; $i++) { 
    $this->weight[$i] += $learningRate * $error;
}
...