Algorithm-Classifier-NaiveBayes

 view release on metacpan or  search on metacpan

lib/Algorithm/Classifier/NaiveBayes/App/Command/classify.pm  view on Meta::CPAN


    nb_tool classify -m model.json cheap pills for sale
    cat some_message.txt | nb_tool classify -m model.json -p
';
} ## 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' );
	}

	return 1;
}

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/explain.pm  view on Meta::CPAN

over the runner up.

    nb_tool explain -m model.json you have won a free cruise
';
} ## 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' );
	}

	return 1;
}

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/info.pm  view on Meta::CPAN

	return 'Show the settings, classes, and stats for a saved model.

    nb_tool info -m model.json
';
}

sub validate {
	my ( $self, $opt, $args ) = @_;

	if ( !-f $opt->{'m'} ) {
		$self->usage_error( '-m, "' . $opt->{'m'} . '", is not a file or does not exist' );
	}

	return 1;
}

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/prune.pm  view on Meta::CPAN


    # remove all tokens only trained once
    nb_tool prune -m model.json 2
';
} ## 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' );
	}

	if ( !defined( $args->[0] ) ) {
		$self->usage_error('No min count specified');
	}
	if ( $args->[0] !~ /\A\d+\z/ || $args->[0] < 1 ) {
		$self->usage_error( 'The min count, "' . $args->[0] . '", is not a whole number greater than 0' );
	}

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/tokens.pm  view on Meta::CPAN


    nb_tool tokens -m model.json spam
    nb_tool tokens -m model.json -c spam
';
}

sub validate {
	my ( $self, $opt, $args ) = @_;

	if ( !-f $opt->{'m'} ) {
		$self->usage_error( '-m, "' . $opt->{'m'} . '", is not a file or does not exist' );
	}

	if ( !defined( $args->[0] ) ) {
		$self->usage_error('No class specified');
	}

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/train.pm  view on Meta::CPAN

model file does not exist yet, as they are stored in the model.
';
} ## end sub description

my @new_args = ( 'token_splitter', 'stop_regex', 'smoothing', 'alpha', 'ngrams', 'token_weighting', 'priors' );

sub validate {
	my ( $self, $opt, $args ) = @_;

	if ( !defined( $opt->{'c'} ) ) {
		$self->usage_error('-c has not been specified');
	}

	if ( defined( $opt->{'f'} ) ) {
		if ( @{$args} ) {
			$self->usage_error('-f and text args may not be used together');
		}
		if ( !-f $opt->{'f'} ) {
			$self->usage_error( '-f, "' . $opt->{'f'} . '", is not a file or does not exist' );
		} elsif ( !-r $opt->{'f'} ) {
			$self->usage_error( '-f, "' . $opt->{'f'} . '", is not readable' );
		}
	} ## end if ( defined( $opt->{'f'} ) )

	if ( -f $opt->{'m'} ) {
		foreach my $new_arg ( @new_args, 'no_lc' ) {
			if ( defined( $opt->{$new_arg} ) ) {
				my $flag = $new_arg;
				$flag =~ s/_/-/g;
				$self->usage_error( '--' . $flag . ' may only be used when creating a new model file' );
			}
		}
	}

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

lib/Algorithm/Classifier/NaiveBayes/App/Command/tweak.pm  view on Meta::CPAN


    nb_tool tweak -m model.json --smoothing lidstone --alpha 0.1
    nb_tool tweak -m model.json --priors uniform
';
} ## 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' );
	}

	if ( !defined( $opt->{'smoothing'} ) && !defined( $opt->{'alpha'} ) && !defined( $opt->{'priors'} ) ) {
		$self->usage_error('Nothing to change... at least one of --smoothing, --alpha, or --priors is needed');
	}

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;
	$nb->load( $opt->{'m'} );

lib/Algorithm/Classifier/NaiveBayes/App/Command/untrain.pm  view on Meta::CPAN

    nb_tool untrain -m model.json -c spam something that is not spam
    nb_tool untrain -m model.json -c spam -f some_ham.txt
    cat ham | nb_tool untrain -m model.json -c spam
';
} ## end sub description

