128 lines
4.3 KiB
PHP
128 lines
4.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Classifier\Lib;
|
||
|
|
|
||
|
|
use Classifier\Lib\Enum\MessageType;
|
||
|
|
use Classifier\Lib\MessageClassifier;
|
||
|
|
use Classifier\Lib\Model\ModelTrainer;
|
||
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||
|
|
use Symfony\Component\Console\Input\ArgvInput;
|
||
|
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||
|
|
|
||
|
|
class RawDataClassifier
|
||
|
|
{
|
||
|
|
const OUTPUT_DIR = __DIR__ . '/../../output';
|
||
|
|
const GOOD_FILE = self::OUTPUT_DIR . '/positive.php';
|
||
|
|
const BAD_FILE = self::OUTPUT_DIR . '/negative.php';
|
||
|
|
const IGNORE_FILE = self::OUTPUT_DIR . '/ignored.php';
|
||
|
|
|
||
|
|
private $strings = [];
|
||
|
|
private $io;
|
||
|
|
private $results = ['good' => [], 'bad' => [], 'ignored' => []];
|
||
|
|
private $master;
|
||
|
|
private $trainer;
|
||
|
|
|
||
|
|
public function __construct(array $strings, MessageClassifier $master, ModelTrainer $trainer)
|
||
|
|
{
|
||
|
|
$this->io = new SymfonyStyle(new ArgvInput(), new ConsoleOutput());
|
||
|
|
$this->master = $master;
|
||
|
|
$this->trainer = $trainer;
|
||
|
|
|
||
|
|
$this->loadExistingData();
|
||
|
|
|
||
|
|
$cleaned = array_unique(array_map(function ($v) {
|
||
|
|
return preg_replace('/\s+/', ' ', trim(mb_strtolower($v)));
|
||
|
|
}, $strings));
|
||
|
|
$alreadyDone = array_merge($this->results['good'], $this->results['bad'], $this->results['ignored']);
|
||
|
|
|
||
|
|
$this->strings = array_diff($cleaned, $alreadyDone);
|
||
|
|
|
||
|
|
if (!is_dir(self::OUTPUT_DIR)) {
|
||
|
|
mkdir(self::OUTPUT_DIR, 0777, true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function loadExistingData(): void
|
||
|
|
{
|
||
|
|
$files = ['good' => self::GOOD_FILE, 'bad' => self::BAD_FILE, 'ignored' => self::IGNORE_FILE];
|
||
|
|
foreach ($files as $key => $path) {
|
||
|
|
if (file_exists($path)) {
|
||
|
|
$this->results[$key] = include $path;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function runQuestions(): void
|
||
|
|
{
|
||
|
|
$count = count($this->strings);
|
||
|
|
if ($count === 0) {
|
||
|
|
$this->io->success("Toutes les chaînes sont déjà traitées.");
|
||
|
|
$this->askForTraining();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->io->title('Test & Labellisation Assistée par Machine');
|
||
|
|
|
||
|
|
foreach ($this->strings as $string) {
|
||
|
|
// L'algorithme fait une suggestion
|
||
|
|
$prediction = $this->master->classify($string);
|
||
|
|
|
||
|
|
$this->io->section("Message à analyser");
|
||
|
|
$this->io->text($string);
|
||
|
|
$this->io->note(sprintf(
|
||
|
|
"Suggestion : %s (via %s) - Confiance : %d%% - Détails : %s",
|
||
|
|
$prediction->decision->getValue(),
|
||
|
|
$prediction->method,
|
||
|
|
$prediction->confidence * 100,
|
||
|
|
implode(', ', array_map(function (object $d) {
|
||
|
|
return $d->word . " (" . round($d->score * 100, 2) . "%)";
|
||
|
|
}, $prediction->getDetails()))
|
||
|
|
));
|
||
|
|
|
||
|
|
$answer = $this->io->choice('Valider cette classification ?', [
|
||
|
|
'y' => 'Oui (Alerte)',
|
||
|
|
'n' => 'Non (Info)',
|
||
|
|
'i' => 'Ignorer',
|
||
|
|
'q' => 'Sauvegarder et Quitter'
|
||
|
|
], $prediction->decision->equals(MessageType::ALERTE()) ? 'y' : 'n');
|
||
|
|
|
||
|
|
if ($answer === 'q') {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($answer === 'y') {
|
||
|
|
$this->results['good'][] = $string;
|
||
|
|
} elseif ($answer === 'i') {
|
||
|
|
$this->results['ignored'][] = $string;
|
||
|
|
} else {
|
||
|
|
$this->results['bad'][] = $string;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->saveResults();
|
||
|
|
$this->askForTraining();
|
||
|
|
}
|
||
|
|
|
||
|
|
private function askForTraining(): void
|
||
|
|
{
|
||
|
|
if ($this->io->confirm("Voulez-vous lancer l'apprentissage immédiat du modèle avec ces données ?", true)) {
|
||
|
|
$this->io->comment("Entraînement en cours...");
|
||
|
|
$this->trainer->trainFromData([
|
||
|
|
'ALERTE' => $this->results['good'],
|
||
|
|
'INFO' => $this->results['bad']
|
||
|
|
]);
|
||
|
|
$this->io->success("Modèle mis à jour et sauvegardé !");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function saveResults(): void
|
||
|
|
{
|
||
|
|
foreach (['good' => self::GOOD_FILE, 'bad' => self::BAD_FILE, 'ignored' => self::IGNORE_FILE] as $key => $path) {
|
||
|
|
sort($this->results[$key]);
|
||
|
|
file_put_contents($path, "<?php\n\nreturn " . var_export($this->results[$key], true) . ";\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|