@@ -24,6 +24,7 @@ TNeuralFitBase = class(TMObject)
24
24
FNN: TNNet;
25
25
FGlobalHit: integer;
26
26
FGlobalMiss: integer;
27
+ FGlobalTotal: integer;
27
28
FGlobalTotalLoss: single;
28
29
FGlobalErrorSum: single;
29
30
FFinishedThread: TNNetVolume;
@@ -71,18 +72,18 @@ TNeuralFitBase = class(TMObject)
71
72
property Verbose: boolean read FVerbose write FVerbose;
72
73
end ;
73
74
74
- TNNetDefineHitFn = function(vPair: TNNetVolumePair): boolean of object ;
75
75
TNNetDataAugmentationFn = function(vPair: TNNetVolume): TNeuralFloat of object ;
76
76
TNNetLossFn = function(vPair: TNNetVolume): TNeuralFloat of object ;
77
+ TNNetInferHitFn = function(A, B: TNNetVolume): boolean;
77
78
78
79
// / Generic Neural Network Fitting Algorithm
79
80
TNeuralFit = class (TNeuralFitBase)
80
81
protected
81
82
FTrainingVolumes, FValidationVolumes, FTestVolumes: TNNetVolumePairList;
82
83
FWorkingVolumes: TNNetVolumePairList;
83
84
84
- FInferHitFn: TNNetDefineHitFn;
85
85
FDataAugmentationFn: TNNetDataAugmentationFn;
86
+ FInferHitFn: TNNetInferHitFn;
86
87
FLossFn: TNNetLossFn;
87
88
public
88
89
constructor Create();
@@ -95,6 +96,12 @@ TNeuralFit = class(TNeuralFitBase)
95
96
Item: TMultiThreadProcItem);
96
97
procedure TestNNThread (Index: PtrInt; Data: Pointer;
97
98
Item: TMultiThreadProcItem);
99
+ procedure EnableMonopolarHitComparison ();
100
+ procedure EnableBipolarHitComparison ();
101
+
102
+ property DataAugmentationFn: TNNetDataAugmentationFn read FDataAugmentationFn write FDataAugmentationFn;
103
+ property InferHitFn: TNNetInferHitFn read FInferHitFn write FInferHitFn;
104
+ property LossFn: TNNetLossFn read FLossFn write FLossFn;
98
105
end ;
99
106
100
107
// / Image Classification Fitting Algorithm
@@ -121,10 +128,52 @@ TNeuralImageFit = class(TNeuralFitBase)
121
128
procedure ClassifyImage (pNN: TNNet; pImgInput, pOutput: TNNetVolume);
122
129
end ;
123
130
131
+
132
+ function MonopolarCompare (A, B: TNNetVolume): boolean;
133
+ function BipolarCompare (A, B: TNNetVolume): boolean;
134
+
124
135
implementation
125
136
uses
126
137
math;
127
138
139
+ function MonopolarCompare (A, B: TNNetVolume): boolean;
140
+ var
141
+ Pos: integer;
142
+ ACount, BCount: integer;
143
+ begin
144
+ ACount := A.Size;
145
+ BCount := B.Size;
146
+ Result := (ACount>0 ) and (ACount = BCount);
147
+ Pos := 0 ;
148
+ while Result and (Pos < ACount) do
149
+ begin
150
+ Result := Result and (
151
+ ( (A.FData[Pos]>0.5 ) and (B.FData[Pos]>0.5 ) ) or
152
+ ( (A.FData[Pos]<0.5 ) and (B.FData[Pos]<0.5 ) )
153
+ );
154
+ Inc(Pos);
155
+ end ;
156
+ end ;
157
+
158
+ function BipolarCompare (A, B: TNNetVolume): boolean;
159
+ var
160
+ Pos: integer;
161
+ ACount, BCount: integer;
162
+ begin
163
+ ACount := A.Size;
164
+ BCount := B.Size;
165
+ Result := (ACount>0 ) and (ACount = BCount);
166
+ Pos := 0 ;
167
+ while Result and (Pos < ACount) do
168
+ begin
169
+ Result := Result and (
170
+ ( (A.FData[Pos]>0 ) and (B.FData[Pos]>0 ) ) or
171
+ ( (A.FData[Pos]<0 ) and (B.FData[Pos]<0 ) )
172
+ );
173
+ Inc(Pos);
174
+ end ;
175
+ end ;
176
+
128
177
constructor TNeuralFit.Create();
129
178
begin
130
179
inherited Create();
@@ -263,11 +312,12 @@ procedure TNeuralFit.Fit(pNN: TNNet; pTrainingVolumes, pValidationVolumes,
263
312
FNN.UpdateWeights();
264
313
FNN.ComputeL2Decay();
265
314
266
- if (FGlobalHit > 0 ) then
315
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
316
+ if (FGlobalTotal > 0 ) then
267
317
begin
268
- TrainingError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
269
- TrainingLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
270
- CurrentAccuracy := (FGlobalHit*100 ) div (FGlobalHit+FGlobalMiss) ;
318
+ TrainingError := FGlobalErrorSum / FGlobalTotal ;
319
+ TrainingLoss := FGlobalTotalLoss / FGlobalTotal ;
320
+ CurrentAccuracy := (FGlobalHit*100 ) div FGlobalTotal ;
271
321
if (FStepSize < 100 ) then
272
322
begin
273
323
AccuracyWithInertia := AccuracyWithInertia*0.99 + CurrentAccuracy*0.01 ;
@@ -282,7 +332,7 @@ procedure TNeuralFit.Fit(pNN: TNNet; pTrainingVolumes, pValidationVolumes,
282
332
end ;
283
333
end ;
284
334
285
- if ( (FGlobalHit > 0 ) and (I mod 10 = 0 ) ) then
335
+ if ( (FGlobalTotal > 0 ) and (I mod 10 = 0 ) ) then
286
336
begin
287
337
totalTimeSeconds := (Now() - startTime) * 24 * 60 * 60 ;
288
338
@@ -334,11 +384,12 @@ procedure TNeuralFit.Fit(pNN: TNNet; pTrainingVolumes, pValidationVolumes,
334
384
FMessageProc(' Starting Validation.' );
335
385
ProcThreadPool.DoParallel(@TestNNThread, 0 , FThreadNN.Count-1 , Nil , FThreadNN.Count);
336
386
337
- if FGlobalHit + FGlobalMiss > 0 then
387
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
388
+ if FGlobalTotal > 0 then
338
389
begin
339
- ValidationRate := FGlobalHit / (FGlobalHit + FGlobalMiss) ;
340
- ValidationLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
341
- ValidationError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
390
+ ValidationRate := FGlobalHit / FGlobalTotal ;
391
+ ValidationLoss := FGlobalTotalLoss / FGlobalTotal ;
392
+ ValidationError := FGlobalErrorSum / FGlobalTotal ;
342
393
end ;
343
394
344
395
if (ValidationRate > ValidationRecord) then
@@ -384,13 +435,14 @@ procedure TNeuralFit.Fit(pNN: TNNet; pTrainingVolumes, pValidationVolumes,
384
435
FMessageProc(' Starting Testing.' );
385
436
ProcThreadPool.DoParallel(@TestNNThread, 0 , FThreadNN.Count-1 , Nil , FThreadNN.Count);
386
437
387
- if FGlobalHit + FGlobalMiss > 0 then
438
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
439
+ if FGlobalTotal > 0 then
388
440
begin
389
- TestRate := FGlobalHit / (FGlobalHit + FGlobalMiss) ;
390
- TestLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
391
- TestError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
441
+ TestRate := FGlobalHit / FGlobalTotal ;
442
+ TestLoss := FGlobalTotalLoss / FGlobalTotal ;
443
+ TestError := FGlobalErrorSum / FGlobalTotal ;
392
444
end ;
393
- if (FGlobalHit > 0 ) and (FVerbose) then
445
+ if (FGlobalTotal > 0 ) and (FVerbose) then
394
446
begin
395
447
WriteLn(
396
448
' Epochs: ' , iEpochCount,
@@ -520,7 +572,7 @@ procedure TNeuralFit.RunNNThread(Index: PtrInt; Data: Pointer;
520
572
521
573
if Assigned(FInferHitFn) then
522
574
begin
523
- if FInferHitFn(FTrainingVolumes[ElementIdx]) then
575
+ if FInferHitFn(FTrainingVolumes[ElementIdx].O, pOutput ) then
524
576
begin
525
577
Inc(LocalHit);
526
578
end
@@ -612,7 +664,7 @@ procedure TNeuralFit.TestNNThread(Index: PtrInt; Data: Pointer;
612
664
613
665
if Assigned(FInferHitFn) then
614
666
begin
615
- if FInferHitFn(FTrainingVolumes[ElementIdx]) then
667
+ if FInferHitFn(FTrainingVolumes[ElementIdx].O, pOutput ) then
616
668
begin
617
669
Inc(LocalHit);
618
670
end
@@ -640,6 +692,16 @@ procedure TNeuralFit.TestNNThread(Index: PtrInt; Data: Pointer;
640
692
pOutput.Free;
641
693
end ;
642
694
695
+ procedure TNeuralFit.EnableMonopolarHitComparison ();
696
+ begin
697
+ FInferHitFn := @MonopolarCompare;
698
+ end ;
699
+
700
+ procedure TNeuralFit.EnableBipolarHitComparison ();
701
+ begin
702
+ FInferHitFn := @BipolarCompare;
703
+ end ;
704
+
643
705
{ TNeuralFitBase }
644
706
645
707
constructor TNeuralFitBase.Create();
@@ -809,8 +871,8 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
809
871
' Step size:' , FStepSize,
810
872
' Staircase ephocs:' ,FStaircaseEpochs);
811
873
if Assigned(FImgVolumes) then WriteLn(' Training images:' , FImgVolumes.Count);
812
- if Assigned(FImgValidationVolumes) then WriteLn(' Training images:' , FImgValidationVolumes.Count);
813
- if Assigned(FImgTestVolumes) then WriteLn(' Training images:' , FImgTestVolumes.Count);
874
+ if Assigned(FImgValidationVolumes) then WriteLn(' Validation images:' , FImgValidationVolumes.Count);
875
+ if Assigned(FImgTestVolumes) then WriteLn(' Test images:' , FImgTestVolumes.Count);
814
876
end ;
815
877
816
878
FThreadNN.SetLearningRate(FCurrentLearningRate, FInertia);
@@ -856,11 +918,12 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
856
918
FNN.UpdateWeights();
857
919
FNN.ComputeL2Decay();
858
920
859
- if (FGlobalHit > 0 ) then
921
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
922
+ if (FGlobalTotal > 0 ) then
860
923
begin
861
- TrainingError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
862
- TrainingLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
863
- CurrentAccuracy := (FGlobalHit*100 ) div (FGlobalHit+FGlobalMiss) ;
924
+ TrainingError := FGlobalErrorSum / FGlobalTotal ;
925
+ TrainingLoss := FGlobalTotalLoss / FGlobalTotal ;
926
+ CurrentAccuracy := (FGlobalHit*100 ) div FGlobalTotal ;
864
927
if (FStepSize < 100 ) then
865
928
begin
866
929
AccuracyWithInertia := AccuracyWithInertia*0.99 + CurrentAccuracy*0.01 ;
@@ -875,7 +938,7 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
875
938
end ;
876
939
end ;
877
940
878
- if ( (FGlobalHit > 0 ) and (I mod 10 = 0 ) ) then
941
+ if ( (FGlobalTotal > 0 ) and (I mod 10 = 0 ) ) then
879
942
begin
880
943
totalTimeSeconds := (Now() - startTime) * 24 * 60 * 60 ;
881
944
@@ -927,11 +990,12 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
927
990
FMessageProc(' Starting Validation.' );
928
991
ProcThreadPool.DoParallel(@TestNNThread, 0 , FThreadNN.Count-1 , Nil , FThreadNN.Count);
929
992
930
- if FGlobalHit + FGlobalMiss > 0 then
993
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
994
+ if (FGlobalTotal > 0 ) then
931
995
begin
932
- ValidationRate := FGlobalHit / (FGlobalHit + FGlobalMiss) ;
933
- ValidationLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
934
- ValidationError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
996
+ ValidationRate := FGlobalHit / FGlobalTotal ;
997
+ ValidationLoss := FGlobalTotalLoss / FGlobalTotal ;
998
+ ValidationError := FGlobalErrorSum / FGlobalTotal ;
935
999
end ;
936
1000
937
1001
if (ValidationRate > ValidationRecord) then
@@ -940,7 +1004,8 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
940
1004
FMessageProc(' VALIDATION RECORD! Saving NN at ' +fileName);
941
1005
FAvgWeight.SaveToFile(fileName);
942
1006
end ;
943
- if (FGlobalHit > 0 ) and (FVerbose) then
1007
+
1008
+ if (FGlobalTotal > 0 ) and (FVerbose) then
944
1009
begin
945
1010
WriteLn(
946
1011
' Epochs: ' ,iEpochCount,
@@ -976,13 +1041,15 @@ procedure TNeuralImageFit.Fit(pNN: TNNet;
976
1041
FMessageProc(' Starting Testing.' );
977
1042
ProcThreadPool.DoParallel(@TestNNThread, 0 , FThreadNN.Count-1 , Nil , FThreadNN.Count);
978
1043
979
- if FGlobalHit + FGlobalMiss > 0 then
1044
+ FGlobalTotal := (FGlobalHit + FGlobalMiss);
1045
+ if (FGlobalTotal > 0 ) then
980
1046
begin
981
- TestRate := FGlobalHit / (FGlobalHit + FGlobalMiss) ;
982
- TestLoss := FGlobalTotalLoss / (FGlobalHit + FGlobalMiss) ;
983
- TestError := FGlobalErrorSum / (FGlobalHit + FGlobalMiss) ;
1047
+ TestRate := FGlobalHit / FGlobalTotal ;
1048
+ TestLoss := FGlobalTotalLoss / FGlobalTotal ;
1049
+ TestError := FGlobalErrorSum / FGlobalTotal ;
984
1050
end ;
985
- if (FGlobalHit > 0 ) and (FVerbose) then
1051
+
1052
+ if (FGlobalTotal > 0 ) and (FVerbose) then
986
1053
begin
987
1054
WriteLn(
988
1055
' Epochs: ' ,iEpochCount,
0 commit comments