我是靠谱客的博主 着急学姐,最近开发中收集的这篇文章主要介绍190523日志,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

 

 

特殊模型问题

 

模型最基本的东西

1. 确定要预测问题(对象)

2. forward输出什么

3. loss选择什么

 

对确定预测的问题(对象),一般可以用简单A+B=C来表达。

我想研究A在B的限制下的C。那么A用一系列特征表达,B用一系列特征表达,花里胡哨运算一通得到某个值,我们再强行许愿令其为C即可。

 

forward输出什么,倒是可以与loss一起考虑的东西。 因为forward的output,又是loss的input。

确定loss怎么算,就知道我们希望模型forward出一个什么东西来。

 

回归问题。

loss function一般是mse,rmse之类的。

 

但对于特殊的问题,比如我想预测的东西只能是正数,且数值极小,且是离散的整数。

比如某个冷门小卖部每天卖出的毛巾数量,可能几天都未必有1个交易量。

 

预测值小且十分稀疏时,(例如0,1,0,2,0,0,1...),mse之类的就不好用了。

比如true_y=1,预测值pred_y=0.8,差值0.2,在算法MSE里,平方之后会变成0.04,太小了。

如此一来,反向传播的梯度也是在小数点后2位以下了。你的学习率lr又是一个小数点后几位的东西,很容易就逼近了精度极限,怎么训练得动。

float16一般是小数点后6位,换成float64也就小数点后14位,可能好是好一点,显存开销也加倍呀。

而假如换一个问题,预测书包价格,true_y=10,pred_y=8,则会拿到4,这样的梯度就比较让人喜闻乐见。

所以可以看出来,y的值域对选什么loss fn,选MSE还是RMSE,有挺大的影响。

 

那么x的值域有没有影响呢?奇怪的是,现在的人似乎都默认会做输入的scaling,弄成均值0方差1的input_x,所以x的值域就不怎么被考虑了。

 

回到这个【非负数】、【稀疏】、【离散】的小卖部毛巾问题上。

 

为了解决值域太小的问题,我试过一种思路。

可以对【tartget_value的分布】做shift,比如大概看了一眼,true_y的分布在[0,3]之间。

我们令true_y' = 10*true_y + 10,这样整个分布就移动到了[10,40]。

之后对(pred_y-10)/10,就是真正的预测值了。

这样的效果好不好呢?只能说对精度有一点提升,实际上拟合能力还是受限于模型本身。

 

建模的时候,要处理【必须大于等于0】这个事情,还是很难啊。

比如预测出来是9,经过减10除10,得到-0.1,怎么办呢,强制取0?

我是这样做的,但还是会影响精度。

因为得到【-0.1】,有可能是模型想告诉你【我有很小的概率认为它是1,因为有点小所以被表达成负数】。

【如果我想告诉你,我很确定这件事完全不可能发生,预测的购买量就是0,那我为什么不给你输出一个-1000,-10000呢?】

你看,单侧限制【非负数】的预测问题,却输出【负数】的结果,是有很大的可解释空间的。

【负数的绝对值越大】表明【越不可能发生购买】。那么对于【-0.1】这种【绝对值很小的负数】,是不是就说明【不会发生购买】的可能性并没有那么大? 也就是说还是存在一定可能性发生购买的。

如果直接令【-0.1】得0,那么这部分可能性的信息就被丢弃了。

你对【-0.1】表达的信息,认为等价于【-1000000】,这显然是不合理的。

 

所谓的回归问题,本来就是绕着真实值y进行双侧波动,但这类问题等于只许你在单侧波动。

但另一侧的波动也是包含信息的。本来一个正弦波,你切一刀,下半边就没了,这不是残疾波么。

我试过用ReLU强制平抑,这样可以保证输出一定非负,但容易欠拟合。

loss降不下去,我认为这是信息丢失的缘故。

 

而且,我们要注意到,这个问题还要求结果必须是离散的。

那么预测出来是25,减10除以10得到1.5,你是算1去提交,还是算2去提交呢?

四舍五入未免太蠢了吧。

对线性模型最后一层来说,1.5到1和到2的距离是一致的,你用四舍五入就违背了这种【等价距离】。

 

好一点的思路是,对这种离散值问题,用区间分割的方法。

这样一来我们就不再预测某个具体的值,而是预测落在第i个区间的概率。

如此,也就不必为1.5是属于1还是2困扰了。

因为会输出n个概率值,softmax一下就知道是取区间1还是区间2了。

对小卖部毛巾这个问题,可以取n=10,毕竟如果一天卖出10条以上我们反而认为是异常值了。

进一步地,我们得到10个区间,[0,1),[1,2)......[9,10),每个区间取左端点,分别预测概率即可。

 

用如上方法,我们成功地把一个回归问题,转化成了分类问题

这样听起来是不是机智多了?至少听上去,信息几乎是无损的。

但这种方法,对于取值范围很大的问题不适用。

如果取值1~2w,softmax要接受2w个维度,显然有点强人所难。

 

当然也不是不能做,拿算力怼嘛。

可是你有2w个区间,那数据集得多大?

有多少个区间是从来训练不到的?

光有算力有啥用呢,对数据的要求也太高了。

 

 

 

梯度NaN问题

 

1.输入+输出检查

如果一开始就爆NaN而不是训练一段时间之后,那么就需要检查输入输出数据。

主要是检查输出。毕竟输入里面存在NaN这么蠢的事情,数据清洗的时候如果还没发现也太丢人了。

i)输出的大小,范围。

根据这个区思考,拿输出去计算loss的时候有没有可能出现分母为0,log(0)之类的情况。

当然出于简单无脑易操作的角度,你可以在每一个除法的分母和对数运算的括号里都加上一下小正数。

比如a/(b+1e-20),以及log(x+1e-20)。

虽然无脑,但是隐患也是有的。好自斟酌。

i)输出的形状,这是我踩过的另一个坑了。

回归问题linear最后一层dim=1的话,输出的output.shape=(batchsize,1)

但是输入的标签label_y.shape=(batchsize)

所以根据pytorch的特性,(batchsize,1)-(batchsize) = (batchsize,batchsize)

神奇的计算。

这会影响求导,最后导致梯度爆炸,于是算出来的东西爆炸。

所以在linear之后view一下形状很重要。

out=out.view(-1) #(batchsize)

 

2.学习率+梯度裁剪

lr=0.08+max_norm=5.0,爆炸NaN。

lr=0.02+max_norm=2.0,正常训练。

就是这么神奇......玄学调参。

optimizer.zero_grad()
loss.backward()
 
#梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm = 2.0)
optimizer.step()

 

 

 

 

 

最后

以上就是着急学姐为你收集整理的190523日志的全部内容,希望文章能够帮你解决190523日志所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部