Рубрики
Без рубрики

Условные случайные поля в Python – чередование последовательности (часть 4)

Быстрый тур по Python-Crfsuite для выполнения последовательности маркировки. Теги с Python, наукой данных, NLP.

Это четвертый пост в моей серии Последовательность маркировки в Python Найдите предыдущий здесь: Извлечение дополнительных характеристик . Получить код для этой серии на Гадость .

После того, как мы имеем наш набор данных со всеми функциями, которые мы хотим включить, а также все этикетки для наших последовательностей; Мы можем перейти к фактической подготовке нашего алгоритма. Для этой задачи мы будем использовать Python-Crfsuite Пакет, так что Pip-установите его (или используйте свой любимый менеджер пакета, чтобы получить его):

pip install python-crfsuite

После установки давайте загрузим наш набор набора данных:

import pandas as pd
import string

features_labels = pd.read_csv("data/features-labels.csv")
features_labels = features_labels[~features_labels['label'].isna()]
features_labels.head()

Нам также нужен функции помощника, которые работают на струнах; Вы увидите, как они используются немного дальше:

punctuation = set(string.punctuation)

def is_punctuation(token):
    return token in punctuation

def is_numeric(token):
    try:
        float(token.replace(",", ""))
        return True
    except:
        return False

Вход в pycrfsuite.

Входы в алгоритм должны соблюдать определенный формат, где каждый токен имеет свои функции, представленные пар ключа, каждый токен может также иметь разные функции на основе разных факторов, таких как его положение. Следующая функция принимает в DataFrame и возвращает соответствующие функции, которые могут потребляться методом обучения нашим алгоритмом:

def featurise(sentence_frame, current_idx):
    current_token = sentence_frame.iloc[current_idx]
    token = current_token['token']
    position = current_token['position']
    token_count = current_token['token_count']
    pos = current_token['pos_tag']

    # Shared features across tokens
    features = {
            'bias': True,
            'word.lower': token.lower(),
            'word.istitle': token.istitle(),
            'word.isdigit': is_numeric(token),
            'word.ispunct': is_punctuation(token),
            'word.position':position,
            'word.token_count': token_count,
            'postag': pos, 
    }

    if current_idx > 0: # The word is not the first one...
        prev_token = sentence_frame.iloc[current_idx-1]['token']
        prev_pos = sentence_frame.iloc[current_idx-1]['pos_tag']
        features.update({
            '-1:word.lower': prev_token.lower(),
            '-1:word.istitle':prev_token.istitle(),
            '-1:word.isdigit': is_numeric(prev_token),
            '-1:word.ispunct': is_punctuation(prev_token),
            '-1:postag':prev_pos 
        })
    else:
        features['BOS'] = True

    if current_idx < len(sentence_frame) - 1: # The word is not the last one...
        next_token = sentence_frame.iloc[current_idx+1]['token']
        next_tag = sentence_frame.iloc[current_idx+1]['pos_tag']
        features.update({
            '+1:word.lower': next_token.lower(),
            '+1:word.istitle': next_token.istitle(),
            '+1:word.isdigit': is_numeric(next_token),
            '+1:word.ispunct': is_punctuation(next_token),
            '+1:postag': next_tag 
        })
    else:
        features['EOS'] = True

    return features

featurise(offer_0, 1)

Смешив первый токен первого предложения, мы получаем следующее:

{'bias': True,
 'word.lower': 'cun',
 'word.istitle': False,
 'word.isdigit': False,
 'word.ispunct': False,
 'word.position': 1,
 'word.token_count': 11,
 'postag': 'np00000',
 '-1:word.lower': '¡',
 '-1:word.istitle': False,
 '-1:word.isdigit': False,
 '-1:word.ispunct': False,
 '-1:postag': 'faa',
 '+1:word.lower': 'a',
 '+1:word.istitle': False,
 '+1:word.isdigit': False,
 '+1:word.ispunct': False,
 '+1:postag': 'sp000'}

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

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

def featurize_sentence(sentence_frame):
    labels = list(sentence_frame['label'].values)
    features = [featurize(sentence_frame, i) for i in range(len(sentence_frame))]

    return features, labels

