Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
lib/Algorithm/Classifier/IsolationForest/App/Command/set_voting.pm view on Meta::CPAN
package Algorithm::Classifier::IsolationForest::App::Command::set_voting;
use strict;
use warnings;
use Algorithm::Classifier::IsolationForest ();
use Algorithm::Classifier::IsolationForest::App -command;
use File::Slurp qw(read_file write_file);
use Scalar::Util qw(looks_like_number);
sub opt_spec {
return (
[
'm=s',
'Input model JSON file path/name.',
{ 'default' => 'iforest_model.json', 'completion' => 'files' }
],
[
'voting=s',
"Target scoring-time aggregation: 'mean' (classic averaged score) or 'majority' (MVIForest per-tree vote)."
],
[
'i=s',
'CSV training data. Required only when the model was fit with contamination, so the decision threshold can be recalibrated for the new mode.',
{ 'completion' => 'files' }
],
[ 'o=s', 'Write the updated model here instead of overwriting -m.', { 'completion' => 'files' } ],
[ 'p', 'Print the updated model JSON instead of saving it.' ],
[ 'w', 'Overwrite the -o file if it already exists.' ],
);
} ## end sub opt_spec
sub abstract { 'Switch a saved model between mean and majority voting' }
sub description {
'Switches the scoring-time aggregation of a saved model between "mean" and
"majority" and writes it back (in place over -m by default, or to -o / stdout).
The forest itself is voting-independent, so no tree is rebuilt. The one thing
that does not carry over is a contamination-learned decision threshold: it is a
quantile of whichever per-point quantity the mode thresholds against (the
averaged anomaly score under mean, the per-tree majority pivot under majority),
so switching relearns it for the target mode. That recalibration needs the
original training data, supplied as a CSV via -i. Models fit without
contamination carry no threshold and switch without -i.
Switches to new args are like below...
--voting -> voting
-i -> training CSV (contamination models only)
';
} ## end sub description
sub validate {
my ( $self, $opt, $args ) = @_;
if ( !-f $opt->{'m'} ) {
$self->usage_error( '-m, "' . $opt->{'m'} . '", is not a file or does not exist' );
} elsif ( !-r $opt->{'m'} ) {
$self->usage_error( '-m, "' . $opt->{'m'} . '", is not readable' );
}
if ( !defined( $opt->{'voting'} ) ) {
$self->usage_error('--voting has not been specified');
} elsif ( $opt->{'voting'} !~ /\A(?:mean|majority)\z/ ) {
$self->usage_error( '--voting, "' . $opt->{'voting'} . '", must be either mean or majority' );
}
if ( defined( $opt->{'i'} ) ) {
if ( !-f $opt->{'i'} ) {
$self->usage_error( '-i, "' . $opt->{'i'} . '", is not a file or does not exist' );
} elsif ( !-r $opt->{'i'} ) {
$self->usage_error( '-i, "' . $opt->{'i'} . '", is not readable' );
}
}
if ( defined( $opt->{'o'} ) && !$opt->{'w'} && -e $opt->{'o'} ) {
$self->usage_error( '-o, "' . $opt->{'o'} . '", already exists and -w is not specified' );
}
return 1;
} ## end sub validate
sub execute {
my ( $self, $opt, $args ) = @_;
my $iforest = Algorithm::Classifier::IsolationForest->load( $opt->{'m'} );
( run in 1.659 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )