Пометка последовательности на уровне вложения абзаца / предложения с использованием Bi-LSTM + CRF с Keras - PullRequest
1 голос
/ 29 марта 2019

Я работаю над задачей маркировки последовательности, в которой тегируемый элемент - это предложения (или абзац). Большинство реализаций, которые я нашел, представляют настоящее решение на уровне токенов (NER, POS-Tagging и т. Д.), Тогда как здесь мне сначала нужно создать встраивание абзаца перед выполнением маркировки последовательности.

На самом деле есть два шага: первая модель строит плотное представление (с вниманием) из вложения слова Первая модель принимает входные данные (# пакетный размер, #words в абзаце)

вторая модель имеет следующую форму: - (# размер пакета, # параграфы, # слова) берет выходные данные первой модели и применяет архитектуру Bi-LSTM + CRF поверх нее, чтобы пометить каждый абзац данного документа.

import keras 
import keras.backend as K
from keras.layers import *
from keras.activations import *
from keras.regularizers import *
from keras.initializers import *

from keras.models import Model
from keras.layers import Embedding, Input, Bidirectional, GRU, LSTM, TimeDistributed, Layer
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.text import Tokenizer, text_to_word_sequence
from keras_contrib.layers import CRF
from keras_contrib.metrics import crf_accuracy
from keras_contrib.losses import crf_loss
from keras_contrib.metrics import crf_viterbi_accuracy

MAX_PARAGRAPHS = 200 # number of max paragraphs (ie. sequences)
MAX_PARAGRAPH_LENGTH = 60 # number of max token in a given sequences 
EMBEDDING_DIM = 100 # word embedding dimension 
PARAGRAPH_DIM = 100 # paragraph/sentence embedding dimension
MAX_NB_WORDS = 5000 # max nb words in the vocabulary 

n_tags = 5 #number of classes

class AttLayer(Layer):
    def __init__(self, attention_dim):
        self.init = initializers.get('normal')
        self.supports_masking = True
        self.attention_dim = attention_dim
        super(AttLayer, self).__init__()

    def build(self, input_shape):
        assert len(input_shape) == 3
        self.W = K.variable(self.init((input_shape[-1], self.attention_dim)))
        self.b = K.variable(self.init((self.attention_dim, )))
        self.u = K.variable(self.init((self.attention_dim, 1)))
        self.trainable_weights = [self.W, self.b, self.u]
        super(AttLayer, self).build(input_shape)

    def compute_mask(self, inputs, mask=None):
        return mask

    def call(self, x, mask=None):
        # size of x :[batch_size, sel_len, attention_dim]
        # size of u :[batch_size, attention_dim]
        # uit = tanh(xW+b)
        uit = K.tanh(K.bias_add(K.dot(x, self.W), self.b))
        ait = K.dot(uit, self.u)
        ait = K.squeeze(ait, -1)

        ait = K.exp(ait)

        if mask is not None:
            # Cast the mask to floatX to avoid float64 upcasting in theano
            ait *= K.cast(mask, K.floatx())
        ait /= K.cast(K.sum(ait, axis=1, keepdims=True) + K.epsilon(), K.floatx())
        ait = K.expand_dims(ait)
        weighted_input = x * ait
        output = K.sum(weighted_input, axis=1)

        return output

    def compute_output_shape(self, input_shape):
        return (input_shape[0], input_shape[-1])



tokenizer = Tokenizer(nb_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(flatten_texts)

vocab_size = len(tokenizer.word_index) + 1
embedding_layer = Embedding(vocab_size,
                            EMBEDDING_DIM,
                            #weights=[embedding_matrix],
                            input_length=MAX_PARAGRAPH_LENGTH,
                            trainable=True,
                            mask_zero=True)

paragraph_input = Input(shape=(MAX_PARAGRAPH_LENGTH,), dtype='int32')
embedded_sequences = embedding_layer(paragraph_input)
l_lstm = Bidirectional(GRU(100, return_sequences=True))(embedded_sequences)
l_att = AttLayer(60)(l_lstm)
paragraph_encoder = Model(paragraph_input, l_att)

sequence_input = Input(shape=(MAX_PARAGRAPHS, MAX_PARAGRAPH_LENGTH), dtype='int32')

parag_encoder = TimeDistributed(paragraph_encoder)(sequence_input)
bi_lstm = Bidirectional(LSTM(units=128, return_sequences=True,recurrent_dropout=0.2))(parag_encoder)  # variational biLSTM
final_dense = TimeDistributed(Dense(128, activation="relu"))(bi_lstm)
crf = CRF(n_tags, sparse_target=True)  # CRF layer
out = crf(final_dense)  # output
model = Model(sequence_input, out)
model.compile(optimizer="rmsprop", loss=crf_loss, metrics=[crf_accuracy])

компиляция модели приводит к следующей ошибке:

AssertionError: маска ввода для CRF должна иметь dim 2, если не None

Я использую слой CRF из пакета keras_contrib

Не могли бы вы сказать мне, что я делаю не так?

1 Ответ

1 голос
/ 02 апреля 2019

Это, кажется, заставляет его работать:

final_dense = TimeDistributed(Dense(128, activation="relu"))(bi_lstm)
final_dense = Masking()(final_dense) # insert this
crf = CRF(n_tags, sparse_target=True)  # CRF layer
out = crf(final_dense)

Но не совсем уверен, почему.

...