65.9K
CodeProject 正在变化。 阅读更多。
Home

并非那么经典的垃圾邮件过滤器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.46/5 (7投票s)

2018年3月4日

CPOL

9分钟阅读

viewsIcon

8180

downloadIcon

156

机器学习与人工智能挑战赛

引言

人工智能是一个流行且不断发展的学科,在开发者社区中越来越受关注。我们自14世纪(至少)以来就在谈论它,但现代计算机及其计算能力的增长极大地推动了这一领域的发展。我们大多数人(我想绝大多数)认为它不是日常工作必需的知识,因此我们从未深入了解。我们对人工智能是什么以及它有什么用处还有很多误导性的定义,这加剧了人们的排斥。我将尝试澄清一些疑虑,并提供一个非常简单的垃圾邮件过滤器解决方案,以展示人工智能如何成为最基础软件的一部分。

背景

简要概述当今不同的垃圾邮件过滤方法……

基于人工选择

此类过滤方法需要一些人工干预来判断某个内容是垃圾邮件还是正常邮件……

它包括基于社区的过滤,即周围社区的反馈可能会或可能不会将某些内容定义为垃圾邮件。我们可以在 CP 上看到这一点……当一定数量的成员投票将某人或某事标记为垃圾邮件时,它/他们将被网站删除……

另一种可能性是在发送前阻止垃圾邮件,通过强制发件人执行只有人类才能解决的挑战。这很有用,因为大多数垃圾邮件系统都是基于机器大规模传播的,但同时也会很烦人,因为它会减慢投递速度……这类系统最广为人知的例子是 CAPTCHA(**C**ompletely **A**utomated **P**ublic **T**uring test to tell **C**omputers and **H**umans **A**part)和 OTP(**O**ne **T**ime **P**assword)。

基于列表

黑/白名单——一个列表,其中包含应被阻止/允许的特定发件人(基于姓名、IP 地址、域名等)。例如,Gmail 的“报告垃圾邮件”按钮实际上会将发件人添加到黑名单中。

灰名单是一种现代方法,试图即时创建黑/白名单。其思想是,垃圾邮件机器只会尝试发送一次邮件,而不会费心检查收件人的响应。在灰名单技术中,服务器将自动阻止来自未知来源的传入邮件(已知来源将在白名单中定义),并返回失败消息。大多数合法的邮件服务器会自动尝试重新发送消息(通常会多次尝试以排除网络故障的可能性),如果发生这种情况,过滤器将使用新的发件人扩展原始白名单。

基于内容

这种过滤器将单独处理每条消息——与基于列表的方法相反。其思想是检查内容(文本和附件),并将其与以前已知的垃圾邮件进行比较。为了使此类识别更智能,比较可以基于一个不断发展的邮件集合,该集合会在每次识别后进行更新。改进基于内容的垃圾邮件过滤器的另一种选择是使用更复杂的文本分析方法,即从单词到短语,从文本分析到文本理解……

关于人工智能

在我们尝试编写代码之前,我认为理解不同类型的人工智能非常重要……虽然有许多方法可以对人工智能进行分类,但我希望提出我发现的四种易于理解的步骤……

1. 强大的机器(但不真正智能)

IMHO,这类软件算不上真正的人工智能,因为它不满足利用过去决策结果的要求。最有意思的例子是现代的国际象棋引擎。它们非常强大,可以击败任何人类,但它们并不真正智能,因为它们无法重复利用当前对局的过去,只能适应预定义的规则集(开局和残局手册,以及之前的对局统计数据)。所以它有适应的能力,但没有进化的能力,但我们不能忽视它代表的强大力量……

2. 有过往经历的机器

这类机器与前一类不同,因为它不仅可以基于预定义的数据来优化决策,还可以基于其“个人”经验。例如,一辆自动驾驶汽车可以从预定义的数据中“知道”当前道路的速度限制是 80 公里/小时,但会通过观察周围的汽车及其速度来优化它,并且可能只以 65 公里/小时的速度行驶。

我们在这里尝试构建的垃圾邮件过滤器是另一种边学边用的例子,因为它应该能够根据传入数据重新定义什么是垃圾邮件……

3. 有意识的机器

这类机器(目前很大程度上是假设的)不仅能够响应周围世界的外部属性,还可以考虑更复杂的社会行为并适应它们……

阿西莫夫创造的三定律机器人及其过度保护的本性是这类机器的一个很好的例子……

4. 天网

我们都在谈论它并害怕它,但不要再害怕了——这是比前一种更进一步的、仍然是假设的类型,我们可能仍然能在 3 和 4 之间阻止它……

这种机器实际上是完全拟人化的。它可以为了自身利益而覆盖任何预定义的行为,就像某人杀戮一样……

解决方案

解决方案包含两部分

  • 学习 - 此部分可以获取已分类(垃圾邮件或正常邮件)的消息,并在分析其数据后将其添加到事实库中。这是训练部分。
  • 分类 - 此部分可以获取新消息,并将其数据与事实库进行比较。分类消息后,它将被传送到学习部分以改进事实库。

两部分都有一些共同的功能,可以处理文本并将其分解以创建要添加到/比较事实库中的数据。这种分析基于单词。

主要思想是根据每个单词在垃圾邮件和正常邮件中的出现次数来为其赋予权重。每个单词的当前权重存储在事实库中,并在每次分类后更新。

核心 - 文本分析

