Я пытаюсь реализовать иерархический преобразователь для классификации документов в Keras / tensorflow, в котором:
(1) преобразователь уровня слова создает представление каждого предложения и веса внимания для каждого слова, и,
(2) преобразователь уровня предложения использует выходные данные из (1) для создания представления каждого документа и веса внимания для каждого предложения, и, наконец,
(3) представления документов, созданные с помощью (2), используются для классификации документов (в следующем примере как принадлежащие или не принадлежащие данному классу).
Я пытаюсь смоделировать классификатор на основе Янга и др. подход здесь (https://www.cs.cmu.edu/~./hovy/papers/16HLT-hierarchical-attention-networks.pdf), но заменяя уровни ГРУ и внимания на трансформаторы.
Я использую реализацию трансформатора Apoorv Nandan из https://keras.io/examples/nlp/text_classification_with_transformer/.
У меня две проблемы, по которым я был бы признателен за помощь сообщества:
(1) Я получаю ошибку в модели верхнего (предложения) уровня что я не могу разрешить (подробности и код ниже)
(2) Я не знаю, как извлечь веса внимания на уровне слов и предложений, и ценю советы о том, как лучше сделать это.
Я новичок как в Керасе, так и в этом форуме, поэтому приношу извинения за очевидные ошибки и заранее благодарю вас за любую помощь.
Вот воспроизводимый пример, с указанием, где я обнаружил ошибки:
Сначала установите sh слои встраивания внимания, трансформатора и токена / позиции после Nandan.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import pandas as pd
import numpy as np
class MultiHeadSelfAttention(layers.Layer):
def __init__(self, embed_dim, num_heads=8):
super(MultiHeadSelfAttention, self).__init__()
self.embed_dim = embed_dim
self.num_heads = num_heads
if embed_dim % num_heads != 0:
raise ValueError(
f"embedding dimension = {embed_dim} should be divisible by number of heads = {num_heads}"
)
self.projection_dim = embed_dim // num_heads
self.query_dense = layers.Dense(embed_dim)
self.key_dense = layers.Dense(embed_dim)
self.value_dense = layers.Dense(embed_dim)
self.combine_heads = layers.Dense(embed_dim)
def attention(self, query, key, value):
score = tf.matmul(query, key, transpose_b=True)
dim_key = tf.cast(tf.shape(key)[-1], tf.float32)
scaled_score = score / tf.math.sqrt(dim_key)
weights = tf.nn.softmax(scaled_score, axis=-1)
output = tf.matmul(weights, value)
return output, weights
def separate_heads(self, x, batch_size):
x = tf.reshape(x, (batch_size, -1, self.num_heads, self.projection_dim))
return tf.transpose(x, perm=[0, 2, 1, 3])
def call(self, inputs):
# x.shape = [batch_size, seq_len, embedding_dim]
batch_size = tf.shape(inputs)[0]
query = self.query_dense(inputs) # (batch_size, seq_len, embed_dim)
key = self.key_dense(inputs) # (batch_size, seq_len, embed_dim)
value = self.value_dense(inputs) # (batch_size, seq_len, embed_dim)
query = self.separate_heads(
query, batch_size
) # (batch_size, num_heads, seq_len, projection_dim)
key = self.separate_heads(
key, batch_size
) # (batch_size, num_heads, seq_len, projection_dim)
value = self.separate_heads(
value, batch_size
) # (batch_size, num_heads, seq_len, projection_dim)
attention, weights = self.attention(query, key, value)
attention = tf.transpose(
attention, perm=[0, 2, 1, 3]
) # (batch_size, seq_len, num_heads, projection_dim)
concat_attention = tf.reshape(
attention, (batch_size, -1, self.embed_dim)
) # (batch_size, seq_len, embed_dim)
output = self.combine_heads(
concat_attention
) # (batch_size, seq_len, embed_dim)
return output
class TransformerBlock(layers.Layer):
def __init__(self, embed_dim, num_heads, ff_dim, dropout_rate, name=None):
super(TransformerBlock, self).__init__(name=name)
self.att = MultiHeadSelfAttention(embed_dim, num_heads)
self.ffn = keras.Sequential(
[layers.Dense(ff_dim, activation="relu"), layers.Dense(embed_dim),]
)
self.layernorm1 = layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = layers.LayerNormalization(epsilon=1e-6)
self.dropout1 = layers.Dropout(dropout_rate)
self.dropout2 = layers.Dropout(dropout_rate)
def call(self, inputs, training):
attn_output = self.att(inputs)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(inputs + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
return self.layernorm2(out1 + ffn_output)
class TokenAndPositionEmbedding(layers.Layer):
def __init__(self, maxlen, vocab_size, embed_dim, name=None):
super(TokenAndPositionEmbedding, self).__init__(name=name)
self.token_emb = layers.Embedding(input_dim=vocab_size, output_dim=embed_dim)
self.pos_emb = layers.Embedding(input_dim=maxlen, output_dim=embed_dim)
def call(self, x):
maxlen = tf.shape(x)[-1]
positions = tf.range(start=0, limit=maxlen, delta=1)
positions = self.pos_emb(positions)
x = self.token_emb(x)
return x + positions
Для этого Например, данные представляют собой 10 000 документов, каждый из которых усечен до 15 предложений, каждое предложение содержит не более 60 слов, которые уже преобразованы в целочисленные токены 1-1000.
X - это трехмерный тензор (10000, 15, 60), содержащий эти токены. y - одномерный тензор, содержащий классы документов (1 или 0). Для целей этого примера нет никакой связи между X и y.
Следующее дает данные примера:
max_docs = 10000
max_sentences = 15
max_words = 60
X = tf.random.uniform(shape=(max_docs, max_sentences, max_words), minval=1, maxval=1000, dtype=tf.dtypes.int32, seed=1)
y = tf.random.uniform(shape=(max_docs,), minval=0, maxval=2, dtype=tf.dtypes.int32, seed=1)
Здесь я пытаюсь построить кодировщик уровня слов после https://keras.io/examples/nlp/text_classification_with_transformer/:
# Lower level (produce a representation of each sentence):
embed_dim = 100 # Embedding size for each token
num_heads = 2 # Number of attention heads
ff_dim = 64 # Hidden layer size in feed forward network inside transformer
L1_dense_units = 100 # Size of the sentence-level representations output by the word-level model
dropout_rate = 0.1
vocab_size=1000
word_input = layers.Input(shape=(max_words,), name='word_input')
word_embedding = TokenAndPositionEmbedding(maxlen=max_words, vocab_size=vocab_size,
embed_dim=embed_dim, name='word_embedding')(word_input)
word_transformer = TransformerBlock(embed_dim=embed_dim, num_heads=num_heads, ff_dim=ff_dim,
dropout_rate=dropout_rate, name='word_transformer')(word_embedding)
word_pool = layers.GlobalAveragePooling1D(name='word_pooling')(word_transformer)
word_drop = layers.Dropout(dropout_rate,name='word_drop')(word_pool)
word_dense = layers.Dense(L1_dense_units, activation="relu",name='word_dense')(word_drop)
word_encoder = keras.Model(word_input, word_dense)
word_encoder.summary()
Похоже, что этот кодировщик слов работает так, как задумано, для создания представления каждого предложения. Здесь, запустите 1-й документ, он создает тензор формы (15, 100), содержащий векторы, представляющие каждое из 15 предложений:
word_encoder(X[0]).shape
Моя проблема заключается в том, чтобы связать это с высшим (предложением) модель уровня для создания представлений документа.
Я получаю сообщение об ошибке NotImplementedError при попытке применить кодировщик слов к каждому предложению в документе. Я был бы благодарен за любую помощь в устранении этой проблемы, поскольку сообщение об ошибке не информативно относительно конкретной проблемы c.
После применения кодировщика слов к каждому предложению цель состоит в том, чтобы применить другой преобразователь для получения весов внимания для каждого предложения и представления на уровне документа, с помощью которого будет выполняться классификация. Я не могу определить, будет ли эта часть модели работать из-за указанной выше ошибки.
Наконец, я хотел бы извлечь веса внимания на уровне слов и предложений для каждого документа, и я бы благодарен за совет о том, как это сделать.
Заранее благодарим вас за любую информацию.
# Upper level (produce a representation of each document):
L2_dense_units = 100
sentence_input = layers.Input(shape=(max_sentences, max_words), name='sentence_input')
# This is the line producing "NotImplementedError":
sentence_encoder = tf.keras.layers.TimeDistributed(word_encoder, name='sentence_encoder')(sentence_input)
sentence_transformer = TransformerBlock(embed_dim=L1_dense_units, num_heads=num_heads, ff_dim=ff_dim,
dropout_rate=dropout_rate, name='sentence_transformer')(sentence_encoder)
sentence_dense = layers.TimeDistributed(Dense(int(L2_dense_units)),name='sentence_dense')(sentence_transformer)
sentence_out = layers.Dropout(dropout_rate)(sentence_dense)
preds = layers.Dense(1, activation='sigmoid', name='sentence_output')(sentence_out)
model = keras.Model(sentence_input, preds)
model.summary()