朴素贝叶斯(Naive Bayes)

从机器学习到深度学习--概述


Sheldon镇楼~~~

image

贝叶斯定理是英国数学家贝叶斯提出的,当时的目标是要解决所谓「逆概率」问题。在前贝叶斯时代处理概率问题的时候,总是先取一定假设(比如抛硬币时,每次出现正反面的概率相同),然后在假设下讨论一定事件的概率(比如说连续出现 10 次正面的概率)。「逆概率」则反过来考虑问题,比如说,如果连续出现 10 次正面,我们想知道一次抛硬币时出现正反面的概率。贝叶斯定理的相关论文在贝叶斯去世后才发表,此后法国大数学家拉普拉斯对这一理论进行了深入的研究,使之成为我们今天使用的形式,如下图所示。

image

此公式表示两个互换的条件概率之间的关系,他们通过联合概率关联起来,这样使得我们知道P(D|H)的情况下去计算P(H|D)成为了可能,而我们的贝叶斯模型便是通过贝叶斯准则去计算某个样本在不同类别条件下的条件概率并取具有最大条件概率的那个类型作为分类的预测结果。

实现简单贝叶斯分类器

下面以进行文本分类为目的使用Python实现一个朴素贝叶斯文本分类器.

为了计算条件概率,我们需要计算各个特征的在不同类别下的条件概率以及类型的边际概率,这就需要我们通过大量的训练数据进行统计获取近似值了,这也就是我们训练我们朴素贝叶斯模型的过程.

针对不同的文本,我们可以将所有出现的单词作为数据特征向量,统计每个文本中出现词条的数目(或者是否出现某个词条)作为数据向量。这样一个文本就可以处理成一个整数列表,并且长度是所有词条的数目,这个向量也许会很长,用于本文的数据集中的短信词条大概一共3000多个单词。

def get_doc_vector(words, vocabulary):
    ''' 根据词汇表将文档中的词条转换成文档向量
    :param words: 文档中的词条列表
    :type words: list of str
    :param vocabulary: 总的词汇列表
    :type vocabulary: list of str
    :return doc_vect: 用于贝叶斯分析的文档向量
    :type doc_vect: list of int
    '''
    doc_vect = [0]*len(vocabulary)
    for word in words:
        if word in vocabulary:
            idx = vocabulary.index(word)
            doc_vect[idx] = 1
    return doc_vect

统计训练的过程:

def train(self, dataset, classes):
    ''' 训练朴素贝叶斯模型
    :param dataset: 所有的文档数据向量
    :type dataset: MxN matrix containing all doc vectors.
    :param classes: 所有文档的类型
    :type classes: 1xN list
    :return cond_probs: 训练得到的条件概率矩阵
    :type cond_probs: dict
    :return cls_probs: 各种类型的概率
    :type cls_probs: dict
    '''
    # 按照不同类型记性分类
    sub_datasets = defaultdict(lambda: [])    # defaultdict:当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值
    cls_cnt = defaultdict(lambda: 0)
    for doc_vect, cls in zip(dataset, classes):
        sub_datasets[cls].append(doc_vect)
        cls_cnt[cls] += 1
    # 计算类型概率
    cls_probs = {k: v/len(classes) for k, v in cls_cnt.items()}
    # 计算不同类型的条件概率
    cond_probs = {}
    dataset = np.array(dataset)
    for cls, sub_dataset in sub_datasets.items():
        sub_dataset = np.array(sub_dataset)
        # Improve the classifier.
        cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) + 1)/(np.sum(dataset) + 2))
        cond_probs[cls] = cond_prob_vect
    return cond_probs, cls_probs

注意这里对于基本的条件概率直接相乘有两处改进:

  • 各个特征的概率初始值为1,分母上统计的某一类型的样本总数的初始值是1,这是为了避免如果有一个特征统计的概率为0,则联合概率也为零那自然没有什么意义了, 如果训练样本足够大时,并不会对比较结果产生影响.
  • 由于各个独立特征的概率都是小于1的数,累积起来必然会是个更小的书,这会遇到浮点数下溢的问题,因此在这里我们对所有的概率都取了对数处理,这样在保证不会有损失的情况下避免了下溢的问题。

