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

基于Keras的循环自动编码器构建:嵌入层RNN实现求助

循环自动编码器实现指南:DNN+RNN+权重共享

嘿,我明白你要搭建的这个循环自动编码器的核心需求了——用DNN做编码器和解码器还得对应共享权重,中间把所有编码结果凑成时间序列交给RNN处理对吧?刚好之前折腾过类似的结构,给你捋清楚关键思路再上可运行的代码示例,应该能解决你卡在RNN嵌入这一步的问题~

核心结构拆解

先把你的需求拆解成可落地的模块:

  • 共享权重的DNN编解码器:编码器把单个输入样本压缩成低维编码向量,解码器基于同样的权重(逆结构)把编码向量还原回原样本空间
  • RNN序列处理模块:把多个样本的编码向量按时间步排列成序列,让RNN学习序列中的时序依赖,再把RNN的输出映射回编码维度,交给解码器还原
  • 时序数据流:输入序列→逐时间步编码→编码序列喂RNN→RNN输出映射→逐时间步解码→还原序列

代码实现(以PyTorch为例)

1. 定义共享权重的DNN模块

这里重点处理权重共享:解码器的线性层权重直接复用编码器对应层的转置权重,偏置也同步共享,确保编解码器的权重严格对应。

import torch
import torch.nn as nn
import torch.optim as optim

class SharedDNN(nn.Module):
    def __init__(self, input_dim, hidden_dim, code_dim):
        super().__init__()
        # 编码器:输入→隐藏层→编码向量
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, code_dim)
        )
        # 解码器:编码向量→隐藏层→还原输入(逆结构)
        self.decoder = nn.Sequential(
            nn.Linear(code_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim)
        )
        
        # 核心:实现权重共享
        # 解码器最后一层 ↔ 编码器第一层 共享权重(转置)
        self.decoder[2].weight = self.encoder[0].weight.t()
        self.decoder[2].bias = self.encoder[0].bias
        # 解码器第一层 ↔ 编码器最后一层 共享权重(转置)
        self.decoder[0].weight = self.encoder[2].weight.t()
        self.decoder[0].bias = self.encoder[2].bias

    def encode(self, x):
        return self.encoder(x)
    
    def decode(self, x):
        return self.decoder(x)

2. 整合RNN的循环自动编码器整体结构

把DNN编解码器和RNN模块组合起来,处理时序编码和还原的流程:

class RecurrentAutoencoder(nn.Module):
    def __init__(self, input_dim, dnn_hidden_dim, code_dim, rnn_hidden_dim, seq_len):
        super().__init__()
        self.shared_dnn = SharedDNN(input_dim, dnn_hidden_dim, code_dim)
        # RNN处理编码后的时间序列(batch_first=True让输入维度为[batch, seq_len, feature])
        self.rnn = nn.RNN(input_size=code_dim, hidden_size=rnn_hidden_dim, batch_first=True)
        # RNN输出维度和编码维度不一致,加个投影层映射回code_dim,才能喂给解码器
        self.rnn_proj = nn.Linear(rnn_hidden_dim, code_dim)
        self.seq_len = seq_len

    def forward(self, x):
        # x形状:[batch_size, seq_len, input_dim]
        batch_size = x.size(0)
        
        # 第一步:逐时间步编码,生成编码序列
        encoded_seq = []
        for t in range(self.seq_len):
            # 取出第t个时间步的输入样本
            x_t = x[:, t, :]
            code_t = self.shared_dnn.encode(x_t)
            encoded_seq.append(code_t.unsqueeze(1))
        # 拼接成完整的编码序列:[batch_size, seq_len, code_dim]
        encoded_seq = torch.cat(encoded_seq, dim=1)
        
        # 第二步:用RNN处理编码序列,学习时序依赖
        rnn_out, _ = self.rnn(encoded_seq)  # rnn_out形状:[batch_size, seq_len, rnn_hidden_dim]
        # 把RNN输出映射回编码维度
        projected_rnn_out = self.rnn_proj(rnn_out)  # 形状:[batch_size, seq_len, code_dim]
        
        # 第三步:逐时间步解码,生成还原序列
        decoded_seq = []
        for t in range(self.seq_len):
            code_t = projected_rnn_out[:, t, :]
            x_recon_t = self.shared_dnn.decode(code_t)
            decoded_seq.append(x_recon_t.unsqueeze(1))
        decoded_seq = torch.cat(decoded_seq, dim=1)  # 形状:[batch_size, seq_len, input_dim]
        
        return decoded_seq, encoded_seq

3. 训练示例

模拟数据跑一遍训练流程,验证结构可行性:

# 超参数设置
input_dim = 10    # 单个样本的特征维度
dnn_hidden_dim = 20  # DNN隐藏层维度
code_dim = 5     # 编码向量维度
rnn_hidden_dim = 8  # RNN隐藏层维度
seq_len = 15     # 时间序列长度
batch_size = 32
epochs = 50

# 初始化模型、损失函数、优化器
model = RecurrentAutoencoder(input_dim, dnn_hidden_dim, code_dim, rnn_hidden_dim, seq_len)
criterion = nn.MSELoss()  # 回归任务用MSE,分类任务可以换交叉熵
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 生成模拟训练数据:[总样本数, seq_len, input_dim]
train_data = torch.randn(batch_size * 100, seq_len, input_dim)

# 训练循环
model.train()
for epoch in range(epochs):
    total_loss = 0.0
    for i in range(0, len(train_data), batch_size):
        batch = train_data[i:i+batch_size]
        optimizer.zero_grad()
        # 前向传播得到还原序列
        recon_batch, _ = model(batch)
        # 计算还原损失
        loss = criterion(recon_batch, batch)
        # 反向传播+更新参数
        loss.backward()
        total_loss += loss.item()
        optimizer.step()
    # 打印每轮损失
    print(f"Epoch {epoch+1}/{epochs}, 平均损失: {total_loss/(len(train_data)//batch_size):.4f}")

关键细节提醒

  • 权重共享的注意事项:如果你的DNN用了BatchNorm或者Dropout这类带状态的层,共享权重时要注意这些层的状态是否会影响编解码器的一致性,一般建议编解码器的非线性激活用ReLU这类无状态的。
  • RNN的选择:示例用了基础RNN,你可以根据需求换成LSTM或GRU,代码结构几乎不用改,只需要把nn.RNN换成nn.LSTM或nn.GRU即可。
  • 序列处理效率:示例用了循环遍历时间步的方式,你也可以用PyTorch的torch.unbind来批量处理时间步,效率更高,比如把编码部分改成:
    encoded_seq = self.shared_dnn.encode(x.unbind(1))
    encoded_seq = torch.stack(encoded_seq, dim=1)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:12:23