private string[ ] Message2Words ( string Message )
{
    string szMessage = Message.ToLower( );
    List<char> oPunctuations = szMessage.Where( Char.IsPunctuation ).Distinct( ).ToList( );

    oPunctuations.AddRange( new char[ ] { ' ', '<', '>' } );

    string[ ] oWords = szMessage.Split( oPunctuations.ToArray( ) ).Where
                                ( szWord => szWord.Length > 2 ).ToArray( );

    return ( oWords.Except( _TransparentList ).ToArray( ) );
}

此方法负责将句子分解为单词,并删除那些在定义消息方面被认为不重要的单词。不重要单词的列表(TransparentList)最初只初始化了几个单词(这是可以改进的一点),并会定期更新或手动更新。

其他值得提及的事项

  • 在标点符号周围添加“<”和“>”以在 HTML 标签周围进行拆分,但不会删除像链接地址这样的内容,这些内容肯定可以用来识别垃圾邮件。
  • 删除长度小于 2 个字符的单词 - 这主要是一种快捷方式,避免不重要单词列表过长。
  • 我不会删除重复的单词。出现次数对后续分类非常重要,所以我们应该准确地计算单词出现的次数。例如,单词“love”。它可能既出现在垃圾邮件也出现在正常邮件中,但训练数据的实际计数显示它在垃圾邮件中比在正常邮件中更频繁。单独计数可能会导致对包含此类单词的消息识别错误。

学习方法

public void Learn ( string Message, MessageType Type, string[ ] List = null )
{
    string[ ] szList = List ?? Message2Words( Message );
    decimal nTemp;

    foreach ( string szWord in szList )
    {
        BaseFact oValue = _BaseFacts.ContainsKey( szWord ) ? _BaseFacts[szWord] : new BaseFact( );

        nTemp = (Type == MessageType.Spam) ? oValue.Spam++ : oValue.Ham++;

        _BaseFacts[szWord] = oValue;
    }
}

此方法相当简单——它所做的就是记录特定单词在指定类型消息中出现的次数……换句话说,如果一个单词出现在标记为垃圾邮件的消息中,它的垃圾邮件计数器就会增加;如果它出现在正常邮件中,它的正常邮件计数器就会增加。

分类方法

public MessageType Classify ( string Message )
{
    string[ ] szList = Message2Words( Message );
    Dictionary<string, BaseFact> oList = _BaseFacts.Where
    ( oFact => szList.Contains( oFact.Key ) ).ToDictionary( oItem => oItem.Key, oItem => oItem.Value );
    MessageType eType = oList.Sum( oItem => oItem.Value.Spam ) > oList.Sum
                 ( oItem => oItem.Value.Ham ) ? MessageType.Spam : MessageType.Ham;

    Learn( Message, eType, szList );

    if ( ++_Trigger > _Max )
    {
        _Trigger = 0;
        Transparent( );
    }

    return ( eType );
}

此方法根据单词计数器对新消息的垃圾邮件和正常邮件投票进行汇总,并根据结果决定如何分类。在将决策返回给调用者之前,它会使用新消息的数据更新基础事实集合。它还将根据预定义的频率(目前每 1000 条新消息后)启动对不重要单词列表的更新。

透明列表

public void Transparent ( )
{
    _TransparentList.AddRange( _BaseFacts.Where( oItem => oItem.Value.IsTransparent ).ToDictionary
                             ( oItem => oItem.Key, oItem => oItem.Value ).Keys.ToList( ) );
    _BaseFacts = _BaseFacts.Where( oItem => !oItem.Value.IsTransparent ).ToDictionary
                                 ( oItem => oItem.Key, oItem => oItem.Value );
}

调用此方法时,它会将不重要单词从 BaseFacts 列表移动到 TransparentList。单词的显著性计算如下:\(\frac{ \left | Spam - Ham \right | }{ Spam + Ham }\)。如果小于预定义的显著性值(目前为 0.5),则移动该单词。

支持代码

当然,有一些支持代码(参见附件)用于将学习数据输入垃圾邮件过滤器,然后测试测试数据。

它有效吗?

是!不是!取决于!

是的!在用训练数据进行训练后,它以 100% 的成功率通过了提供的测试数据的测试。

不!它在处理真实垃圾邮件时大多会失败,但大多数情况下都能成功处理真实邮件……原因其实非常清楚。提供的训练数据(可能是故意的)是一些乱码,甚至不像垃圾邮件(至少不像我遇到的垃圾邮件),但与测试数据一致,所以代码只学会了识别特定类型的垃圾邮件。

取决于!这完全取决于训练数据。数据越多、越广泛,软件就越智能。有趣的是,输入足够多的不同类型训练数据可以完全改变/扩展软件分类垃圾邮件的方式,而这正是我们对高级人工智能的期望。

摘要

我敢肯定,你们中的大多数人现在都在想:“就这样吗?”,而且你们完全正确。在我的解决方案中,我甚至没有引入任何复杂的光学统计计算(比如著名的朴素贝叶斯过滤器,它在垃圾邮件过滤器中非常流行),而是使用了一种低级的计数算法。

原因是我有一个单一的目的:向你们展示如何轻松地让人工智能为您工作。揭开人工智能的神秘面纱。展示并非只有会说话的类人机器人才是人工智能。它甚至不是从自动驾驶汽车开始的。在此之前还有一个全新的世界,您可以利用它来为自己谋利。

如今,我们有各种各样的电器、烤面包机、助听器等,它们代表了人工智能使用的较低(且实用的)端——试着成为其中的一部分吧!

© . All rights reserved.