You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于字符嵌入的LSTM句子分类:向量转换与模型构建咨询

嘿,我来帮你拆解基于字符嵌入的LSTM句子分类实现——其实核心是把「词-字符」的层级结构转成LSTM能接受的三维张量,再通过层级模型或者池化把字符信息聚合到句子层面。下面一步步给你讲清楚:

一、字符嵌入的数据格式化步骤

和词嵌入直接把句子转成词索引序列不同,字符嵌入需要先拆分到字符级,再处理成三维输入格式(样本数 × 句子最大词数 × 每个词的最大字符数)。结合你的示例来看:

给定:

  • sentence_list = ['this is a dog', 'the cat and the mouse']
  • char_dict = {'t':1, 'h':2, 'i':3, 's':4, 'a':5, 'd':6, 'o':7, 'g':8}

我们需要先定义两个关键参数:

  • max_word_len = 4:每个词最多保留4个字符(你的示例里最长词是"this",4个字符)
  • max_sent_len = 5:每个句子最多保留5个词(第二个句子刚好5个词)

具体处理流程:

  1. 拆分句子到词-字符层级
    把每个句子拆成单词,再把每个单词拆成字符:

    • 第一句:["this", "is", "a", "dog"][['t','h','i','s'], ['i','s'], ['a'], ['d','o','g']]
    • 第二句:["the", "cat", "and", "the", "mouse"][['t','h','e'], ['c','a','t'], ['a','n','d'], ['t','h','e'], ['m','o','u','s','e']]
  2. 字符转索引+统一词长度
    char_dict把字符转成索引,对长度不足max_word_len的词补0,超过的截断:

    • 第一句的词处理后:

      "this" → [1,2,3,4]
      "is" → [3,4,0,0]
      "a" → [5,0,0,0]
      "dog" → [6,7,8,0]
      

      再补一个全0的词,凑够max_sent_len=5
      [[1,2,3,4], [3,4,0,0], [5,0,0,0], [6,7,8,0], [0,0,0,0]]

    • 第二句的词处理(注意字典里没有的字符用0表示未知):

      "the" → [1,2,0,0]('e'不在字典,用0)
      "cat" → [0,5,1,0]('c'不在字典,用0)
      "and" → [5,0,6,0]('n'不在字典,用0)
      "the" → [1,2,0,0]
      "mouse" → [0,7,0,4](截断到4个字符,'m'/'u'不在字典用0)
      

      最终:
      [[1,2,0,0], [0,5,1,0], [5,0,6,0], [1,2,0,0], [0,7,0,4]]

  3. 最终输入格式
    把两个句子的处理结果组合起来,得到形状为(2, 5, 4)的三维张量,这就是LSTM可以接受的输入(格式:(batch_size, timesteps, features),这里timesteps是句子的词数,features是每个词的字符索引序列)。

二、字符嵌入转句子向量的两种核心方案

LSTM要完成句子分类,需要把字符层面的信息逐步聚合到句子层面,常见的有两种实现方式:

1. 双层LSTM(层级LSTM)

这是最常用的方案,通过两层LSTM分别处理字符→词、词→句子的聚合:

  • 第一层字符LSTM:输入每个词的字符序列,输出固定长度的词向量
  • 第二层句子LSTM:输入句子的词向量序列,输出整个句子的向量,再接分类层

用Keras实现的示例代码:

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, LSTM, Dense, TimeDistributed

# 定义字符级LSTM:把字符序列转成词向量
max_word_len = 4
max_sent_len = 5
char_vocab_size = len(char_dict) + 1  # +1是给未知字符留位置

char_input = Input(shape=(max_word_len,))
char_emb = Embedding(input_dim=char_vocab_size, output_dim=20)(char_input)  # 字符嵌入维度设为20
char_lstm = LSTM(32)(char_emb)  # 每个词的向量维度设为32
char_model = Model(char_input, char_lstm)

# 定义句子级LSTM:把词向量序列转成句子向量,做分类
sent_input = Input(shape=(max_sent_len, max_word_len))
# 用TimeDistributed把字符模型应用到每个词的字符序列上
word_vectors = TimeDistributed(char_model)(sent_input)
sent_lstm = LSTM(64)(word_vectors)  # 句子向量维度设为64
output = Dense(1, activation='sigmoid')(sent_lstm)  # 二分类任务

model = Model(sent_input, output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

2. 字符嵌入+池化得到词向量

如果你的场景不需要捕捉字符的顺序信息(或者字符序列很短),可以用池化替代字符LSTM,简化模型:

  • 对每个词的字符嵌入向量做均值/最大值池化,得到固定长度的词向量
  • 再把词向量序列输入LSTM得到句子向量

示例代码:

from tensorflow.keras.layers import GlobalAveragePooling1D

# 字符模型:用均值池化得到词向量
char_input = Input(shape=(max_word_len,))
char_emb = Embedding(input_dim=char_vocab_size, output_dim=20)(char_input)
word_vector = GlobalAveragePooling1D()(char_emb)  # 对字符嵌入做均值池化
char_model = Model(char_input, word_vector)

# 句子级模型和之前一致
sent_input = Input(shape=(max_sent_len, max_word_len))
word_vectors = TimeDistributed(char_model)(sent_input)
sent_lstm = LSTM(64)(word_vectors)
output = Dense(1, activation='sigmoid')(sent_lstm)

model = Model(sent_input, output)
几个注意点
  • 未知字符处理:建议在char_dict里加入<UNK>标记(比如索引设为0),所有不在字典里的字符都用这个索引代替
  • 填充/截断:必须统一每个词的字符长度和句子的词长度,否则LSTM无法处理变长输入(如果想保留变长,可以配合Masking层,但统一长度更简单)
  • 预训练字符嵌入:如果数据量不大,可以用预训练的字符嵌入(比如FastText的字符级嵌入)初始化Embedding层,提升效果

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

火山引擎 最新活动