You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Keras:如何让Embedding层适配多维时间步输入?

解决Keras Embedding层适配二维字符序列的问题

首先,我完全理解你的场景:每个样本是51个时间步、每个时间步包含2个字符的二维序列,而Keras的Embedding层默认只接受一维整数序列输入。下面我会给出两种保留时序语义的解决方案,同时适配你的原有网络结构:


第一步:先把字符转成整数索引

Embedding层只能处理整数类型的输入(每个字符对应唯一的整数ID),所以首先需要把字符串样本转换成整数数组:

import numpy as np

# 构建字符到索引的映射(确保词汇表大小为你设定的74)
all_chars = set()
# 遍历所有训练样本,收集所有出现过的字符
for sample in your_training_data:
    for char_pair in sample:
        all_chars.update(char_pair)

char_to_idx = {char: idx for idx, char in enumerate(sorted(all_chars))}
assert len(char_to_idx) == 74, "词汇表大小必须和设定的74一致"

# 把单个字符串样本转换成整数数组
sample_str = np.array([['0', ' '], [' ', 'l'], ...])  # 你的原始样本
sample_int = np.vectorize(char_to_idx.get)(sample_str)
# 此时sample_int的形状为 (51, 2),可直接作为模型输入

方案一:对每个特征列单独做Embedding后拼接

这种方法保留每个时间步两个字符的独立语义,再将它们的向量表示合并,适合需要区分两个字符位置含义的场景:

import keras
from keras.layers import Input, Embedding, Concatenate, LSTM, Dense, Dropout

num_classes = 5  # 从你的one-hot标签维度可知是5分类任务
vocab_size = 74
emb_dim = 74
timesteps = 51
num_char_per_step = 2  # 每个时间步的字符数量

# 定义输入层,接受(51, 2)形状的样本
input_layer = Input(shape=(timesteps, num_char_per_step))

# 共享一个Embedding层(也可使用两个独立层,按需调整)
embedding_layer = Embedding(input_dim=vocab_size, output_dim=emb_dim)

# 拆分每个时间步的两个字符特征
char1_seq = input_layer[:, :, 0]  # 形状:(None, 51)
char2_seq = input_layer[:, :, 1]  # 形状:(None, 51)

# 分别做Embedding转换
emb1 = embedding_layer(char1_seq)  # 形状:(None, 51, 74)
emb2 = embedding_layer(char2_seq)  # 形状:(None, 51, 74)

# 在特征维度拼接两个Embedding结果
combined_emb = Concatenate(axis=-1)([emb1, emb2])  # 形状:(None, 51, 148)

# 沿用你原有的网络结构
x = Dropout(0.2)(combined_emb)
x = LSTM(64, dropout=0.5, recurrent_dropout=0.5, return_sequences=True)(x)
x = LSTM(64, dropout=0.5, recurrent_dropout=0.5)(x)
output = Dense(num_classes, activation='sigmoid')(x)

# 构建并编译模型
model = keras.Model(inputs=input_layer, outputs=output)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()

方案二:用TimeDistributed包装Embedding层

这种方法更简洁,通过TimeDistributed让Embedding层批量处理每个时间步的多个字符,再合并特征维度:

import keras
from keras.layers import Input, Embedding, TimeDistributed, Reshape, LSTM, Dense, Dropout

num_classes = 5
vocab_size = 74
emb_dim = 74
timesteps = 51
num_char_per_step = 2

# 定义输入层
input_layer = Input(shape=(timesteps, num_char_per_step))

# 用TimeDistributed让Embedding层处理每个时间步的2个字符
# 输出形状为:(None, 51, 2, 74)
embedding = TimeDistributed(Embedding(input_dim=vocab_size, output_dim=emb_dim))(input_layer)

# 合并最后两个维度(字符数和Embedding维度),得到(None, 51, 148)
reshaped_emb = Reshape((timesteps, num_char_per_step * emb_dim))(embedding)

# 后续网络结构与原代码一致
x = Dropout(0.2)(reshaped_emb)
x = LSTM(64, dropout=0.5, recurrent_dropout=0.5, return_sequences=True)(x)
x = LSTM(64, dropout=0.5, recurrent_dropout=0.5)(x)
output = Dense(num_classes, activation='sigmoid')(x)

model = keras.Model(inputs=input_layer, outputs=output)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()

补充说明

  • 若你对字符位置的语义不敏感,也可以将二维序列直接展平为一维(比如把(51,2)变成(102,)),再调整Embedding的input_length=102,但这种方法会丢失“每个时间步有两个并行字符”的时序信息,不推荐用于需要保留时序特征的任务。
  • 原代码中LSTM层的input_shape参数可以省略,因为前面的层已经明确了输入形状。

内容的提问来源于stack exchange,提问作者Fardin Abdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:12