Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method--论文笔记

论文笔记

资料

1.代码地址

https://github.com/iBelieveCJM/pseudo_label-pytorch

2.论文地址

3.数据集地址

论文摘要的翻译

本文提出了一种简单有效的深度神经网络半监督学习方法。基本上,所提出的网络是以有监督的方式同时使用标记数据和未标记数据来训练的。对于未标记的数据,只要选取具有最大预测概率的类别,就可以使用伪标签,就好像它们是真标签一样。这实际上等同于熵正则化。它支持类之间的低密度分离,这是半监督学习通常假设的先验条件。在MNIST手写数字数据集上,利用去噪自动编码器和丢弃,这种简单的方法在标签数据非常少的情况下优于传统的半监督学习方法。

1背景

所有训练深度神经网络的成功方法都有一个共同点:它们都依赖于无监督学习算法。大多数工作分两个主要阶段进行。在第一阶段,无监督预训练,所有层的权重通过这种分层的无监督训练来初始化。在第二阶段,微调,在有监督的方式下,使用反向传播算法用标签全局地训练权值。所有这些方法也都以半监督的方式工作。我们只需要使用额外的未标记数据来进行无监督的预训练。
我们提出了一种更简单的半监督方式训练神经网络的方法。基本上,所提出的网络是以有监督的方式同时使用标记数据和未标记数据来训练的。对于未标记的数据,只需选取每次权重更新具有最大预测概率的类,就像使用真标签一样使用伪标签。该方法原则上可以结合几乎所有的神经网络模型和训练方法。
这种方法实际上等同于熵正则化(Granvalet等人,2006年)。类概率的条件熵可用于类重叠的度量。通过最小化未标记数据的熵,可以减少类概率分布的重叠性。它支持类之间的低密度分离,这是半监督学习的常见先验假设。

2论文的创新点

3 论文方法的概述

3.1 思路

