Байесовская классификация машинного обучения (реализация на Python)

машинное обучение искусственный интеллект Python алгоритм

朴素贝叶斯(Naive Bayesian)是最为广泛使用的分类方法,它以概率论为基础,是基于贝叶斯定理和特征条件独立假设的分类方法。

принцип

Наивный байесовский метод — это метод классификации, основанный на теореме Байеса и принципе предположения о независимости признаков от условий. По заданным признакам рассчитывается вероятность классификации, и для классификации выбирается случай с высокой вероятностью. также на основетеория вероятностиМетод классификации машинного обучения. Цель классификации определена и относится к обучению с учителем.

通过概率来衡量事件发生的可能性。概率论和统计学恰好是两个相反的概念,统计学是抽取部分样本进行统计来估算总体的情况,而概率论是通过总体情况来估计单个事件或者部分事情的发生情况。因此,概率论需要已知的数据去预测未知的事件。 
例如,我们看到天气乌云密布,电闪雷鸣并阵阵狂风,在这样的天气特征(F)下,我们推断下雨的概率比不下雨的概率大,也就是p(下雨)>p(不下雨),所以认为待会儿会下雨。这个从经验上看对概率进行判断。 
而气象局通过多年长期积累的数据,经过计算,今天下雨的概率p(下雨)=85%,p(不下雨)=15%,同样的,p(下雨)>p(不下雨),因此今天的天气预报肯定预报下雨。这是通过一定的方法计算概率从而对下雨事件进行判断。

почему это называетсяпростоБайесовский: простой, простой в эксплуатации, основанный на предположении о независимости признаков, то есть каждый признак независим друг от друга и не влияет друг на друга.

Условная возможность

Вероятность того, что другое событие произойдет, если другое событие уже произошло. Рассчитывается следующим образом:P(A|B)=P(A∩B) / P(B)Простое понимание: нарисуйте диаграмму Венна, часть, где две окружности пересекаются, указывает на то, что происходит А и также происходит Б, потому что спрашивается вероятность того, что А произойдет, когда произойдет Б. B эквивалентно новому выборочному пространству. АВ/В может быть.

Правило умножения вероятностей: P(A∩B)=P(A)P(B|A) или P(A∩B)=P(B)P(A|B) Вероятность независимых событий: P(A∩B)=P(A)P(B)

теорема Байеса

Если имеется конечное k взаимоисключающих событий, B1, B2, , Bk и P(B1)+P(B2)+⋅⋅⋅+P(Bk)=1 и наблюдаемое событие A, то существуют:

image.png

Принцип классификации

На основе теории вероятностей задача бинарной классификации выглядит следующим образом: Если p1 > p2, то он относится к категории 1, в противном случае — к категории 2.

Во-вторых, по теореме Байеса имеемp(ci|x,y) = p(x,y|ci) * p(ci) / p(x,y)x, y представляют переменные признаков, такие как слова в следующем примере. Ci представляет категорию. p(ci | x, y) — вероятность попадания в категорию Ci при появлении признаков x и y. В сочетании, как указано выше: Если p(ci | x, y) > p(cj | x, y), он классифицируется в категорию i, в противном случае он классифицируется в категорию j.

贝叶斯定理最大的好处是可以用已知的三个概率去计算未知的概率,而如果仅仅是为了比较p(ci|x,y)和p(cj|x,y)的大小,只需要已知两个概率即可,分母相同,比较p(x,y|ci)p(ci)和p(x,y|cj)p(cj)即可。

Принцип предположения условной независимости характеристик

Наивный Байес часто используется для классификации документов. Определите, к какой категории относится статья, на основе словарного запаса, который появляется в документе. Используйте характерное условие появления слова свектор слова WПредставление, состоящее из нескольких значений, количество значений равно количеству словарей в обучающем наборе. Вышеупомянутая байесовская формула может быть выражена как:p(ci|ω)=p(ω|ci) * p(ci) / p(ω)Вхождение каждого слова не повлияет друг на друга, тогдаp(ω|ci) = p(ω0|ci)*p(ω1|ci)*...* p(ωk|ci)

Реализация алгоритма

