Простой и понятный принцип и код CRF

NLP

1. Принцип ИРК

1.1 Пример CRF

CRF просто означает, удовлетворяют ли соседние переменные на карте вероятностей характеристической функции[公式]Модель , такая как пример ниже, представляет собой CRF-приложение для идентификации продавца. Для ввода адреса продавца, ключевых слов имени, сферы деятельности и другой информации используйте метод маркировки BIOS, отмеченный следующим образом:

  • Функция передаточной характеристики:t(yi1,yi,x,i)t(y_{i-1}, y_i, x, i)

  • Функция функции состояния:s(yi,x,i)s(y_i, x, i)

  • Характеристическая функция перехода (t) принимает четыре параметра, а характеристическая функция состояния (s) принимает три параметра:

    • xx, предложение обозначить частью речи
    • ii, используется для представления i-го слова в предложении s
    • yiy_i, указывающий на часть речи тега i-го слова
    • yi1y_{i-1}, представляющий часть речи тега i-1-го слова

    Его выходное значение равно 0 или 1, 0 указывает, что оцениваемая последовательность меток не соответствует этому признаку, 1 указывает, что оцениваемая последовательность меток соответствует этому признаку,λ,мю\lambda, \mu- веса функции признаков перехода t и функции признаков состояния s соответственно.

1.2 В приведенной выше задаче идентификации продавца

  • За ключевым словом бизнеса следует сфера бизнеса, мы можем дать положительную оценку и передать характеристическую функцию: (I-KEYWORDS B-BUSINESS)

    t(yi1="KEYWORDS",yi="BUSINESS",x,i)t(y_{i-1}="KEYWORDS", y_{i}="BUSINESS", x, i) = 1;

  • отметить мега какKEYWORDS , мы можем дать положительную оценку функции признаков состояния:

    s(yi="KEYWORDS",x="Мега",i)=1s(y_i="КЛЮЧЕВЫЕ СЛОВА", x="МЕГА", я)=1

1.3 Параметризация вышеуказанного процесса

score(yx)=i,kλktk(yi1,yi,x,i)+i,lмюlsl(yi,x,i)score(y|x) = \sum_{i,k}^{}{\lambda_{k}}t_{k}(y_{i-1},y_{i},x,i)+\sum_{i,l}^{}{\mu_{l}}s_{l}(y_{i},x,i)

Вероятностный (с использованием функции softmax):

P(yx)=1Z(x)exp(i,kλktk(yi1,yi,x,i)+i,lмюlsl(yi,x,i))P(y|x) = \frac{1}{Z(x)}exp( \sum_{i,k}^{}{\lambda_{k}}t_{k}(y_{i-1},y_{i},x,i)+\sum_{i,l}^{}{\mu_{l}}s_{l}(y_{i},x,i) )

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

P(yx)=1Z(x)exp(wF(y,x))P(y|x) = \frac{1}{Z(x)}exp( w\cdot F(y,x) )

Среди них,Z(x)Z(x)используется для нормализации,Z(x)=yexp(i,kλktk(yi1,yi,x,i)+i,lмюlsl(yi,x,i))Z(x)= \sum_y exp( \sum_{i,k}^{}{\lambda_{k}}t_{k}(y_{i-1},y_{i},x,i)+\sum_{i,l}^{}{\mu_{l}}s_{l}(y_{i},x,i) )

2. Построение функции CRF

В модели CRF задействованы следующие два типа шаблонов объектов:

2.1 функции базового класса, то есть часто используемые функции в модели CRF, включая следующие 4 категории:

  • это число

    • Будь то английские цифры: 1-10

    • Китайские цифры: «один», «два», «три», «четыре», «пять», «шесть», «семь», «восемь», «девять», «десять».

    • Традиционные китайские числа: «A», «B», «C», «D», «Wu», «Ji», «Geng», «Xin», «Ren», «Kui».

  • верхний регистр Нижний регистр

  • Начинается ли текст / заканчивается ли текст

  • Является ли первое соседнее слово строчным/прописным; является ли второе соседнее слово строчным/прописным

2.2 Возможности класса Ngrams

Сам Ngram также относится к набору, состоящему из N слов или слов, каждое слово или слово имеет последовательность, и они не обязаны отличаться друг от друга

3. Применение CRF в NER

CRF широко используется для маркировки последовательностей Пакет sklearn-crfsuite используется ниже для реализации модели маркировки последовательностей CRF с четырех аспектов: импорт данных, создание признаков, обучение и оценка. Код можно запускать напрямую

3.1 Подготовка данных

импортировать зависимости

import sklearn
import scipy.stats

import sklearn_crfsuite
from sklearn_crfsuite import scorers
from sklearn_crfsuite import metrics

Загрузите данные CoNLL 2002 с помощью nltk

import nltk
nltk.download('conll2002')

----------------- # 分割线下是输出,不是代码
>>> [nltk_data] Downloading package conll2002 to /root/nltk_data...
    [nltk_data]   Package conll2002 is already up-to-date!
    True

Загрузить данные conll2002

%%time
train_sents = list(nltk.corpus.conll2002.iob_sents('esp.train'))
test_sents = list(nltk.corpus.conll2002.iob_sents('esp.testb'))

Просмотр фрагмента данных

train_sents[0]

