svn commit: r1934130 - spamassassin/trunk/lib/Mail/SpamAssassin/Plugin
[email protected] Tue, 12 May 2026 06:28:14 -0000
| Newsgroups | gmane.mail.spam.spamassassin.cvs |
|---|---|
| Message-ID | <177856729457.1770233.8588032564857314849@svn03-he-fi> |
Author: gbechis
Date: Tue May 12 06:28:14 2026
New Revision: 1934130
Log:
improve replay algorithm
Modified:
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/NeuralNetwork.pm
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/NeuralNetwork.pm
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/NeuralNetwork.pm Tue May 12 05:09:38 2026 (r1934129)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/NeuralNetwork.pm Tue May 12 06:28:14 2026 (r1934130)
@@ -44,7 +44,7 @@ use strict;
use warnings;
use re 'taint';
-my $VERSION = 0.9.1;
+my $VERSION = 0.9.2;
use AI::FANN qw(:all);
use Storable qw(store retrieve);
@@ -797,12 +797,24 @@ unless ($locker->safe_lock($dataset_path
$num_input = $model_size;
$network = $existing_network;
} else {
- # Model and vocab files are inconsistent, discard the stale network and fall through to
- # a fresh one.
- my $stored_size = defined $stored_vocab_ref ? scalar(@$stored_vocab_ref) : 0;
- info("Model/vocab mismatch (stored=$stored_size vs model=$model_size inputs); " .
- "discarding stale model, starting fresh network");
- undef $existing_network;
+ my $stored_size = defined $stored_vocab_ref ? scalar(@$stored_vocab_ref) : 0;
+ my $growth_ratio = ($model_size > 0) ? abs($num_input - $model_size) / $model_size : 1;
+ if ($growth_ratio < 0.10) {
+ # Vocab changed by less than 10%: preserve existing weights by adapting feature
+ # vectors to the old model's input size rather than discarding a trained network.
+ dbg("Vocab/model size mismatch (new=$num_input vs model=$model_size, " .
+ sprintf("%.1f", $growth_ratio * 100) . "% change); " .
+ "adapting feature vectors to preserve existing model");
+ for my $fv (@$feature_vectors) {
+ $fv->{vec} = _adjust_vector_size($fv->{vec}, $model_size);
+ }
+ $num_input = $model_size;
+ $network = $existing_network;
+ } else {
+ info("Model/vocab mismatch (stored=$stored_size vs model=$model_size inputs); " .
+ "discarding stale model, starting fresh network");
+ undef $existing_network;
+ }
}
}
unless (defined $network) {
@@ -1346,10 +1358,10 @@ sub _retrain_from_vocabulary {
my $spam_reps = 1;
my $ham_reps = 1;
if ($spam_docs > $ham_docs) {
- $ham_reps = int($spam_docs / $ham_docs + 0.5) || 1;
+ $ham_reps = int(($spam_docs + $ham_docs - 1) / $ham_docs) || 1;
$ham_reps = 10 if $ham_reps > 10;
} elsif ($ham_docs > $spam_docs) {
- $spam_reps = int($ham_docs / $spam_docs + 0.5) || 1;
+ $spam_reps = int(($ham_docs + $spam_docs - 1) / $spam_docs) || 1;
$spam_reps = 10 if $spam_reps > 10;
}
dbg("Retraining from vocabulary: spam_docs=$spam_docs, ham_docs=$ham_docs, " .