Keras:计算两个平坦输出之间的余弦距离
我的代码 我有以下结构的模型:两个LSTM(问答)和额外的注意层,可以考虑在答案之上。以下是使用sum和softmax比较两个输出的版本: 这里的网络正在工作,但纯和+softmax是一个错误的选择,不能提供所需的结果。我想要的是在Keras:计算两个平坦输出之间的余弦距离,keras,Keras,我的代码 我有以下结构的模型:两个LSTM(问答)和额外的注意层,可以考虑在答案之上。以下是使用sum和softmax比较两个输出的版本: 这里的网络正在工作,但纯和+softmax是一个错误的选择,不能提供所需的结果。我想要的是在qenc和attn之间使用余弦相似性,但是它们的形状是(无,48,32)(这些数字根据使用的数据而不同)。我想的是将两者都展平并使用余弦相似性,与0-1标签进行比较 问题是如何在那里使用余弦?我不能将qenc展平,因为在计算attn时,在Merge中使用它,并且在那
qenc
和attn
之间使用余弦相似性,但是它们的形状是(无,48,32)
(这些数字根据使用的数据而不同)。我想的是将两者都展平并使用余弦相似性,与0-1标签进行比较
问题是如何在那里使用余弦?我不能将qenc
展平,因为在计算attn
时,在Merge中使用它,并且在那里形状很重要。我试过:
Lambda-不起作用。我不接受序列模型,只接受层输出,它不是层,而是张量,所以不能添加
def cosine_distance(vests):
x, y = vests
x = K.batch_flatten(x)
y = K.batch_flatten(y)
x = K.l2_normalize(x, axis=-1)
y = K.l2_normalize(y, axis=-1)
return -K.mean(x * y, axis=-1)
model = Sequential()
model.add(Lambda(cosine_distance)([qenc.layers[-1].output,attn.layers[-1].output]))
中间展平模型-导致“合并对象没有批大小属性”或类似错误:
flattened_attn = Sequential()
flattened_attn.add(attn)
flattened_attn.add(Flatten())
flattened_qenc = ...
model = Sequential()
model.add(Merge([flattened_attn, flattned_qenc], mode="cos", dot_axes=1))
最后,我实现了通过shape(无,1536)
传递展平数据:
并得到了错误信息:
attn.add(Merge([qenc, attn], mode="cos", dot_axes=1))
Traceback (most recent call last):
File "qa-lstm-attn.py", line 175, in <module>
attn.add(Merge([qenc, attn], mode="cos", dot_axes=1))
File "/home/hcl/.local/lib/python3.5/site-packages/keras/models.py", line 492, in add
output_tensor = layer(self.outputs[0])
File "/home/hcl/.local/lib/python3.5/site-packages/keras/engine/topology.py", line 617, in __call__
output = self.call(inputs, **kwargs)
File "/home/hcl/.local/lib/python3.5/site-packages/keras/legacy/layers.py", line 202, in call
'(at least 2). Got: ' + str(inputs))
TypeError: Merge must be called on a list of tensors (at least 2). Got: Tensor("flatten_3/Reshape:0", shape=(?, ?), dtype=float32)
>>> qenc.output_shape
(None, 1536)
>>> aenc.output_shape
(None, 48, 32)
>>> attn.output_shape
(None, 1536)
错误消息:
File "qa-lstm-attn.py", line 195, in <module>
callbacks=[checkpoint])
File "/home/hcl/.local/lib/python3.5/site-packages/keras/models.py", line 963, in fit
validation_steps=validation_steps)
File "/home/hcl/.local/lib/python3.5/site-packages/keras/engine/training.py", line 1637, in fit
batch_size=batch_size)
File "/home/hcl/.local/lib/python3.5/site-packages/keras/engine/training.py", line 1483, in _standardize_user_data
exception_prefix='input')
File "/home/hcl/.local/lib/python3.5/site-packages/keras/engine/training.py", line 86, in _standardize_input_data
str(len(data)) + ' arrays: ' + str(data)[:200] + '...')
ValueError: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 3 array(s), but instead got the following list of 2 arrays: [array([[ 1676, 19, 328, ..., 1612, 29, 4220],
[ 0, 0, 0, ..., 4, 27, 4807],
[ 2928, 9, 1652, ..., 125, 9, 181],
...,
[ 5970, 14...
我认为Keras不理解其中一个模型被重用,并期望得到更多的投入
我的模型实际上是这段代码的一个修改版本,它不能正常工作,因为模型总是学习回答False(作者对此提出警告):
编辑 @daniel-möller的解释:我想实现文章中的模型。只要模型计算问题和答案之间的余弦,我的标签就是0和1(答案匹配问题,而不是)。数据集由一个问题和4个答案变体组成,其中一个是正确的。下面是我如何通过创建4个数据对来准备它(
kaggle.py
),其中一个具有True:
def get_question_answer_pairs(question_file, is_test=False):
qapairs = []
fqa = open(question_file, "r")
data = json.load(fqa)
for l, line in enumerate(data):
if l%100==0:
print(l)
question = line["question"]+" "+line["support"]
qwords = tokenizer(question)
#qwords = nltk.word_tokenize(question)
if len(qwords)>100:
qwords=qwords[:100]
if not is_test:
correct_ans = line["correct_answer"],
answers = [line["distractor1"],line["distractor2"],line["distractor3"],correct_ans[0]]
new_order = [0,1,2,3]
random.shuffle(new_order)
answers = [ answers[i] for i in new_order]
correct_ans_idx = new_order[-1]
# training file parsing
#correct_ans_idx = ord(correct_ans) - ord('A')
for idx, answer in enumerate(answers):
#awords = nltk.word_tokenize(answer)
#print(answer)
awords = tokenizer(answer)
qapairs.append((qwords, awords, idx == correct_ans_idx))
else:
# test file parsing (no correct answer)
answers = cols[2:]
for answer in answers:
awords = nltk.word_tokenize(answer)
qapairs.append((qwords, awords, None))
fqa.close()
return qapairs
您无需重新计算qapairs,它们已通过主程序中的行保存和加载:
with open("processed_input.pickle", 'rb') as f:
qapairs = pickle.load(f)
以下是示例(请向右滚动查看答案和真假标签):
下一步由github上的kaggle.py
/中的函数vectorize_qapairs()
完成。它使用余弦距离,根据您的评论,我已将其更改为余弦相似性(1-最相似(零角度),0-不相似(正交)):
def vectorize_qapairs(qapairs, word2idx, seq_maxlen):
Xq, Xa, Y = [], [], []
for qapair in qapairs:
Xq.append([word2idx[qword] for qword in qapair[0]])
Xa.append([word2idx[aword] for aword in qapair[1]])
#Y.append(np.array([1, 0]) if qapair[2] else np.array([0, 1]))
# cosine similarity: 1 for 0 degree angle
Y.append(np.array([1]) if qapair[2] else np.array([0]))
return (pad_sequences(Xq, maxlen=seq_maxlen),
pad_sequences(Xa, maxlen=seq_maxlen),
np.array(Y))
如您所见,如果有“True”标签,它将放置1,否则将放置0
现在,我想让模型计算余弦,就像在图片上一样,然后将其与0-1标签进行比较。我相信你们所做的是正确的,模型现在正在运行,但我希望它开始学习,而不是输出精度为0.75左右的数字,这对应于输出总是错误的。为了调试的目的,我甚至简化了代码,排除了卷积:
#question
qenc = Sequential()
qenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
qenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
aenc = Sequential()
aenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
aenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
# attention model
#notice that I'm taking "tensors" qenc.output and aenc.output
#I'm not passing "models" to a layer, I'm passing tensors
#that was the problem with your lambda
attOut = Dot(axes=1)([qenc.output, aenc.output])
#shape = (samples,QA_EMBED_SIZE//2, QA_EMBED_SIZE//2)
#I really don't understand this output shape....
#I'd swear it should be (samples, 1, QA_EMBED_SIZE//2)
attOut = Flatten()(attOut) #shape is now only (samples,)
#attOut = Dense((qenc.output_shape[1]*(QA_EMBED_SIZE // 2)))(attOut)
#attOut = Reshape((qenc.output_shape[1], QA_EMBED_SIZE // 2))(attOut)
attOut = Dense((qenc.output_shape[1]*(QA_EMBED_SIZE)))(attOut)
attOut = Reshape((qenc.output_shape[1], QA_EMBED_SIZE))(attOut)
flatAttOut = Flatten()(attOut)
flatQencOut = Flatten()(qenc.output)
similarity = Dot(axes=1,normalize=True)([flatQencOut,flatAttOut])
model = Model([qenc.input,aenc.input],similarity)
# I tried MSE and binary crossentropy
model.compile(optimizer="adam", loss="binary_crossentropy",
metrics=["accuracy"])
print("Training...")
checkpoint = ModelCheckpoint(
filepath=os.path.join(MODEL_DIR, "qa-lstm-attn-best.hdf5"),
verbose=1, save_best_only=True)
model.fit([Xqtrain, Xatrain], Ytrain, batch_size=BATCH_SIZE,
nb_epoch=NBR_EPOCHS, validation_split=0.1,
callbacks=[checkpoint])
当然,代码不完全是我的,我使用了计算密集(2)层的实现,并且遇到了学习仅输出false的相同问题
我不知道我是否犯了一些我无法理解的错误。谢谢 我认为问题在于您使用的是
顺序
模型,以下代码块导致了问题(请注意,您使用的是attn.add()
而不是model.add()
)
我认为在您的案例中使用图形
模型更有意义
还有,你在这里犯了一个错误
# Plain sum - not working properly!
model = Sequential()
model.add(Merge([qenc, attn], mode="sum"))
model.add(Flatten())
model.add(Dense(1, activation="softmax")) # <--- ERROR
#普通求和-工作不正常!
模型=顺序()
model.add(合并([qenc,attn],mode=“sum”))
model.add(展平())
model.add(Dense(1,activation=“softmax”)#我认为问题在于您使用的是顺序
模型,以下代码块导致了问题(请注意,您使用attn.add()
而不是model.add()
)
我认为在您的案例中使用图形
模型更有意义
还有,你在这里犯了一个错误
# Plain sum - not working properly!
model = Sequential()
model.add(Merge([qenc, attn], mode="sum"))
model.add(Flatten())
model.add(Dense(1, activation="softmax")) # <--- ERROR
#普通求和-工作不正常!
模型=顺序()
model.add(合并([qenc,attn],mode=“sum”))
model.add(展平())
添加(密集(1,activation=“softmax”)#您正在使用分支。不要使用带有分支的顺序模型
您可以使用qenc
和aenc
作为顺序
模型,没有问题,因为它们是单一路径,没有分支
这里我将从代码的第一部分中选取一些示例
更新使用keras 1的呼叫:
#question
qenc = Sequential()
qenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
qenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
qenc.add(Dropout(0.3))
qenc.add(Convolution1D(QA_EMBED_SIZE // 2, 5, padding="valid"))
qenc.add(MaxPooling1D(pool_size=2, padding="valid"))
qenc.add(Dropout(0.3))
# answer
aenc = Sequential()
aenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
aenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
aenc.add(Dropout(0.3))
aenc.add(Convolution1D(QA_EMBED_SIZE // 2, 5, padding="valid"))
aenc.add(MaxPooling1D(pool_size=2, padding="valid"))
aenc.add(Dropout(0.3))
注意观察每个模型的输入和输出形状:
- qenc输出形状为:
(示例,(seq\u maxlen-4)/2,QA\u EMBED\u SIZE//2)
- aenc输出形状为:
(示例,(序号maxlen-4)/2,QA\u嵌入\u大小//2)
但是attn
正在合并两个分支,让它成为一个功能APIModel
# attention model
#notice that I'm taking "tensors" qenc.output and aenc.output
#I'm not passing "models" to a layer, I'm passing tensors
#that was the problem with your lambda
attOut = Dot(axes=1)([qenc.output, aenc.output])
#shape = (samples,QA_EMBED_SIZE//2, QA_EMBED_SIZE//2)
#I really don't understand this output shape....
#I'd swear it should be (samples, 1, QA_EMBED_SIZE//2)
attOut = Flatten()(attOut) #shape is now only (samples,)
attOut = Dense((qenc.output_shape[1]*(QA_EMBED_SIZE // 2)))(attOut)
attOut = Reshape((qenc.output_shape[1], QA_EMBED_SIZE // 2))(attOut)
- 注意输出形状:
(samples,(seq\u maxlen-4)/2,QA\u EMBED\u SIZE//2)
- 还要注意,此注意部分需要两个输入
如果出于某种原因,您“需要”将attn
型号与其他型号分开,请告诉我,因为上面的代码需要稍作更改
现在,您可以展平qenc
和attn
的输出,没问题,您只是不能在qenc
模型的“内部”进行展平
flatAttOut = Flatten()(attOut)
flatQencOut = Flatten()(qenc.output)
similarity = Dot(axes=1,normalize=True)([flatQencOut,flatAttOut])
最后创建完整模型:
model = Model([qenc.input,aenc.input],similarity)
警告:此模型输出相似性-您确定y\u train
是相似性吗?(形状=(样本,1))。
如果是的话,好的。如果不是,请更详细地说明您的问题,并解释您的模型输出、培训数据,以及您希望在何时何地显示这种相似性
平衡类别的损失函数:
您可以尝试使用自定义损失函数来平衡这些类,因为对于假-真输出,您有75%-25%的比率
import keras.backend as K
def balanceLoss(yTrue,yPred):
loss = K.binary_crossentropy(yTrue,yPred)
scaledTrue = (2*yTrue) + 1
#true values are 3 times worth the false values
#contains 3 for true and 1 for false
return scaledTrue * loss
model.compile(optimizer='adam', loss=balanceLoss)
不知道比娜
#question
qenc = Sequential()
qenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
qenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
qenc.add(Dropout(0.3))
qenc.add(Convolution1D(QA_EMBED_SIZE // 2, 5, padding="valid"))
qenc.add(MaxPooling1D(pool_size=2, padding="valid"))
qenc.add(Dropout(0.3))
# answer
aenc = Sequential()
aenc.add(Embedding(output_dim=WORD2VEC_EMBED_SIZE, input_dim=vocab_size,
input_length=seq_maxlen))
aenc.add(Bidirectional(LSTM(QA_EMBED_SIZE, return_sequences=True),
merge_mode="sum"))
aenc.add(Dropout(0.3))
aenc.add(Convolution1D(QA_EMBED_SIZE // 2, 5, padding="valid"))
aenc.add(MaxPooling1D(pool_size=2, padding="valid"))
aenc.add(Dropout(0.3))
# attention model
#notice that I'm taking "tensors" qenc.output and aenc.output
#I'm not passing "models" to a layer, I'm passing tensors
#that was the problem with your lambda
attOut = Dot(axes=1)([qenc.output, aenc.output])
#shape = (samples,QA_EMBED_SIZE//2, QA_EMBED_SIZE//2)
#I really don't understand this output shape....
#I'd swear it should be (samples, 1, QA_EMBED_SIZE//2)
attOut = Flatten()(attOut) #shape is now only (samples,)
attOut = Dense((qenc.output_shape[1]*(QA_EMBED_SIZE // 2)))(attOut)
attOut = Reshape((qenc.output_shape[1], QA_EMBED_SIZE // 2))(attOut)
flatAttOut = Flatten()(attOut)
flatQencOut = Flatten()(qenc.output)
similarity = Dot(axes=1,normalize=True)([flatQencOut,flatAttOut])
model = Model([qenc.input,aenc.input],similarity)
import keras.backend as K
def balanceLoss(yTrue,yPred):
loss = K.binary_crossentropy(yTrue,yPred)
scaledTrue = (2*yTrue) + 1
#true values are 3 times worth the false values
#contains 3 for true and 1 for false
return scaledTrue * loss
model.compile(optimizer='adam', loss=balanceLoss)