import numpy as np
np.seterr(divide='ignore', invalid='ignore')  #消除向量中除以0的警告
# 获取数据
def loadDataSet():
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1] #1表示侮辱性言论,0表示正常
    return postingList, classVec

Построить векторы слов из словаря документа:

def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

# 对输入的词汇表构建词向量
def setOfWords2Vec(vocabList, inputSet):
    returnVec = np.zeros(len(vocabList)) #生成零向量的array
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1 #有单词,该位置填充1
        else:
            print("the word: %s is not in my Vocabulary" % word)
            # pass
    return returnVec  #返回0,1的向量

if __name__ == '__main__':
    listPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listPosts)
    print(myVocabList)
 

Результат выглядит следующим образом:['flea', 'ate', 'how', 'licks', 'quit', 'problems', 'dog', 'I', 'garbage', 'help', 'is', 'cute', 'steak', 'to', 'worthless', 'please', 'has', 'posting', 'buying', 'love', 'food', 'so', 'my', 'take', 'dalmation', 'stop', 'park', 'not', 'stupid', 'him', 'mr', 'maybe'], представляет собой вектор слов, полученный после дедупликации различных категорий речи.[ 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]: Указывает, появляется ли слово из словарного набора 1 в векторе слов.

Как и выше, этот метод только записывает, появляется ли каждое слово, и не записывает количество вхождений, становясьмодель набора слов. Если регистрируется количество вхождений слова, такой метод построения вектора слов называетсямодель мешка слов,следующее. В этой статье используется только модель набора слов.

# 词袋模型
def bagofWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return vocabList #返回非负整数的词向量

Используйте векторы слов для расчета вероятностей:

def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)  #文档数目
    numWord = len(trainMatrix[0])  #词汇表数目
    print(numTrainDocs, numWord)
    pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出现侮辱性评论的概率 [0, 1, 0, 1, 0, 1]
    p0Num = np.zeros(numWord)
    p1Num = np.zeros(numWord)

    p0Demon = 0
    p1Demon = 0

    for i in range(numTrainDocs):
        if trainCategory[i] == 0:
            p0Num += trainMatrix[i] #向量相加
            p0Demon += sum(trainMatrix[i]) #向量中1累加其和
        else:
            p1Num += trainMatrix[i]
            p1Demon += sum(trainMatrix[i])
    p0Vec = p0Num / p0Demon
    p1Vec = p1Num / p1Demon

    return p0Vec, p1Vec, pAbusive

if __name__ == '__main__':
    listPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listPosts)
    trainMat = []
    trainMat = []
    for postinDoc in listPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    print(trainMat)
    p0Vec, p1Vec, pAbusive = trainNB0(trainMat, listClasses)
    print(p0Vec, p1Vec, pAbusive)

Вывод немного больше, взгляните на него медленно:trainMat: представляет появление шести заданных признаков в данных в модели набора слов.

array([ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  1.,  1.,  0.,
        0.,  0.,  1.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  1.,  1.]), array([ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,
        0.,  1.,  0.,  1.,  1.,  1.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,
        0.,  0.,  1.,  0.,  0.,  0.]), array([ 1.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,
        1.,  0.,  0.,  1.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,
        1.,  1.,  0.,  0.,  0.,  1.]), array([ 0.,  1.,  1.,  1.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.]), array([ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  1.,  0.,  0.,  0.,
        0.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  1.,  0.,  0.,  0.,  0.,
        0.,  0.,  1.,  1.,  0.,  1.]), array([ 0.,  0.,  1.,  0.,  1.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,
        0.,  0.,  0.,  0.,  0.,  0.])]

print(numTrainDocs, numWord): 6 32 (6 документов, всего 32 слова)print(p0Vec, p1Vec, pAbusive): pAbusive – вероятность оскорбительного высказывания в документе, равная 0,5. иp0VecПредставляет вероятность появления слова категории 0 (неоскорбительная речь) в векторе слов:

