line
stmt
bran
cond
sub
pod
time
code
1
package AI::Perceptron::Simple;
2
3
17
17
1477807
use 5.008001;
17
246
4
17
17
102
use strict;
17
35
17
386
5
17
17
81
use warnings;
17
33
17
583
6
17
17
93
use Carp "croak";
17
33
17
948
7
8
17
17
9707
use utf8;
17
252
17
89
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
12696
use Text::CSV qw( csv );
17
301836
17
1024
13
17
17
8192
use Text::Matrix;
17
336842
17
690
14
17
17
156
use File::Basename qw( basename );
17
40
17
1852
15
17
17
122
use List::Util qw( shuffle );
17
46
17
2063
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.04
26
27
=cut
28
29
our $VERSION = '1.04';
30
31
# default values
32
17
17
137
use constant LEARNING_RATE => 0.05;
17
33
17
1562
33
17
17
104
use constant THRESHOLD => 0.5;
17
39
17
798
34
17
17
103
use constant TUNE_UP => 1;
17
39
17
844
35
17
17
104
use constant TUNE_DOWN => 0;
17
36
17
1473
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
$nerve = AI::Perceptron::Simple->new( {
45
initial_value => $size_of_each_dendrite,
46
learning_rate => 0.3, # optional
47
threshold => 0.85, # optional
48
attribs => \@dendrites,
49
} );
50
51
# train
52
$nerve->tame( ... );
53
$nerve->exercise( ... );
54
$nerve->train( $training_data_csv, $expected_column_name, $save_nerve_to );
55
# or
56
$nerve->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
$nerve->take_lab_test( ... );
63
$nerve->take_mock_exam( ... );
64
65
# fill results to original file
66
$nerve->validate( {
67
stimuli_validate => $validation_data_csv,
68
predicted_column_index => 4,
69
} );
70
# or
71
# fill results to a new file
72
$nerve->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
$nerve->take_real_exam( ... );
81
$nerve->work_in_real_world( ... );
82
$nerve->test( ... );
83
84
85
# confusion matrix
86
my %c_matrix = $nerve->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
$nerve->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( $nerve, $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 ( $nerve, $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
122
use Exporter qw( import );
17
34
17
65247
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
3994
shuffle_data( @_ );
231
}
232
233
sub shuffle_data {
234
8
100
8
1
4076
my $stimuli = shift or croak "Please specify the original file name";
235
6
100
284
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
8622
my $aoa = csv (in => $stimuli, encoding => ":encoding(utf-8)");
243
12
39045
my $attrib_array_ref = shift @$aoa; # 'remove' the header, it's annoying :)
244
12
289
@aoa = shuffle( @$aoa ); # this can only process actual array
245
12
36
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
4753
my $class = shift;
296
297
13
31
my $data_ref = shift;
298
13
26
my %data = %{ $data_ref };
13
66
299
300
# check keys
301
13
100
66
$data{ learning_rate } = LEARNING_RATE if not exists $data{ learning_rate };
302
13
100
43
$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
40
for ( qw( initial_value attribs ) ) {
309
26
100
78
push @missing_keys, $_ unless exists $data{ $_ };
310
}
311
312
13
100
428
croak "Missing keys: @missing_keys" if @missing_keys;
313
#####
314
315
# continue to process the rest of the data
316
10
23
my %attributes;
317
10
21
for ( @{ $data{ attribs } } ) {
10
36
318
259
539
$attributes{ $_ } = $data{ initial_value };
319
}
320
321
my %processed_data = (
322
learning_rate => $data{ learning_rate },
323
threshold => $data{ threshold },
324
10
53
attributes_hash_ref => \%attributes,
325
);
326
327
10
56
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
1701
my $self = shift;
338
1246
1563
%{ $self->{attributes_hash_ref} };
1246
12917
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
6345
my $self = shift;
351
4204
100
6641
if ( @_ ) {
352
1
3
$self->{learning_rate} = shift;
353
} else {
354
$self->{learning_rate}
355
4203
8245
}
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
2297
my $self = shift;
368
1528
100
2799
if ( @_ ) {
369
1
3
$self->{ threshold } = shift;
370
} else {
371
1527
7870
$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
12703
my $self = shift;
431
24
91
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
187
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
1011
open my $data_fh, "<:encoding(UTF-8)", $stimuli_train_csv
8
64
8
16
8
51
440
or croak "Can't open $stimuli_train_csv: $!";
441
442
24
76012
my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
443
444
24
4820
my $attrib = $csv->getline($data_fh);
445
24
2201
$csv->column_names( $attrib );
446
447
# individual row
448
24
2227
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
11836
while (1) {
454
429
1221
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
863
if ( ($output >= $self->threshold) and ( $row->{$expected_output_header} eq 0 ) ) {
100
100
100
66
50
33
469
33
111
_tune( $self, $row, TUNE_DOWN );
470
471
33
50
117
if ( $display_stats ) {
472
33
1018
print $row->{$identifier}, "\n";
473
33
276
print " -> TUNED DOWN";
474
33
305
print " Old sum = ", $output;
475
33
120
print " Threshold = ", $self->threshold;
476
33
126
print " New Sum = ", _calculate_output( $self, $row ), "\n";
477
}
478
479
} elsif ( ($output < $self->threshold) and ( $row->{$expected_output_header} eq 1 ) ) {
480
276
666
_tune( $self, $row, TUNE_UP );
481
482
276
50
778
if ( $display_stats ) {
483
276
8309
print $row->{$identifier}, "\n";
484
276
2153
print " -> TUNED UP";
485
276
2409
print " Old sum = ", $output;
486
276
926
print " Threshold = ", $self->threshold;
487
276
948
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
121
if ( $display_stats ) {
493
42
1338
print $row->{$identifier}, "\n";
494
42
346
print " -> NO TUNING NEEDED";
495
42
167
print " Sum = ", _calculate_output( $self, $row );
496
42
186
print " Threshold = ", $self->threshold, "\n";
497
}
498
499
48
520
next ROW;
500
501
} elsif ( ($output >= $self->threshold) and ( $row->{$expected_output_header} eq 1 ) ) {
502
503
72
100
158
if ( $display_stats ) {
504
63
1982
print $row->{$identifier}, "\n";
505
63
554
print " -> NO TUNING NEEDED";
506
63
191
print " Sum = ", _calculate_output( $self, $row );
507
63
294
print " Threshold = ", $self->threshold, "\n";
508
}
509
510
72
736
next ROW;
511
} #else { print "Something's not right\n'" }
512
}
513
}
514
515
24
2842
close $data_fh;
516
517
24
104
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
1590
my $self = shift;
535
933
1277
my $stimuli_hash_ref = shift;
536
537
933
1946
my %dendrites = $self->get_attributes;
538
933
2656
my $sum; # this is the output
539
540
933
4363
for ( keys %dendrites ) {
541
# if input is 1 for a dendrite, then calculate it
542
33588
100
58317
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
18590
$sum += $dendrites{ $_ };
546
}
547
}
548
549
933
12067
$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
465
my $self = shift;
580
309
568
my ( $stimuli_hash_ref, $tuning_status ) = @_;
581
582
309
561
my %dendrites = $self->get_attributes;
583
584
309
1810
for ( keys %dendrites ) {
585
11124
100
21153
if ( $tuning_status == TUNE_DOWN ) {
50
586
587
1188
100
2143
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
811
$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
17593
if ( $stimuli_hash_ref->{ $_ } ) {
595
3717
6161
$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
3751
my ( $self, $data_hash_ref ) = @_;
648
2
8
$self->_real_validate_or_test( $data_hash_ref );
649
}
650
651
sub take_lab_test {
652
2
2
1
3858
my ( $self, $data_hash_ref ) = @_;
653
2
8
$self->_real_validate_or_test( $data_hash_ref );
654
}
655
656
sub validate {
657
2
2
1
3580
my ( $self, $data_hash_ref ) = @_;
658
2
9
$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
3264
my ( $self, $data_hash_ref ) = @_;
683
2
7
$self->_real_validate_or_test( $data_hash_ref );
684
}
685
686
sub work_in_real_world {
687
2
2
1
3117
my ( $self, $data_hash_ref ) = @_;
688
2
8
$self->_real_validate_or_test( $data_hash_ref );
689
}
690
691
sub test {
692
2
2
1
3559
my ( $self, $data_hash_ref ) = @_;
693
2
11
$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
27
my $self = shift; my $data_hash_ref = shift;
12
22
709
710
#####
711
12
102
my @missing_keys;
712
12
81
for ( qw( stimuli_validate predicted_column_index ) ) {
713
24
50
83
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
28
my $stimuli_validate = $data_hash_ref->{ stimuli_validate };
720
12
25
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
46
: $stimuli_validate;
726
727
# open for writing results
728
12
46
my $aoa = csv (in => $stimuli_validate, encoding => ":encoding(utf-8)");
729
730
12
47379
my $attrib_array_ref = shift @$aoa; # 'remove' the header, it's annoying :)
731
732
12
45
$aoa = _fill_predicted_values( $self, $stimuli_validate, $predicted_index, $aoa );
733
734
# put back the array of headers before saving file
735
12
41
unshift @$aoa, $attrib_array_ref;
736
737
12
494
print "Saving data to $output_file\n";
738
12
85
csv( in => $aoa, out => $output_file, encoding => ":encoding(utf-8)" );
739
12
9477
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
43
my ( $self, $stimuli_validate, $predicted_index, $aoa ) = @_;
753
754
# CSV processing is all according to the documentation of Text::CSV
755
12
50
448
open my $data_fh, "<:encoding(UTF-8)", $stimuli_validate
756
or croak "Can't open $stimuli_validate: $!";
757
758
12
1194
my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
759
760
12
1980
my $attrib = $csv->getline($data_fh);
761
762
12
1025
$csv->column_names( $attrib );
763
764
# individual row
765
12
1146
my $row = 0;
766
12
68
while ( my $data = $csv->getline_hr($data_fh) ) {
767
768
90
100
8149
if ( _calculate_output( $self, $data ) >= $self->threshold ) {
769
# write 1 into aoa
770
36
76
$aoa->[ $row ][ $predicted_index ] = 1;
771
} else {
772
#write 0 into aoa
773
54
106
$aoa->[ $row ][ $predicted_index ] = 0;
774
}
775
776
90
595
$row++;
777
}
778
779
12
1251
close $data_fh;
780
781
12
171
$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
19
my ( $self, $info ) = @_;
835
836
2
11
$self->get_confusion_matrix( $info );
837
}
838
839
sub get_confusion_matrix {
840
841
6
6
1
3039
my ( $self, $info ) = @_;
842
843
6
23
my %c_matrix = _collect_stats( $info ); # processes total_entries, accuracy, sensitivity etc
844
845
4
46
%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
13
my $info = shift;
857
6
15
my $file = $info->{ full_data_file };
858
6
14
my $actual_header = $info->{ actual_output_header };
859
6
14
my $predicted_header = $info->{ predicted_output_header };
860
6
100
26
my $more_stats = defined ( $info->{ more_stats } ) ? 1 : 0;
861
862
6
38
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
405
open my $data_fh, "<:encoding(UTF-8)", $file
869
or croak "Can't open $file: $!";
870
871
6
25198
my $csv = Text::CSV->new( {auto_diag => 1, binary => 1} );
872
873
6
1434
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
711
$csv->column_names( $attrib );
877
878
# individual row
879
6
582
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
3898
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
108
$c_matrix{ true_negative }++;
891
892
} elsif ( $row->{ $actual_header } == 1 and $row->{ $predicted_header } == 0 ) {
893
894
# false negative
895
12
88
$c_matrix{ false_negative }++;
896
897
} elsif ( $row->{ $actual_header } == 0 and $row->{ $predicted_header } == 1 ) {
898
899
# false positive
900
4
29
$c_matrix{ false_positive }++;
901
902
} else {
903
904
2
409
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
522
close $data_fh;
911
912
4
26
_calculate_total_entries( \%c_matrix );
913
914
4
18
_calculate_sensitivity( \%c_matrix );
915
916
4
15
_calculate_accuracy( \%c_matrix );
917
918
4
100
16
if ( $more_stats == 1 ) {
919
2
10
_calculate_precision( \%c_matrix );
920
921
2
9
_calculate_specificity( \%c_matrix );
922
923
2
9
_calculate_f1_score( \%c_matrix );
924
925
# unimplemented, some more left
926
2
8
_calculate_negative_predicted_value( \%c_matrix ); #
927
2
9
_calculate_false_negative_rate( \%c_matrix ); #
928
2
8
_calculate_false_positive_rate( \%c_matrix ); #
929
2
8
_calculate_false_discovery_rate( \%c_matrix ); #
930
2
8
_calculate_false_omission_rate( \%c_matrix ); #
931
2
6
_calculate_balanced_accuracy( \%c_matrix ); #
932
}
933
934
4
99
%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
17
my $c_matrix = shift;
946
6
19
my $total = $c_matrix->{ true_negative } + $c_matrix->{ false_positive };
947
6
16
$total += $c_matrix->{ false_negative } + $c_matrix->{ true_positive };
948
949
6
27
$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
16
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
27
$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
15
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
34
$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
6
my $c_matrix = shift;
996
997
2
5
my $numerator = $c_matrix->{ true_positive };
998
2
6
my $denominator = $numerator + $c_matrix->{ false_positive };
999
1000
2
11
$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
5
my $numerator = $c_matrix->{ true_negative };
1013
2
6
my $denominator = $numerator + $c_matrix->{ false_positive };
1014
1015
2
8
$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
7
my $numerator = 2 * $c_matrix->{ true_positive };
1028
2
27
my $denominator = $numerator + $c_matrix->{ false_positive } + $c_matrix->{ false_negative };
1029
1030
2
9
$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
5
my $c_matrix = shift;
1041
1042
2
5
my $numerator = $c_matrix->{ true_negative };
1043
2
6
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
4
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
5
my $c_matrix = shift;
1071
1072
2
5
my $numerator = $c_matrix->{ false_positive };
1073
2
4
my $denominator = $numerator + $c_matrix->{ true_negative };
1074
1075
2
8
$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
2
my $c_matrix = shift;
1086
1087
2
6
my $numerator = $c_matrix->{ false_positive };
1088
2
5
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
4
my $c_matrix = shift;
1101
1102
2
4
my $numerator = $c_matrix->{ false_negative };
1103
2
7
my $denominator = $numerator + $c_matrix->{ true_negative };
1104
1105
2
7
$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
4
my $c_matrix = shift;
1116
1117
2
9
my $numerator = $c_matrix->{ sensitivity } + $c_matrix->{ specificity };
1118
2
4
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 labels 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
134110
my ( $self, $c_matrix, $labels ) = @_;
1152
1153
59
217
$self->display_confusion_matrix( $c_matrix, $labels );
1154
}
1155
1156
sub display_confusion_matrix {
1157
62
62
1
4063
my ( $self, $c_matrix, $labels ) = @_;
1158
1159
#####
1160
62
112
my @missing_keys;
1161
62
179
for ( qw( zero_as one_as ) ) {
1162
124
100
403
push @missing_keys, $_ unless exists $labels->{ $_ };
1163
}
1164
1165
62
100
857
croak "Missing keys: @missing_keys" if @missing_keys;
1166
#####
1167
1168
56
184
_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
181
my ( $c_matrix, $labels ) = @_;
1185
1186
56
255
my $predicted_columns = [ "P: ".$labels->{ zero_as }, "P: ".$labels->{ one_as }, "Sum" ];
1187
56
215
my $actual_rows = [ "A: ".$labels->{ zero_as }, "A: ".$labels->{ one_as }, "Sum"];
1188
1189
# row sum
1190
56
156
my $actual_0_sum = $c_matrix->{ true_negative } + $c_matrix->{ false_positive };
1191
56
140
my $actual_1_sum = $c_matrix->{ false_negative } + $c_matrix->{ true_positive };
1192
# column sum
1193
56
128
my $predicted_0_sum = $c_matrix->{ true_negative } + $c_matrix->{ false_negative };
1194
56
133
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
240
[ $predicted_0_sum, $predicted_1_sum, $c_matrix->{ total_entries } ],
1200
];
1201
56
484
my $matrix = Text::Matrix->new(
1202
rows => $actual_rows,
1203
columns => $predicted_columns,
1204
data => $data,
1205
);
1206
1207
56
4717
$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
143
my ( $matrix, $c_matrix ) = @_;
1221
1222
56
2525
print "~~" x24, "\n";
1223
56
726
print "CONFUSION MATRIX (A:actual P:predicted)\n";
1224
56
632
print "~~" x24, "\n";
1225
1226
56
335
print $matrix->matrix();
1227
1228
56
28668
print "~~" x24, "\n";
1229
56
839
print "Total of ", $c_matrix->{ total_entries } , " entries\n";
1230
56
1078
print " Accuracy: $c_matrix->{ accuracy } %\n";
1231
56
751
print " Sensitivity: $c_matrix->{ sensitivity } %\n";
1232
# more stats
1233
56
100
764
print " Precision: $c_matrix->{ precision } %\n" if exists $c_matrix->{ precision };
1234
56
100
602
print " Specificity: $c_matrix->{ specificity } %\n" if exists $c_matrix->{ specificity };
1235
56
100
620
print " F1 Score: $c_matrix->{ F1_Score } %\n" if exists $c_matrix->{ F1_Score };
1236
56
100
654
print " Negative Predicted Value: $c_matrix->{ negative_predicted_value } %\n" if exists $c_matrix->{ negative_predicted_value };
1237
56
100
630
print " False Negative Rate: $c_matrix->{ false_negative_rate } %\n" if exists $c_matrix->{ false_negative_rate };
1238
56
100
604
print " False Positive Rate: $c_matrix->{ false_positive_rate } %\n" if exists $c_matrix->{ false_positive_rate };
1239
56
100
616
print " False Discovery Rate: $c_matrix->{ false_discovery_rate } %\n" if exists $c_matrix->{ false_discovery_rate };
1240
56
100
752
print " False Omission Rate: $c_matrix->{ false_omission_rate } %\n" if exists $c_matrix->{ false_omission_rate };
1241
56
100
605
print " Balanced Accuracy: $c_matrix->{ balanced_accuracy } %\n" if exists $c_matrix->{ balanced_accuracy };
1242
56
1144
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, $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
945
save_perceptron( @_ );
1266
}
1267
1268
0
0
sub save_perceptron {
1269
28
28
1
27128
my $self = shift;
1270
28
53
my $nerve_file = shift;
1271
17
17
219
use Storable;
17
53
17
1492
1272
28
128
store $self, $nerve_file;
1273
17
17
148
no Storable;
17
65
17
1656
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
1108
load_perceptron( @_ );
1288
}
1289
1290
sub load_perceptron {
1291
13
13
1
4857
my $nerve_file_to_load = shift;
1292
17
17
114
use Storable;
17
37
17
1095
1293
13
89
my $loaded_nerve = retrieve( $nerve_file_to_load );
1294
17
17
109
no Storable;
17
40
17
1449
1295
1296
13
1859
$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, $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
5
save_perceptron_yaml( @_ );
1317
}
1318
1319
0
0
sub save_perceptron_yaml {
1320
2
2
1
4041
my $self = shift;
1321
2
5
my $nerve_file = shift;
1322
17
17
8732
use YAML;
17
133316
17
1152
1323
2
7
YAML::DumpFile( $nerve_file, $self );
1324
17
17
160
no YAML;
17
38
17
1278
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
15443
my $nerve_file_to_load = shift;
1343
17
17
102
use YAML;
17
39
17
1248
1344
2
4
local $YAML::LoadBlessed = 1;
1345
2
6
my $loaded_nerve = YAML::LoadFile( $nerve_file_to_load );
1346
17
17
124
no YAML;
17
52
17
1503
1347
1348
2
17526
$loaded_nerve;
1349
}
1350
1351
=head1 TO DO
1352
1353
These are the to-do's that B be done in the future. Don't put too much hope in them please :)
1354
1355
=over 4
1356
1357
=item * Clean up and refactor source codes
1358
1359
=item * Add more useful data for confusion matrix
1360
1361
=item * Implement shuffling data feature
1362
1363
=item * Implement fast/smart training feature
1364
1365
=item * Write a tutorial or something for this module
1366
1367
=item * and something yet to be known...
1368
1369
=back
1370
1371
=head1 KNOWN ISSUES
1372
1373
=head2 Portability of Nerve Data
1374
1375
Take note that the C nerve data is not compatible across different versions.
1376
1377
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:
1378
1379
=over 4
1380
1381
=item * C<&preserve_as_yaml> or C<&save_perceptron_yaml> for storing data.
1382
1383
=item * C<&revive_from_yaml> or C<&load_perceptron_yaml> for retrieving the data.
1384
1385
=back
1386
1387
=head1 AUTHOR
1388
1389
Raphael Jong Jun Jie, C<< >>
1390
1391
=head1 BUGS
1392
1393
Please report any bugs or feature requests to C, or through
1394
the web interface at L. I will be notified, and then you'll
1395
automatically be notified of progress on your bug as I make changes.
1396
1397
=head1 SUPPORT
1398
1399
You can find documentation for this module with the perldoc command.
1400
1401
perldoc AI::Perceptron::Simple
1402
1403
1404
You can also look for information at:
1405
1406
=over 4
1407
1408
=item * RT: CPAN's request tracker (report bugs here)
1409
1410
L
1411
1412
=item * CPAN Ratings
1413
1414
L
1415
1416
=item * Search CPAN
1417
1418
L
1419
1420
=back
1421
1422
1423
=head1 ACKNOWLEDGEMENTS
1424
1425
Besiyata d'shmaya, Wikipedia
1426
1427
=head1 SEE ALSO
1428
1429
AI::Perceptron, Text::Matrix, YAML
1430
1431
=head1 LICENSE AND COPYRIGHT
1432
1433
This software is Copyright (c) 2021 by Raphael Jong Jun Jie.
1434
1435
This is free software, licensed under:
1436
1437
The Artistic License 2.0 (GPL Compatible)
1438
1439
1440
=cut
1441
1442
1; # End of AI::Perceptron::Simple