《Free Lunch for Few-shot Learning: Distribution Calibration 》阅读笔记
2021 ICLR
Free Lunch for Few-shot Learning: Distribution Calibration https://arxiv.org/pdf/2101.06395.pdf
论文从基类Base Class中估计新类Novel Class的分布,并基于该分布生成Novel Class样本,生成的样本用于训练分类器。
@杨朔 作者大佬本人已经在知乎做过分享了,本着学习的态度,这里做个论文和代码的阅读笔记,如果有幸被作者看到,还请多指教.
作者认为少样本带来的过拟合问题是由样本过少导致的对少样本域(目标域)的分布估计不准确造成的。
作者通过具有充足样本的域来校准目标少样本域(目标域)的分布,并进一步从估计出的少样本域(目标域)中采样,达到扩充目标域样本数量的目的。扩充后的样本,用来训练分类器。下文中多样本域和少样本域分别称为Base Class和Novel Class。
作者假设样本特征的每一个维度均服从高斯分布,这样,当样本量充足时,可以对该类样本分布的均值和方差有较为准确的估计。样本特征的提取可通过预训练模型得到。
论文的实验证明,利用上述分布估计策略,仅通过逻辑回归方法即可在小样本任务中达到SOTA。
从上表可以看出,语义上相似的类别,其假设的高斯分布均值Mean和方差Variance也非常相似。
那么有理由相信,如果可以衡量两个类别之间的相似性,就可以将与新类(Novel Class)相似的基类(Base Class)的分布信息传播给Novel Class。
这种分布信息的统计建立在特征层面,且以每个特征维度为单位。特征可由任意预训练模型得到。
直观的理解,Mean表示该类的整体样貌,Variance代表该类某属性的变化范围,比如颜色和姿态。
图像数据的特征向量可通过任意预训练特征提取网络得到,本文中采用pretrained WideResNet,训练方式完全参考[1]。应用self-supervised pretext task去学习一个用于图像理解任务的通用表示。训练数据为Base Class样本。
对于每一个基类(base class)通过feature extractor得到的特征向量,Mean计算方式为:
为第个样本的特征向量。为类别的样本数。因为样本特征为多维,所以协方差更为合适,计算方式为:
# ---- Base class statistics
base_means = []
base_cov = []
# pretrained 基类的特征向量
base_features_path = "./checkpoints/%s/base_features.plk"%dataset
with open(base_features_path, 'rb') as f:
data = pickle.load(f)
# 循环每一个基类
for key in data.keys():
feature = np.array(data[key])
mean = np.mean(feature, axis=0)
cov = np.cov(feature.T)
base_means.append(mean)
base_cov.append(cov)
为了使特征分布更加的接近高斯分布,作者通过Tukey's Ladder of Powers Transformation将Novel Class的支持集和查询集样本进行了映射(为超参数):
# ---- Tukey's transform
beta = 0.5
support_data = np.power(support_data[:, ] ,beta)
query_data = np.power(query_data[:, ] ,beta)
Novel Class分布的校准通过比较该类样本与Base Class的均值的欧式距离实现。
具体地,选择TopK相似的Base Class用来校准:
校准方式为(决定采样分散程度):
利用校准后的Novel Class分布,生成若干样本,用于训练分类器。
# ---- 核心代码
def distribution_calibration(query, base_means, base_cov, k,alpha=0.21):
dist = []
for i in range(len(base_means)):
dist.append(np.linalg.norm(query-base_means[i]))
index = np.argpartition(dist, k)[:k]
mean = np.concatenate([np.array(base_means)[index], query[np.newaxis, :]])
calibrated_mean = np.mean(mean, axis=0)
calibrated_cov = np.mean(np.array(base_cov)[index], axis=0)+alpha
return calibrated_mean, calibrated_cov
# ---- distribution calibration and feature sampling
sampled_data = []
sampled_label = []
num_sampled = int(750/n_shot)
for i in range(n_lsamples):
# 校准
mean, cov = distribution_calibration(support_data[i], base_means, base_cov, k=2)
# 校准后的样本生成
sampled_data.append(np.random.multivariate_normal(mean=mean, cov=cov, size=num_sampled))
# 为生成样本制作标签
sampled_label.extend([support_label[i]]*num_sampled)
sampled_data = np.concatenate([sampled_data[:]]).reshape(n_ways * n_shot * num_sampled, -1)
# 最后的训练数据为生成的样本和Novel class的Support Set
X_aug = np.concatenate([support_data, sampled_data])
Y_aug = np.concatenate([support_label, sampled_label])
# ---- train classifier
classifier = LogisticRegression(max_iter=1000).fit(X=X_aug, y=Y_aug)
# 对Novel Class的Query Set进行预测
predicts = classifier.predict(query_data)
我用CUB数据集做了复现,结果与论文中结果一致。这里直接贴上结果之一看看SOTA的结果。可以看到本文方法+SVM或LR就可以达到SOTA的结果。
论文可视化了采样的样本分布和Novel Class的分布,可以证明其采样策略的有效性和准确性。
我对这篇论文的思路很感兴趣,也很钦佩作者的能力,对我启发很大。其实作者本人已在知乎做了分享[2],但本着学习的态度,还是在拜读了文章和代码后写了这篇博客。
论文所提方法基于强烈的先验假设:即Base Class和Novel Class之间存在非常相似的类别。这样才可以根据近邻域样本的分布生成Novel Class样本。面对跨域Cross Domain问题,该方法恐怕难以奏效,所以面对跨域问题时,值得进一步挖掘该方法的变体。