PyTorch使用字母字符串作为Tensor输入报too many dimensions 'str'如何解决
报错原因
PyTorch 中的torch.Tensor是数值张量类型,原生不支持直接存储字符串,你直接将字符串列表传入torch.Tensor构造函数,就会触发类型不匹配的报错。
神经网络只能接收数值型输入,所以你需要先把字母字符串转换为数值编码,再输入模型,下面是两种可直接落地的实现方案:
方案1:固定字符映射(轻量场景适用)
先建立字符到数字的映射字典,将每个字母转换为对应的整数,再构造张量即可运行。
修改后的完整代码如下:
import torch import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable as var import torch.optim as optim # 新增:字符到数值的映射表 char_to_idx = {char: idx for idx, char in enumerate(['a','b','c','d','e','f','g','h','i','j'])} # 反向映射可选,需要还原字符的时候用 idx_to_char = {idx: char for char, idx in char_to_idx.items()} class Nnetz(nn.Module): def __init__(self): super(Nnetz, self).__init__() self.lin1 = nn.Linear(10, 10) self.lin2 = nn.Linear(10, 10) self.lin3 = nn.Linear(10, 10) self.lin4 = nn.Linear(10, 10) self.lin5 = nn.Linear(10, 10) self.lin6 = nn.Linear(10, 10) def forward(self, x): x = F.relu(self.lin1(x)) x = self.lin2(x) x = self.lin3(x) x = self.lin4(x) x = self.lin5(x) x = self.lin6(x) return x def num_flat_features(self, x): size = x.size()[1:] num = 1 for i in size: num *= i return num netz = Nnetz() for i in range(100): # 替换原来的数值输入为字母输入,转成数值列表 x_char = ['j','i','h','g','f','e','d','c','b','a'] x = [char_to_idx[c] for c in x_char] input = var(torch.Tensor([x for _ in range(10)])) out = netz(input) x_target_char = ['a','b','c','d','e','f','g','h','i','j'] target = var(torch.Tensor([[char_to_idx[c] for c in x_target_char] for _ in range(10)])) criterion = nn.MSELoss() loss = criterion(out, target) print(loss) netz.zero_grad() loss.backward() optimizer = optim.SGD(netz.parameters(), lr=0.01) optimizer.step()
该方案的优势是改动量极小,无需调整网络结构;劣势是映射的数值是序数,神经网络会默认认为数值有大小关系(比如a对应0 < b对应1),如果你的字符本身没有顺序语义,会引入不必要的偏差。
方案2:Embedding层编码(工业界常用方案)
如果要消除数值序数的偏差,获得更好的训练效果,可以在网络最前面增加nn.Embedding层,将字符索引映射为可学习的向量,是文本输入场景的标准处理方式。
修改后的完整代码如下:
import torch import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable as var import torch.optim as optim # 字符到数值的映射表 char_to_idx = {char: idx for idx, char in enumerate(['a','b','c','d','e','f','g','h','i','j'])} vocab_size = len(char_to_idx) # 字典大小10 embedding_dim = 1 # 这里可以调整嵌入向量维度,比如设为2、4都可以 class Nnetz(nn.Module): def __init__(self): super(Nnetz, self).__init__() # 新增Embedding层 self.embedding = nn.Embedding(vocab_size, embedding_dim) # 输入维度调整为 10 * embedding_dim,因为10个字符每个输出embedding_dim维,展开后输入全连接层 self.lin1 = nn.Linear(10 * embedding_dim, 10) self.lin2 = nn.Linear(10, 10) self.lin3 = nn.Linear(10, 10) self.lin4 = nn.Linear(10, 10) self.lin5 = nn.Linear(10, 10) self.lin6 = nn.Linear(10, 10) def forward(self, x): # x的形状是(batch_size, 10),每个元素是字符索引 x = self.embedding(x) # 输出形状(batch_size, 10, embedding_dim) x = x.flatten(1) # 展平为(batch_size, 10 * embedding_dim) x = F.relu(self.lin1(x)) x = self.lin2(x) x = self.lin3(x) x = self.lin4(x) x = self.lin5(x) x = self.lin6(x) return x def num_flat_features(self, x): size = x.size()[1:] num = 1 for i in size: num *= i return num netz = Nnetz() for i in range(100): x_char = ['j','i','h','g','f','e','d','c','b','a'] x = [char_to_idx[c] for c in x_char] # 注意这里转LongTensor,因为Embedding层要求输入是整数索引 input = var(torch.LongTensor([x for _ in range(10)])) out = netz(input) x_target_char = ['a','b','c','d','e','f','g','h','i','j'] target = var(torch.Tensor([[char_to_idx[c] for c in x_target_char] for _ in range(10)])) criterion = nn.MSELoss() loss = criterion(out, target) print(loss) netz.zero_grad() loss.backward() optimizer = optim.SGD(netz.parameters(), lr=0.01) optimizer.step()
内容的提问来源于stack exchange,提问作者FruchtigeBanane
相关产品推荐
相关产品推荐

