File Coverage

blib/lib/AI/Perceptron/Simple.pm
Criterion Covered Total %
statement 341 346 98.5
branch 85 96 88.5
condition 21 27 77.7
subroutine 67 69 97.1
pod 27 27 100.0
total 541 565 95.7


line stmt bran cond sub pod time code
1             package AI::Perceptron::Simple;
2              
3 17     17   1589628 use 5.008001;
  17         211  
4 17     17   102 use strict;
  17         32  
  17         388  
5 17     17   96 use warnings;
  17         39  
  17         609  
6 17     17   102 use Carp "croak";
  17         33  
  17         1005  
7              
8 17     17   11217 use utf8;
  17         256  
  17         98  
9             binmode STDOUT, ":utf8";
10              
11             require local::lib; # no local::lib in tests, this is also to avoid loading local::lib multiple times
12 17     17   14465 use Text::CSV qw( csv );
  17         312411  
  17         1120  
13 17     17   9450 use Text::Matrix;
  17         359570  
  17         735  
14 17     17   161 use File::Basename qw( basename );
  17         41  
  17         2005  
15 17     17   134 use List::Util qw( shuffle );
  17         38  
  17         2296  
16              
17             =head1 NAME
18              
19             AI::Perceptron::Simple
20              
21             A Newbie Friendly Module to Create, Train, Validate and Test Perceptrons / Neurons
22              
23             =head1 VERSION
24              
25             Version 1.03
26              
27             =cut
28              
29             our $VERSION = '1.03';
30              
31             # default values
32 17     17   147 use constant LEARNING_RATE => 0.05;
  17         40  
  17         1626  
33 17     17   125 use constant THRESHOLD => 0.5;
  17         51  
  17         885  
34 17     17   115 use constant TUNE_UP => 1;
  17         34  
  17         795  
35 17     17   105 use constant TUNE_DOWN => 0;
  17         35  
  17         1423  
36              
37             =head1 SYNOPSIS
38              
39             #!/usr/bin/perl
40              
41             use AI::Perceptron::Simple qw(...);
42              
43             # create a new nerve / neuron / perceptron
44             $perceptron = AI::Perceptron::Simple->new( {
45             initial_value => $any_value_that_makes_sense, # size of each dendrite :)
46             learning_rate => 0.3, # optional
47             threshold => 0.85, # optional
48             attribs => \@attributes, # dendrites
49             } );
50              
51             # train
52             $perceptron->tame( ... );
53             $perceptron->exercise( ... );
54             $perceptron->train( $training_data_csv, $expected_column_name, $save_nerve_to );
55             # or
56             $perceptron->train(
57             $training_data_csv, $expected_column_name, $save_nerve_to,
58             $show_progress, $identifier); # these two parameters must go together
59              
60              
61             # validate
62             $perceptron->take_lab_test( ... );
63             $perceptron->take_mock_exam( ... );
64              
65             # fill results to original file
66             $perceptron->validate( {
67             stimuli_validate => $validation_data_csv,
68             predicted_column_index => 4,
69             } );
70             # or
71             # fill results to a new file
72             $perceptron->validate( {
73             stimuli_validate => $validation_data_csv,
74             predicted_column_index => 4,
75             results_write_to => $new_csv
76             } );
77              
78              
79             # test - see "validate" method, same usage
80             $perceptron->take_real_exam( ... );
81             $perceptron->work_in_real_world( ... );
82             $perceptron->test( ... );
83              
84              
85             # confusion matrix
86             my %c_matrix = $perceptron->get_confusion_matrix( {
87             full_data_file => $file_csv,
88             actual_output_header => $header_name,
89             predicted_output_header => $predicted_header_name,
90             more_stats => 1, # optional
91             } );
92              
93             # accessing the confusion matrix
94             my @keys = qw( true_positive true_negative false_positive false_negative
95             total_entries accuracy sensitivity );
96             for ( @keys ) {
97             print $_, " => ", $c_matrix{ $_ }, "\n";
98             }
99              
100             # output to console
101             $perceptron->display_confusion_matrix( \%c_matrix, {
102             zero_as => "bad apples", # cat milk green etc.
103             one_as => "good apples", # dog honey pink etc.
104             } );
105              
106              
107             # saving and loading data of perceptron locally
108             # NOTE: nerve data is automatically saved after each trainning process
109             use AI::Perceptron::Simple ":local_data";
110              
111             my $nerve_file = "apples.nerve";
112             preserve( ... );
113             save_perceptron( $perceptron, $nerve_file );
114              
115             # load data of percpetron for use in actual program
116             my $apple_nerve = revive( ... );
117             my $apple_nerve = load_perceptron( $nerve_file );
118              
119              
120             # for portability of nerve data
121             use AI::Perceptron::Simple ":portable_data";
122              
123             my $yaml_nerve_file = "pearls.yaml";
124             preserve_as_yaml ( ... );
125             save_perceptron_yaml ( $perceptron, $yaml_nerve_file );
126              
127             # load nerve data on the other computer
128             my $pearl_nerve = revive_from_yaml ( ... );
129             my $pearl_nerve = load_perceptron_yaml ( $yaml_nerve_file );
130              
131              
132             # processing data
133             use AI::Perceptron::Simple ":process_data";
134             shuffle_stimuli ( ... )
135             shuffle_data ( ORIGINAL_STIMULI, $new_file_1, $new_file_2, ... );
136             shuffle_data ( $original_stimuli => $new_file_1, $new_file_2, ... );
137              
138             =head1 EXPORT
139              
140             None by default.
141              
142             All the subroutines from C, C and C sections are importable through tags or manually specifying them.
143              
144             The tags available include the following:
145              
146             =over 4
147              
148             =item C<:process_data> - subroutines under C section.
149              
150             =item C<:local_data> - subroutines under C section.
151              
152             =item C<:portable_data> - subroutines under C section.
153              
154             =back
155              
156             Most of the stuff are OO.
157              
158             =cut
159              
160 17     17   172 use Exporter qw( import );
  17         46  
  17         70143  
