forked from jorgecasas/php-ml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Linear classifiers: Perceptron, Adaline, DecisionStump (#50)
* Linear classifiers * Code formatting to PSR-2 * Added basic test cases for linear classifiers
- Loading branch information
1 parent
f0a7984
commit cf222bc
Showing
9 changed files
with
676 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Phpml\Classification\Linear; | ||
|
||
use Phpml\Helper\Predictable; | ||
use Phpml\Helper\Trainable; | ||
use Phpml\Classification\Classifier; | ||
use Phpml\Classification\Linear\Perceptron; | ||
use Phpml\Preprocessing\Normalizer; | ||
|
||
class Adaline extends Perceptron | ||
{ | ||
|
||
/** | ||
* Batch training is the default Adaline training algorithm | ||
*/ | ||
const BATCH_TRAINING = 1; | ||
|
||
/** | ||
* Online training: Stochastic gradient descent learning | ||
*/ | ||
const ONLINE_TRAINING = 2; | ||
|
||
/** | ||
* The function whose result will be used to calculate the network error | ||
* for each instance | ||
* | ||
* @var string | ||
*/ | ||
protected static $errorFunction = 'output'; | ||
|
||
/** | ||
* Training type may be either 'Batch' or 'Online' learning | ||
* | ||
* @var string | ||
*/ | ||
protected $trainingType; | ||
|
||
/** | ||
* @var Normalizer | ||
*/ | ||
private $normalizer; | ||
|
||
/** | ||
* Initalize an Adaline (ADAptive LInear NEuron) classifier with given learning rate and maximum | ||
* number of iterations used while training the classifier <br> | ||
* | ||
* Learning rate should be a float value between 0.0(exclusive) and 1.0 (inclusive) <br> | ||
* Maximum number of iterations can be an integer value greater than 0 <br> | ||
* If normalizeInputs is set to true, then every input given to the algorithm will be standardized | ||
* by use of standard deviation and mean calculation | ||
* | ||
* @param int $learningRate | ||
* @param int $maxIterations | ||
*/ | ||
public function __construct(float $learningRate = 0.001, int $maxIterations = 1000, | ||
bool $normalizeInputs = true, int $trainingType = self::BATCH_TRAINING) | ||
{ | ||
if ($normalizeInputs) { | ||
$this->normalizer = new Normalizer(Normalizer::NORM_STD); | ||
} | ||
|
||
if (! in_array($trainingType, [self::BATCH_TRAINING, self::ONLINE_TRAINING])) { | ||
throw new \Exception("Adaline can only be trained with batch and online/stochastic gradient descent algorithm"); | ||
} | ||
$this->trainingType = $trainingType; | ||
|
||
parent::__construct($learningRate, $maxIterations); | ||
} | ||
|
||
/** | ||
* @param array $samples | ||
* @param array $targets | ||
*/ | ||
public function train(array $samples, array $targets) | ||
{ | ||
if ($this->normalizer) { | ||
$this->normalizer->transform($samples); | ||
} | ||
|
||
parent::train($samples, $targets); | ||
} | ||
|
||
/** | ||
* Adapts the weights with respect to given samples and targets | ||
* by use of gradient descent learning rule | ||
*/ | ||
protected function runTraining() | ||
{ | ||
// If online training is chosen, then the parent runTraining method | ||
// will be executed with the 'output' method as the error function | ||
if ($this->trainingType == self::ONLINE_TRAINING) { | ||
return parent::runTraining(); | ||
} | ||
|
||
// Batch learning is executed: | ||
$currIter = 0; | ||
while ($this->maxIterations > $currIter++) { | ||
$outputs = array_map([$this, 'output'], $this->samples); | ||
$updates = array_map([$this, 'gradient'], $this->targets, $outputs); | ||
$sum = array_sum($updates); | ||
|
||
// Updates all weights at once | ||
for ($i=0; $i <= $this->featureCount; $i++) { | ||
if ($i == 0) { | ||
$this->weights[0] += $this->learningRate * $sum; | ||
} else { | ||
$col = array_column($this->samples, $i - 1); | ||
$error = 0; | ||
foreach ($col as $index => $val) { | ||
$error += $val * $updates[$index]; | ||
} | ||
|
||
$this->weights[$i] += $this->learningRate * $error; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Returns the direction of gradient given the desired and actual outputs | ||
* | ||
* @param int $desired | ||
* @param int $output | ||
* @return int | ||
*/ | ||
protected function gradient($desired, $output) | ||
{ | ||
return $desired - $output; | ||
} | ||
|
||
/** | ||
* @param array $sample | ||
* @return mixed | ||
*/ | ||
public function predictSample(array $sample) | ||
{ | ||
if ($this->normalizer) { | ||
$samples = [$sample]; | ||
$this->normalizer->transform($samples); | ||
$sample = $samples[0]; | ||
} | ||
|
||
return parent::predictSample($sample); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Phpml\Classification\Linear; | ||
|
||
use Phpml\Helper\Predictable; | ||
use Phpml\Helper\Trainable; | ||
use Phpml\Classification\Classifier; | ||
use Phpml\Classification\DecisionTree; | ||
|
||
class DecisionStump extends DecisionTree | ||
{ | ||
use Trainable, Predictable; | ||
|
||
/** | ||
* @var int | ||
*/ | ||
protected $columnIndex; | ||
|
||
|
||
/** | ||
* A DecisionStump classifier is a one-level deep DecisionTree. It is generally | ||
* used with ensemble algorithms as in the weak classifier role. <br> | ||
* | ||
* If columnIndex is given, then the stump tries to produce a decision node | ||
* on this column, otherwise in cases given the value of -1, the stump itself | ||
* decides which column to take for the decision (Default DecisionTree behaviour) | ||
* | ||
* @param int $columnIndex | ||
*/ | ||
public function __construct(int $columnIndex = -1) | ||
{ | ||
$this->columnIndex = $columnIndex; | ||
|
||
parent::__construct(1); | ||
} | ||
|
||
/** | ||
* @param array $samples | ||
* @param array $targets | ||
*/ | ||
public function train(array $samples, array $targets) | ||
{ | ||
// Check if a column index was given | ||
if ($this->columnIndex >= 0 && $this->columnIndex > count($samples[0]) - 1) { | ||
$this->columnIndex = -1; | ||
} | ||
|
||
if ($this->columnIndex >= 0) { | ||
$this->setSelectedFeatures([$this->columnIndex]); | ||
} | ||
|
||
parent::train($samples, $targets); | ||
} | ||
} |
Oops, something went wrong.