从零实现LSTM二分类序列模型 多对多转多对一问题求助
解决方案
1. 修复前向传播核心逻辑bug
你当前代码最核心的错误是计算隐状态时误用了上一时间步的细胞状态,修改方法:
把原来的
h_prev = cache['o'] * cm.tanh(cache['C'])
改成
h_prev = cache['o'] * cm.tanh(C_prev)
cache['C']存储的是上一轮的旧细胞状态,你刚更新完的当前时间步细胞状态存在C_prev变量里,必须用这个新值计算隐状态。
2. 调整前向传播为多对一逻辑(移除冗余计算)
你现在的代码每个时间步都计算了输出logitsv,多对一任务不需要这部分重复计算,直接把logits和softmax计算移到循环外,只在最后一个时间步执行即可,修改后的lstm_forward完整代码如下:
def lstm_forward(inputs,lstmNet): fw_cache = [] # lstmNet.d[0] is the hidden_size h_prev = np.zeros((lstmNet.d[0], 1)) C_prev = np.zeros((lstmNet.d[0], 1)) for x in inputs: cache = {'C': C_prev, 'h': h_prev} # Concatenate input and hidden state cache['z'] = np.row_stack((cache['h'], x)) # Calculate forget gate cache['f'] = cm.sigmoid(np.dot(lstmNet.p['W_f'], cache['z']) + lstmNet.p['b_f']) # Calculate input gate cache['i'] = cm.sigmoid(np.dot(lstmNet.p['W_i'], cache['z']) + lstmNet.p['b_i']) # Calculate candidate cache['g'] = cm.tanh(np.dot(lstmNet.p['W_g'], cache['z']) + lstmNet.p['b_g']) # Calculate memory state C_prev = cache['f'] * cache['C'] + cache['i'] * cache['g'] cache['C_current'] = C_prev # 存储新细胞状态方便后续反向传播 # Calculate output gate cache['o'] = cm.sigmoid(np.dot(lstmNet.p['W_o'], cache['z']) + lstmNet.p['b_o']) # Calculate hidden state h_prev = cache['o'] * cm.tanh(C_prev) cache['h_current'] = h_prev # 存储新隐状态方便后续反向传播 fw_cache.append(copy.deepcopy(cache)) # 仅在最后一个时间步计算分类输出 v = np.dot(lstmNet.p['W_v'], h_prev) + lstmNet.p['b_v'] outputs = cm.softmax(v, rnn=True) return outputs, fw_cache
3. 修正参数初始化维度
你当前W_v的输出维度错误使用了vocab_size,如果是序列分类任务,输出维度应该是分类类别数也就是你定义的output_size,修改init_params里的对应代码:
# 替换原有W_v、b_v初始化逻辑 lstmNet.p['W_v'] = np.random.randn(output_size, hidden_size) lstmNet.p['b_v'] = np.zeros((output_size, 1))
另外需要确认z_size的取值为hidden_size + 输入特征维度,也就是输入x的维度加上隐状态的维度,这个值设置错误会触发矩阵相乘维度不匹配报错,要确保lstmNet.d[2] = hidden_size + 输入特征数。
修改完成后你的LSTM就变成了标准的多对一序列分类模型,逻辑和你之前跑通的RNN完全对齐:循环处理所有时间步的输入,仅拿最后一个时间步的隐状态计算分类输出。
内容的提问来源于stack exchange,提问作者Synthaze
相关产品推荐
相关产品推荐