161             our @EXPORT_OK = qw(
162             shuffle_data shuffle_stimuli
163             preserve save_perceptron revive load_perceptron
164             preserve_as_yaml save_perceptron_yaml revive_from_yaml load_perceptron_yaml
165             );
166             our %EXPORT_TAGS = (
167             process_data => [ qw( shuffle_data shuffle_stimuli ) ],
168             local_data => [ qw( preserve save_perceptron revive load_perceptron ) ],
169             portable_data => [ qw( preserve_as_yaml save_perceptron_yaml revive_from_yaml load_perceptron_yaml ) ],
170             );
171              
172             =head1 DESCRIPTION
173              
174             This module provides methods to build, train, validate and test a perceptron. It can also save the data of the perceptron for future use for any actual AI programs.
175              
176             This module is also aimed to help newbies grasp hold of the concept of perceptron, training, validation and testing as much as possible. Hence, all the methods and subroutines in this module are decoupled as much as possible so that the actual scripts can be written as simple complete programs.
177              
178             The implementation here is super basic as it only takes in input of the dendrites and calculate the output. If the output is higher than the threshold, the final result (category) will
179             be 1 aka perceptron is activated. If not, then the result will be 0 (not activated).
180              
181             Depending on how you view or categorize the final result, the perceptron will fine tune itself (aka train) based on the learning rate until the desired result is met. Everything from
182             here on is all mathematics and numbers which only makes sense to the computer and not humans anymore.
183              
184             Whenever the perceptron fine tunes itself, it will increase/decrease all the dendrites that is significant (attributes labelled 1) for each input. This means that even when the
185             perceptron successfully fine tunes itself to suite all the data in your file for the first round, the perceptron might still get some of the things wrong for the next round of training.
186             Therefore, the perceptron should be trained for as many rounds as possible. The more "confusion" the perceptron is able to correctly handle, the more "mature" the perceptron is.
187             No one defines how "mature" it is except the programmer himself/herself :)
188              
189             =head1 CONVENTIONS USED
190              
191             Please take note that not all subroutines/method must be used to make things work. All the subroutines and methods are listed out for the sake of writing the documentation.
192              
193             Private methods/subroutines are prefixed with C<_> or C<&_> and they aren't meant to be called directly. You can if you want to. There are quite a number of them to be honest, just ignore them if you happen to see them :)
194              
195             Synonyms are placed before the actual ie. technical subroutines/methods. You will see C<...> as the parameters if they are synonyms. Move to the next subroutine/method until you find something like C<\%options> as the parameter or anything that isn't C<...> for the description.
196              
197             =head1 DATASET STRUCTURE
198              
199             I
200              
201             Any field ie columns that will be used for processing must be binary ie. C<0> or C<1> only. Your dataset can contain other columns with non-binary data as long as they are not one of the dendrites.
202              
203             There are soem sample dataset which can be found in the C directory. The original dataset can also be found in C. The files can also be found L.
204              
205             =head1 PERCEPTRON DATA
206              
207             The perceptron/neuron data is stored using the C module.
208              
209             See C section below for more info on some known issues.
210              
211             =head1 DATA PROCESSING RELATED SUBROUTINES
212              
213             These subroutines can be imported using the tag C<:process_data>.
214              
215             These subroutines should be called in the procedural way.
216              
217             =head2 shuffle_stimuli ( ... )
218              
219             The parameters and usage are the same as C. See the next two subroutines.
220              
221             =head2 shuffle_data ( $original_data => $shuffled_1, $shuffled_2, ... )
222              
223             =head2 shuffle_data ( ORIGINAL_DATA, $shuffled_1, $shuffled_2, ... )
224              
225             Shuffles C<$original_data> or C and saves them to other files.
226              
227             =cut
228              
229             sub shuffle_stimuli {
230 4     4 1 4462 shuffle_data( @_ );
231             }
232              
233             sub shuffle_data {
234 8 100   8 1 4329 my $stimuli = shift or croak "Please specify the original file name";
235 6 100       269 my @shuffled_stimuli_names = @_
236             or croak "Please specify the output files for the shuffled data";
237            
238 4         8 my @aoa;
239 4         10 for ( @shuffled_stimuli_names ) {
240             # copied from _real_validate_or_test
241             # open for shuffling
242 12         8404 my $aoa = csv (in => $stimuli, encoding => ":encoding(utf-8)");
243 12         39571 my $attrib_array_ref = shift @$aoa; # 'remove' the header, it's annoying :)
244 12         299 @aoa = shuffle( @$aoa ); # this can only process actual array
245 12         34 unshift @aoa, $attrib_array_ref; # put back the headers before saving file
246              
247 12 50       42 csv( in => \@aoa, out => $_, encoding => ":encoding(utf-8)" )
248             and
249             print "Saved shuffled data into ", basename($_), "!\n";;
250              
251             }
252             }
253              
254             =head1 CREATION RELATED SUBROUTINES/METHODS
255              
256             =head2 new ( \%options )
257              
258             Creates a brand new perceptron and initializes the value of each attribute / dendrite aka. weight. Think of it as the thickness or plasticity of the dendrites.
259              
260             For C<%options>, the followings are needed unless mentioned:
261              
262             =over 4
263              
264             =item initial_value => $decimal
265              
266             The value or thickness of ALL the dendrites when a new perceptron is created.
267              
268             Generally speaking, this value is usually between 0 and 1. However, it all depend on your combination of numbers for the other options.
269              
270             =item attribs => $array_ref
271              
272             An array reference containing all the attributes / dendrites names. Yes, give them some names :)
273              
274             =item learning_rate => $decimal
275              
276             Optional. The default is C<0.05>.
277              
278             The learning rate of the perceptron for the fine-tuning process.
279              
280             This value is usually between 0 and 1. However, it all depends on your combination of numbers for the other options.
281              
282             =item threshold => $decimal
283              
284             Optional. The default is C<0.5>
285              
286             This is the passing rate to determine the neuron output (C<0> or C<1>).
287              
288             Generally speaking, this value is usually between C<0> and C<1>. However, it all depend on your combination of numbers for the other options.
289              
290             =back
291              
292             =cut
293              
294             sub new {
295 13     13 1 4975 my $class = shift;
296            
297 13         29 my $data_ref = shift;
298 13         25 my %data = %{ $data_ref };
  13         68  
299            
300             # check keys
301 13 100       66 $data{ learning_rate } = LEARNING_RATE if not exists $data{ learning_rate };
302 13 100       50 $data{ threshold } = THRESHOLD if not exists $data{ threshold };
303            
304             #####
305             # don't pack this key checking process into a subroutine for now
306             # this is also used in &_real_validate_or_test
307 13         26 my @missing_keys;
308 13         38 for ( qw( initial_value attribs ) ) {
309 26 100       78 push @missing_keys, $_ unless exists $data{ $_ };
310             }
311            
312 13 100       474 croak "Missing keys: @missing_keys" if @missing_keys;
313             #####
314            
315             # continue to process the rest of the data
316 10         24 my %attributes;
317 10         20 for ( @{ $data{ attribs } } ) {
  10         37  
318 259         504 $attributes{ $_ } = $data{ initial_value };
319             }
320            
321             my %processed_data = (
322             learning_rate => $data{ learning_rate },
323             threshold => $data{ threshold },
324 10         49 attributes_hash_ref => \%attributes,
325             );
326            
327 10         55 bless \%processed_data, $class;
328             }
329              
330             =head2 get_attributes
331              
332             Obtains a hash of all the attributes of the perceptron
333              
334             =cut
335              
336             sub get_attributes {
337 1246     1246 1 1791 my $self = shift;
338 1246         1784 %{ $self->{attributes_hash_ref} };
  1246         13906  
339             }
340              
341             =head2 learning_rate ( $value )
342              
343             =head2 learning_rate
344              
345             If C<$value> is given, sets the learning rate to C<$value>. If not, then it returns the learning rate.
346              
347             =cut
348              
349             sub learning_rate {
350 4204     4204 1 6175 my $self = shift;
351 4204 100       6401 if ( @_ ) {
352 1         3 $self->{learning_rate} = shift;
353             } else {
354             $self->{learning_rate}
355 4203         7820 }
356             }
357              
358             =head2 threshold ( $value )
359              
360             =head2 threshold
361              
362             If C<$value> is given, sets the threshold / passing rate to C<$value>. If not, then it returns the passing rate.
363              
364             =cut
365              
366             sub threshold {
367 1528     1528 1 2404 my $self = shift;
368 1528 100       2827 if ( @_ ) {
369 1         3 $self->{ threshold } = shift;
370             } else {
371 1527         12893 $self->{ threshold };
372             }
373             }
374              
375             =head1 TRAINING RELATED SUBROUTINES/METHODS
376              
377             All the training methods here have the same parameters as the two actual C method and they all do the same stuff. They are also used in the same way.
378              
379             =head2 tame ( ... )
380              
381             =head2 exercise ( ... )
382              
383             =head2 train ( $stimuli_train_csv, $expected_output_header, $save_nerve_to_file )
384              
385             =head2 train ( $stimuli_train_csv, $expected_output_header, $save_nerve_to_file, $display_stats, $identifier )
386              
387             Trains the perceptron.
388              
389             C<$stimuli_train_csv> is the set of data / input (in CSV format) to train the perceptron while C<$save_nerve_to_file> is
390             the filename that will be generate each time the perceptron finishes the training process. This data file is the data of the C
391             object and it is used in the C method.
392              
393             C<$expected_output_header> is the header name of the columns in the csv file with the actual category or the exepcted values. This is used to determine to tune the nerve up or down. This value should only be 0 or 1 for the sake of simplicity.
394              
395             C<$display_stats> is B and the default is 0. It will display more output about the tuning process. It will show the followings:
396              
397             =over 4
398              
399             =item tuning status
400              
401             Indicates the nerve was tuned up, down or no tuning needed
402              
403             =item old sum
404              
405             The original sum of all C or C
406              
407             =item threshold
408              
409             The threshold of the nerve
410              
411             =item new sum
412              
413             The new sum of all C after fine-tuning the nerve
414              
415             =back
416              
417             If C<$display_stats> is specified ie. set to C<1>, then you B specify the C<$identifier>. C<$identifier> is the column / header name that is used to identify a specific row of data in C<$stimuli_train_csv>.
418              
419             =cut
420              
421             sub tame {
422 0     0 1 0 train( @_ );
423             }
424              
425             sub exercise {
426 0     0 1 0 train( @_ );
427             }
428              
429             sub train {
430 24     24 1 13680 my $self = shift;
431 24         92 my( $stimuli_train_csv, $expected_output_header, $save_nerve_to_file, $display_stats, $identifier ) = @_;
432            
433 24 100       92 $display_stats = 0 if not defined $display_stats;
434 24 50 66     142 if ( $display_stats and not defined $identifier ) {
435 0         0 croak "Please specifiy a string for \$identifier if you are trying to display stats";
436             }
437            
438             # CSV processing is all according to the documentation of Text::CSV
439 24 50   8   1027 open my $data_fh, "<:encoding(UTF-8)", $stimuli_train_csv
  8         68  
  8         17  
  8         59  
440             or die "Can't open $stimuli_train_csv: $!";
441            
442 24         80728 my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
443            
444 24         5186 my $attrib = $csv->getline($data_fh);
445 24         2281 $csv->column_names( $attrib );
446              
447             # individual row
448 24         2235 ROW: while ( my $row = $csv->getline_hr($data_fh) ) {
449             # print $row->{book_name}, " -> ";
450             # print $row->{$expected_output_header} ? "意林\n" : "魅丽优品\n";
451              
452             # calculate the output and fine tune parameters if necessary
453 120         12583 while (1) {
454 429         1439 my $output = _calculate_output( $self, $row );
455            
456             #print "Sum = ", $output, "\n";
457            
458             # $expected_output_header to be checked together over here
459             # if output >= threshold
460             # then category/result aka output is considered 1
461             # else output considered 0
462            
463             # output expected/actual tuning
464             # 0 0 -
465             # 1 0 down
466             # 0 1 up
467             # 1 1 -
468 429 100 100     973 if ( ($output >= $self->threshold) and ( $row->{$expected_output_header} eq 0 ) ) {
    100 100        
    100 66        
    50 33        
469 33         138 _tune( $self, $row, TUNE_DOWN );
470              
471 33 50       109 if ( $display_stats ) {
472 33         1816 print $row->{$identifier}, "\n";
473 33         659 print " -> TUNED DOWN";
474 33         541 print " Old sum = ", $output;
475 33         145 print " Threshold = ", $self->threshold;
476 33         151 print " New Sum = ", _calculate_output( $self, $row ), "\n";
477             }
478            
479             } elsif ( ($output < $self->threshold) and ( $row->{$expected_output_header} eq 1 ) ) {
480 276         722 _tune( $self, $row, TUNE_UP );
481            
482 276 50       836 if ( $display_stats ) {
483 276         14052 print $row->{$identifier}, "\n";
484 276         5178 print " -> TUNED UP";
485 276         4391 print " Old sum = ", $output;
486 276         1053 print " Threshold = ", $self->threshold;
487 276         1139 print " New Sum = ", _calculate_output( $self, $row ), "\n";
488             }
489              
490             } elsif ( ($output < $self->threshold) and ( $row->{$expected_output_header} eq 0 ) ) {
491            
492 48 100       149 if ( $display_stats ) {
493 42         2373 print $row->{$identifier}, "\n";
494 42         854 print " -> NO TUNING NEEDED";
495 42         195 print " Sum = ", _calculate_output( $self, $row );
496 42         233 print " Threshold = ", $self->threshold, "\n";
497             }
498            
499 48         596 next ROW;
500            
501             } elsif ( ($output >= $self->threshold) and ( $row->{$expected_output_header} eq 1 ) ) {
502            
503 72 100       164 if ( $display_stats ) {
504 63         3494 print $row->{$identifier}, "\n";
505 63         1306 print " -> NO TUNING NEEDED";
506 63         242 print " Sum = ", _calculate_output( $self, $row );
507 63         350 print " Threshold = ", $self->threshold, "\n";
508             }
509            
510 72         955 next ROW;
511             } #else { print "Something's not right\n'" }
512             }
513             }
514              
515 24         2972 close $data_fh;
516            
517 24         124 save_perceptron( $self, $save_nerve_to_file ); # this doesn't return anything
518            
519             }
520              
521             =head2 &_calculate_output( $self, \%stimuli_hash )
522              
523             Calculates and returns the C for each individual row of data. Actually, it justs add up all the existing weight since the C is always 1 for now :)
524              
525             C<%stimuli_hash> is the actual data to be used for training. It might contain useless columns.
526              
527             This will get all the avaible dendrites using the C method and then use all the keys ie. headers to access the corresponding values.
528              
529             This subroutine should be called in the procedural way for now.
530              
531             =cut
532              
533             sub _calculate_output {
534 933     933   1569 my $self = shift;
535 933         1344 my $stimuli_hash_ref = shift;
536            
537 933         2108 my %dendrites = $self->get_attributes;
538 933         2793 my $sum; # this is the output
539            
540 933         4441 for ( keys %dendrites ) {
541             # if input is 1 for a dendrite, then calculate it
542 33588 100       57691 if ( $stimuli_hash_ref->{ $_ } ) {
543             # $sum += $dendrites{ $_ } * 1; # no need, if 1 then it is always the value itself
544             # this is very efficient, nice :)
545 12402         19103 $sum += $dendrites{ $_ };
546             }
547             }
548            
549 933         21387 $sum;
550             }
551              
552             =head2 &_tune( $self, \%stimuli_hash, $tune_up_or_down )
553              
554             Fine tunes the nerve. This will directly alter the attributes values in C<$self> according to the attributes / dendrites specified in C.
555              
556             The C<%stimuli_hash> here is the same as the one in the C<_calculate_output> method.
557              
558             C<%stimuli_hash> will be used to determine which dendrite in C<$self> needs to be fine-tuned. As long as the value of any key in C<%stimuli_hash> returns true (1) then that dendrite in C<$self> will be tuned.
559              
560             Tuning up or down depends on C<$tune_up_or_down> specifed by the C method. The following constants can be used for C<$tune_up_or_down>:
561              
562             =over 4
563              
564             =item TUNE_UP
565              
566             Value is C<1>
567              
568             =item TUNE_DOWN
569              
570             Value is C<0>
571              
572             =back
573              
574             This subroutine should be called in the procedural way for now.
575              
576             =cut
577              
578             sub _tune {
579 309     309   443 my $self = shift;
580 309         557 my ( $stimuli_hash_ref, $tuning_status ) = @_;
581              
582 309         576 my %dendrites = $self->get_attributes;
583              
584 309         1826 for ( keys %dendrites ) {
585 11124 100       20492 if ( $tuning_status == TUNE_DOWN ) {
    50          
586            
587 1188 100       2097 if ( $stimuli_hash_ref->{ $_ } ) { # must check this one, it must be 1 before we can alter the actual dendrite size in the nerve :)
588 480         795 $self->{ attributes_hash_ref }{ $_ } -= $self->learning_rate;
589             }
590             #print $_, ": ", $self->{ attributes_hash_ref }{ $_ }, "\n";
591            
592             } elsif ( $tuning_status == TUNE_UP ) {
593            
594 9936 100       17286 if ( $stimuli_hash_ref->{ $_ } ) {
595 3717         6054 $self->{ attributes_hash_ref }{ $_ } += $self->learning_rate;
596             }
597             #print $_, ": ", $self->{ attributes_hash_ref }{ $_ }, "\n";
598            
599             }
600             }
601              
602             }
603              
604             =head1 VALIDATION RELATED METHODS
605              
606             All the validation methods here have the same parameters as the actual C method and they all do the same stuff. They are also used in the same way.
607              
608             =head2 take_mock_exam (...)
609              
610             =head2 take_lab_test (...)
611              
612             =head2 validate ( \%options )
613              
614             This method validates the perceptron against another set of data after it has undergone the training process.
615              
616             This method calculates the output of each row of data and write the result into the predicted column. The data begin written into the new file or the original file will maintain it's sequence.
617              
618             Please take note that this method will load all the data of the validation stimuli, so please split your stimuli into multiple files if possible and call this method a few more times.
619              
620             For C<%options>, the followings are needed unless mentioned:
621              
622             =over 4
623              
624             =item stimuli_validate => $csv_file
625              
626             This is the CSV file containing the validation data, make sure that it contains a column with the predicted values as it is needed in the next key mentioned: C
627              
628             =item predicted_column_index => $column_number
629              
630             This is the index of the column that contains the predicted output values. C<$index> starts from C<0>.
631              
632             This column will be filled with binary numbers and the full new data will be saved to the file specified in the C key.
633              
634             =item results_write_to => $new_csv_file
635              
636             Optional.
637              
638             The default behaviour will write the predicted output back into C ie the original data. The sequence of the data will be maintained.
639              
640             =back
641              
642             I<*This method will call C<_real_validate_or_test> to do the actual work.>
643              
644             =cut
645              
646             sub take_mock_exam {
647 2     2 1 3955 my ( $self, $data_hash_ref ) = @_;
648 2         6 $self->_real_validate_or_test( $data_hash_ref );
649             }
650              
651             sub take_lab_test {
652 2     2 1 4346 my ( $self, $data_hash_ref ) = @_;
653 2         10 $self->_real_validate_or_test( $data_hash_ref );
654             }
655              
656             sub validate {
657 2     2 1 4608 my ( $self, $data_hash_ref ) = @_;
658 2         13 $self->_real_validate_or_test( $data_hash_ref );
659             }
660              
661             =head1 TESTING RELATED SUBROUTINES/METHODS
662              
663             All the testing methods here have the same parameters as the actual C method and they all do the same stuff. They are also used in the same way.
664              
665             =head2 take_real_exam (...)
666              
667             =head2 work_in_real_world (...)
668              
669             =head2 test ( \%options )
670              
671             This method is used to put the trained nerve to the test. You can think of it as deploying the nerve for the actual work or maybe putting the nerve into an empty brain and see how
672             well the brain survives :)
673              
674             This method works and behaves the same way as the C method. See C for the details.
675              
676             I<*This method will call &_real_validate_or_test to do the actual work.>
677              
678             =cut
679              
680             # redirect to _real_validate_or_test
681             sub take_real_exam {
682 2     2 1 3239 my ( $self, $data_hash_ref ) = @_;
683 2         8 $self->_real_validate_or_test( $data_hash_ref );
684             }
685              
686             sub work_in_real_world {
687 2     2 1 3987 my ( $self, $data_hash_ref ) = @_;
688 2         11 $self->_real_validate_or_test( $data_hash_ref );
689             }
690              
691             sub test {
692 2     2 1 3310 my ( $self, $data_hash_ref ) = @_;
693 2         7 $self->_real_validate_or_test( $data_hash_ref );
694             }
695              
696             =head2 _real_validate_or_test ( $data_hash_ref )
697              
698             This is where the actual validation or testing takes place.
699              
700             C<$data_hash_ref> is the list of parameters passed into the C or C methods.
701              
702             This is a B, so use the OO way. This is one of the exceptions to the rules where private subroutines are treated as methods :)
703              
704             =cut
705              
706             sub _real_validate_or_test {
707              
708 12     12   28 my $self = shift; my $data_hash_ref = shift;
  12         22  
709            
710             #####
711 12         77 my @missing_keys;
712 12         66 for ( qw( stimuli_validate predicted_column_index ) ) {
713 24 50       87 push @missing_keys, $_ unless exists $data_hash_ref->{ $_ };
714             }
715            
716 12 50       36 croak "Missing keys: @missing_keys" if @missing_keys;
717             #####
718            
719 12         35 my $stimuli_validate = $data_hash_ref->{ stimuli_validate };
720 12         23 my $predicted_index = $data_hash_ref->{ predicted_column_index };
721            
722             # actual processing starts here
723             my $output_file = defined $data_hash_ref->{ results_write_to }
724             ? $data_hash_ref->{ results_write_to }
725 12 100       45 : $stimuli_validate;
726            
727             # open for writing results
728 12         57 my $aoa = csv (in => $stimuli_validate, encoding => ":encoding(utf-8)");
729            
730 12         51930 my $attrib_array_ref = shift @$aoa; # 'remove' the header, it's annoying :)
731              
732 12         55 $aoa = _fill_predicted_values( $self, $stimuli_validate, $predicted_index, $aoa );
733              
734             # put back the array of headers before saving file
735 12         48 unshift @$aoa, $attrib_array_ref;
736              
737 12         615 print "Saving data to $output_file\n";
738 12         99 csv( in => $aoa, out => $output_file, encoding => ":encoding(utf-8)" );
739 12         10738 print "Done saving!\n";
740              
741             }
742              
743             =head2 &_fill_predicted_values ( $self, $stimuli_validate, $predicted_index, $aoa )
744              
745             This is where the filling in of the predicted values takes place. Take note that the parameters naming are the same as the ones used in the C and C method.
746              
747             This subroutine should be called in the procedural way.
748              
749             =cut
750              
751             sub _fill_predicted_values {
752 12     12   41 my ( $self, $stimuli_validate, $predicted_index, $aoa ) = @_;
753              
754             # CSV processing is all according to the documentation of Text::CSV
755 12 50       474 open my $data_fh, "<:encoding(UTF-8)", $stimuli_validate
756             or die "Can't open $stimuli_validate: $!";
757            
758 12         1418 my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
759            
760 12         2111 my $attrib = $csv->getline($data_fh);
761            
762 12         1084 $csv->column_names( $attrib );
763              
764             # individual row
765 12         1141 my $row = 0;
766 12         67 while ( my $data = $csv->getline_hr($data_fh) ) {
767            
768 90 100       7981 if ( _calculate_output( $self, $data ) >= $self->threshold ) {
769             # write 1 into aoa
770 36         75 $aoa->[ $row ][ $predicted_index ] = 1;
771             } else {
772             #write 0 into aoa
773 54         111 $aoa->[ $row ][ $predicted_index ] = 0;
774             }
775            
776 90         562 $row++;
777             }
778            
779 12         1286 close $data_fh;
780            
781 12         204 $aoa;
782             }
783              
784             =head1 RESULTS RELATED SUBROUTINES/METHODS
785              
786             This part is related to generating the confusion matrix.
787              
788             =head2 get_exam_results ( ... )
789              
790             The parameters and usage are the same as C. See the next method.
791              
792             =head2 get_confusion_matrix ( \%options )
793              
794             Returns the confusion matrix in the form of a hash. The hash will contain these keys: C, C, C, C, C, C. More stats like C, C and C can be obtain by setting the optional C key to C<1>.
795              
796             If you are trying to manipulate the confusion matrix hash or something, take note that all the stats are in percentage (%) in decimal (if any) except the total entries.
797              
798             For C<%options>, the followings are needed unless mentioned:
799              
800             =over 4
801              
802             =item full_data_file => $filled_test_file
803              
804             This is the CSV file filled with the predicted values.
805              
806             Make sure that you don't do anything to the actual and predicted output in this file after testing the nerve. These two columns must contain binary values only!
807              
808             =item actual_output_header => $actual_column_name
809              
810             =item predicted_output_header => $predicted_column_name
811              
812             The binary values are treated as follows:
813              
814             =over 4
815              
816             =item C<0> is negative
817              
818             =item C<1> is positive
819              
820             =back
821              
822             =item more_stats => 1
823              
824             Optional.
825              
826             Setting it to C<1> will process more stats that are usually not so important eg. C, C and C
827              
828             =back
829              
830             =cut
831              
832             sub get_exam_results {
833              
834 2     2 1 18 my ( $self, $info ) = @_;
835            
836 2         11 $self->get_confusion_matrix( $info );
837             }
838              
839             sub get_confusion_matrix {
840              
841 6     6 1 3489 my ( $self, $info ) = @_;
842              
843 6         28 my %c_matrix = _collect_stats( $info ); # processes total_entries, accuracy, sensitivity etc
844            
845 4         52 %c_matrix;
846             }
847              
848              
849             =head2 &_collect_stats ( \%options )
850              
851             Generates a hash of confusion matrix based on C<%options> given in the C method.
852              
853             =cut
854              
855             sub _collect_stats {
856 6     6   15 my $info = shift;
857 6         20 my $file = $info->{ full_data_file };
858 6         18 my $actual_header = $info->{ actual_output_header };
859 6         15 my $predicted_header = $info->{ predicted_output_header };
860 6 100       26 my $more_stats = defined ( $info->{ more_stats } ) ? 1 : 0;
861            
862 6         43 my %c_matrix = (
863             true_positive => 0, true_negative => 0, false_positive => 0, false_negative => 0,
864             accuracy => 0, sensitivity => 0
865             );
866            
867             # CSV processing is all according to the documentation of Text::CSV
868 6 50       507 open my $data_fh, "<:encoding(UTF-8)", $file
869             or die "Can't open $file: $!";
870            
871 6         28302 my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
872            
873 6         1720 my $attrib = $csv->getline($data_fh); # get the row of headers, can't specify any column
874             # shouldn't be a problem, since we're reading line by line :)
875              
876 6         728 $csv->column_names( $attrib );
877              
878             # individual row
879 6         596 while ( my $row = $csv->getline_hr($data_fh) ) {
880            
881             # don't pack this part into another subroutine, number of rows can be very big
882 44 100 100     4063 if ( $row->{ $actual_header } == 1 and $row->{ $predicted_header } == 1 ) {
    100 100        
    100 66        
    100 66        
883              
884             # true positive
885 10         67 $c_matrix{ true_positive }++;
886            
887             } elsif ( $row->{ $actual_header } == 0 and $row->{ $predicted_header } == 0 ) {
888            
889             # true negative
890 16         113 $c_matrix{ true_negative }++;
891            
892             } elsif ( $row->{ $actual_header } == 1 and $row->{ $predicted_header } == 0 ) {
893            
894             # false negative
895 12         90 $c_matrix{ false_negative }++;
896            
897             } elsif ( $row->{ $actual_header } == 0 and $row->{ $predicted_header } == 1 ) {
898            
899             # false positive
900 4         33 $c_matrix{ false_positive }++;
901            
902             } else {
903            
904 2         478 croak "Something's wrong!\n".
905             "Make sure that the actual and predicted values in your file are binary ie 0 or 1" ;
906            
907             }
908             }
909            
910 4         482 close $data_fh;
911              
912 4         31 _calculate_total_entries( \%c_matrix );
913              
914 4         21 _calculate_sensitivity( \%c_matrix );
915            
916 4         18 _calculate_accuracy( \%c_matrix );
917            
918 4 100       17 if ( $more_stats == 1 ) {
919 2         14 _calculate_precision( \%c_matrix );
920            
921 2         10 _calculate_specificity( \%c_matrix );
922            
923 2         9 _calculate_f1_score( \%c_matrix );
924            
925             # unimplemented, some more left
926 2         9 _calculate_negative_predicted_value( \%c_matrix ); #
927 2         10 _calculate_false_negative_rate( \%c_matrix ); #
928 2         9 _calculate_false_positive_rate( \%c_matrix ); #
929 2         9 _calculate_false_discovery_rate( \%c_matrix ); #
930 2         9 _calculate_false_omission_rate( \%c_matrix ); #
931 2         8 _calculate_balanced_accuracy( \%c_matrix ); #
932             }
933            
934 4         107 %c_matrix;
935             }
936              
937             =head2 &_calculate_total_entries ( $c_matrix_ref )
938              
939             Calculates and adds the data for the C key in the confusion matrix hash.
940              
941             =cut
942              
943             sub _calculate_total_entries {
944              
945 6     6   20 my $c_matrix = shift;
946 6         22 my $total = $c_matrix->{ true_negative } + $c_matrix->{ false_positive };
947 6         18 $total += $c_matrix->{ false_negative } + $c_matrix->{ true_positive };
948              
949 6         29 $c_matrix->{ total_entries } = $total;
950              
951             }
952              
953             =head2 &_calculate_accuracy ( $c_matrix_ref )
954              
955             Calculates and adds the data for the C key in the confusion matrix hash.
956              
957             =cut
958              
959             sub _calculate_accuracy {
960              
961 6     6   15 my $c_matrix = shift;
962            
963 6         17 my $numerator = $c_matrix->{ true_positive } + $c_matrix->{ true_negative };
964 6         19 my $denominator = $numerator + $c_matrix->{ false_positive } + $c_matrix->{ false_negative };
965            
966 6         32 $c_matrix->{ accuracy } = $numerator / $denominator * 100;
967            
968             # no need to return anything, we're using ref
969             }
970              
971             =head2 &_calculate_sensitivity ( $c_matrix_ref )
972              
973             Calculates and adds the data for the C key in the confusion matrix hash.
974              
975             =cut
976              
977             sub _calculate_sensitivity {
978 6     6   16 my $c_matrix = shift;
979            
980 6         16 my $numerator = $c_matrix->{ true_positive };
981 6         15 my $denominator = $numerator + $c_matrix->{ false_negative };
982            
983 6         37 $c_matrix->{ sensitivity } = $numerator / $denominator * 100;
984              
985             # no need to return anything, we're using ref
986             }
987              
988             =head2 &_calculate_precision ( $c_matrix_ref )
989              
990             Calculates and adds the data for the C key in the confusion matrix hash.
991              
992             =cut
993              
994             sub _calculate_precision {
995 2     2   5 my $c_matrix = shift;
996            
997 2         18 my $numerator = $c_matrix->{ true_positive };
998 2         6 my $denominator = $numerator + $c_matrix->{ false_positive };
999            
1000 2         12 $c_matrix->{ precision } = $numerator / $denominator * 100;
1001             }
1002              
1003             =head2 &_calculate_specificity ( $c_matrix_ref )
1004              
1005             Calculates and adds the data for the C key in the confusion matrix hash.
1006              
1007             =cut
1008              
1009             sub _calculate_specificity {
1010 2     2   5 my $c_matrix = shift;
1011            
1012 2         4 my $numerator = $c_matrix->{ true_negative };
1013 2         6 my $denominator = $numerator + $c_matrix->{ false_positive };
1014            
1015 2         7 $c_matrix->{ specificity } = $numerator / $denominator * 100;
1016             }
1017              
1018             =head2 &_calculate_f1_score ( $c_matrix_ref )
1019              
1020             Calculates and adds the data for the C key in the confusion matrix hash.
1021              
1022             =cut
1023              
1024             sub _calculate_f1_score {
1025 2     2   4 my $c_matrix = shift;
1026            
1027 2         6 my $numerator = 2 * $c_matrix->{ true_positive };
1028 2         5 my $denominator = $numerator + $c_matrix->{ false_positive } + $c_matrix->{ false_negative };
1029            
1030 2         7 $c_matrix->{ F1_Score } = $numerator / $denominator * 100;
1031             }
1032              
1033             =head2 &_calculate_negative_predicted_value( $c_matrix_ref )
1034              
1035             Calculates and adds the data for the C key in the confusion matrix hash.
1036              
1037             =cut
1038              
1039             sub _calculate_negative_predicted_value {
1040 2     2   4 my $c_matrix = shift;
1041            
1042 2         5 my $numerator = $c_matrix->{ true_negative };
1043 2         7 my $denominator = $numerator + $c_matrix->{ false_negative };
1044            
1045 2         7 $c_matrix->{ negative_predicted_value } = $numerator / $denominator * 100;
1046             }
1047              
1048             =head2 &_calculate_false_negative_rate( $c_matrix_ref )
1049              
1050             Calculates and adds the data for the C key in the confusion matrix hash.
1051              
1052             =cut
1053              
1054             sub _calculate_false_negative_rate {
1055 2     2   3 my $c_matrix = shift;
1056            
1057 2         5 my $numerator = $c_matrix->{ false_negative };
1058 2         6 my $denominator = $numerator + $c_matrix->{ true_positive };
1059            
1060 2         8 $c_matrix->{ false_negative_rate } = $numerator / $denominator * 100;
1061             }
1062              
1063             =head2 &_calculate_false_positive_rate( $c_matrix_ref )
1064              
1065             Calculates and adds the data for the C key in the confusion matrix hash.
1066              
1067             =cut
1068              
1069             sub _calculate_false_positive_rate {
1070 2     2   4 my $c_matrix = shift;
1071            
1072 2         6 my $numerator = $c_matrix->{ false_positive };
1073 2         6 my $denominator = $numerator + $c_matrix->{ true_negative };
1074            
1075 2         7 $c_matrix->{ false_positive_rate } = $numerator / $denominator * 100;
1076             }
1077              
1078             =head2 &_calculate_false_discovery_rate( $c_matrix_ref )
1079              
1080             Calculates and adds the data for the C key in the confusion matrix hash.
1081              
1082             =cut
1083              
1084             sub _calculate_false_discovery_rate {
1085 2     2   5 my $c_matrix = shift;
1086            
1087 2         6 my $numerator = $c_matrix->{ false_positive };
1088 2         4 my $denominator = $numerator + $c_matrix->{ true_positive };
1089            
1090 2         6 $c_matrix->{ false_discovery_rate } = $numerator / $denominator * 100;
1091             }
1092              
1093             =head2 &_calculate_false_omission_rate( $c_matrix_ref )
1094              
1095             Calculates and adds the data for the C key in the confusion matrix hash.
1096              
1097             =cut
1098              
1099             sub _calculate_false_omission_rate {
1100 2     2   5 my $c_matrix = shift;
1101            
1102 2         8 my $numerator = $c_matrix->{ false_negative };
1103 2         5 my $denominator = $numerator + $c_matrix->{ true_negative };
1104            
1105 2         8 $c_matrix->{ false_omission_rate } = $numerator / $denominator * 100;
1106             }
1107              
1108             =head2 &_calculate_balanced_accuracy( $c_matrix_ref )
1109              
1110             Calculates and adds the data for the C key in the confusion matrix hash.
1111              
1112             =cut
1113              
1114             sub _calculate_balanced_accuracy {
1115 2     2   5 my $c_matrix = shift;
1116            
1117 2         6 my $numerator = $c_matrix->{ sensitivity } + $c_matrix->{ specificity };
1118 2         5 my $denominator = 2;
1119            
1120 2         9 $c_matrix->{ balanced_accuracy } = $numerator / $denominator; # numerator already in %
1121             }
1122              
1123             =head2 display_exam_results ( ... )
1124              
1125             The parameters are the same as C. See the next method.
1126              
1127             =head2 display_confusion_matrix ( \%confusion_matrix, \%labels )
1128              
1129             Display the confusion matrix. If C<%confusion_matrix> has C elements, it will display them if they exists. The default elements ie C and C must be present, while the rest can be absent.
1130              
1131             C<%confusion_matrix> is the same confusion matrix returned by the C method.
1132              
1133             For C<%labels>, since C<0>'s and C<1>'s won't make much sense as the output in most cases, therefore, the following keys must be specified:
1134              
1135             =over 4
1136              
1137             =item zero_as => $category_zero_name
1138              
1139             =item one_as => $category_one_name
1140              
1141             =back
1142              
1143             Please take note that non-ascii characters ie. non-English alphabets B cause the output to go off :)
1144              
1145             For the C<%labels>, there is no need to enter "actual X", "predicted X" etc. It will be prefixed with C for actual and C for the predicted values by default.
1146              
1147             =cut
1148              
1149             sub display_exam_results {
1150              
1151 59     59 1 152573 my ( $self, $c_matrix, $labels ) = @_;
1152            
1153 59         253 $self->display_confusion_matrix( $c_matrix, $labels );
1154             }
1155              
1156             sub display_confusion_matrix {
1157 62     62 1 3949 my ( $self, $c_matrix, $labels ) = @_;
1158            
1159             #####
1160 62         125 my @missing_keys;
1161 62         189 for ( qw( zero_as one_as ) ) {
1162 124 100       418 push @missing_keys, $_ unless exists $labels->{ $_ };
1163             }
1164            
1165 62 100       888 croak "Missing keys: @missing_keys" if @missing_keys;
1166             #####
1167            
1168 56         171 _print_extended_matrix ( _build_matrix( $c_matrix, $labels ) );
1169              
1170             }
1171              
1172             =head2 &_build_matrix ( $c_matrix, $labels )
1173              
1174             Builds the matrix using C module.
1175              
1176             C<$c_matrix> and C<$labels> are the same as the ones passed to C and C<>display_confusion_matrix.
1177              
1178             Returns a list C<( $matrix, $c_matrix )> which can directly be passed to C<_print_extended_matrix>.
1179              
1180             =cut
1181              
1182             sub _build_matrix {
1183              
1184 56     56   161 my ( $c_matrix, $labels ) = @_;
1185              
1186 56         235 my $predicted_columns = [ "P: ".$labels->{ zero_as }, "P: ".$labels->{ one_as }, "Sum" ];
1187 56         226 my $actual_rows = [ "A: ".$labels->{ zero_as }, "A: ".$labels->{ one_as }, "Sum"];
1188            
1189             # row sum
1190 56         160 my $actual_0_sum = $c_matrix->{ true_negative } + $c_matrix->{ false_positive };
1191 56         157 my $actual_1_sum = $c_matrix->{ false_negative } + $c_matrix->{ true_positive };
1192             # column sum
1193 56         137 my $predicted_0_sum = $c_matrix->{ true_negative } + $c_matrix->{ false_negative };
1194 56         131 my $predicted_1_sum = $c_matrix->{ false_positive } + $c_matrix->{ true_positive };
1195            
1196             my $data = [
1197             [ $c_matrix->{ true_negative }, $c_matrix->{ false_positive }, $actual_0_sum ],
1198             [ $c_matrix->{ false_negative }, $c_matrix->{ true_positive }, $actual_1_sum ],
1199 56         268 [ $predicted_0_sum, $predicted_1_sum, $c_matrix->{ total_entries } ],
1200             ];
1201 56         606 my $matrix = Text::Matrix->new(
1202             rows => $actual_rows,
1203             columns => $predicted_columns,
1204             data => $data,
1205             );
1206            
1207 56         5111 $matrix, $c_matrix;
1208             }
1209              
1210             =head2 &_print_extended_matrix ( $matrix, $c_matrix )
1211              
1212             Extends and outputs the matrix on the screen.
1213              
1214             C<$matrix> and C<$c_matrix> are the same as returned by C<&_build_matrix>.
1215              
1216             =cut
1217              
1218             sub _print_extended_matrix {
1219              
1220 56     56   150 my ( $matrix, $c_matrix ) = @_;
1221            
1222 56         2694 print "~~" x24, "\n";
1223 56         800 print "CONFUSION MATRIX (A:actual P:predicted)\n";
1224 56         755 print "~~" x24, "\n";
1225              
1226 56         356 print $matrix->matrix();
1227              
1228 56         21448 print "~~" x24, "\n";
1229 56         927 print "Total of ", $c_matrix->{ total_entries } , " entries\n";
1230 56         1262 print " Accuracy: $c_matrix->{ accuracy } %\n";
1231 56         938 print " Sensitivity: $c_matrix->{ sensitivity } %\n";
1232             # more stats
1233 56 100       875 print " Precision: $c_matrix->{ precision } %\n" if exists $c_matrix->{ precision };
1234 56 100       758 print " Specificity: $c_matrix->{ specificity } %\n" if exists $c_matrix->{ specificity };
1235 56 100       872 print " F1 Score: $c_matrix->{ F1_Score } %\n" if exists $c_matrix->{ F1_Score };
1236 56 100       897 print " Negative Predicted Value: $c_matrix->{ negative_predicted_value } %\n" if exists $c_matrix->{ negative_predicted_value };
1237 56 100       791 print " False Negative Rate: $c_matrix->{ false_negative_rate } %\n" if exists $c_matrix->{ false_negative_rate };
1238 56 100       774 print " False Positive Rate: $c_matrix->{ false_positive_rate } %\n" if exists $c_matrix->{ false_positive_rate };
1239 56 100       766 print " False Discovery Rate: $c_matrix->{ false_discovery_rate } %\n" if exists $c_matrix->{ false_discovery_rate };
1240 56 100       726 print " False Omission Rate: $c_matrix->{ false_omission_rate } %\n" if exists $c_matrix->{ false_omission_rate };
1241 56 100       706 print " Balanced Accuracy: $c_matrix->{ balanced_accuracy } %\n" if exists $c_matrix->{ balanced_accuracy };
1242 56         1393 print "~~" x24, "\n";
1243             }
1244              
1245             =head1 NERVE DATA RELATED SUBROUTINES
1246              
1247             This part is about saving the data of the nerve. These subroutines can be imported using the C<:local_data> tag.
1248              
1249             B. No checking is done currently.
1250              
1251             See C and C sections for more details on the subroutines in this section.
1252              
1253             =head2 preserve ( ... )
1254              
1255             The parameters and usage are the same as C. See the next subroutine.
1256              
1257             =head2 save_perceptron ( $nerve_file )
1258              
1259             Saves the C object into a C file. There shouldn't be a need to call this method manually since after every training
1260             process this will be called automatically.
1261              
1262             =cut
1263              
1264             sub preserve {
1265 1     1 1 904 save_perceptron( @_ );
1266             }
1267              
1268 0         0 sub save_perceptron {
1269 28     28 1 6056 my $self = shift;
1270 28         56 my $nerve_file = shift;
1271 17     17   253 use Storable;
  17         60  
  17         1418  
1272 28         137 store $self, $nerve_file;
1273 17     17   127 no Storable;
  17         41  
  17         1721  
1274             }
1275              
1276             =head2 revive (...)
1277              
1278             The parameters and usage are the same as C. See the next subroutine.
1279              
1280             =head2 load_perceptron ( $nerve_file_to_load )
1281              
1282             Loads the data and turns it into a C object as the return value.
1283              
1284             =cut
1285              
1286             sub revive {
1287 2     2 1 1138 load_perceptron( @_ );
1288             }
1289              
1290             sub load_perceptron {
1291 13     13 1 5125 my $nerve_file_to_load = shift;
1292 17     17   129 use Storable;
  17         47  
  17         1225  
1293 13         74 my $loaded_nerve = retrieve( $nerve_file_to_load );
1294 17     17   132 no Storable;
  17         33  
  17         1582  
1295            
1296 13         1839 $loaded_nerve;
1297             }
1298              
1299             =head1 NERVE PORTABILITY RELATED SUBROUTINES
1300              
1301             These subroutines can be imported using the C<:portable_data> tag.
1302              
1303             The file type currently supported is YAML. Please be careful with the data as you won't want the nerve data accidentally modified.
1304              
1305             =head2 preserve_as_yaml ( ... )
1306              
1307             The parameters and usage are the same as C. See the next subroutine.
1308              
1309             =head2 save_perceptron_yaml ( $nerve_file )
1310              
1311             Saves the C object into a C file.
1312              
1313             =cut
1314              
1315             sub preserve_as_yaml {
1316 1     1 1 4 save_perceptron_yaml( @_ );
1317             }
1318              
1319 0         0 sub save_perceptron_yaml {
1320 2     2 1 4436 my $self = shift;
1321 2         5 my $nerve_file = shift;
1322 17     17   10208 use YAML;
  17         143876  
  17         1200  
1323 2         8 YAML::DumpFile( $nerve_file, $self );
1324 17     17   165 no YAML;
  17         43  
  17         1565  
1325             }
1326              
1327             =head2 revive_from_yaml (...)
1328              
1329             The parameters and usage are the same as C. See the next subroutine.
1330              
1331             =head2 load_perceptron_yaml ( $yaml_nerve_file )
1332              
1333             Loads the YAML data and turns it into a C object as the return value.
1334              
1335             =cut
1336              
1337             sub revive_from_yaml {
1338 1     1 1 5 load_perceptron_yaml( @_ );
1339             }
1340              
1341             sub load_perceptron_yaml {
1342 2     2 1 16660 my $nerve_file_to_load = shift;
1343 17     17   164 use YAML;
  17         94  
  17         1097  
1344 2         10 my $loaded_nerve = YAML::LoadFile( $nerve_file_to_load );
1345 17     17   178 no YAML;
  17         59  
  17         2047  
1346            
1347 2         18678 $loaded_nerve;
1348             }
1349              
1350             =head1 TO DO
1351              
1352             These are the to-do's that B be done in the future. Don't put too much hope in them please :)
1353              
1354             =over 4
1355              
1356             =item * Clean up and refactor source codes
1357              
1358             =item * Add more useful data for confusion matrix
1359              
1360             =item * Implement shuffling data feature
1361              
1362             =item * Implement fast/smart training feature
1363              
1364             =item * Write a tutorial or something for this module
1365              
1366             =item * and something yet to be known...
1367              
1368             =back
1369              
1370             =head1 KNOWN ISSUES
1371              
1372             =head2 Portability of Nerve Data
1373              
1374             Take note that the C nerve data is not compatible across different versions.
1375              
1376             If you really need to send the nerve data to different computers with different versions of C module, see the docs of the following subroutines:
1377              
1378             =over 4
1379              
1380             =item * C<&preserve_as_yaml> or C<&save_perceptron_yaml> for storing data.
1381              
1382             =item * C<&revive_from_yaml> or C<&load_perceptron_yaml> for retrieving the data.
1383              
1384             =back
1385              
1386             =head1 AUTHOR
1387              
1388             Raphael Jong Jun Jie, C<< >>
1389              
1390             =head1 BUGS
1391              
1392             Please report any bugs or feature requests to C, or through
1393             the web interface at L. I will be notified, and then you'll
1394             automatically be notified of progress on your bug as I make changes.
1395              
1396             =head1 SUPPORT
1397              
1398             You can find documentation for this module with the perldoc command.
1399              
1400             perldoc AI::Perceptron::Simple
1401              
1402              
1403             You can also look for information at:
1404              
1405             =over 4
1406              
1407             =item * RT: CPAN's request tracker (report bugs here)
1408              
1409             L
1410              
1411             =item * CPAN Ratings
1412              
1413             L
1414              
1415             =item * Search CPAN
1416              
1417             L
1418              
1419             =back
1420              
1421              
1422             =head1 ACKNOWLEDGEMENTS
1423              
1424             Besiyata d'shmaya, Wikipedia
1425              
1426             =head1 SEE ALSO
1427              
1428             AI::Perceptron, Text::Matrix, YAML
1429              
1430             =head1 LICENSE AND COPYRIGHT
1431              
1432             This software is Copyright (c) 2021 by Raphael Jong Jun Jie.
1433              
1434             This is free software, licensed under:
1435              
1436             The Artistic License 2.0 (GPL Compatible)
1437              
1438              
1439             =cut
1440              
1441             1; # End of AI::Perceptron::Simple