最近在试prompt tuning做文本分类,用的是BERT-base + PyTorch,自己写了个soft prompt拼接在输入前面。但调了好几天,发现每次训练完测试,准确率波动特别大,有时候能到85%,有时候直接掉到50%多,感觉像随机猜的。我用的学习率是1e-4,加了weight decay,prompt长度设了20个token,batch size 16。是不是prompt初始化方式有问题?还是说需要固定住预训练模型的部分参数?网上教程大多讲理论,实际踩坑的细节很少,求各位大佬指点下稳定训练的思路。
用PyTorch写prompt tuning时,模型输出总是不稳定,求指点
全部回复
共 155 条prompt初始化用bert embedding试试,另外把lr降到1e-5,冻结bert只训prompt参数会稳很多。
我之前也遇过这问题,后来加了种子固定和early stopping,波动明显小多了。
初始化建议试下用标签词的embedding均值,能稳不少。另外BERT最好冻住,只训prompt参数。
这波动幅度确实有点离谱,50%和85%之间跳,基本可以排除正常随机性了。我猜大概率是prompt初始化在搞鬼,你可以试试用训练集里类别词的embedding均值去初始化对应位置的token,比随机高斯强很多。另外BERT最好别全放开,至少前几层冻住,只调后几层和prompt参数,不然小数据下很容易灾难性遗忘。还有,你试试把学习率降到5e-5以下,prompt tuning对lr特别敏感,我上次就是1e-4直接训飞了。要是还不行,检查下数据顺序,固定seed后打乱方式不一致也会导致这种伪随机波动。
试试把prompt初始化成训练样本里class label的embedding均值,能稳不少。
这个波动幅度确实有点夸张,八成不是学习率的问题。我建议你先试试把prompt的初始化换成基于预训练词表的embedding均值,别用随机正态分布,能稳一大截。另外BERT最好冻住前几层,只训最后两三层和prompt参数,不然soft prompt很容易把底层表征带偏。还有个细节,你试试batch size翻倍到32,同时学习率降到5e-5,我怀疑你现在的梯度噪声太大了。最后强烈建议加个梯度裁剪,max_norm设1.0,对这类微调任务特别管用。
初始化用随机词向量试试,或者固定BERT只训prompt参数,波动能小很多。
我上次也这样,把学习率降到5e-5,prompt长度减到10个token就稳了。
建议先固定BERT只训prompt参数,lr降到5e-5试试,我这么调之后稳多了。
prompt初始化用词汇表里真实词的embedding均值,别用随机,能压住方差。
试试把prompt用预训练词表的embedding初始化,别随机,另外lr降到1e-5,固定BERT只训prompt参数会稳很多。
我先前也遇到过类似的波动,后来发现是prompt初始化的问题,用T5或者BERT自带embedding的均值来初始化会稳很多,随机初始化的效果全看运气。另外,你那个1e-4的学习率对prompt来说可能偏大了,建议单独给prompt设个更小的lr,比如5e-5,然后预训练模型参数确实要冻住,只训prompt相关层,不然微调一扰动整个特征分布就崩了。还有个小坑,batch size 16对prompt tuning来说可能不够,试试32或64,梯度估计会更准,方差会小不少。
八成是prompt初始化没对齐,试试用预训练词表的embedding均值来初始化,能稳很多。
试试把prompt用bert的embedding均值初始化,lr降到5e-5,固定bert只训prompt,应该能稳不少。
初始化建议试试用预训练词表的平均向量,另外把BERT冻住只训prompt参数,波动会小很多。
prompt长度20有点长,试试10以内,学习率降到5e-5,稳定性会好不少。
这波动幅度确实有点离谱,八成不是单纯初始化的问题。我建议你先试试固定住BERT主干,只训prompt向量和分类头,学习率可以再调低点到5e-5左右,顺便把seed固定下来看波动是不是还在。还有你那个20个token的prompt长度可能偏长了,对BERT来说反而容易引入噪声,砍到10以内试试。另外初始化的话,直接用对应词表的embedding均值会比随机正态稳很多。
这种波动我之前也踩过,大概率是prompt初始化的问题,用bert自带token的embedding做初始化会稳很多,别用随机初始化。另外建议把预训练模型冻住,只训prompt参数,学习率可以再调低点,试试5e-5。最好加个seed固定和early stopping,不然每次跑结果差异大也正常。
我之前也踩过这个坑,prompt tuning对随机种子和初始化特别敏感,你试过固定seed吗?如果没固定,光这一项就能解释你看到的波动。另外1e-4的学习率对BERT backbone来说可能偏高了,尤其是你只训练prompt向量的时候,建议降到5e-5甚至更低,否则预训练权重容易被带偏。还有一个我后来发现很关键的点:soft prompt的初始化方式比想象中重要,用随机正态分布容易让模型前期陷入混乱,可以试试用训练数据里某个高频class的embedding均值来初始化,或者直接用预训练模型的[CLS]向量做基底,效果会稳很多。至于固定参数,我个人经验是最好把BERT冻结,只优化prompt和分类头,不然你等于在微调整个模型,波动大很正常。还有你提到长度20个token,这个其实也挺玄学的,我试过10和30差别很大,你可以做个简单搜索,看看哪个长度在你数据集上方差最小。最后建议加个early stopping,监控验证集loss而不是acc,不然acc随机性太强容易误判。
看到你这个准确率跳来跳去的情况,我第一反应是soft prompt的初始化大概率是罪魁祸首。用BERT默认的token embedding或者随机正态分布初始化,会导致训练初期优化方向特别敏感,尤其prompt长度一长,累积的扰动就很容易把分类头带偏。我自己之前试过用任务相关的词向量(比如类别名的embedding)去初始化,或者干脆用预训练模型[CLS]位置的输出向量做初始化,稳定性会明显好一些。
另外你说的固定预训练参数这个想法,我实际跑下来感觉得看情况。如果完全冻结BERT只训练prompt和分类头,收敛确实快但上限会低,而且对学习率特别挑;如果你放开最后两层transformer让它跟着调,反而容易在prompt和原始特征之间产生竞争,波动更大。我现在的习惯是先用冻结方式跑几个epoch摸个底,再解冻最后两层做微调,但学习率得降到1e-5级别。
还有一点你可能没留意,就是prompt的token embedding要不要加个正则化,比如限制它的L2范数或者强制它跟原始embedding空间保持一定距离。我试过在loss里加一个小的contrastive项,让prompt向量跟随机初始化的向量拉开,跟真实语义向量靠拢,这样即使不同次运行,初始化的随机性也不会影响太大。另外batch size 16对BERT-base来说偏小,梯度噪声大,建议至少加到32,或者用梯度累积模拟大batch,方差会明显下降。
最后想确认下,你是每次训练完测试用的是同一个测试集吗?如果测试集本身样本少,那85%和50%的差距可能有一半是评估噪声,多跑几次取平均或者用k折交叉验证再看趋势。我建议你先固定随机种子,把训练和测试流程完全复现一遍,如果同样参数下两次结果还是差很多,那就基本能确定是prompt初始化或者优化器的问题,而不是数据划分的问题了。
这波动范围确实有点离谱,八成不是单纯学习率的问题。我之前也踩过类似坑,后来发现soft prompt的初始化影响巨大,用词汇表的随机向量不如直接用任务相关标签词的embedding均值起步。另外建议把BERT backbone冻住只训prompt参数试试,全量微调在数据量不够时很容易把预训练知识冲歪。还有个细节,你试试把学习率降到5e-5以下,prompt tuning对lr比常规微调敏感得多。
试试把prompt用特定词初始化,或者冻结BERT只训prompt参数,波动会小很多。
你这波动幅度确实有点夸张,八成不是单纯随机性问题。我之前也踩过类似的坑,建议把prompt初始化改成从预训练模型的embedding里采样(比如取几个类别关键词的平均向量),别用随机正态分布,收敛会稳很多。另外你试试把学习率降到1e-5以下,只训练prompt参数,BERT全冻结,我这么改之后方差小了不少。还有个小细节,20个token可能有点长,试下10个以内,有时候更短的prompt反而更稳定。
初始化建议用随机正态分布,加个seed固定,另外试试冻结BERT只调prompt参数,稳很多。
prompt长度20有点长,缩短到10以内,学习率降到5e-5,波动会小不少。