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