结合VQ-VAE与GCN时遇'Tensor'无'ndata'属性错误求助
问题分析与解决:AttributeError: 'Tensor' object has no attribute 'ndata'
问题背景
我是深度学习领域新手,在Google Colab环境中使用DGL库尝试结合VQ-VAE模型与GCN,已确认PyTorch和DGL版本均为最新,模型构建过程正常,但训练拟合阶段触发AttributeError: 'Tensor' object has no attribute 'ndata'。
模型定义代码
class Model(nn.Module): def __init__(self, num_hiddens, num_residual_layers, num_residual_hiddens, num_embeddings, embedding_dim, commitment_cost, decay=0): super(Model, self).__init__() self._encoder = Encoder(3, num_hiddens, num_residual_layers, num_residual_hiddens) self._pre_vq_conv = nn.Conv2d(in_channels=num_hiddens, out_channels=embedding_dim, kernel_size=1, stride=1) if decay > 0.0: self._vq_vae = VectorQuantizerEMA(num_embeddings, embedding_dim, commitment_cost, decay) else: self._vq_vae = VectorQuantizer(num_embeddings, embedding_dim, commitment_cost) self._decoder = Decoder(embedding_dim, num_hiddens, num_residual_layers, num_residual_hiddens) def forward(self, g): x = g.ndata['feat'] z = self._encoder(x) z = self._pre_vq_conv(z) loss, quantized, perplexity, _ = self._vq_vae(z) x_recon = self._decoder(quantized) return loss, x_recon, perplexity model = Model(num_hiddens, num_residual_layers, num_residual_hiddens, num_embeddings, embedding_dim, commitment_cost, decay).to(device) optimizer = optim.Adam(model.parameters(), lr=learning_rate, amsgrad=False)
训练代码
model.train() train_res_recon_error = [] train_res_perplexity = [] for i, batch in enumerate(training_loader): batch_graph = batch.to(device) # Memindahkan graf ke perangkat yang sesuai optimizer.zero_grad() for batch_graph in training_loader: batch_graph = batch_graph.to(device) inputs = batch_graph.ndata['feat'] batch_size = inputs.shape[0] # Memastikan dimensi input sesuai dengan kebutuhan model inputs = inputs.reshape(batch_size, -1, 1, 1) optimizer.zero_grad() vq_loss, data_recon, perplexity = model(inputs) recon_error = F.mse_loss(data_recon, inputs) / data_variance loss = recon_error + vq_loss loss.backward() optimizer.step() # Menjalankan model pada data input inputs = batch_graph.ndata['feat'].to(device) vq_loss, data_recon, perplexity = model(inputs) # Menghitung error rekonstruksi recon_error = F.mse_loss(data_recon, inputs) / data_variance # Menghitung total loss loss = recon_error + vq_loss # Backward pass dan update parameter optimizer.zero_grad() loss.backward() optimizer.step() #optimizer.step() train_res_recon_error.append(recon_error.item()) train_res_perplexity.append(perplexity.item()) if (i+1) % 100 == 0: print('%d iterations' % (i+1)) print('recon_error: %.3f' % np.mean(train_res_recon_error[-100:])) print('perplexity: %.3f' % np.mean(train_res_perplexity[-100:])) print()
报错信息
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-80-717a0674743f> in <cell line: 14>() 20 21 optimizer.zero_grad() ---> 22 vq_loss, data_recon, perplexity = model(inputs) 23 recon_error = F.mse_loss(data_recon, inputs) / data_variance 24 loss = recon_error + vq_loss 1 frames /usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs) 1499 or _global_backward_pre_hooks or _global_backward_hooks 1500 or _global_forward_hooks or _global_forward_pre_hooks): -> 1501 return forward_call(*args, **kwargs) 1502 # Do not call functions when jit is used 1503 full_backward_hooks, non_full_backward_hooks = [], [] <ipython-input-75-1fbfd62ffc59> in forward(self, g) 23 24 def forward(self, g): ---> 25 x = g.ndata['feat'] 26 z = self._encoder(x) 27 z = self._pre_vq_conv(z) AttributeError: 'Tensor' object has no attribute 'ndata'
问题根源
模型的forward方法设计为接收DGL Graph对象,但训练代码中传入的是从图中提取出的Tensor(batch_graph.ndata['feat']),而PyTorch Tensor没有ndata属性,因此触发报错。同时训练代码存在重复执行前向传播、梯度更新的冗余逻辑。
修复方案
方案1:调整模型以直接接收Tensor输入(适配现有CNN结构)
从代码中的Conv2d层可以看出,Encoder/Decoder是基于CNN的结构,不需要Graph对象,直接修改模型的forward方法:
def forward(self, x): # 直接使用传入的Tensor输入,不再从Graph中提取特征 z = self._encoder(x) z = self._pre_vq_conv(z) loss, quantized, perplexity, _ = self._vq_vae(z) x_recon = self._decoder(quantized) return loss, x_recon, perplexity
同时清理训练代码中的冗余逻辑:
model.train() train_res_recon_error = [] train_res_perplexity = [] # 保留单个循环,去掉外层无效的enumerate循环 for i, batch_graph in enumerate(training_loader): batch_graph = batch_graph.to(device) inputs = batch_graph.ndata['feat'] batch_size = inputs.shape[0] # 调整输入维度适配Conv2d的[batch_size, channels, height, width]格式 inputs = inputs.reshape(batch_size, -1, 1, 1).to(device) optimizer.zero_grad() # 传入处理好的Tensor输入 vq_loss, data_recon, perplexity = model(inputs) recon_error = F.mse_loss(data_recon, inputs) / data_variance loss = recon_error + vq_loss loss.backward() optimizer.step() train_res_recon_error.append(recon_error.item()) train_res_perplexity.append(perplexity.item()) if (i+1) % 100 == 0: print('%d iterations' % (i+1)) print('recon_error: %.3f' % np.mean(train_res_recon_error[-100:])) print('perplexity: %.3f' % np.mean(train_res_perplexity[-100:])) print()
方案2:如果需要结合GCN(保留Graph输入)
若你的Encoder包含GCN层,需要传入Graph对象,则修改模型的forward方法以适配Graph处理,同时调整训练代码传入Graph:
# 模型forward方法示例(假设Encoder是GCN结构) def forward(self, g): x = g.ndata['feat'] # 用GCN处理Graph节点特征 z = self._encoder(g, x) # 调整维度适配后续Conv2d层 z = z.reshape(-1, num_hiddens, 1, 1) z = self._pre_vq_conv(z) loss, quantized, perplexity, _ = self._vq_vae(z) # 解码后恢复节点特征维度 x_recon = self._decoder(quantized).reshape(-1, x.shape[1]) return loss, x_recon, perplexity # 训练代码中直接传入Graph对象 vq_loss, data_recon, perplexity = model(batch_graph) # 计算损失时使用Graph中的原始特征作为目标 recon_error = F.mse_loss(data_recon, batch_graph.ndata['feat']) / data_variance
内容的提问来源于stack exchange,提问作者Fairuuz Amaanullah
相关产品推荐
相关产品推荐

