Скрытые марковские модели для классификации

Генеративные и дискриминативные классификаторы

Здравствуйте и вновь добро пожаловать на занятия по теме «Машинное обучение без учителя: скрытые марковские модели на языке Python».

В этой статье мы продолжим наше обсуждение из первой части. Во введении я показал, что несмотря на то, что скрытые марковские модели относятся к моделям обучения без учителя, поскольку моделируют вероятности, их можно «развернуть» и использовать в качестве классификатора с помощью теоремы Байеса. На самом деле у этого типа классификаторов есть своё название – генеративные модели, поскольку в них мы берём плотность вероятности класса и предполагаем, что они генерируют наблюдаемые данные.

Сравните это с дискриминативными моделями, такими как логистическая регрессия и глубокие нейронные сети. Напомним, что эти модели дают вероятность класса исходя из прямого ввода данных; там нет никакого моделирования вероятности правдоподобия.

Иногда эти два типа моделей пересекаются. Как вы помните из моего курса по логистической регрессии, мы не можем прямо найти весовые коэффициенты, а потому вынуждены пользоваться методами вроде градиентного спуска. Я показал, что из этого правила есть исключение, когда предполагается, что каждый класс имеет одинаковую ковариацию, но разные средние значения, и все они подчиняются гауссовому распределению. В таком случае весовые коэффициенты можно найти напрямую. Но если мы знаем, что оба класса подчиняются гауссовому распределению, и знаем их средние значения и ковариацию, то мы знаем и функцию правдоподобия. Следовательно, это также генеративная модель. В данном случае генеративная и дискриминативная модели пересекаются.

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

Одним из недостатков глубокого обучения является то, что очень трудно объяснить, что именно происходит или какие именно признаки были изучены. Нет такого графика, на который можно указать и заявить, что существует тесная взаимосвязь между вот этими двумя узлами, представляющими что-то в физической реальности. Это хорошо, поскольку зачастую нас заботит точность и функциональность, а не простая взаимосвязь между входными переменными. Сила нейронных сетей в том, что они моделируют сложные взаимосвязи. Но, к сожалению, эта сложность также означает, что они не могут быть объяснены в понятиях, которые бы удовлетворили, скажем, статистиков.

Классификация поэзии с помощью скрытой марковской модели. Роберт Фрост и Эдгар Аллан По

А сейчас мы создадим классификатор на основе скрытой марковской модели. Мы возьмём стихи, уже анализировавшиеся ранее, – стихи Роберта Фрота, и попытаемся провести различие между ними и стихами Эдгара Аллана По.

import string

import numpy as np

import matplotlib.pyplot as plt

from hmmd_theano import HMM

from sklearn.utils import shuffle

from nltk import pos_tag, word_tokenize

Начнём с функции main. Предположим, у нас есть функция get_data, возвращающая X, Y и размер словаря V. На самом деле мы не будем напрямую использовать слова, поскольку их очень много, а вместо этого воспользуемся разметкой по частям речи. N – количество примеров – установим равным 20.

def main():

    X, Y, V = get_data()

    X, Y = shuffle(X, Y)

    N = 20

    Xtrain, Ytrain = X[:-N], Y[:-N]

    Xtest, Ytest = X[-N:], Y[-N:]

    model = HMMClassifier()

    model.fit(Xtrain, Ytrain, V)

    print(“Score:”, model.score(Xtest, Ytest))

if __name__ == ‘__main__’:

main()

Теперь напишем саму эту функцию get_data. X – это будут наши последовательности, а Y – метки.

def get_data():

    word2idx = {}

    current_idx = 0

    X = []

    Y = []

    for fn, label in zip((‘robert_frost.txt’, ‘edgar_allan_poe.txt’), (0, 1)):

        count = 0

        for line in open(fn):

            line = line.rstrip()

            if line:

                tokens = get_tags(line)

                if len(tokens) > 1:

                    for token in tokens:

                        if token not in word2idx:

                            word2idx[token] = current_idx

                            current_idx += 1

                    sequence = np.array([word2idx[w] for w in tokens])

                    X.append(sequence)

                    Y.append(label)

                    count += 1

                    print(count)

                    if count >= 50:

                        break

    print(“Vocabulary:”, word2idx.keys())

    return X, Y, current_idx

Теперь определим функцию get_tags, которую использовали выше.

def get_tags(s):

    tuples = pos_tag(word_tokenize(s))

    return [y for x, y in tuples]

Теперь мы готовы написать классификатор на основе скрытой марковской модели.

class HMMClassifier:

    def __init__(self):

        pass

Функция fit. K – количество классов, они пронумерованы от нуля до K-1.

    def fit(self, X, Y, V):

        K = len(set(Y))

        self.models = []

        self.priors = []

        for k in xrange(K):

            thisX = [x for x, y in zip(X, Y) if y == k]

            C = len(thisX)

            self.priors.append(np.log(C) – np.log(N))

            hmm = HMM(5)

            hmm.fit(thisX, V=V, p_cost=0.1, print_period=1, learning_rate=10e-5, max_iter=100)

            self.models.append(hmm)

Далее определим функцию score.

    def score(self, X, Y):

        N = len(Y)

        correct = 0

        for x, y in zip(X, Y):

            lls = [hmm.log_likelihood(x) + prior for hmm, prior in zip(self.models, self.priors)]

            p = np.argmax(lls)

            if p == y:

                correct += 1

        return float(correct) / N

Готово. Можем проверить.

Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: