Я хотел бы узнать о нейронных сетях, начиная с самого основного алгоритма персептрона. Итак, я реализовал один на PHP и получаю странные результаты после обучения. Все 4 возможных комбинации ввода возвращают либо неправильные, либо правильные результаты (чаще всего неправильные).
1) Что-то не так с моей реализацией, или результаты, которые я получаю, нормальные?
2) Может ли этот тип реализации работать с более чем 2 входами?
3) Каким будет следующий (самый легкий) шаг в изучении нейронных сетей после этого? Может быть, добавление нейронов, изменение функции активации или …?
Постскриптум Я довольно плохо разбираюсь в математике и не обязательно понимаю математику за перцептроном на 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";
Я должен поблагодарить вас за размещение этого вопроса, я хотел получить шанс немного глубже погрузиться в нейронные сети. Во всяком случае, до бизнеса. После возни с подробным описанием всего происходящего, в итоге потребовалось всего лишь 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 эпох обучения.