File Coverage

blib/lib/Algorithm/Classifier/NaiveBayes/App/Command/train.pm
Criterion Covered Total %
statement 48 53 90.5
branch 19 22 86.3
condition n/a
subroutine 9 11 81.8
pod 3 5 60.0
total 79 91 86.8


line stmt bran cond sub pod time code
1             package Algorithm::Classifier::NaiveBayes::App::Command::train;
2              
3 1     1   683 use 5.006;
  1         2  
4 1     1   3 use strict;
  1         3  
  1         18  
5 1     1   3 use warnings;
  1         1  
  1         43  
6 1     1   4 use Algorithm::Classifier::NaiveBayes ();
  1         2  
  1         23  
7 1     1   4 use Algorithm::Classifier::NaiveBayes::App -command;
  1         1  
  1         8  
8 1     1   259 use File::Slurp qw(read_file);
  1         2  
  1         614  
9              
10             sub options {
11             return (
12 7     7 0 85 [ 'm=s', 'Model JSON file path/name.', { 'default' => 'nb_model.json', 'completion' => 'files' } ],
13             [ 'c=s', 'Class to train.' ],
14             [ 'f=s', 'File to read the text from.', { 'completion' => 'files' } ],
15             [ 'token-splitter=s', 'New model only. Regex to use for splitting a string into tokens.' ],
16             [ 'stop-regex=s', 'New model only. Drop tokens entirely matching this regex.' ],
17             [ 'no-lc', 'New model only. Do not lowercase tokens.' ],
18             [ 'smoothing=s', 'New model only. Smoothing to use... laplace or lidstone.' ],
19             [ 'alpha=f', 'New model only. Alpha for lidstone smoothing.' ],
20             [ 'ngrams=i', 'New model only. Max size of n-grams to generate from adjacent tokens.' ],
21             [ 'token-weighting=s', 'New model only. How token occurrences are weighted... count or binary.' ],
22             [ 'priors=s', 'New model only. How class priors are computed... trained or uniform.' ],
23             );
24             } ## end sub options
25              
26 0     0 1 0 sub abstract { 'Train a class on the specified text' }
27              
28             sub description {
29 0     0 1 0 return 'Train a class on the specified text, creating the model file if needed.
30              
31             The text is taken from the file specified via -f, the remaining args
32             joined by a space, or from stdin.
33              
34             nb_tool train -m model.json -c spam buy cheap pills now
35             nb_tool train -m model.json -c spam -f some_spam.txt
36             cat some_spam.txt | nb_tool train -m model.json -c spam
37              
38             Model settings such as --smoothing may only be specified when the
39             model file does not exist yet, as they are stored in the model.
40             ';
41             } ## end sub description
42              
43             my @new_args = ( 'token_splitter', 'stop_regex', 'smoothing', 'alpha', 'ngrams', 'token_weighting', 'priors' );
44              
45             sub validate {
46 7     7 0 14 my ( $self, $opt, $args ) = @_;
47              
48 7 100       18 if ( !defined( $opt->{'c'} ) ) {
49 1         4 $self->usage_error('-c has not been specified');
50             }
51              
52 6 100       16 if ( defined( $opt->{'f'} ) ) {
53 3 100       4 if ( @{$args} ) {
  3         8  
54 1         6 $self->usage_error('-f and text args may not be used together');
55             }
56 2 100       131 if ( !-f $opt->{'f'} ) {
    50          
57 1         11 $self->usage_error( '-f, "' . $opt->{'f'} . '", is not a file or does not exist' );
58             } elsif ( !-r $opt->{'f'} ) {
59 0         0 $self->usage_error( '-f, "' . $opt->{'f'} . '", is not readable' );
60             }
61             } ## end if ( defined( $opt->{'f'} ) )
62              
63 4 100       120 if ( -f $opt->{'m'} ) {
64 3         8 foreach my $new_arg ( @new_args, 'no_lc' ) {
65 21 100       38 if ( defined( $opt->{$new_arg} ) ) {
66 1         2 my $flag = $new_arg;
67 1         3 $flag =~ s/_/-/g;
68 1         26 $self->usage_error( '--' . $flag . ' may only be used when creating a new model file' );
69             }
70             }
71             }
72              
73 3         9 return 1;
74             } ## end sub validate
75              
76             sub execute {
77 3     3 1 16 my ( $self, $opt, $args ) = @_;
78              
79 3         3 my $nb;
80 3 100       22 if ( -f $opt->{'m'} ) {
81 2         15 $nb = Algorithm::Classifier::NaiveBayes->new;
82 2         9 $nb->load( $opt->{'m'} );
83             } else {
84 1         3 my %args_for_new;
85 1         3 foreach my $new_arg (@new_args) {
86 7 50       15 if ( defined( $opt->{$new_arg} ) ) {
87 0         0 $args_for_new{$new_arg} = $opt->{$new_arg};
88             }
89             }
90 1 50       4 if ( $opt->{'no_lc'} ) {
91 0         0 $args_for_new{'lc_tokens'} = 0;
92             }
93 1         13 $nb = Algorithm::Classifier::NaiveBayes->new(%args_for_new);
94             } ## end else [ if ( -f $opt->{'m'} ) ]
95              
96 3 100       15 my $text = defined( $opt->{'f'} ) ? read_file( $opt->{'f'} ) : $self->text_from($args);
97 3         122 $nb->train( $opt->{'c'}, $text );
98 3         14 $nb->save( $opt->{'m'} );
99              
100 3         31 print 'Trained "' . $opt->{'c'} . '", ' . $nb->{'model'}{'total_docs'} . ' total documents in the model' . "\n";
101             } ## end sub execute
102              
103             1;