伪标签是未标记数据的目标类,就好像它们是真标签一样。我们只选取对每个未标记样本具有最大预测概率的类别。
y i ′ = { 1 if  i = argmax i ′ f i ′ ( x ) 0 otherwise y_i^{\prime}=\begin{cases}1&\text{if }i=\text{argmax}_{i'}f_{i'}(x)\\0&\text{otherwise}\end{cases} yi={10if i=argmaxifi(x)otherwise我们在Dropout的微调阶段使用伪标签。用标记和未标记的数据同时以有监督的方式训练预先训练的网络。对于未标记的数据,每次权值更新重新计算的伪标签被用于相同的监督学习任务的损失函数。
由于有标签数据和无标签数据的总数有很大不同,并且它们之间的训练平衡对网络性能非常重要,因此总体损失函数为 L = 1 n ∑ m = 1 n ∑ i = 1 C L ( y i m , f i m ) + α ( t ) 1 n ′ ∑ m = 1 n ′ ∑ i = 1 C L ( y i ′ m , f i ′ m L=\frac{1}{n}\sum_{m=1}^{n}\sum_{i=1}^{C}L(y_{i}^{m},f_{i}^{m})+\alpha(t)\frac{1}{n'}\sum_{m=1}^{n'}\sum_{i=1}^{C}L(y_{i}^{\prime m},f_{i}^{\prime m} L=n1m=1ni=1CL(yim,fim)+α(t)n1m=1ni=1CL(yim,fim
其中n是SGD的已标记数据中的批次数, n ′ n\prime n用于未标记数据, f i m f^m_i fim是已标记数据中 m m m个样本的输出单位, y i m y^m_i yim是标签, f i ′ m f^{\prime m}_{i} fim用于未标记数据, y i ′ m y^{\prime m}_{i} yim是未标记数据的伪标签, α ( t ) \alpha(t) α(t)是平衡它们的系数。
α ( t ) \alpha(t) α(t)的合理调度对网络性能非常重要。如果 α ( t ) \alpha(t) α(t)太高,即使对于已标记的数据,也会干扰训练。而如果 α ( t ) \alpha(t) α(t)太小了,我们就不能利用未标记数据的好处。此外, α ( t ) \alpha(t) α(t)缓慢增加的确定性退火过程有望帮助优化过程避免较差的局部极小值,从而使未标记数据的伪标签尽可能类似于真实标签。 α ( t ) = { 0 t < T 1 t − T 1 T 2 − T 1 α f T 1 ≤ t < T 2 α f T 2 ≤ t \alpha(t)=\begin{cases}0&t<T_1\\\frac{t-T_1}{T_2-T_1}\alpha_f&T_1\leq t<T_2\\\alpha_f&T_2\leq t\end{cases} α(t)= 0T2T1tT1αfαft<T1T1t<T2T2t α f {\alpha}_f αf=3、 T 1 T_1 T1=100、 T 2 T_2 T2=600的情况下,不进行预训练;在DAE的情况下, T 1 T_1 T1=200、 T 2 T_2 T2=800。

3.2 Pseudo-Label为什么有效?

半监督学习的目标是利用未标记的数据来提高泛化性能。聚集学习假设指出,决策边界应位于低密度区域,以提高泛化性能。
最近提出的使用流形学习训练神经网络的方法,如半监督嵌入和流形切线分类器,都利用了这一假设。半监督嵌入使用基于嵌入的正则化来提高深度神经网络的泛化性能。由于数据样本的邻居通过嵌入惩罚项与样本具有相似的激活,因此高密度区域的数据样本更有可能具有相同的标签。流形切线分类器鼓励网络输出对低维流形方向的变化不敏感。因此,同样的目的也达到了。

3.3 Entropy Regularization

在最大后验估计的框架下,熵正则化是一种从未标记数据中获益的方法。该方案通过最小化未标记数据的类概率的条件熵来支持类之间的低密度分离,而不需要对密度进行任何建模。 H ( y ∣ x ′ ) = − 1 n ′ ∑ m = 1 n ′ ∑ i = 1 C P ( y i m = 1 ∣ x ′ m ) log ⁡ P ( y i m = 1 ) H(y|x')=-\frac{1}{n'}\sum_{m=1}^{n'}\sum_{i=1}^{C}P(y_{i}^{m}=1|x'^{m})\operatorname{log}P(y_{i}^{m}=1) H(yx)=n1m=1ni=1CP(yim=1∣xm)logP(yim=1)
其中 n ′ n^\prime n是未标记数据的数目, C C C是类数, y i m y^m_i yim是第 m m m个未标记样本的未知标记, x ′ m x^{\prime m} xm是第m个未标记样本的输入向量,熵是类重叠的一种度量。随着类重叠的减少,决策边界上的数据点密度变得更低。
MAP估计被定义为后验分布的最大值: C ( θ , λ ) = ∑ m = 1 n log ⁡ P ( y m ∣ x m ; θ ) − λ H ( y ∣ x ′ ; θ ) C(\theta,\lambda)=\sum_{m=1}^n\log P(y^m|x^m;\theta)-\lambda H(y|x';\theta) C(θ,λ)=m=1nlogP(ymxm;θ)λH(yx;θ)
其中n是标记数据的数目, x m x^m xm是第 m m m个标记样本, λ λ λ是平衡两项的系数。通过最大化已标记数据(第一项)的条件对数似然和最小化未标记数据(第二项)的熵,可以获得更好的泛化性能。
图1示出了t-SNE 在MNIST测试数据(未包括在未标记数据中)的网络输出的2D嵌入结果。神经网络用600个已标记数据以及60000个未标记数据和伪标签进行训练。虽然在两种情况下训练误差为零,但通过使用未标记数据和伪标签进行训练,测试数据的网络输出更接近于1-OFK码,换言之,(17)的熵被最小化。
在这里插入图片描述
表2显示了(17)的估计熵。虽然两种情况下已标记数据的熵都接近于零,但通过伪标签训练,未标记数据的熵变低,另外,测试数据的熵也随之降低。这使得分类问题变得更容易,甚至对于测试数据也是如此,并且使得决策边界处的数据点密度更低。根据聚类假设,我们可以得到更好的泛化性能。
在这里插入图片描述

4 实验

4.1 手写数字识别(MNIST)

MNIST是深度学习文献中最著名的数据集之一。为了进行比较,我们使用了相同的半监督设置。我们将标记的训练集的大小减少到100、600、1000和3000。训练集在每个标签上具有相同数量的样本。对于验证集,我们拿到了1000个标签验证。我们使用验证集来确定一些超参数。其余数据用于未标记的数据。由于我们不能得到相同的数据集分割,所以使用相同的网络和参数进行了10个随机分割实验。在100个标记数据的情况下,结果在很大程度上依赖于数据分割,因此进行了30个实验。对于100个标记数据,95%的可信区间约为±1∼1.5%,对于600个标记数据,约为±0.1∼0.我们使用具有1个隐含层的神经网络。激活函数使用Relu函数,输出单元采用Sigmoid单元。隐藏单元的数量为5000个。15%,对于1000和3000个标记数据,小于±0.1%。为了进行优化,我们使用了带Dropout1的小批次随机梯度下降算法,初始学习率为1.5,有标签数据的小批次数为32,未标签数据的小批次数为256。这些参数是使用验证集确定的。表2将我们的方法与其他方法的结果进行了比较。我们的方法虽然简单,但在处理小标签数据时表现优于传统的方法。该训练方案比流形切线分类器简单,并且不使用半监督嵌入中使用的样本之间的计算代价高昂的相似性矩阵。在这里插入图片描述

5总结

在这项工作中,我们展示了一种简单有效的神经网络半监督学习方法。该方法在不需要复杂的训练方案和计算代价较高的相似矩阵的情况下,具有最好的性能。

6复现

def train(net, trainloader, criterion, n_labeled, labeled_bs, device='cuda'):
    '''训练网络
    n_labeled: 标记数据的数量
    labeled_bs: 标记数据的批量大小
    '''
    learning_rate = 1e-3  # 学习率
    epochs = 200  # 训练的轮数

    optimizer = optim.Adam(net.parameters(), lr=learning_rate)  # Adam优化器

    T1 = 100  # 第一个阈值
    T2 = 600  # 第二个阈值
    alpha = 0  # 初始alpha值
    af = 0.3  # alpha的最大值

    for epoch in range(epochs):
        net.train()  # 设置网络为训练模式
        running_loss = 0.0  # 累积的损失
        running_acc = 0.0  # 累积的准确度

        for img, label in trainloader:
            img = img.to(device)  # 将图片移动到指定设备
            label = label.to(device)  # 将标签移动到指定设备

            # 将数据分成标记数据和未标记数据
            img_labeled = img[:labeled_bs]  # 获取标记数据
            label_labeled = label[:labeled_bs]  # 获取标记数据的标签

            img_unlabeled = img[labeled_bs:]  # 获取未标记数据

            # 标记数据的前向传播

            output_labeled = net(img_labeled)  # 计算标记数据的输出

            # 未标记数据的前向传播
            if epoch > T1:
                alpha = (epoch - T1) / (T2 - T1) * af  # 计算alpha值
                if epoch > T2:
                    alpha = af  # 设置alpha为最大值

            # 为未标记数据生成伪标签
            output_unlabeled = net(img_unlabeled)  # 计算未标记数据的输出
            _, pseudo_labels = torch.max(output_unlabeled, 1)  # 获取伪标签

            # 半监督损失
            loss = criterion(output_labeled, label_labeled) + alpha * criterion(output_unlabeled, pseudo_labels)  # 计算总损失

            # 反向传播并更新网络
            optimizer.zero_grad()  # 清空梯度
            loss.backward()  # 反向传播
            optimizer.step()  # 更新参数

            # 计算训练损失和训练准确度
            running_loss += loss.item()  # 累积损失
            _, predicted = torch.max(output_labeled, 1)  # 获取预测结果
            correct_num = (predicted == label_labeled).sum().item()  # 计算正确预测的数量
            running_acc += correct_num  # 累积准确度

        running_loss /= n_labeled  # 计算平均损失
        running_acc /= n_labeled  # 计算平均准确度
        print('Train[%d / %d] loss: %.5f, Acc: %.2f%%' % (epoch + 1, epochs, running_loss, 100 * running_acc))  # 打印当前轮次的损失和准确度

相关推荐

  1. 论文阅读笔记】清单

    2024-07-09 17:50:07       89 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-09 17:50:07       169 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-09 17:50:07       185 阅读
  3. 在Django里面运行非项目文件

    2024-07-09 17:50:07       155 阅读
  4. Python语言-面向对象

    2024-07-09 17:50:07       169 阅读

热门阅读

  1. 单例模式的实现

    2024-07-09 17:50:07       28 阅读
  2. 【MIT 6.5840/6.824】Lab1 MapReduce

    2024-07-09 17:50:07       33 阅读
  3. 【云原生】Kubernetes之持久化

    2024-07-09 17:50:07       35 阅读
  4. urlib Python爬虫

    2024-07-09 17:50:07       41 阅读
  5. 【MySQL】SQL中的DROP、DELETE和TRUNCATE的区别

    2024-07-09 17:50:07       51 阅读
  6. 云原生监控-Kubernetes-Promethues-Grafana

    2024-07-09 17:50:07       45 阅读
  7. arm (exti中断)

    2024-07-09 17:50:07       42 阅读
  8. LRU Cache 双向链表以及STL list实现----面试常考

    2024-07-09 17:50:07       41 阅读
  9. gitlab每日备份以及restore

    2024-07-09 17:50:07       45 阅读
  10. Centos安装Node.js

    2024-07-09 17:50:07       53 阅读