dogvscat 一个关于协变量偏移(covariate shift)的例子
![]() 猫没卡通化你的训练集 |
![]() 猫卡通化过你的测试集 |
![]() 狗没卡通化过你的训练集 |
![]() 狗卡通化过你的测试集 |
为了体验一下协变量偏移(covariate shift)的例子我自己构建了一个猫狗分类的数据集,数据来自kaggla dogvscat的训练集,我把它重新分成了训练集和测试集两部分然后对测试集部分进行卡通化处理(卡通化代码地址)构成了一个协变量偏移的例子,试着实现协变量偏移纠正,不过可惜的事效果并不好。
理论部分
分布偏移的类型
训练数据分布\(p_S(x,y)\) 测试数据分布\(p_T(x,y)\) 一个清醒的现实是:如果没有任何关于\(p_S\)和\(p_T\)之间相互关系的假设, 学习到一个分类器是不可能的
如果\(p_S(x)=p_T(x)\)但是标签全部翻转 \(p_S(y|x)=1-p_T(y|x)\)如果我们此时简单的使用\(p_S(x)\)那很难得到好的结果
所以我们可以对数据发生的变化进行一些限制的假设,并以此设计可以检测偏移变化的算法,然后动态调整数据。
情况一协变量偏移(covariate shift)
\(p(x)\)改变 \(p(y|x)\)没有改变
情况二标签偏移(label shift)
\(p(y)\)改变 \(p(x|y)\)没有改变情况三 概念偏移(concept shift)
标签的定义发生变化
为了更好的认识这一问题我们要了解经验风险和真实风险的概念
经验风险(empirical risk)
\[
\underset{f}{\operatorname{minimize}} \frac{1}{n} \sum_{i=1}^n
l\left(f\left(\mathbf{x}_i\right), y_i\right)
\]
真实风险(true risk)
\[
E_{p(\mathbf{x}, y)}[l(f(\mathbf{x}), y)]=\iint l(f(\mathbf{x}), y)
p(\mathbf{x}, y) d \mathbf{x} d y
\]
协变量偏移纠正
\(q(x)\)源分布 \(p(x)\)目标分布 假设:\(p(y|x)=q(y|x)\)
\[
\iint l(f(\mathbf{x}), y) p(y \mid \mathbf{x}) p(\mathbf{x}) d
\mathbf{x} d y=\iint l(f(\mathbf{x}), y) q(y \mid \mathbf{x})
q(\mathbf{x}) \frac{p(\mathbf{x})}{q(\mathbf{x})} d \mathbf{x} d y
\]
我们需要根据数据来自正确分布与来自错误分布的概率之比,
来重新衡量每个数据样本的权重
\[
\beta_i \stackrel{\text { def }}{=}
\frac{p\left(\mathbf{x}_i\right)}{q\left(\mathbf{x}_i\right)}
\]
将权重 \(\beta_i\)
代入到每个数据样本 \(\left(\mathbf{x}_i,
y_i\right)\) 中,
我们可以使用”加权经验风险最小化“来训练模型:
\[
\underset{f}{\operatorname{minimize}} \frac{1}{n} \sum_{i=1}^n \beta_i
l\left(f\left(\mathbf{x}_i\right), y_i\right) .
\]
\(\beta_i\)
可以用一个二分类网络生成
代码实现
数据处理
1 | import shutil |
复制test文件夹重命名为testcartoon 进入文件夹下用如下的代码卡通化测试文件夹
python test.py --input_dir ./samples/inputs1 --output dir ./samples/result1/ --device cpu
创建协变量偏移检测器训练集
在dogvscat文件夹下
1 | mkdir train2 |
1 | import shutil |
然后你可以删除train2
以下是大概的文件结构
1 | │─cartoonvsnormal |
展示部分图片
![]() 猫没卡通化你的训练集 |
![]() 猫卡通化过你的测试集 |
![]() 狗没卡通化过你的训练集 |
![]() 狗卡通化过你的测试集 |
别问为什么没有对齐就是要逼死强迫症 不过值得一提这些图片是我挑选过的卡通化有些图效果不是很好 你可以尝试修改代码让它效果更好 加油
协变量偏移检测器实现
1 | import torch |
1 | #数据加载 |
定义检测器
这里convariate_shift_detecor\((x_i)=\beta_i =\frac{p\left(\mathbf{x}_i\right)}{q\left(\mathbf{x}_i\right)}\)
1 | def covariate_shift_detector(input_image,model_pth='../data/ch04-4-9-2and4-9-3/covariate_shift_detectormodel.pth'): |
测试结果
1 | import random |
1 | gc.collect() |
实现协变量偏移纠正
首先展示没有使用分类器来训练模型的结果,可以看到训练的后期,即使各个数据曲线已经趋平,训练准确率仍然高于测试准确率。
1 | #import库 以及 用到的函数定义见4.9.2 |
使用分类器来训练模型的结果
1 | gc.collect()#内存清理 |
比较
1 | gc.collect() |
结果
testcartoon mean: tensor([0.4168, 0.3945, 0.3463]) std: tensor([0.2118, 0.2033, 0.1590]) 没使用分类器测试准确率:89.8000%,使用分类器测试准确率:92.3600%
事实上如果你在优化器不用SGD而是用Adam会得到更好的结果 而且你会发现用不用分类器结果都差不多orz
在训练的时候会遇见test_acc高于train_acc的情况 这可能是因为train_acc 是在每个batchsize之后统计的 而 test_acc是在一个epoch后统计的(一个牵强的解释,始终不能理解为什么可以在一个epoch后就产生这么高的test_acc)