I have a document classification task, that classifies documents as good (1) or bad (0), and I use some sentence embeddings for each document to classify the documents accordingly.
What I like to do is retrieving the attention scores for each document, to obtain the most "relevant" sentences (i.e., those with high attention scores)
I padded each document to the same length (i.e., 1000 sentences per document). So my tensor for 5000 documents looks like this X = np.ones(shape=(5000, 1000, 200))
(5000 documents with each having a 1000 sequence of sentence vectors and each sentence vector consisting of 200 features).
My network looks like this:
no_sentences_per_doc = 1000
sentence_embedding = 200
sequence_input = Input(shape=(no_sentences_per_doc, sentence_embedding))
gru_layer = Bidirectional(GRU(50,
return_sequences=True
))(sequence_input)
sent_dense = Dense(100, activation='relu', name='sent_dense')(gru_layer)
sent_att,sent_coeffs = AttentionLayer(100,return_coefficients=True, name='sent_attention')(sent_dense)
preds = Dense(1, activation='sigmoid',name='output')(sent_att)
model = Model(sequence_input, preds)
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=[TruePositives(name='true_positives'),
TrueNegatives(name='true_negatives'),
FalseNegatives(name='false_negatives'),
FalsePositives(name='false_positives')
])
history = model.fit(X, y, validation_data=(x_val, y_val), epochs=10, batch_size=32)
After training I retrieved the attention scores as follows:
sent_att_weights = Model(inputs=sequence_input,outputs=sent_coeffs)
## load a single sample
## from file with 150 sentences (one sentence per line)
## each sentence consisting of 200 features
x_sample = np.load(x_sample)
## and reshape to (1, 1000, 200)
x_sample = x_sample.reshape(1,1000,200)
output_array = sent_att_weights.predict(x_sample)
However, if I show the top 3 attention scores for the sentences, I also obtain sentence indices that are, for example, [432, 434, 999]
for a document that has only 150 sentences (the rest is padded, i.e., just zeros).
Does that make sense or am I doing something wrong here? (is there a mistake in my attention layer? Or is due to a low F-score?)
The attention layer I use is the following:
class AttentionLayer(Layer):
"""
https://humboldt-wi.github.io/blog/research/information_systems_1819/group5_han/
"""
def __init__(self,attention_dim=100,return_coefficients=False,**kwargs):
# Initializer
self.supports_masking = True
self.return_coefficients = return_coefficients
self.init = initializers.get('glorot_uniform') # initializes values with uniform distribution
self.attention_dim = attention_dim
super(AttentionLayer, self).__init__(**kwargs)
def build(self, input_shape):
# Builds all weights
# W = Weight matrix, b = bias vector, u = context vector
assert len(input_shape) == 3
self.W = K.variable(self.init((input_shape[-1], self.attention_dim)),name='W')
self.b = K.variable(self.init((self.attention_dim, )),name='b')
self.u = K.variable(self.init((self.attention_dim, 1)),name='u')
self.trainable_weights = [self.W, self.b, self.u]
super(AttentionLayer, self).build(input_shape)
def compute_mask(self, input, input_mask=None):
return None
def call(self, hit, mask=None):
# Here, the actual calculation is done
uit = K.bias_add(K.dot(hit, self.W),self.b)
uit = K.tanh(uit)
ait = K.dot(uit, self.u)
ait = K.squeeze(ait, -1)
ait = K.exp(ait)
if mask is not None:
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 = hit * ait
if self.return_coefficients:
return [K.sum(weighted_input, axis=1), ait]
else:
return K.sum(weighted_input, axis=1)
def compute_output_shape(self, input_shape):
if self.return_coefficients:
return [(input_shape[0], input_shape[-1]), (input_shape[0], input_shape[-1], 1)]
else:
return input_shape[0], input_shape[-1]
Note that I use keras
with tensorflow
backend version 2.1.; the attention layer was originally written for theano, but I use import tensorflow.keras.backend as K