自从我的 One-Shot 学习项目大约 3 周以来,我一直在苦苦挣扎。我正试图用我的脸解锁我的电脑。不幸的是,我离这个任务还很远。首先,我想很好地理解 one-shot learning 背后的概念,尤其是三元组损失。所以知道我尝试用转移学习训练一个网络(在 PyTorch 中),这将引导我实现我的目标。
到目前为止我所理解的:
一键学习
- 这是一种方法,模型应该能够最小化同一个人的两张脸的两个嵌入之间的欧几里德距离,相反,最大化不同人的两张脸之间的欧几里德距离。换句话说,模型应该将任何一张脸放在一个d维欧几里得空间中,相同的人彼此靠近,不同的人彼此远离。
- 这个模型不应该特别用已知的身份进行训练。换句话说,一旦训练有素,任何人都可以使用它来比较他/她的一张固定的、未更改的面部照片和他/她的另一张脸。
- 人脸验证是最大化不属于(比方说)授权人的任何人脸之间的距离并仅最小化属于授权人的人脸之间的距离的能力(1:1 问题)。
- 人脸识别是最大化任何不属于(比方说)授权人的人脸之间的距离并最小化属于一组授权人的人脸之间的距离的能力(1:K 问题)。
三元组挖掘
为确保模型能够学习信息,需要为其提供定义明确且不明显的三元组。对于面部数据集,这会导致:
三元组,例如 [for all (i,j,k) distincts] : face[i] == face[j]; 和面[i]!=面[k];和面[j]!=面[k]
这些三元组称为“有效三元组”,面被定义为锚;正面和负面。
诸如欧几里得空间中的面之类的三元组彼此之间的距离并不远(防止崩溃到零的琐碎损失)。它们被定义为半硬和硬三元组。
根据这些基线,我在互联网上寻找示例。我了解到,生产三胞胎的常用方式是在线挖矿或离线挖矿。我使用https://omoindrot.github.io/triplet-loss的绝妙代码实现了在线挖矿的批量硬策略和批量所有策略。
我的问题:
从这一点开始,我有点迷路了。我尝试了不同的方法来构建我的数据集,但我的损失永远不会收敛。该模型似乎没有学到任何东西。
我的方法描述(通过 PyTorch)
模型和数据集
我InceptionResnetV1
从pytorch_facenet
图书馆使用,用 Casia-Webfaces 预训练。我解冻了最后两层:线性层model.last_linear(1792, 512)
,model.last_bn()
它引导我得到 918,528 个可训练参数和一个 dim (512,1) 的输出嵌入。
对于数据集,我使用的是HeadPoseImageDatabase,它是一个包含 15 个人的数据集,每个人:2 张正面图片和 186 张不同的头部姿势图片。这导致了一组 2797 张图片(一个人有 193 张图片)和 30 张正面图片。
我的工作
我知道模型应该看到各种身份。所以首先,我尝试了
nn.TripletMarginLoss
PyTorch 并提供了一个锚点(每个身份的两个正面图片之一);正面(与主播身份相关的183张图片之一);和否定的(具有不同身份的随机其他面孔)。
这是不成功的:损失似乎减少了,但模型并没有在测试集上泛化。
我想也许我没有为损失提供足够的半硬或硬三元组,所以我构建了与每个身份“ i ”相关的 15 个数据集:每个数据集包含身份“i”的正面面孔和其他负面面孔。因此,每个数据集包含 2797 张图像并返回带有标签的图像(如果人脸的身份对应于数据集I ,则为 1,否则为 0)。我在每个身份数据集上做了一个循环(每个数据集中都有一个批处理循环)。这次我使用了批处理( https://omoindrot.github.io/triplet-loss),但还是失败了。
问题
- 我是否需要创建一个更简单的模型并从头开始训练它?
- 我的方法看起来是否正确:Anchor 是否应该通过与 Positive 和 Negative 相同的模型?
- 我应该如何设置边距?
- 关于人脸验证我上面的说法是否正确?我希望在没有我的照片的情况下训练我的模型,然后能够最小化/最大化任何嵌入面之间的欧几里德距离。这是对的吗 ?
- 作为一个小项目(即大约 95%),这项工作是否可行且具有不错的准确性?
谢谢大家的时间,我希望我的解释很清楚。下面我给你一段代码。
model = InceptionResnetV1(pretrained='casia-webface')
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
num_epochs = 10
for param in model.parameters():
param.requires_grad = False
model.last_linear.weight.requires_grad = True
model.last_bn.weight.requires_grad = True
. . .
. . .
. . .
Block8-511 [-1, 1792, 3, 3] 0
AdaptiveAvgPool2d-512 [-1, 1792, 1, 1] 0
Dropout-513 [-1, 1792, 1, 1] 0
Linear-514 [-1, 512] 917,504
BatchNorm1d-515 [-1, 512] 1,024
================================================================
Total params: 23,482,624
Trainable params: 918,528
Non-trainable params: 22,564,096
----------------------------------------------------------------
Input size (MB): 0.29
Forward/backward pass size (MB): 88.63
Params size (MB): 89.58
Estimated Total Size (MB): 178.50
----------------------------------------------------------------
def model_loop(model, epochs, trainloader, batch_size, pytorchLoss, optimizer, device):
### This model uses the PyTorchMarginLoss with a training set of 4972 images
### separate between Anchors / Positives / Negatives
delta_time = datetime.timedelta(hours=1)
timezone = datetime.timezone(offset=delta_time)
model.to(device)
train_loss_list = []
size_train = len(trainloader.dataset)
for epoch in range(num_epochs):
t = datetime.datetime.now(tz=timezone)
str_t = '{:%Y-%m-%d %H:%M:%S}'.format(t)
print(f"{str_t} : Epoch {epoch+1} on {device} \n---------------------------")
train_loss = 0.0
model.train()
for batch, (imgsA, imgsP, imgsN) in enumerate(trainloader):
# Transfer Data to GPU if available
imgsA, imgsP, imgsN = imgsA.to(device), imgsP.to(device), imgsN.to(device)
# Clear the gradients
optimizer.zero_grad()
# Make prediction & compute the mini-batch training loss
predsA, predsP, predsN = model(imgsA), model(imgsP), model(imgsN)
loss = pytorchLoss(predsA, predsP, predsN)
# Compute the gradients
loss.backward()
# Update Weights
optimizer.step()
# Aggregate mini-batch training losses
train_loss += loss.item()
train_loss_list.append(train_loss)
if batch == 0 or batch == 19:
loss, current = loss.item(), batch*BATCH_SIZE + len(imgsA)
print(f"mini-batch loss for training : \
{loss:>7f} [{current:>5d}/{size_train:>5d}]")
# Compute the global training & as the mean of the mini-batch losses
train_loss /= len(trainloader)
print(f"--Fin Epoch {epoch+1}/{epochs} \n Training Loss: {train_loss:>7f}" )
print('\n')
return train_loss_list
train_loss = model_loop(model = model,
epochs = num_epochs,
trainloader = train_dataloader,
batch_size = 256,
pytorchLoss = nn.TripletMarginLoss(margin=0.1),
optimizer = optimizer,
device = device)
2022-02-18 20:26:30 : Epoch 1 on cuda
-------------------------------
mini-batch loss for training : 0.054199 [ 256/ 4972]
mini-batch loss for training : 0.007469 [ 4972/ 4972]
--Fin Epoch 1/10
Training Loss: 0.026363
2022-02-18 20:27:48 : Epoch 5 on cuda
-------------------------------
mini-batch loss for training : 0.005694 [ 256/ 4972]
mini-batch loss for training : 0.011877 [ 4972/ 4972]
--Fin Epoch 5/10
Training Loss: 0.004944
2022-02-18 20:29:24 : Epoch 10 on cuda
-------------------------------
mini-batch loss for training : 0.002713 [ 256/ 4972]
mini-batch loss for training : 0.001007 [ 4972/ 4972]
--Fin Epoch 10/10
Training Loss: 0.003000
Stats through a dataset of 620 images :
TP : 11.25%
TN : 98.87%
FN : 88.75%
FP : 1.13%
The model accuracy is 55.06%