[ 0.  0.04166667  0.04166667  0.04166667  0.04166667  0.
  0.08333333  0.04166667  0.          0.04166667  0.          0.04166667
  0.          0.04166667  0.          0.          0.04166667  0.04166667
  0.04166667  0.04166667  0.04166667  0.          0.          0.04166667
  0.04166667  0.04166667  0.          0.125       0.          0.04166667
  0.04166667  0.04166667] 

Улучшения алгоритма:

  1. Частичная вероятность равна 0, а умножение вероятности независимых признаков, используемых в приведенном выше расчете, всегда равно 0. Следовательно, количество вхождений всех слов инициализируется равным 1, а определенный класс терминов инициализируется равным 2.
  2. Поскольку рассчитанная вероятность слишком мала, непрерывное умножение может привести к переполнению результата. Поэтому логарифмирование его имеет ту же монотонность и не повлияет на окончательное сравнение результатов. Функция выглядит следующим образом:
def trainNB1(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)  #文档数目
    numWord = len(trainMatrix[0])  #词汇表数目
    pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出现侮辱性评论的概率
    p0Num = np.ones(numWord)  #修改为1
    p1Num = np.ones(numWord)

    p0Demon = 2 #修改为2
    p1Demon = 2

    for i in range(numTrainDocs):
        if trainCategory[i] == 0:
            p0Num += trainMatrix[i] #向量相加
            p0Demon += sum(trainMatrix[i]) #向量中1累加其和
        else:
            p1Num += trainMatrix[i]
            p1Demon += sum(trainMatrix[i])
    p0Vec = np.log(p0Num / p0Demon)  #求对数
    p1Vec = np.log(p1Num / p1Demon)

    return p0Vec, p1Vec, pAbusive

Примечание. Получение p0Vec здесь может быть неправильным, но это не влияет на окончательное сравнение вероятностей.

Классификация документов с использованием функций классификатора

def classifyNB(vec2Classify, p0Vc,  p1Vc, pClass1):
    p1 = sum(vec2Classify * p1Vc) * pClass1
    p0 = sum(vec2Classify * p0Vc) * (1-pClass1)
    # p1 = sum(vec2Classify * p1Vc) + np.log(pClass1)    #取对数,防止结果溢出
    # p0 = sum(vec2Classify * p0Vc) + np.log(1 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

Поясню: vec2Classify — это размер словаря документа, который нужно классифицировать. По формулеp(ci|ω)=p(ω|ci)p(ci) / p(ω), вероятность классификации известных собственных векторов равнаp(ω|ci)p(ci). Не обращайте внимания на знаменатель:

p(ci)好求,用样本集中,ci的数量/总样本数即可 
p(ω|ci)由于各个条件特征相互独立且地位相同,`p(ω|ci)=p(w0|ci)p(w1|ci)p(w2|ci)......p(wN|ci)`,可以分别求p(w0|ci),p(w1|ci),p(w2|ci),......,p(wN|ci),从而得到p(ω|ci)。  
而求p(ωk|ci)也就变成了求在分类类别为ci的文档词汇表集合中,单个词项ωk出现的概率。

Протестируйте функцию классификации

Используйте два разных образца для проверки функции классификации:


# 构造样本测试
def testingNB():
    listPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listPosts)
    trainMat = []
    for postinDoc in listPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0v, p1v, pAb = trainNB0(trainMat, listClasses)
    # print(p0v, p1v, pAb)
    testEntry = ['love']
    thisDoc = setOfWords2Vec(myVocabList, testEntry)
    print(testEntry, 'classified as', classifyNB(thisDoc, p0v, p1v, pAb))

    testEntry = ['stupid', 'garbage']
    thisDoc = (setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))

if __name__ == '__main__':
    testingNB()

Глядя на результаты, вы можете видеть, что два документа классифицированы правильно. Пожалуйста, смотрите полный код:

github:naive_bayes

Суммировать

  • Наивная байесовская классификация
  • Условная возможность
  • теорема Байеса
  • Принцип предположения условной независимости характеристик
  • Создание векторов слов из документов
  • Модель набора слов и модель набора слов
  • Вероятность равна 0, улучшение удобства вычислений и улучшение логарифмирования для предотвращения переполнения.

Справочная статья:
Наивный байесовский алгоритм машинного обучения (NB) и реализация Python