弱监督学习是一种机器学习方法,其训练过程中使用的标签信息相对不完整或不精确。与传统的监督学习不同,弱监督学习可以利用不完全的标记信息来进行模型训练,这些信息可能是不精确的、嘈杂的或者只有部分标注的数据。本文将通过 A brief introduction to weakly supervised learning 这篇文献,了解弱监督学习。
Weakly supervised learning is a machine learning method that uses relatively incomplete or imprecise labeling information during training. Unlike traditional supervised learning, weakly supervised learning can utilize incomplete labeling information for model training, which may be imprecise, noisy, or only partially labeled data. In this paper, we will learn about weakly supervised learning through A brief introduction to weakly supervised learning this literature.
文献链接:A brief introduction to weakly supervised learning
监督学习技术通过从大量训练示例中学习来构建预测模型,其中每个训练示例都有一个指示其真实输出的标签。尽管当前技术取得了巨大成功,但值得注意的是,在许多任务中,由于数据标记过程的成本高昂,很难获得像完全真实标签这样的强监督信息。因此,机器学习技术需要在弱监督下工作。本文回顾了弱监督学习的一些研究进展,重点讨论了三种典型的弱监督类型:不完全监督,仅给训练数据的子集加上标签;不精确的监督,训练数据仅带有粗粒度的标签;以及不准确的监督,给定的标签并不总是真实的。
机器学习在各种任务中取得了巨大的成功,特别是在分类和回归等监督学习任务中。通常,预测模型是从包含大量训练示例的训练数据集中学习的,每个训练示例对应一个事件/对象。训练示例由两部分组成:描述事件/对象的特征向量(或实例),以及指示真实输出的标签。
在分类中,标签表示训练样例所属的类,在回归中,标签是与示例相对应的实值响应。大多数成功的技术,例如深度学习 ,都需要为大型训练数据集提供真实标签。然而,在许多任务中,由于数据标记过程的成本高昂,很难获得强有力的监督信息。因此,机器学习技术需要能够在弱监督下工作。
通常,弱监督分为三种类型。分别如下:
弱监督学习是一个涵盖各种研究的总称,这些研究试图通过弱监督学习来构建预测模型。在本文中,作者讨论了这方面研究的一些进展,重点关注不完整、不精确和不准确监督下的学习。为了简单起见,在本文中,作者考虑关于两个可交换类 Y Y Y 和 N N N 的二元分类。
形式上,具有强监督学习的任务是从训练数据集
D
=
{
(
x
1
,
y
1
)
,
.
.
.
,
(
x
m
,
y
m
)
)
}
D=\left \{ (x_{1},y_{1}),... ,(x_{m},y_{m})) \right \}
D={(x1?,y1?),...,(xm?,ym?))} 中学习
f
:
X
~
Y
f:X \sim Y
f:X~Y,其中
X
X
X 是特征空间,
Y
=
{
Y
,
N
}
Y=\left \{ Y,N \right \}
Y={Y,N},
x
i
?
X
x_{i}\epsilon X
xi??X,
y
i
?
Y
y_{i}\epsilon Y
yi??Y,作者假设
(
x
i
,
y
i
)
(x_i , y_i )
(xi?,yi?) 根据未知的相同且独立的分布
D
D
D 生成;换句话说,
(
x
i
,
y
i
)
(x_i , y_i )
(xi?,yi?) 是独立同分布的samples。图 1 展示了我们将在本文中讨论的三种弱监督类型。
不完全监督涉及这样的情况:给我们少量的标记数据,不足以训练一个好的学习器,而有大量的未标记数据可用。形式上,任务从训练数据集 D = ( x 1 , y 1 ) , . . . , ( x l , y l ) , x l + 1 , . . . , x m D ={(x_1, y_1),..., (x_l , y_l ), x_{l+1},..., x_m} D=(x1?,y1?),...,(xl?,yl?),xl+1?,...,xm? 学习 f : X → Y f : X → Y f:X→Y,其中有 l l l 个标记训练示例(即用 y i y_{i} yi? 给出的示例)和 u = m ? l u = m ? l u=m?l 未标记实例的数量;其他条件与强监督的监督学习相同,如引言末尾所定义。为了方便讨论,作者将 l l l 个标记实例称为“标记数据”,将 u u u 个未标记实例称为“未标记数据”。
主动学习假设存在一个“预言机”,例如人类专家,可以查询该“预言机”以获得所选未标记实例的真实标签。相比之下,半监督学习尝试自动利用除了标记数据之外的未标记数据来提高学习性能,而无需人工干预。有一种特殊的半监督学习称为传导式学习。这种学习和(纯)半监督学习之间的主要区别在于它们对测试数据(即由训练模型预测的数据)的不同假设。传导式学习持有“封闭世界”假设,即测试数据是提前给出的,目标是优化测试数据上的性能;换句话说,未标记的数据正是测试数据。纯半监督学习持有“开放世界”假设,即测试数据未知,未标记的数据不一定是测试数据。图2直观地展示了主动学习、(纯)半监督学习和转导学习之间的区别。
主动学习假设可以从预言机中查询未标记实例的真实标签。为简单起见,假设标记成本仅取决于查询数量。因此,主动学习的目标是最小化查询数量,从而最小化训练好的模型的标记成本。给定一小组标记数据和大量未标记数据,主动学习尝试选择最有价值的未标记实例进行查询。有两个广泛使用的选择标准,即信息性和代表性。
信息性衡量未标记实例有助于减少统计模型的不确定性的程度,而代表性衡量实例有助于表示输入模式结构的程度。
基于信息量的方法的主要弱点在于,它们严重依赖标记数据来构建初始模型来选择查询实例,并且当只有少数标记示例可用时,性能通常不稳定。基于代表性的方法的主要弱点在于,性能很大程度上取决于未标记数据主导的聚类结果,尤其是当只有少数标记示例时。因此,最近的几种主动学习方法试图利用信息性和代表性。
半监督学习尝试在人工不干预的情况下利用未标记的数据。人们可能会好奇为什么没有标签的数据可以帮助构建预测模型。为了简单解释,假设数据来自具有 n 个混合分量的高斯混合模型,即
其中
α
i
\alpha_{i}
αi? 是混合系数,
∑
i
=
1
n
α
i
=
1
{\textstyle \sum_{i=1}^{n}}\alpha_i=1
∑i=1n?αi?=1,
Θ
=
{
θ
}
\Theta=\left \{ \theta \right \}
Θ={θ} 是模型参数,在这种情况下,标签
y
i
y_i
yi? 可以被认为是一个随机变量,其分布
P
(
y
i
∣
x
i
,
g
i
)
P (y_i |x_i , g_i )
P(yi?∣xi?,gi?) 由混合分量
g
i
g_i
gi? 和特征向量
x
i
x_i
xi? 确定。根据最大后验准则,我们有模型
其中
该目标是通过根据训练数据估计项
P
(
y
i
=
c
∣
g
i
=
j
,
x
i
)
P (y_{i} = c |g_{i} = j, x_{i} )
P(yi?=c∣gi?=j,xi?) 和
P
(
g
i
=
j
∣
x
i
)
P (g_{i} = j |x_{i} )
P(gi?=j∣xi?) 来实现的。显然,只有第一个术语需要标签信息。因此,未标记的数据可用于帮助改进第二项的估计,从而提高学习模型的性能。图 3 提供了直观的解释。如果我们必须基于唯一的正点和负点进行预测,我们所能做的只是随机猜测,因为测试数据点恰好位于两个标记数据点之间;如果允许我们观察一些未标记的数据点(如图中的灰色部分),我们可以以高置信度将测试数据点预测为正值。在这里,虽然未标记的数据点没有显式地具有标签信息,但它们隐式地传达了一些有关数据分布的信息,这有助于预测建模。
实际上,半监督学习有两个基本假设,即 聚类假设 和 流形假设 ;两者都与数据分布有关。前者假设数据具有固有的簇结构,因此落入同一簇的实例具有相同的类标签。后者假设数据位于流形上,因此附近的实例具有相似的预测。这两个假设的本质在于相信相似的数据点应该具有相似的输出,而未标记的数据有助于揭示哪些数据点相似。
半监督学习方法主要有四大类,即生成方法、基于图的方法、低密度分离方法和基于分歧的方法。
不确切的监督是指给出了一些监督信息,但没有达到预期的精确程度。典型的情况是只有粗粒度的标签信息可用。
例如,在药物活性预测问题中,目标是通过学习一组已知分子来建立一个模型来预测新分子是否有资格制造特殊药物。一个分子可以有多种低能形状,该分子能否用来制造药物取决于该分子是否具有某些特殊的形状。然而,即使对于已知的分子,人类专家也只知道分子是否合格,而不知道哪些特殊形状具有决定性。
形式上,任务是从训练数据集 D = ( X 1 , y 1 ) , . . . , ( X m , y m ) D={(X_1, y_1), ..., (X_m, y_m)} D=(X1?,y1?),...,(Xm?,ym?) 学习 f : X → Y f : X → Y f:X→Y,其中 X i = x i 1 , . . . , x i , m i ? X X_i ={x_{i 1},..., x_{i, mi} }? X Xi?=xi1?,...,xi,mi??X 称为包, x i j ∈ X ( j ∈ 1 , . . . , m i ) x_{ij} ∈ X (j ∈ {1, ..., m_i}) xij?∈X(j∈1,...,mi?) 是实例, m i m_i mi? 是 X i X_i Xi? 中实例的数量, y i ∈ Y = { Y , N } y_i ∈ Y =\left \{ Y,N \right \} yi?∈Y={Y,N}。 X i X_{i} Xi? 是正包,即 y i = Y y_{i} =Y yi?=Y,如果存在 x i p x_{ip} xip? 为正,而 p ∈ 1 , . . . , m i p ∈ {1, ..., m_i} p∈1,...,mi? 未知。目标是预测未见过的袋子的标签。这称为多实例学习。
人们已经为多实例学习开发了许多有效的算法。实际上,几乎所有监督学习算法都有其多实例同行。大多数算法试图将单实例监督学习算法适应多实例表示,主要是将重点从对实例的区分转移到对包的区分;其他一些算法试图通过表示转换使多实例表示适应单实例算法。还有一种分类,将算法分组为实例空间范式,其中实例级响应被聚合;袋子空间范式,其中袋子被视为一个整体;以及嵌入式空间范式,其中学习是在嵌入式特征空间中进行的。实例通常被视为独立同分布的samples。
多实例学习已成功应用于各种任务,例如图像分类/检索/注释、文本分类、垃圾邮件检测、医学诊断、人脸/物体检测 ,对象类发现,对象跟踪等。
在这些任务中,很自然地将真实对象(例如图像或文本文档)视为一个包;然而,与药物活性预测相反,在药物活性预测中,袋子中存在自然形成的实例(即分子的形状),需要为每个袋子生成实例。包生成器指定如何生成实例来构成包。通常,可以从图像中提取许多小块作为其实例,而章节/段落甚至句子可以用作文本文档的实例。
多实例学习的最初目标是预测未见过的袋子的标签;然而,有研究试图确定使阳性袋子呈阳性的关键实例。这对于在没有细粒度标记训练数据的情况下定位图像中感兴趣区域等任务非常有帮助。值得注意的是,标准多实例学习假设每个正袋必须包含一个关键实例,而有研究假设没有关键实例并且每个实例都对袋标签有贡献,或者甚至假设有多个概念,并且只有当包包含满足每个概念的实例时,包才是正的。更多变体可以在中找到。
监督不准确是指监督信息不真实的情况;换句话说,某些标签信息可能会出现错误。该公式与引言末尾所示的几乎相同,只是训练数据集中的 y i y_i yi? 可能不正确。
一个典型的场景是使用标签噪声进行学习。有很多理论研究,其中大多数假设随机分类噪声,即标签受到随机噪声的影响。在实践中,一个基本的想法是识别可能被错误标记的示例,然后尝试进行一些纠正。例如,数据编辑方法构建了一个相对邻域图,其中每个节点对应于一个训练示例,连接具有不同标签的两个节点的边称为切割边。然后,测量切边权重统计数据,直觉上认为如果实例与许多切边相关联,则该实例是可疑的。可疑实例可以被删除或重新标记,如图6所示。值得一提的是,这种方法通常依赖于咨询邻域信息,因此,它们在高维特征空间中不太可靠,因为邻域的识别通常不太可靠。
当存在强大的监督信息(例如带有真实标签的大量训练示例)时,监督学习技术取得了巨大的成功。然而,在实际任务中,收集监督信息需要成本,因此,通常希望能够进行弱监督学习。
弱监督学习的创新点在于,利用只有部分标记或不完全准确的标记数据进行训练,并尝试从中学习出准确的模型。与传统的监督学习不同,其中要求全部数据都有准确的标签,弱监督学习通过利用标记不完全的数据集进行训练,克服了大量标记数据的获取和标注的困难,从而实现了更高的效率和可扩展性。
弱监督学习具有以下几个创新点:
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class Test_module(nn.Module):
def __init__(self):
super(Test_module, self).__init__()
self.module1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
Test_module = Test_module()
print(Test_module)
input = torch.ones((64, 3, 32, 32))
writer = SummaryWriter("./logs_seq")
writer.add_graph(Test_module, input)
writer.close()
运行结果:
# 对现有的模型进行修改
import torchvision
from torch import nn
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
train_data = torchvision.datasets.CIFAR10('./data', train=True, transform=torchvision.transforms.ToTensor(),
download=True)
vgg16_true.classifier.add_module('add_Linear', nn.Linear(1000, 10))
print(vgg16_true)
运行结果:
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset, batch_size=1)
class Test_module(nn.Module):
def __init__(self):
super(Test_module, self).__init__()
self.module1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
loss = nn.CrossEntropyLoss()
Test_module = Test_module()
optim = torch.optim.SGD(Test_module.parameters(), lr=0.01)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = Test_module(imgs)
result_loss = loss(outputs, targets)
optim.zero_grad()
result_loss.backward()
optim.step()
running_loss = running_loss + result_loss
print(running_loss)
运行结果:
一共20轮训练,控制台打印出的是每轮训练的总损失。
网络架构
文件结构:
神经网络:model.py
# 搭建神经网络
import torch
from torch import nn
class Test_module(nn.Module):
def __init__(self):
super(Test_module, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
if __name__ == '__main__':
Test_module = Test_module()
input = torch.ones(64, 3, 32, 32)
output = Test_module(input)
print(output.shape)
整体模型训练:
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="./train_data", train=True, transform=torchvision.transforms.ToTensor(), download=True)
test_data = torchvision.datasets.CIFAR10(root="./test_data", train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))
# 利用 DataLoader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 创建网络模型
Test_module = Test_module()
# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(Test_module.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter("./logs_train")
for i in range(epoch):
print("------第 {} 轮训练开始------".format(i+1))
for data in train_dataloader:
# 训练步骤开始
imgs, targets = data
outputs = Test_module(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step += 1
if total_train_step % 100 == 0:
print("训练次数:{}, loss:{}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_test_step)
# 测试步骤开始
total_test_loss = 0
# 整体的正确率
total_accuracy = 0
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
outputs = Test_module(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
total_test_step += 1
# 保存训练的模型
torch.save(Test_module, "Test_module_{}.pth".format(i))
print("模型已保存")
writer.close()
运行结果:
tensorboard图示:
这周了解了弱监督学习,同时也针对一个神经网络,用pytorch进行了代码实现和模型训练,整体来说收获还是很大的,下周将继续阅读文献,同时将之前学习过的神经网络逐个实现,加油~