В настоящее время я пытаюсь построить модель в keras, вдохновленную HiCE (https://github.com/acbull/HiCE), где в основном вводится набор входных предложений, используется блок самообслуживания для создания представления предложения. (на основе слов) для каждого предложения, а затем для объединения каждого предложения используется другой блок самовосприятия (цель состоит в том, чтобы вывести вложение слова, оценивая слово из словаря). Однако моя модель работает очень плохо. После некоторого исследования я обнаружил, что проблема заключается во втором блоке внимания, но я не могу понять, что происходит не так. Кто-нибудь знает, где это может быть? Я использую эту реализацию преобразователя для внимания: https://github.com/CyberZHG/keras-transformer
Вот (сокращенная) версия моей модели:
context_encoder:
word_embs = np.zeros((self.vocab_size+1, self.word_emb_dim)) #MASK
largest_ind = 0
for word in self.word2id_dict:
ind = self.word2id_dict[word]
#print(ind)
if ind > largest_ind:
largest_ind = ind
w_emb = self.word_embedding_model.wv[word]
word_embs[ind] = w_emb
emb_layer = EmbeddingRet(
input_dim=self.vocab_size+1,
output_dim=self.word_emb_dim,
mask_zero=True,
weights=[word_embs],
trainable=False,
name='Encoder-Token-Embedding',
)
emb = emb_layer(context)
emb2 = emb[0]
encoder_embed = emb2 #Removed positional embedding instead!
sa_context_encoder = transformer.get_encoders(self.encoder_num, encoder_embed, self.head_num, self.hidden_dim)
context_emb = SumInternal()(sa_context_encoder)
context_encoder = Model(inputs=[context], outputs=context_emb)
context_encoder.summary()
return context_encoder
полная модель:
contexts = Input(shape=(self.max_num_context, self.max_num_words_per_context, ), dtype=tf.int64)
context_embs = TimeDistributed(self.context_encoder)(contexts)
context_embs = ZeroVectorMasker()(context_embs) #added to rebuild mask
aggregator_encoder_out = transformer.get_encoders(self.encoder_num, context_embs, self.head_num, self.hidden_dim)
final_estimate = MeanInternal()(aggregator_encoder_out)
model = Model(inputs=[contexts], outputs=final_estimate)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=[])
model.summary()
return model
пользовательские слои:
класс SumInternal (Слой):
def __init__(self, **kwargs):
super(SumInternal, self).__init__(**kwargs)
def build(self, input_shape):
# Create a trainable weight variable for this layer.
super(SumInternal, self).build(input_shape) # Be sure to call this at the end
def compute_mask(self, inputs, mask=None):
return None #We don't need the current masking after this step!
def call(self, x, mask):
mask = K.cast(mask, dtype = "float32")
mask = tf.expand_dims(mask, -1)
masked_vecs = x * mask
final_sum = K.sum(masked_vecs, axis=1, keepdims=False)
return final_sum
def compute_output_shape(self, input_shape):
return (input_shape[0], input_shape[2])
класс MeanInternal (Слой):
def __init__(self, **kwargs):
super(MeanInternal, self).__init__(**kwargs)
def build(self, input_shape):
super(MeanInternal, self).build(input_shape) # Be sure to call this at the end
def compute_mask(self, inputs, mask=None):
return None
def call(self, x, mask):
mask = K.cast(mask, dtype = "float32")
mask = tf.expand_dims(mask, -1)
masked_vecs = x * mask
div = tf.count_nonzero(mask, axis=1, dtype = "float32")
sum = K.sum(masked_vecs, axis=1, keepdims=False)
mean = sum / div
return mean
def compute_output_shape(self, input_shape):
return (input_shape[0], input_shape[2])
класс ZeroVectorMasker (Layer):
def __init__(self, **kwargs):
super(ZeroVectorMasker, self).__init__(**kwargs)
def build(self, input_shape):
# Create a trainable weight variable for this layer.
super(ZeroVectorMasker, self).build(input_shape) # Be sure to call this at the end
def compute_mask(self, inputs, mask=None):
#calculate zero vector masks
zero_vector_mask = tf.not_equal(tf.count_nonzero(inputs, axis=2), 0)
#zero_vector_mask = K.cast(zero_vector_mask, dtype = "bool")
zero_vector_mask = tf.Print(zero_vector_mask, [zero_vector_mask], message='Value of Zero Vec Mask !!!', summarize=100)
return zero_vector_mask #We don't need the current masking after this step!
def call(self, x, mask=None):
#here we ignore the mask, the compute_mask will output the real one (based on 0 vectors inputed through here)
print('0vec call')
print(x)
#print(mask)
return x
def compute_output_shape(self, input_shape):
return input_shape