如何利用概率思想解决编程问题?

开始正文前,先给大家推荐一本好书——《黑客与画家》,这本书是作者的一些文稿,中文版出版于2011年,看完全书,会被作者的远见所折服,很多软件方面的发展都与作者的预测不谋而合,而且很多作者提出的思想也值得我们去深思。今天的文章也是从本书中得到的启示。


书中作者介绍了自己垃圾邮件过滤器的开发思路,起初他通过查找垃圾邮件特征对垃圾邮件进行过滤,如发件人的地址,邮件主题,正文中的敏感词等。这样的垃圾邮件过滤器,能够达到78%的正确率,但是,当作者试图进一步提高正确率时,却发现过滤规则可能太过严格——垃圾邮件的阻挡是变强的,但是,误判邮件(非垃圾邮件被当作垃圾邮件)的数量也增加了。


于是,作者改用了概率过滤器,实际上就是一个贝叶斯分类器,基于概率的方法,可以极大地提高垃圾邮件的识别正确率,同时误判并不大。看样,当时作者已经具备了机器学习的思想,或许还没有意识到,他将其称之为概率过滤器。


如果从这个例子我们只是看到了一种邮件过滤器的实现方式,那么这和普通的技术书籍就没有差别了。作者提到,程序员似乎养成了一种习惯,那就是过度强调确定性,我们要求输入要有确定的输出,如果没有什么特征,我们就无从下手。可是现实中,更加直接的方式,可能是总结经验,也就是利用先验经验作为依据,来判断一件事情是否符合逻辑。不是所有的事情都需要确定性,概率的方法往往会更加贴合实际。


想想的确如此,前几天看到一篇文章是关于贝叶斯公式的,里面的一个例子非常贴切。追求确定性的方式,先提出假设,然后提出证据。假如我提出的假设本就是错误的,很可能就导致我们得到错误的结果。例如,我提出假设月亮是奶酪做的,经过我的观察,月亮是奶酪黄色的,得到结论月亮就是奶酪做的。很明显,这是错误的结论,你可能会惊呼,任何有常识的,都会认为这是错误的。当你这样想的时候,你已经加入了你的先验知识,那就是常识,所以你不会被错误的结论蒙骗。当然,如果一个人的先验知识都是错误的,那就另当别论了。


前段时间学习了一轮深度学习,有了一些感性的认识,机器学习领域何尝不是采用了概率的思想,这样更符合人类的思考方式。这就给我们带来了一种新的解决代码中问题的思路,我们不需要完全的确定性,很多问题,概率能更好的帮助我们。


为了加深理解,下面是我按照作者的思路,完成的一个贝叶斯分类器,用来区分一句英文句子是好话还是坏话。贝叶斯公式是概率论中很重要的部分,目前主要用在质检,保险,趋势预判等领域,关于它的历史也很有趣,它的伟大之处在于,我们可以利用先验知识得到后验知识,网上介绍的文章很多,可以补补理论。闲话少说,看代码,我们强大的Python上场。

good_sentences=[
    'i like you',
    'i love you',
    'you are so cool',
    'you are right',
    'you are beautiful'
]


bad_sentences=[
    'i hate you',
    'i beat you',
    'you are not cool',
    'you are wrong',
    'you are ugly'
]
为了训练模型,我提供了一些基本数据,数据不多,如果希望正确率更高,可以提供更多的句子。句子被分为了两组,好话和坏话。
good_sentences_join = ' '.join(good_sentences)
good_words = good_sentences_join.split()

bad_sentences_join = ' '.join(bad_sentences)
bad_words = bad_sentences_join.split()

good_words_freq = {}
for word in good_words:
    good_words_freq.setdefault(word, round(good_words.count(word)/len(good_words), 2))

bad_words_freq = {}
for word in bad_words:
bad_words_freq.setdefault(word, round(bad_words.count(word)/len(bad_words), 2))
接下来,分别计算句子中的词在好话中出现的概率和坏话中出现的概率,这就是我们的先验知识了。
p_bad = {}
keys = set(good_words + bad_words)
for key in keys:
    bad_freq = 0.0
    if key in bad_words_freq:
        bad_freq = bad_words_freq[key]

    good_freq = 0.0
    if key in good_words_freq:
        good_freq = good_words_freq[key]

p_bad.setdefault(key, max(0.01, min(0.99, bad_freq/(bad_freq+good_freq))))
我们计算出模型中的各个词使一个句子成为坏话的权重。这里大家可以添加打印看看p_bad,会惊奇的发现,i,you等词,权重是0.5,也就是说,这些是中性词,因为这些词在好话和坏话中都有出现,我们当然可以认为是中性的。
def analyse_bad_sentence(sentence, p_not_found=0.5):
    words = sentence.lower().split()
    p_bad_word = 1.0
    p_bad_word_reverse = 1.0
    for word in words:
        if word in p_bad:
            p_bad_word *= p_bad[word]
            p_bad_word_reverse *= 1 - p_bad[word]
        else:
            p_bad_word *= p_not_found
            p_bad_word_reverse *= 1 - p_not_found
return p_bad_word / (p_bad_word + p_bad_word_reverse)
现在,我们可以利用模型,定义分析句子的函数了,主要就是利用先验知识,对当前的实际句子进行计算,给出置信度,这里使用了贝叶斯定理。注意一下第二个参数,p_not_found,这里默认值是0.5,也就是说,如果一个词不存在先验知识,我将其当做中性词处理,你可以尝试给这个参数赋不同的值,大于0.5,你的小机器人有点心理阴暗,没见过的词,它会认为这是一个坏话中的词;小于0.5,你的小机器人有点天真,没见过的,它会当它是好话。
if __name__ == '__main__':
    import sys

    if len(sys.argv) != 2:
        print('Usage: ai_simulate sentence')
    
    sentence = sys.argv[1]
    confidence = analyse_bad_sentence(sentence)
    if confidence > 0.5:
        print('You should not swear at me!')
    else:
print('Thank you!')

最后的主程序部分很简单,不再多说,下面运行试试,我使用的ubuntu。

$ ./ai_simulate.py "He like you"

Thank you!


$./ai_simulate.py "He hate you"

You should not swear at me!


$ ./ai_simulate.py "He love and hate you"

Thank you!


对于第三个例子,如果你修改p_not_found为0.6,就会变为

You should not swear at me!


因为He还有and,我们的模型数据中并没有,阴暗的机器人会认为你在骂他XD。代码在这里

猜你喜欢

转载自blog.csdn.net/yjp19871013/article/details/79049749
今日推荐