获取了统计概率信息后,我们便可以通过贝叶斯准则预测我们数据的类型了,这里我并没有直接计算每种情况的概率,而是通过统计得到的向量与数据向量进行内积获取条件概率的相对值并进行相对比较做出决策的。

def classify(self, doc_vect, cond_probs, cls_probs):
    ''' 使用朴素贝叶斯将doc_vect进行分类.
    '''
    pred_probs = {}
    for cls, cls_prob in cls_probs.items():
        cond_prob_vect = cond_probs[cls]
        pred_probs[cls] = np.sum(cond_prob_vect*doc_vect) + np.log(cls_prob)
    return max(pred_probs, key=pred_probs.get)

进行短信分类

已经构建好了朴素贝叶斯模型,我们就可以使用此模型来统计数据并用来预测了。这里我使用了SMS垃圾短信语料库中的垃圾短信数据, 并随机抽取90%的数据作为训练数据,剩下10%的数据作为测试数据来测试我们的贝叶斯模型预测的准确性。

当然在建立模型前我们需要将数据处理成模型能够处理的格式:

ENCODING = 'ISO-8859-1'
TRAIN_PERCENTAGE = 0.9
def get_doc_vector(words, vocabulary):
    ''' 根据词汇表将文档中的词条转换成文档向量
    :param words: 文档中的词条列表
    :type words: list of str
    :param vocabulary: 总的词汇列表
    :type vocabulary: list of str
    :return doc_vect: 用于贝叶斯分析的文档向量
    :type doc_vect: list of int
    '''
    doc_vect = [0]*len(vocabulary)
    for word in words:
        if word in vocabulary:
            idx = vocabulary.index(word)
            doc_vect[idx] = 1
    return doc_vect
def parse_line(line):
    ''' 解析数据集中的每一行返回词条向量和短信类型.
    '''
    cls = line.split(',')[-1].strip()
    content = ','.join(line.split(',')[: -1])
    word_vect = [word.lower() for word in re.split(r'\W+', content) if word]
    return word_vect, cls
def parse_file(filename):
    ''' 解析文件中的数据
    '''
    vocabulary, word_vects, classes = [], [], []
    with open(filename, 'r', encoding=ENCODING) as f:
        for line in f:
            if line:
                word_vect, cls = parse_line(line)
                vocabulary.extend(word_vect)
                word_vects.append(word_vect)
                classes.append(cls)
    vocabulary = list(set(vocabulary))
    return vocabulary, word_vects, classes

有了上面三个函数我们就可以直接将我们的文本转换成模型需要的数据向量,之后我们就可以划分数据集并将训练数据集给贝叶斯模型进行统计。

# 训练数据 & 测试数据
ntest = int(len(classes)*(1-TRAIN_PERCENTAGE))
test_word_vects = []
test_classes = []
for i in range(ntest):
    idx = random.randint(0, len(word_vects)-1)
    test_word_vects.append(word_vects.pop(idx))
    test_classes.append(classes.pop(idx))
train_word_vects = word_vects
train_classes = classes
train_dataset = [get_doc_vector(words, vocabulary) for words in train_word_vects]

训练模型:

cond_probs, cls_probs = clf.train(train_dataset, train_classes)

剩下我们用测试数据来测试我们贝叶斯模型的预测准确度:

# 测试模型
error = 0
for test_word_vect, test_cls in zip(test_word_vects, test_classes):
    test_data = get_doc_vector(test_word_vect, vocabulary)
    pred_cls = clf.classify(test_data, cond_probs, cls_probs)
    if test_cls != pred_cls:
        print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls))
        error += 1
print('Error Rate: {}'.format(error/len(test_classes)))

参考链接:
https://www.jiqizhixin.com/articles/2017-09-19-6
https://zhuanlan.zhihu.com/p/27906640

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容