Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
examples/axis-vs-extended.pl view on Meta::CPAN
#!/usr/bin/env perl
# axis-vs-extended.pl
#
# Isolation Forest comes in two flavours here:
# mode => 'axis' classic, axis-parallel splits (the original algorithm)
# mode => 'extended' Extended Isolation Forest, random *hyperplane* splits
#
# Axis-parallel splits can only cut straight across one feature at a time, which
# leaves a rectangular, axis-aligned bias in the score field. On data whose
# features are correlated (a diagonal band), that bias shows up two ways:
# * points that sit off the diagonal but still inside each feature's range
# get under-scored by axis mode, and
# * points on the diagonal can get spuriously inflated scores.
# Extended mode, using oblique cuts, softens both effects.
#
# This script trains one forest of each kind on the same diagonal band and
# compares how they score a few hand-picked probe points.
#
# perl -Ilib examples/axis-vs-extended.pl
use strict;
use warnings;
use Algorithm::Classifier::IsolationForest;
srand(3); # reproducible data
# A tight band along the line y = x, with both features spanning roughly [-3, 3].
my @data;
for ( 1 .. 400 ) {
my $t = -3 + 6 * rand();
push @data, [ $t + 0.05 * ( rand() - 0.5 ), $t + 0.05 * ( rand() - 0.5 ) ];
}
my $axis = Algorithm::Classifier::IsolationForest->new(
mode => 'axis',
n_trees => 200,
sample_size => 256,
seed => 1
)->fit( \@data );
my $ext = Algorithm::Classifier::IsolationForest->new(
mode => 'extended',
n_trees => 200,
sample_size => 256,
seed => 1
)->fit( \@data );
# name => [x, y], with a note on what we expect
my @probes = (
[ 'on the line, centre', [ 0, 0 ], 'normal' ],
[ 'on the line, far end', [ 3, 3 ], 'edge of normal' ],
[ 'off-diagonal, in range', [ 3, -3 ], 'ANOMALY (within each axis range)' ],
[ 'off-diagonal, in range', [ -3, 3 ], 'ANOMALY (within each axis range)' ],
[ 'far outside everything', [ 10, 10 ], 'ANOMALY (obvious)' ],
);
print "Anomaly scores (higher = more anomalous), trained on a diagonal band:\n\n";
printf " %-26s %-11s %-6s %-9s %s\n", 'probe point', '(x, y)', 'axis', 'extended', 'note';
print ' ', '-' x 78, "\n";
for my $p (@probes) {
my ( $name, $xy, $note ) = @$p;
my $a = $axis->score_samples( [$xy] )->[0];
my $e = $ext->score_samples( [$xy] )->[0];
printf " %-26s (%-3g,%3g) %-6.3f %-9.3f %s\n", $name, $xy->[0], $xy->[1], $a, $e, $note;
( run in 0.895 second using v1.01-cache-2.11-cpan-9581c071862 )