这次的血泪教训就是,shuffle真的很重要!
训练模型发现一个问题:train集和dev集的performance差别很大。
我的心路历程:
- 怀疑自己的iterator写错了,将其换成dataloader之后,仍然有此问题。
- 有没有可能是模型的问题? 尝试换成以前写过的模型,但仍然有这个现象
- 难道是数据集的问题?我尝试print了几条数据,一切正常,也没有出现大量UNK。
- 是我训练方法出现问题?
- 尝试调整学习率。当调大学习率时,训练集的performance非常差,只有10%,而valid集有35%;当调小学习率时,训练集的performance有70%,而valid集仍然只有30%左右。
- 尝试不clip。训练集的performance变高了,但valid仍然很差。训练多几个epoch也是一样。
- 我把训练集的数据换成验证集的数据,用验证集来训练。神奇的是,居然不会过拟合。训练的performance甚至比验证的performance更差,但明明是同一个数据集训练的。
- 难道是我统计的问题?
- 会不会是我在统计loss和accuracy出现问题? 但肉眼debug了半天,完全没找到问题。
- 打印一下predict的值,发现一个batch内部预测的全部都是一样的label。是模型分类的问题?但不应该啊,明明check过模型了,应该不是模型的锅
- 嗯?难道是label的问题?统计一下label的分布,确实label类别不平衡,但也在合理范围内。
- 试试打印一下batch的label吧。 …这下发现,为什么batch内部的label全部都是同一类的?难怪predict时也是同一类。是不是数据本来就是按label顺序排的?
oh,不会是没有shuffle吧。check了一下代码,发现我的iterator是这样的:
1 | def __iter__(self): |
有shuffle啊。等等,我好像是在iter完后再shuffle的。但这真的有问题吗?可能第一次没shuffle,但后面都shuffle了为啥performance还是没有上去?
反正找不到bug,那就试一下吧,我把shuffle代码放在前面。结果是,训练过程的结果变正常了…
想了一下原因。
第一,数据真的非常需要shuffle,一些数据集是按照label来排的,假如不shuffle直接按顺序喂数据会出现什么情况呢?模型一开始接触到的都是同一类的数据,那么就会疯狂overfit到该类的pattern,等到他有机会接触下一类后,可能已经陷入local minimum了,再怎么训都雷打不动了,这也就是为什么我在训练过程中当调高学习率时出现training set在10%而valid set在30%,这是因为模型已经无法拟合training set的其他类了;或者如果学习率小,那么还没overfit得很严重,在接触到下一类时,仍然会很快overfit到该类,在一个epoch后,模型会overfit到最后一个接触的类,此时做evaluation,当然结果好不到哪里去,但在training set上却还可以。
第二,为什么在第一个epoch后做shuffle,在后续的epoch模型仍然不正常呢?和上述的原因可能相同,因为overfit到一个特定类的pattern了,再也无法从local minimum出来了。假如学习率调的足够小,或许还有救,但一般我们用的都是Adam之类的,在训练过程中学习率可能越变越大,到最后可能还是没救。
所以啊,一定不要忘记shuffle!
最后贴上我在debug过程中的记录,来纪念这次的血泪教训。