sub validate {
	my ( $self, $opt, $args ) = @_;

	if ( !defined( $opt->{'c'} ) ) {
		$self->usage_error('-c has not been specified');
	}

	if ( !-f $opt->{'m'} ) {
		$self->usage_error( '-m, "' . $opt->{'m'} . '", is not a file or does not exist' );
	}

	if ( defined( $opt->{'f'} ) ) {
		if ( @{$args} ) {
			$self->usage_error('-f and text args may not be used together');
		}
		if ( !-f $opt->{'f'} ) {
			$self->usage_error( '-f, "' . $opt->{'f'} . '", is not a file or does not exist' );
		} elsif ( !-r $opt->{'f'} ) {
			$self->usage_error( '-f, "' . $opt->{'f'} . '", is not readable' );
		}
	} ## end if ( defined( $opt->{'f'} ) )

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $nb = Algorithm::Classifier::NaiveBayes->new;

t/07-save-load.t  view on Meta::CPAN

is( $loaded->classify('buy cheap pills'), 'spam', 'loaded model classifies' );

# qr// Regexps are stringified on save
my $qr_nb = Algorithm::Classifier::NaiveBayes->new( 'stop_regex' => qr/at|a/ );
$qr_nb->train( 'ham', 'cat at a noon' );
$qr_nb->save($save_file);
my $qr_loaded = Algorithm::Classifier::NaiveBayes->new;
$qr_loaded->load($save_file);
is_deeply( [ $qr_loaded->tokenize('cat at a noon') ], [ 'cat', 'noon' ], 'qr// stop_regex survives save/load' );

# error handling
eval { $nb->save(); };
like( $@, qr/No file specified/, 'save with no file dies' );

eval { $nb->save( $dir . '/nonexistent/model.json' ); };
like( $@, qr/Failed to write/, 'save to an unwritable path dies' );

eval { $loaded->load(); };
like( $@, qr/No file specified/, 'load with no file dies' );

eval { $loaded->load( $dir . '/nonexistent.json' ); };

t/11-app.t  view on Meta::CPAN

my $app = 'Algorithm::Classifier::NaiveBayes::App';

my $dir   = File::Temp::tempdir( 'CLEANUP' => 1 );
my $model = $dir . '/model.json';

##
## train
##

my $result = test_app( $app => [ 'train', '-m', $model, '-c', 'spam', 'buy', 'cheap', 'pills', 'now' ] );
is( $result->error, undef, 'train runs' );
like( $result->stdout, qr/Trained "spam", 1 total documents/, 'train reports what it did' );
ok( -f $model, 'train creates the model file' );

$result = test_app( $app => [ 'train', '-m', $model, '-c', 'ham', 'meeting', 'at', 'noon', 'tomorrow' ] );
is( $result->error, undef, 'training a second class works' );
like( $result->stdout, qr/2 total documents/, 'total documents incremented' );

# creation only options are rejected on an existing model
$result = test_app( $app => [ 'train', '-m', $model, '-c', 'spam', '--ngrams', '2', 'foo' ] );
like( $result->error, qr/only be used when creating/, 'creation options rejected on a existing model' );

# missing -c
$result = test_app( $app => [ 'train', '-m', $model, 'foo' ] );
like( $result->error, qr/-c has not been specified/, 'train without -c errors' );

# -f reads the text from a file
my $text_file = $dir . '/text.txt';
open( my $fh, '>', $text_file ) or die($!);
print $fh "cheap watches for sale\n";
close($fh);

$result = test_app( $app => [ 'train', '-m', $model, '-c', 'spam', '-f', $text_file ] );
is( $result->error, undef, 'train -f runs' );
like( $result->stdout, qr/3 total documents/, 'train -f trained the document' );

$result = test_app( $app => [ 'train', '-m', $model, '-c', 'spam', '-f', $text_file, 'extra', 'args' ] );
like( $result->error, qr/-f and text args may not be used together/, 'train -f with text args errors' );

$result = test_app( $app => [ 'train', '-m', $model, '-c', 'spam', '-f', $dir . '/nonexistent.txt' ] );
like( $result->error, qr/is not a file or does not exist/, 'train -f with a missing file errors' );

$result = test_app( $app => [ 'untrain', '-m', $model, '-c', 'spam', '-f', $text_file ] );
is( $result->error, undef, 'untrain -f runs' );
like( $result->stdout, qr/2 total documents/, 'untrain -f untrained the document' );

$result = test_app( $app => [ 'untrain', '-m', $model, '-c', 'spam', '-f', $text_file, 'extra' ] );
like( $result->error, qr/-f and text args may not be used together/, 'untrain -f with text args errors' );

##
## classify
##

$result = test_app( $app => [ 'classify', '-m', $model, 'cheap', 'pills' ] );
is( $result->error, undef, 'classify runs' );
like( $result->stdout, qr/\Aspam\n/, 'classify prints the class' );

$result = test_app( $app => [ 'classify', '-m', $model, '-s', '-p', 'cheap', 'pills' ] );
like( $result->stdout, qr/scores:/, '-s prints scores' );
like( $result->stdout, qr/probs:/,  '-p prints probs' );

$result = test_app( $app => [ 'classify', '-m', $model, '--json', 'cheap', 'pills' ] );
like( $result->stdout, qr/"class"\s*:\s*"spam"/, '--json prints JSON' );

$result = test_app( $app => [ 'classify', '-m', $dir . '/nonexistent.json', 'foo' ] );
like( $result->error, qr/is not a file or does not exist/, 'classify with a missing model errors' );

##
## explain
##

$result = test_app( $app => [ 'explain', '-m', $model, 'cheap', 'meeting', 'pills' ] );
is( $result->error, undef, 'explain runs' );
like( $result->stdout, qr/spam, probability/,         'explain prints the class and probability' );
like( $result->stdout, qr/cheap pushed towards spam/, 'explain prints token pulls' );

##
## info
##

$result = test_app( $app => [ 'info', '-m', $model ] );
is( $result->error, undef, 'info runs' );
like( $result->stdout, qr/total_docs: 2/,      'info prints total_docs' );
like( $result->stdout, qr/spam: docs=1/,       'info prints per class stats' );
like( $result->stdout, qr/smoothing: laplace/, 'info prints settings' );

##
## tokens
##

$result = test_app( $app => [ 'tokens', '-m', $model, 'spam' ] );
is( $result->error, undef, 'tokens runs' );
like( $result->stdout, qr/^cheap$/m, 'tokens lists the class tokens' );

$result = test_app( $app => [ 'tokens', '-m', $model, '-c', 'spam' ] );
like( $result->stdout, qr/^cheap: 1$/m, 'tokens -c includes counts' );

$result = test_app( $app => [ 'tokens', '-m', $model ] );
like( $result->error, qr/No class specified/, 'tokens without a class errors' );

##
## prune
##

$result = test_app( $app => [ 'prune', '-m', $model, '2' ] );
is( $result->error, undef, 'prune runs' );
like( $result->stdout, qr/Pruned 8 tokens, 0 remaining/, 'prune reports what it did' );

$result = test_app( $app => [ 'prune', '-m', $model, 'x' ] );
like( $result->error, qr/not a whole number/, 'prune with a bad min count errors' );

##
## tweak
##

$result = test_app( $app => [ 'tweak', '-m', $model, '--smoothing', 'lidstone', '--alpha', '0.1' ] );
is( $result->error, undef, 'tweak runs' );
like( $result->stdout, qr/smoothing: lidstone/, 'tweak reports the smoothing' );
like( $result->stdout, qr/alpha: 0.1/,          'tweak reports the alpha' );

$result = test_app( $app => [ 'info', '-m', $model ] );
like( $result->stdout, qr/smoothing: lidstone/, 'tweak changes are saved to the model' );

$result = test_app( $app => [ 'tweak', '-m', $model, '--priors', 'uniform' ] );
is( $result->error, undef, 'tweak priors runs' );
like( $result->stdout, qr/alpha: 0.1/, 'tweaking priors leaves alpha alone' );

$result = test_app( $app => [ 'tweak', '-m', $model ] );
like( $result->error, qr/Nothing to change/, 'tweak with nothing to change errors' );

$result = test_app( $app => [ 'tweak', '-m', $model, '--smoothing', 'derp' ] );
like( $result->error, qr/smoothing must be either/, 'tweak with a bad smoothing errors' );

##
## untrain
##

$result = test_app( $app => [ 'untrain', '-m', $model, '-c', 'ham', 'meeting', 'at', 'noon', 'tomorrow' ] );
is( $result->error, undef, 'untrain runs' );
like( $result->stdout, qr/Untrained "ham", 1 total documents/, 'untrain reports what it did' );

$result = test_app( $app => [ 'info', '-m', $model ] );
unlike( $result->stdout, qr/ham: docs/, 'untrained class no longer listed by info' );

done_testing;



( run in 0.734 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )