Skip to content

Commit

Permalink
start to implement SVM with libsvm
Browse files Browse the repository at this point in the history
  • Loading branch information
akondas committed May 5, 2016
1 parent a2e8a89 commit 95caef8
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 79 deletions.
53 changes: 53 additions & 0 deletions src/Phpml/Classification/SVC.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare (strict_types = 1);

namespace Phpml\Classification;

use Phpml\Classification\Traits\Predictable;
use Phpml\Classification\Traits\Trainable;
use Phpml\Math\Kernel;

class SVC implements Classifier
{
use Trainable, Predictable;

/**
* @var int
*/
private $kernel;

/**
* @var float
*/
private $cost;

/**
* @param int $kernel
* @param float $cost
*/
public function __construct(int $kernel, float $cost)
{
$this->kernel = $kernel;
$this->cost = $cost;
}

/**
* @param array $samples
* @param array $labels
*/
public function train(array $samples, array $labels)
{
$this->samples = $samples;
$this->labels = $labels;
}

/**
* @param array $sample
*
* @return mixed
*/
protected function predictSample(array $sample)
{
}
}
78 changes: 0 additions & 78 deletions src/Phpml/Classification/SupportVectorMachine.php

This file was deleted.

1 change: 0 additions & 1 deletion src/Phpml/Dataset/Dataset.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

interface Dataset
{
const SOME = 'z';
/**
* @return array
*/
Expand Down
59 changes: 59 additions & 0 deletions src/Phpml/SupportVectorMachine/DataTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare (strict_types = 1);

namespace Phpml\SupportVectorMachine;

class DataTransformer
{
/**
* @param array $samples
* @param array $labels
*
* @return string
*/
public static function trainingSet(array $samples, array $labels): string
{
$set = '';
$numericLabels = self::numericLabels($labels);
foreach ($labels as $index => $label) {
$set .= sprintf('%s %s %s', $numericLabels[$label], self::sampleRow($samples[$index]), PHP_EOL);
}

return $set;
}

/**
* @param array $labels
*
* @return array
*/
public static function numericLabels(array $labels): array
{
$numericLabels = [];
foreach ($labels as $label) {
if (isset($numericLabels[$label])) {
continue;
}

$numericLabels[$label] = count($numericLabels);
}

return $numericLabels;
}

/**
* @param array $sample
*
* @return string
*/
private static function sampleRow(array $sample): string
{
$row = [];
foreach ($sample as $index => $feature) {
$row[] = sprintf('%s:%s', $index, $feature);
}

return implode(' ', $row);
}
}
28 changes: 28 additions & 0 deletions src/Phpml/SupportVectorMachine/Kernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare (strict_types = 1);

namespace Phpml\SupportVectorMachine;

abstract class Kernel
{
/**
* u'*v.
*/
const LINEAR = 0;

/**
* (gamma*u'*v + coef0)^degree.
*/
const POLYNOMIAL = 1;

/**
* exp(-gamma*|u-v|^2).
*/
const RBF = 2;

/**
* tanh(gamma*u'*v + coef0).
*/
const SIGMOID = 3;
}
83 changes: 83 additions & 0 deletions src/Phpml/SupportVectorMachine/SupportVectorMachine.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare (strict_types = 1);

namespace Phpml\SupportVectorMachine;

class SupportVectorMachine
{
/**
* @var int
*/
private $type;

/**
* @var int
*/
private $kernel;

/**
* @var float
*/
private $cost;

/**
* @var string
*/
private $binPath;

/**
* @var
*/
private $varPath;

/**
* @var string
*/
private $model;

/**
* @param int $type
* @param int $kernel
* @param float $cost
*/
public function __construct(int $type, int $kernel, float $cost)
{
$this->type = $type;
$this->kernel = $kernel;
$this->cost = $cost;

$rootPath = realpath(implode(DIRECTORY_SEPARATOR, [dirname(__FILE__), '..', '..', '..'])).DIRECTORY_SEPARATOR;

$this->binPath = $rootPath.'bin'.DIRECTORY_SEPARATOR.'libsvm'.DIRECTORY_SEPARATOR;
$this->varPath = $rootPath.'var'.DIRECTORY_SEPARATOR;
}

/**
* @param array $samples
* @param array $labels
*/
public function train(array $samples, array $labels)
{
$trainingSet = DataTransformer::trainingSet($samples, $labels);
file_put_contents($trainingSetFileName = $this->varPath.uniqid(), $trainingSet);
$modelFileName = $trainingSetFileName.'-model';

$command = sprintf('%ssvm-train -s %s -t %s -c %s %s %s', $this->binPath, $this->type, $this->kernel, $this->cost, $trainingSetFileName, $modelFileName);
$output = '';
exec(escapeshellcmd($command), $output);

$this->model = file_get_contents($modelFileName);

unlink($trainingSetFileName);
unlink($modelFileName);
}

/**
* @return string
*/
public function getModel()
{
return $this->model;
}
}
33 changes: 33 additions & 0 deletions src/Phpml/SupportVectorMachine/Type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare (strict_types = 1);

namespace Phpml\SupportVectorMachine;

abstract class Type
{
/**
* classification.
*/
const C_SVC = 0;

/**
* classification.
*/
const NU_SVC = 1;

/**
* distribution estimation.
*/
const ONE_CLASS_SVM = 2;

/**
* regression.
*/
const EPSILON_SVR = 3;

/**
* regression.
*/
const NU_SVR = 4;
}
25 changes: 25 additions & 0 deletions tests/Phpml/SupportVectorMachine/DataTransformerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare (strict_types = 1);

namespace tests\SupportVectorMachine;

use Phpml\SupportVectorMachine\DataTransformer;

class DataTransformerTest extends \PHPUnit_Framework_TestCase
{
public function testTransformDatasetToTrainingSet()
{
$samples = [[1, 1], [2, 1], [3, 2], [4, 5]];
$labels = ['a', 'a', 'b', 'b'];

$trainingSet =
'0 0:1 1:1 '.PHP_EOL.
'0 0:2 1:1 '.PHP_EOL.
'1 0:3 1:2 '.PHP_EOL.
'1 0:4 1:5 '.PHP_EOL
;

$this->assertEquals($trainingSet, DataTransformer::trainingSet($samples, $labels));
}
}
Loading

0 comments on commit 95caef8

Please sign in to comment.