def rollup(dataset):
    sequences = []
    labels = []
    offers = dataset.groupby('offer_id')
    for name, group in offers:
        sqs, lbls = featurize_sentence(group)
        sequences.append(sqs)
        labels.append(lbls)

    return sequences, labels

all_sequences, all_labels = rollup(features_labels)

У нас сейчас есть в All_squences и all_labels Наши функции и их соответствующие этикетки готовы к обучению.

Обучение

В значительной степени похоже в любой другой контролируемой проблеме, нам нужно разделить наш набор набора тренировок на два (предпочтительно трех) наборов данных; Мы можем использовать rain_test_split. для этого:

from sklearn.model_selection import train_test_split

train_docs, test_docs, train_labels, test_labels = train_test_split(all_sequences, all_labels)

len(train_docs), len(test_docs)

Создание CRF.

Хотя можно использовать kklearn – как Интерфейс для создания, поезда и вывод с Python-Crfsuite, я решил использовать оригинальную упаковку и делать все «вручную».

Первый шаг – создать объект класса Тренер ; Тогда мы можем установить некоторые параметры для фазы тренировок, не стесняйтесь играть с ними, так как они могут улучшить качество теггера. Наконец, нам нужно пройти в наших учебных данных в алгоритме, и мы делаем это с помощью метода Добавить:

import pycrfsuite

trainer = pycrfsuite.Trainer(verbose=False)

trainer.set_params({
    'c1': 1.0,   # coefficient for L1 penalty
    'c2': 1e-3,  # coefficient for L2 penalty
    'max_iterations': 200, 

    'feature.possible_transitions': True
})


# We are feeding our training set to the algorithm here.
for xseq, yseq in zip(train_docs, train_labels):
    trainer.append(xseq, yseq)

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

trainer.train('model/vuelax-bad.crfsuite')

Маркировка «невидимых» последовательностей

Чтобы выполнить последовательность маркировки в случаях, когда наш алгоритм не видел во время тренировок, необходимо использовать объект Tagger класс, а затем загрузить нашу сохраненную модель в него, используя открыть метод.

crf_tagger = pycrfsuite.Tagger()
crf_tagger.open('model/vuelax-bad.crfsuite')

Помните, что каждое из предложений необходимо обрабатывать и помещать в формат, необходимый для Tagger для работы, это означает, что имеют те же функции, которые мы использовали для обучения. У нас уже есть это в нашем test_docs и мы можем использовать их напрямую:

predicted_tags = crf_tagger.tag(test_docs[2])
print("Predicted: ",predicted_tags)
print("Correct  : ",test_labels[2])

Результатом может показать, что в этом конкретном экземпляре Tagger не ошиблись:

Predicted:  ['n', 'o', 's', 'd', 'd', 'n', 'p', 'n']
Correct  :  ['n', 'o', 's', 'd', 'd', 'n', 'p', 'n']

Оценка теггера

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

from sklearn.metrics import classification_report

all_true, all_pred = [], []

for i in range(len(test_docs)):
    all_true.extend(test_labels[i])
    all_pred.extend(crf_tagger.tag(test_docs[i]))

print(classification_report(all_true, all_pred))

Должен дать вам результат, похожий на …

              precision    recall  f1-score   support

           d       0.96      0.99      0.97        98
           f       1.00      1.00      1.00        10
           n       1.00      1.00      1.00       831
           o       1.00      1.00      1.00        80
           p       1.00      1.00      1.00        60
           s       1.00      1.00      1.00        60

    accuracy                           1.00      1139
   macro avg       0.99      1.00      1.00      1139
weighted avg       1.00      1.00      1.00      1139

Наш алгоритм выступает очень, очень хорошо.

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

Как всегда, не стесняйтесь задавать несколько вопросов, если у вас есть их, оставив комментарий здесь или связаться со мной в Twitter через @io_Exception Отказ

Оригинал: “https://dev.to/fferegrino/conditional-random-fields-in-python-sequence-labelling-part-4-5ei2”