我正在比较两种不同网络架构的性能,以解决“IMBD 电影评论”的二元分类问题,该问题在“Python 深度学习”第 3 章中介绍。
加载数据:
# num_words means only use most common `n` words in the vocab
(train_data,train_labels),(test_data,test_labels) = imdb.load_data(num_words=10_000)
train_xs = torch.vstack([multi_vectorize(t) for t in train_data]).to(torch.float)
train_ys = torch.tensor(train_labels).unsqueeze(dim=1).to(torch.float) # 250k -> (250k,1) for compatability w/ train
## train/val split
val_xs = train_xs[0:10_000]
val_ys = train_ys[0:10_000]
partial_train_xs = train_xs[10_000:]
partial_train_ys = train_ys[10_000:]
Sequential(
(0): Linear(in_features=10000, out_features=16, bias=True)
(1): ReLU()
(2): Linear(in_features=16, out_features=16, bias=True)
(3): ReLU()
(4): Linear(in_features=16, out_features=1, bias=True)
(5): Sigmoid()
)
该网络的输入是“multihot”编码的文本片段。例如,给定一篇包含 80 个单词的评论,假设总词汇量为 10k 个单词(域),则每个输入将是一个包含 10k 个元素的向量,如果向量中的索引位置对应于向量中的字母,则带有
0
词汇不存在,如果存在则
1
:VOCAB_SZ = max(max(s) for s in train_data) + 1
def multi_vectorize(seq):
t = torch.zeros(VOCAB_SZ)
for s in seq:
t[s] = 1
return t
这是有道理的,但它也会忽略重复的单词,因为输入只能将它们表示为存在或不存在。
我知道嵌入通常用作 NLP 任务中的第一层,因此我尝试运行一个包含嵌入层的基本实验,假设它会提高性能:
我做了一些修改来创建嵌入层。首先,我们现在不需要每个向量都是 10K 元素,因为我们不对每个输入进行多热编码。相反,我们确保每个输入向量的大小相同,这是通过找到训练集中最大的输入大小并使用给定的“填充标记”将每个输入填充到至少那么大来实现的。那么嵌入层就是
(10001,3)
。第一个维度是
10001
,反映 10000
的词汇大小加上新添加的填充标记的词汇大小。第二个维度是任意的,表示每个嵌入令牌的维度。## make all token lists the same size.
MAX_INPUT_LEN = len(max(train_data,key=len))
## zero is already in the vocab, which starts tokens at zero
## since the max token is VOCAB_SZ - 1 (zero based) we can
## use VOCAB_SZ as the start point for new special tokens
PAD_TOKEN = VOCAB_SZ
def lpad(x,maxlen=MAX_INPUT_LEN,pad_token=PAD_TOKEN):
padlen = maxlen - len(x)
if padlen > 0:
return [pad_token] * padlen + x
return x
EMB_SZ = 3
NUM_EMBED = VOCAB_SZ + 1 # special pad char (10,000)
emb_model = nn.Sequential(
nn.Embedding(NUM_EMBED,EMB_SZ),
nn.Flatten(),
nn.Linear(EMB_SZ * MAX_INPUT_LEN,16),
nn.ReLU(),
nn.Linear(16,16),
nn.ReLU(),
nn.Linear(16,1),
nn.Sigmoid()
)
该网络的训练时间要长一个数量级,而且性能也更差。我不确定为什么训练速度这么慢(第一个版本中的 10 个 epoch 大约需要 3 秒,而这个版本需要 30 秒)。由于嵌入,存在额外的间接层,但该模型的总参数计数实际上少于第一个版本,因为我们没有为每个输入使用 10K 大小的向量。
所以这就是困惑点#1,为什么训练速度会慢很多?
其次是性能。我本以为添加嵌入层将使模型具有更多维度来表达评论的情绪。至少它可以避免像多热版本那样“忽略”在输入中重复的标记。我尝试了更小和更大的嵌入层,但我似乎无法获得高于约 82% 的验证准确度,并且需要约 80 轮才能达到这一目标。第一个版本仅经过 10 个 epoch 后就达到了 90% 的验证准确率。 我该如何思考为什么嵌入版本的性能更差?
我认为困惑来自以下几点:
在第二种方法中,至少按照您定义模型的方式,一个输入对应一个单词。
不过如果不看你的训练循环就不确定。也不是 Pytorch 专家