scoring problems
"Eric S. Johansson" <[email protected]>
| Newsgroups | gmane.mail.spam.crm114 |
|---|---|
| Message-ID | <[email protected]> |
many moons ago I complained about score distributions and it took me a while but I finally have a method where I can run various testing scenarios on a set of data. My dataset is approximately 12,000 messages that have been pre-scored, mostly accurately, as a result of having passed through twopenny blue and its filtering processes. If there are any mis-scored messages is because CRM 114 placed good messages deep within the red zone and I never saw them. (No, I'm not manually going over 12,000 messages to try and correct these mistakes. I view this as a real-world problem that I have to deal with at some point.) anyway, the problem I've been having is that I'm getting way too many messages in the range of 0 to -14 and that makes for a rather unpleasant user experience. If you look at the scores below (some help with gnuplot for graphing this data would be welcome), you'll see there is a bimodal distribution of scores. The green messages peak in the range of 21-23 and the Reds peak in the range of -6 to -7. This is not good. In the past the peaks had occurred well outside of range twopenny blue considers "uncertain" which is +-14. I have started moving the uncertain range to +-7 in the hopes of cutting off some of the peak but it seems to have had the unfortunate effect of moving the red peak closer to zero. another curious thing is I'm getting some message scores of 999. It's probably something in my code that I'm blind about but, I'm still puzzled. I've attached the code I used to drive CRM114 (ignore main, it's the remnants of a test program I haven't finished). And below I've included the scores I get from my test suite. So, any clues as to what the heck is going on I would appreciate it. I am puzzled because I have been using fundamentally the same code for years and things went south before I started changing my code to try and compensate. -304, 4 -277, 0 -260, 0 -249, 0 -244, 0 -237, 0 -171, 0 -162, 0 -160, 0 -158, 0 -151, 0 -144, 0 -140, 2 -136, 0 -133, 0 -132, 0 -131, 0 -130, 0 -128, 1 -126, 0 -125, 0 -116, 1 -111, 0 -107, 1 -104, 1 -102, 0 -100, 1 -92, 0 -90, 0 -89, 1 -88, 0 -86, 0 -83, 0 -82, 0 -79, 0 -78, 3 -77, 8 -76, 9 -75, 6 -74, 5 -73, 1 -72, 1 -70, 3 -69, 0 -68, 1 -64, 0 -63, 2 -62, 2 -61, 8 -60, 22 -59, 14 -58, 5 -57, 4 -56, 13 -55, 5 -54, 13 -53, 29 -52, 55 -51, 95 -50, 108 -49, 60 -48, 42 -47, 24 -46, 20 -45, 17 -44, 13 -43, 12 -42, 13 -41, 7 -40, 8 -39, 31 -38, 25 -37, 37 -36, 51 -35, 46 -34, 24 -33, 26 -32, 10 -31, 21 -30, 19 -29, 20 -28, 52 -27, 52 -26, 46 -25, 56 -24, 65 -23, 55 -22, 61 -21, 123 -20, 134 -19, 170 -18, 205 -17, 169 -16, 175 -15, 136 -14, 164 -13, 235 -12, 289 -11, 394 -10, 427 -9, 463 -8, 557 -7, 794 -6, 815 -5, 603 -4, 389 -3, 195 -2, 81 -1, 43 0, 45 1, 29 2, 16 3, 22 4, 36 5, 39 6, 74 7, 56 8, 77 9, 65 10, 71 11, 56 12, 40 13, 51 14, 49 15, 41 16, 71 17, 75 18, 88 19, 100 20, 176 21, 196 22, 174 23, 180 24, 134 25, 110 26, 119 27, 83 28, 79 29, 86 30, 75 31, 67 32, 82 33, 59 34, 52 35, 49 36, 47 37, 41 38, 19 39, 17 40, 7 41, 11 42, 6 43, 3 44, 3 45, 3 46, 0 47, 1 48, 0 49, 3 50, 5 52, 0 54, 1 55, 2 57, 3 58, 0 59, 4 60, 0 61, 1 63, 0 66, 0 67, 0 69, 0 71, 0 74, 1 76, 0 82, 0 95, 0 97, 0 99, 0 105, 0 118, 1 120, 0 123, 0 128, 0 129, 0 133, 0 167, 0 168, 0 169, 1 171, 0 173, 0 189, 0 196, 0 199, 0 200, 0 221, 0 999, 10 ------------------------------------------------------------------------- This SF.net email is sponsored by the 2008 JavaOne(SM) Conference Don't miss this year's exciting event. There's still time to save $100. Use priority code J8TL2D2. http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone _______________________________________________ Crm114-general mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/crm114-general
crm114.py
(text/x-python, 8.1 KB)
#!/usr/bin/python # Title: # spam_discriminator.py # # Description: # interface between tpblue and CRM 114. Handles train as good, # train as bad, and scoring. # # Copyright: # Eric S. Johansson ([email protected]) # BSD License: see LICENSE or http://www.opensource.org/licenses/bsd-license.php # # Requires: #import email from email.Errors import BoundaryError,HeaderParseError, MessageError import binascii import sys import traceback import subprocess #from esjtools.simple_locker import simple_locker import syslog #from tpblue.log import log #from tpblue import tpblue_utils # from tpblue import brown_utils #from tpblue import common_services #from tpblue import message_pool ####### just code ##### def dump_stack(preamble="default"): etype, value, tb = sys.exc_info() exception_strings = traceback.format_exception(etype, value, tb) for i in exception_strings: syslog.syslog("%s: %s"%(preamble,i)) del tb sys.last_traceback = None class CRM114 (object): def __init__(self, common_CRM, user_CRM): """remember where everything is located """ self.common_CRM = common_CRM self.user_CRM = user_CRM # print common_CRM, user_CRM def build_crm_command(self, command_type): """build a complete crm114 command line """ command_table = { "stats": "--stats_only", "spam": "--learnspam", "nonspam": "--learnnonspam", } # first string is path to normalizemime. # 2ed string is path to crm file. # 3ed is command, # 4th is user path # 5th is error log command_list = [self.common_CRM, command_table[command_type], "--fileprefix=%s"%self.user_CRM, "-u /usr/local/crm/" ] return command_list # ['/usr/local/crm/mailfilter.crm', '--stats_only', '--fileprefix=/var/tpblue/esj/crm114/', '-u /usr/local/crm/'] def CRM114_train(self, learn_as, message): result = True learn_command = {"red": "spam", "green":"nonspam", } command_list = self.build_crm_command(learn_command[learn_as]) try: # train as not spam p = subprocess.Popen(command_list, shell=False, bufsize=4000, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) message_text = message #.as_string (1) (crm114_output, crm114_error) = p.communicate(message_text[:10000]) syslog.syslog("CRM 114_output %s"% crm114_output) syslog.syslog("CRM 114_error %s"% crm114_error) except: # crm114 woofed again. fake a 0 return and return the message syslog.syslog("crm114 call failed") dump_stack() result = False return result def train_as_good(self, message, given_score): """ """ result = 0 # preseeed success try: if given_score <= 3: syslog.syslog( "train it green %s"% (given_score,)) # retrain as green because the user said so. result = self.CRM114_train("green", message) if not result: syslog.syslog("TAG failed to learn %s" % result) except Exception, error: syslog.syslog("train as good error: %s" % (error,)) dump_stack('TAG:') # invert the sense of result so it'll make more sense outside of # this method. True equal success on training, false equals # failure return not result def train_as_bad(self, message, given_score): """for a given user, train messages as bad. """ result = True #indicate failure. Yes, it's weird. try: if given_score >= -3: # retrain as bad because the user said so. syslog.syslog( "train it red %s"% (given_score,)) result = self.CRM114_train("red", message) if not result: syslog.syslog("TAB failed to learn %s" % result) except Exception, error: dump_stack('TAG:') syslog.syslog("train as bad error %s" % (error,)) return not result def score_message(self, message): """run the message through CRM114 return the priority code as well as modifying the message to contain the codes as a header""" try: command_list = self.build_crm_command("stats") message_text = message # print command_list p = subprocess.Popen(command_list, shell=False, bufsize=4000, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) message_text = message #.as_string (1) (crm114_output, crm114_error) = p.communicate(message_text[:10000]) syslog.syslog("CRM 114_output %s"% crm114_output) syslog.syslog("CRM 114_error %s"% crm114_error) try: # convert string to float if it dies, well, it's dead Jim #print "returned result from CRM 114 = %s" % str(result), result = float (crm114_output.rstrip()) except Exception, error: # blech dog food! syslog.syslog("bad score, error = %s %s"% (crm114_output, error)) syslog.syslog("command = %s"% (command_list)) result = "-0.13" except (BoundaryError, HeaderParseError, binascii.Error ), error_message: syslog.syslog("score_message failed %s" % error_message ) return result #################### test code ################## def main(): #development_path = "/usr/local/tpblue/src" #sys.path.insert(1,development_path) from tpblue import configuration from tpblue import tpblue_email #import rpdb2; rpdb2.start_embedded_debugger('1234', fAllowRemote = True) tpblue_ID = 'esj' config = configuration.configuration(tpblue_ID) crm_path = config["crm114_command"] user_path = config["crm114_base"] content_analyzer = CRM114(crm_path, user_path) #start of the main code try: # go grab message from standard in my_message = tpblue_email.tpblue_message() my_message.message_from_file(sys.stdin) my_message.meta['tpblue_ID'] = tpblue_ID my_message.meta['recipient_list'] = ['[email protected]'] score = content_analyzer.score_message(my_message.message) # print "%s score is %s"% (tpblue_ID, score) except (StandardError),e: # exception was thrown, log it and return original message # spamtrap these messages marking them as exception fodder syslog.syslog("tpblue filter threw exception: %s" % e) # print ("tpblue filter threw exception: %s" % e) etype, value, tb = sys.exc_info() exception_strings = traceback.format_exception(etype, value, tb) for i in exception_strings: syslog.syslog(i) # print(i) del tb sys.last_traceback = None except MessageError,e: # this is highly likely an ill formed piece of spam. It did # not even create a message object. So, I say dump it on the # floor after logging the event. syslog.syslog("junk message threw exception: %s" % e) etype, value, tb = sys.exc_info() exception_strings = traceback.format_exception(etype, value, tb) for i in exception_strings: syslog.syslog(i) del tb sys.last_traceback = None if __name__ == "__main__": main()