PHP 中与门的基本感知器,我做对了吗?奇怪的结果

Basic perceptron for AND gate in PHP, am I doing it right? Weird results

我想从最基本的感知器算法开始学习神经网络。所以我在 PHP 中实现了一个,训练后我得到了奇怪的结果。所有 4 种可能的输入组合 return 错误或正确的结果(更多时候是错误的)。

1) 是我的实现有问题还是得到的结果是正常的?

2) 这种实现方式是否适用于 2 个以上的输入?

3) 在此之后学习神经网络的下一个(最简单的)步骤是什么?也许添加更多的神经元,改变激活函数,或者......?

P.S。我的数学很差,不一定 100% 理解感知器背后的数学,至少不是训练部分。

感知器Class

<?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";

非常感谢您提出这个问题,我一直希望有机会更深入地研究神经网络。不管怎样,言归正传。在修改并详细记录所有发生的事情之后,它最终只需要更改 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;
}

发现我的愚蠢错误,我没有调整训练集每一行的偏差,因为我不小心将它放在 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 个 epoch 的训练就足够了。