----------------- # 分割线下是输出,不是代码
>>> [('Melbourne', 'NP', 'B-LOC'),
    ('(', 'Fpa', 'O'),
    ('Australia', 'NP', 'B-LOC'),
    (')', 'Fpt', 'O'),
    (',', 'Fc', 'O'),
    ('25', 'Z', 'O'),
    ('may', 'NC', 'O'),
    ('(', 'Fpa', 'O'),
    ('EFE', 'NC', 'B-ORG'),
    (')', 'Fpt', 'O'),
    ('.', 'Fp', 'O')]

Обычно наши данные имеют только текст и аннотации NER, поэтому мы берем только текст вышеуказанных данных и аннотации в формате BIOS и просматриваем часть данных.

train_sents_ner = [[(i[0], i[2])  for i in row] for row in train_sents]
test_sents_ner = [[(i[0], i[2])  for i in row] for row in test_sents]
train_sents_ner[0]

----------------- # 分割线下是输出,不是代码
>>> [('Melbourne', 'B-LOC'),
    ('(', 'O'),
    ('Australia', 'B-LOC'),
    (')', 'O'),
    (',', 'O'),
    ('25', 'O'),
    ('may', 'O'),
    ('(', 'O'),
    ('EFE', 'B-ORG'),
    (')', 'O'),
    ('.', 'O')]

3.2 Генерация признаков

Создавайте шаблоны, используя функции из официальной документации

def word2features(sent, i):
    word = sent[i][0]
    postag = sent[i][1]

    features = {
        'bias': 1.0,
        'word.lower()': word.lower(),
        'word[-3:]': word[-3:],
        'word[-2:]': word[-2:],
        'word.isupper()': word.isupper(),
        'word.istitle()': word.istitle(),
        'word.isdigit()': word.isdigit()
    }
    if i > 0:
        word1 = sent[i-1][0]
        features.update({
            '-1:word.lower()': word1.lower(),
            '-1:word.istitle()': word1.istitle(),
            '-1:word.isupper()': word1.isupper()
        })
    else:
        features['BOS'] = True

    if i < len(sent)-1:
        word1 = sent[i+1][0]
        features.update({
            '+1:word.lower()': word1.lower(),
            '+1:word.istitle()': word1.istitle(),
            '+1:word.isupper()': word1.isupper()
        })
    else:
        features['EOS'] = True

    return features


def sent2features(sent):
    return [word2features(sent, i) for i in range(len(sent))]

def sent2labels(sent):
    return [label for token, label in sent]

def sent2tokens(sent):
    return [token for token, label in sent]

Посмотрите, как выглядит преобразованный элемент

sent2features(train_sents_ner[0])[2]
# 第一条训练数据中,第3个单词(Australia)的特征如下

----------------- # 分割线下是输出,不是代码
>>>{'+1:word.istitle()': False,
    '+1:word.isupper()': False,
    '+1:word.lower()': ')',
    '-1:word.istitle()': False,
    '-1:word.isupper()': False,
    '-1:word.lower()': '(',
    'bias': 1.0,
    'word.isdigit()': False,
    'word.istitle()': True,
    'word.isupper()': False,
    'word.lower()': 'australia',
    'word[-2:]': 'ia',
    'word[-3:]': 'lia'}

Преобразование как обучающих данных, так и тестовых данных в их представления функций

X_train = [sent2features(s) for s in train_sents_ner]
y_train = [sent2labels(s) for s in train_sents_ner]

X_test = [sent2features(s) for s in test_sents_ner]
y_test = [sent2labels(s) for s in test_sents_ner]

3.3 Обучение модели

%%time
crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    c1=0.1,
    c2=0.1,
    max_iterations=100,
    all_possible_transitions=True)
    
crf.fit(X_train, y_train)

----------------- # 分割线下是输出,不是代码
>>> CPU times: user 35 s, sys: 21.8 ms, total: 35.1 s
    Wall time: 35.1 s

3.4 Предсказание модели

labels = list(crf.classes_)
labels.remove('O')
labels

----------------- # 分割线下是输出,不是代码
>>> ['B-LOC', 'B-ORG', 'B-PER', 'I-PER', 'B-MISC', 'I-ORG', 'I-LOC', 'I-MISC']
y_pred = crf.predict(X_test)
metrics.flat_f1_score(y_test, y_pred, average='weighted', labels=labels)

----------------- # 分割线下是输出,不是代码
>>> 0.7860514251609507
# group B and I results
sorted_labels = sorted(labels,key=lambda name: (name[1:], name[0]))

print(metrics.flat_classification_report(
    y_test, y_pred, labels=sorted_labels, digits=3
))

----------------- # 分割线下是输出,不是代码
>>>             precision   recall   f1-score   support

       B-LOC      0.800     0.778     0.789      1084
       I-LOC      0.672     0.631     0.651       325
      B-MISC      0.721     0.534     0.614       339
      I-MISC      0.686     0.582     0.630       557
       B-ORG      0.804     0.821     0.812      1400
       I-ORG      0.846     0.776     0.810      1104
       B-PER      0.832     0.865     0.849       735
       I-PER      0.884     0.935     0.909       634

   micro avg      0.803     0.775     0.789      6178
   macro avg      0.781     0.740     0.758      6178
weighted avg      0.800     0.775     0.786      6178