赞
踩
pytorch-metric-learning包含9个模块,每个模块都可以在现有的代码库中独立使用,或者组合在一起作为一个完整的训练、测试工作流。

损失函数可以使用距离、规约方法和正则化方法来进行定制化。在下面的图表中,miner在批量中找到难训练的样本对的索引,这些索引被索引到距离矩阵。

Distance类用来计算成对的embedding之间的距离或者相似度。以三元组方法TripletMarginLoss为例,三元组的表示为<anchor, positive, negative>。其中anchor和positive构成正样本对,anchor和negative构成负样本对。
### TripletMarginLoss with squared L2 distance ###
from pytorch_metric_learning.distances import LpDistance
loss_func = TripletMarginLoss(margin=0.2, distance=LpDistance(power=2))
### TripletMarginLoss with unnormalized L1 distance ###
loss_func = TripletMarginLoss(margin=0.2, distance=LpDistance(normalize_embeddings=False, p=1))
### TripletMarginLoss with signal-to-noise ratio###
from pytorch_metric_learning.distances import SNRDistance
loss_func = TripletMarginLoss(margin=0.2, distance=SNRDistance())
### TripletMarginLoss with cosine similarity##
from pytorch_metric_learning.distances import CosineSimilarity
loss_func = TripletMarginLoss(margin=0.2, distance=CosineSimilarity())
备注: 所有的 losses、miners和regularizers都可以接受distance参数,但是有些方法有distance类型的限制,比如只能使用 CosineSimilarity 或者 DotProductSimilarity等,具体可以参考losses 页面
所有的embedding距离/相似度计算子类都继承并扩展自BaseDistance类。
distances.BaseDistance(collect_stats = False,
normalize_embeddings=True,
p=2,
power=1,
is_inverted=False)
继承BaseDistance类需要实现以下两个方法:
# Must return a matrix where mat[j,k] represents the distance/similarity between query_emb[j] and ref_emb[k]
def compute_mat(self, query_emb, ref_emb):
raise NotImplementedError
# Must return a tensor where output[j] represents the distance/similarity between query_emb[j] and ref_emb[j]
def pairwise_distance(self, query_emb, ref_emb):
raise NotImplementedError
没用过,也没搞懂。
余弦相似度,当embedding做了模为 1 的归一化之后,等于点积相似度 DotProductSimilarity。
返回两个embedding向量的点积结果,当embedding做了模为 1 的归一化之后,等于余弦相似度 CosineSimilarity。
Lp范数,默认是L2范数,也就是欧几里得距离。
from pytorch_metric_learning import losses
loss_func = losses.SomeLoss()
loss = loss_func(embeddings, labels) # in your training for-loop
from pytorch_metric_learning import miners
miner_func = miners.SomeMiner()
loss_func = losses.SomeLoss()
miner_output = miner_func(embeddings, labels) # in your training for-loop
loss = loss_func(embeddings, labels, miner_output)
loss = loss_func(embeddings, indices_tuple=pairs)
# it also works with ref_emb
loss = loss_func(embeddings, indices_tuple=pairs, ref_emb=ref_emb)
from pytorch_metric_learning import reducers
reducer = reducers.SomeReducer()
loss_func = losses.SomeLoss(reducer=reducer)
loss = loss_func(embeddings, labels) # in your training for-loop
loss_func = losses.SomeLoss()
# anchors will come from embeddings, positives/negatives will come from ref_emb
loss = loss_func(embeddings, labels, ref_emb=ref_emb, ref_labels=ref_labels)
loss_func = losses.SomeClassificationLoss()
logits = loss_func.get_logits(embeddings)
目前支持以下损失函数,不同的损失函数具有不同的原理,适用于不同的场景,拥有不太一样的配置参数。有些损失函数对于distance有特殊的要求,默认的reducer可能有区别,详情参考官网。pytorch-metric-learning代码当前支持以下损失函数:
Miners的作用是在训练期间,在每个batch内挖掘更难被模型区分开的<anchor, positive, negative>样本对,这些样本对模型的性能影响最大,带来的损失更多。参照“1.1 自定义度量学习损失函数”,miner返回的是样本对的index,如[i, j],根据index可以对应到batch内embeddings构成的distance矩阵上,得到对应样本对的距离值或者相似度值,也可以理解为损失值。
Miner函数接受一个大小为 n 的批量embedding,返回 k对或者k个三元组的索引,用于计算损失:
Miner需要与损失函数配合使用,不同的Miner可能适用的损失函数也不同,具体可以参考官网。Miner与损失函数结合使用的方式如下:
from pytorch_metric_learning import miners, losses
miner_func = miners.SomeMiner()
loss_func = losses.SomeLoss()
miner_output = miner_func(embeddings, labels)
losses = loss_func(embeddings, labels, miner_output)
pytorch-metric-learning代码当前支持以下Miner方法:
Reducers是用来执行如何把batch里面样本产生的每个样本对的损失,汇总成一个总体损失,reducer接收的输入是loss_dict、embeddings、labels。其中loss_dict里面既包含训练损失,也包好正则化损失(对应下文的Regularizer)。方式如下:
self.reducer(loss_dict, embeddings, labels)
下面是Reducer起作用的地方:
这里是reducer适合这个库的过滤器和计算流的地方
reducer在代码中的使用方式如下:
from pytorch_metric_learning import losses, reducers
reducer = reducers.SomeReducer()
loss_func = losses.SomeLoss(reducer=reducer)
loss = loss_func(embeddings, labels) # in your training for-loop
在内部实现上,损失函数创建一个包含损失和其他信息的字典。reducer执行规约,并返回一个值,在这个值上可以调用backward()。大多数reducer都是这样编写的,它们可以被传递到任何损失函数中。
pytorch-metric-learning代码当前支持以下Reducer方法:
[0, 2, 0, 3] -> (2+3) / 2 = 2.5
比如ContrastiveLoss有两个子loss: pos_loss用于正样本对,neg_loss用于负样本对。在下面的例子中,ThresholdReducer用于pos_loss, MeanReducer用于neg_loss。
from pytorch_metric_learning.losses import ContrastiveLoss
from pytorch_metric_learning.reducers import MultipleReducers, ThresholdReducer, MeanReducer
reducer_dict = {"pos_loss": ThresholdReducer(0.1), "neg_loss": MeanReducer()}
reducer = MultipleReducers(reducer_dict)
loss_func = ContrastiveLoss(reducer=reducer)
ThresholdReducer(low=6):过滤 > 6 的loss求平均
[3, 7, 1, 13, 5] -> (7+13)/2 = 10
ThresholdReducer(high=6): 过滤 < 6 的loss求平均
[3, 7, 1, 13, 5] -> (1+3+5)/3 = 3
ThresholdReducer(low=6, high=12): 过滤 6 < losses < 12 的求平均
[3, 7, 1, 13, 5] -> (7)/1 = 7
正则化方法可以应用于损失函数里面的权重参数(通过weight_regularizer参数指定)和最后输出的embedding特征(通过embedding_regularizer参数指定),不需要标签或元组。
下面是一个将权重正则化传递给损失函数的例子。
from pytorch_metric_learning import losses, regularizers
R = regularizers.RegularFaceRegularizer()
loss = losses.ArcFaceLoss(margin=30, num_classes=100, embedding_size=128, weight_regularizer=R)
备注:

上面说了在损失函数里面的regularizer可以分为weight_regularizer和embedding_regularizer两种,weight_regularizer作用于损失函数里面的权重参数(如果有的话),embedding_regularizer作用于输出的embedding特征向量,并且每个损失函数都可以传入embedding_regularizer参数,但是不一定可以传入weight_regularizer参数。那么怎么确定一个损失函数能不能接受weight_regularizer和embedding_regularizer参数呢?
所有的embedding_regularizer都继承自BaseRegularizer基类,所以所有的Regularizer类都可以作为embedding_regularizer参数传给损失函数,适合作为embedding_regularizer的有:
如果一个损失函数继承了WeightRegularizerMixin类(比如基于SoftmaxLoss的损失函数),那么这个损失函数就可以接受weight_regularizer参数,适合作为weight_regularizer的有:
pytorch-metric-learning里面的Sampler只是torch.utils.data.Sampler类的扩展,需要通过指定sampler参数的方式,传递给PyTorch Dataloader。sampler的目的是确定如何构建batch。前面讲到的Miner样本挖掘方法就是在这里和Sampler采样器配合使用起作用的。
关于模型训练过程中,加载数据集的采样过程可以先看看pytorch中Dataset、Dataloader、Sampler、collate_fn相互关系和使用说明这篇文章,详细介绍了数据采样的过程,以及其中每个步骤的作用。
在PyTorch深度学习框架中,Dataset、Dataloader、Sampler和collate_fn是数据加载和处理过程中非常重要的组成部分。这里大概描述一下数据采样过程中各个步骤的功能,以及它们之间的调用关系:
下面介绍一下pytorch-metric-learning里面支持的几种数据采样方法:
在每次迭代中,这将返回每个类的m个样本,假设批大小是m的倍数。例如,如果您的数据加载器的批大小是100,并且m = 5,那么将返回20个类,每个类有5个样本。注意,如果没有指定batch_size,那么大多数batch中每个类将有m个样本,但不能保证每个batch都是这样。
如果自己定义网络结构、损失函数、训练和验证过程,那么使用上面那些内容就可以了。但是有一些度量学习算法不仅仅是需要设置loss function或miner function。有些算法需要额外的网络、数据扩充、学习率衰减等。Trainers模块的目标是提供对这些类型的度量学习算法的使用。
通常,trainer可以通过如下方式使用:
from pytorch_metric_learning import trainers
t = trainers.SomeTrainingFunction(*args, **kwargs)
t.train(num_epochs=10)
所有的Trainer类都继承自这个积累,并继承了它的__init__参数。
trainers.BaseTrainer(models, optimizers, batch_size, loss_funcs, dataset, mining_funcs=None, iterations_per_epoch=None, data_device=None, dtype=None, loss_weights=None, sampler=None, collate_fn=None, lr_schedulers=None, gradient_clippers=None, freeze_these=(), freeze_trunk_batchnorm=False, label_hierarchy_level=0, dataloader_num_workers=2, data_and_label_getter=None, dataset_labels=None, set_min_label_to_zero=False, end_of_iteration_hook=None, end_of_epoch_hook=None)
scheduler_by_iteration 每个迭代调整一次学习率
scheduler_by_epoch 每个epoch调整一次学习率
scheduler_by_plateau 比如当准确率不在升高时调整学习率具体示例如下:
trunk_scheduler_by_iteration
metric_loss_scheduler_by_epoch
embedder_scheduler_by_plateau
MetricLossOnly这个Trainer只计算输出embedding特征的度量学习损失。
TrainWithClassifier这个Trainer适用于trunk -> embedder -> classifier这样的网络结构。对embedder网络的输出应用度量损失,对classifier网络的输出应用分类损失。
这玩意迷之复杂,我理解大概意思就是用多个embedder头来输出特征,多个头单独计算loss,然后最终的输出就是把多个头的embedding拼接起来,没用过,不知道咋样。
这玩意比上一个级联的网络更玄幻,都用上生成对抗了,理解大概意思就是通过生成假样本来提高模型的特征表达能力和鲁棒性。没用过,不知道咋样。
说的是用两路计算,没看懂怎么个两路法。
在解释模型测试之前先解释几个名词:
Testers使用模型和数据集,计算基于最近邻的准确性度量。请注意,Testers需要faiss包。
Testers的代码示例:
from pytorch_metric_learning import testers
t = testers.SomeTestingFunction(*args, **kwargs)
dataset_dict = {"train": train_dataset, "val": val_dataset}
all_accuracies = tester.test(dataset_dict, epoch, model)
# Or if your model is composed of a trunk + embedder
all_accuracies = tester.test(dataset_dict, epoch, trunk, embedder)
默认情况下,传入tester.test的dataset_dict里面的每一个数据集都会单独的计算测试指标。就是说训练集上的指标是在训练集的样本之间计算的,验证集上的指标实在验证集的样本之间计算的。由tester.test()接受的可选参数splits_to_eval允许更大的灵活性。splits_to_eval是一个(query_split, [list_of_reference_splits])元组的列表。其中query_split用来在测试阶段作为查询样本,list_of_reference_splits用来测试阶段作为参考样本(被查询)。那么通过指定splits_to_eval就可以实现多个样本集之间的交叉验证。
假如dataset_dict里面包含两个数据集,对应dataset_a和train两个key,那么通过设置splits_to_eval参数就可以构造下面几种验证方式:
splits_to_eval = [('dataset_a', ['dataset_a']), ('train', ['train'])]
splits_to_eval = [('dataset_a', ['train'])]
splits_to_eval = [('dataset_a', ['dataset_a', 'train'])]
注意:
所有的testers类都继承自这个积累,并继承了它的__init__参数。
testers.BaseTester(normalize_embeddings=True,
use_trunk_output=False,
batch_size=32,
dataloader_num_workers=2,
pca=None,
data_device=None,
dtype=None,
data_and_label_getter=None,
label_hierarchy_level=0,
end_of_testing_hook=None,
dataset_labels=None,
set_min_label_to_zero=False,
accuracy_calculator=None,
visualizer=None,
visualizer_hook=None,)
BaseTesters中的函数:
all_accuracies = tester.test(
dataset_dict, # dictionary mapping strings to datasets
epoch, # used for logging
trunk_model, # your model
embedder_model=None, # by default this will be a no-op
splits_to_eval=None,
collate_fn=None # custom collate_fn for the dataloader
)
embeddings, labels = tester.get_all_embeddings(
dataset, # Any pytorch dataset
trunk_model, # your model
embedder_model=None, # by default this will be a no-op
collate_fn=None, # custom collate_fn for the dataloader
eval=True, # set models to eval mode
return_as_numpy=False
)
通过在全部的embedding嵌入空间中查询所有的点(而不是子集)来计算最近邻。这可能是您正在寻找的Testers。就是说把每一个query都和embedding嵌入空间中的所有样本进行比较,更能真实的反应模型的性能。这个方法有下面两个问题:
这一般用于层级式的标签数据。比如 {cat, dog, car, truck}对应到{animal, vehicle}
这个对应于 TwoStreamMetricLoss 这个Trainer,dataset的__getitem__返回的必须是(anchor, positive, label)格式的数据
AccuracyCalculator类计算给定query和reference embedding特征的多个准确性度量指标。可以很容易地扩展以创建自定义精度度量方法。
from pytorch_metric_learning.utils.accuracy_calculator import AccuracyCalculator
AccuracyCalculator(include=(),
exclude=(),
avg_of_avgs=False,
return_per_class=False,
k=None,
label_comparison_fn=None,
device=None,
knn_func=None,
kmeans_func=None)
from pytorch_metric_learning.distances import SNRDistance
from pytorch_metric_learning.utils.inference import CustomKNN
def example_label_comparison_fn(x, y):
return (x[:, 0] == y[:, 0]) & (x[:, 1] != y[:, 1])
knn_func = CustomKNN(SNRDistance())
AccuracyCalculator(exclude=("NMI", "AMI"),
label_comparison_fn=example_label_comparison_fn,
knn_func=knn_func)
调用Accuracy Calculation的get_accuracy方法获取字典形式的准确率指标。
def get_accuracy(self, query, query_labels, reference=None, reference_labels=None, ref_includes_query=False, include=(), exclude=() ): # returns a dictionary mapping from metric names to accuracy values # The default metrics are: # "NMI" (Normalized Mutual Information) # "AMI" (Adjusted Mutual Information) # "precision_at_1" # "r_precision" # "mean_average_precision_at_r"
如果一些query的label标签没有出现在reference的label集合中,那么这些标签不可能具有非零k-nn精度。这些标签得到的精度是0,但是并不表明embedding空间的质量有任何问题。因此,这些单独的查询标签需要被排除在基于k-nn的精度计算之外。
如果你的数据集很大,并且当k的值为None时,可能会发现k-nn搜索非常慢。这是因为默认行为是将k设置为len(reference_embeddings)。为了避免这种情况,可以将k设置为一个数字,例如k = 1000,或者尝试k = “max_bin_count”。
也可以自定义准确率的计算方法。
utils.inference中包含便于在批处理中或从一组pair中查找匹配对的类。代码示例如下:
from pytorch_metric_learning.utils.inference import InferenceModel
InferenceModel(trunk,
embedder=None,
match_finder=None,
normalize_embeddings=True,
knn_func=None,
data_device=None,
dtype=None)
# initialize with a model im = InferenceModel(model) # pass in a dataset to serve as the search space for k-nn im.train_knn(dataset) # add another dataset to the index im.add_to_knn(dataset2) # get the 10 nearest neighbors of a query distances, indices = im.get_nearest_neighbors(query, k=10) # determine if inputs are close to each other is_match = im.is_match(x, y) # determine "is_match" pairwise for all elements in a batch match_matrix = im.get_matches(x) # save and load the knn function (which is a faiss index by default) im.save_knn_func("filename.index") im.load_knn_func("filename.index")
from pytorch_metric_learning.utils.inference import MatchFinder
MatchFinder(distance=None, threshold=None)
from pytorch_metric_learning.utils.inference import FaissKNN
FaissKNN(reset_before=True,
reset_after=True,
index_init_fn=None,
gpus=None)
# use faiss.IndexFlatIP on 3 gpus
knn_func = FaissKNN(index_init_fn=faiss.IndexFlatIP, gpus=[0,1,2])
# query = query embeddings
# k = the k in k-nearest-neighbors
# reference = the embeddings to search
# last argument is whether or not query and reference share datapoints
distances, indices = knn_func(query, k, references, False)
from pytorch_metric_learning.utils.inference import FaissKMeans
FaissKMeans(**kwargs)
kmeans_func = FaissKMeans(niter=100, verbose=True, gpu=True)
# cluster into 10 groups
cluster_assignments = kmeans_func(embeddings, 10)
可以自定义KNN分类方法,指定使用的距离度量方法。
from pytorch_metric_learning.utils.inference import CustomKNN
CustomKNN(distance, batch_size=None)
from pytorch_metric_learning.distances import SNRDistance
from pytorch_metric_learning.utils.inference import CustomKNN
knn_func = CustomKNN(SNRDistance())
distances, indices = knn_func(query, k, references, False)
在前面讲的AccuracyCalculator部分,默认使用FaissKNN计算模型的性能指标,FaissKNN并没有提供修改距离度量方法的参数,这就可能导致损失函数的距离度量方法与FaissKNN的度量方法不一致,这是最好使用CustomKNN自定义KNN方法,同时使用与损失函数相同的,或者与任务特性相符合的距离度量方法。详情参考这里。代码示例如下:
from pytorch_metric_learning.testers import GlobalEmbeddingSpaceTester
from pytorch_metric_learning.distances import SNRDistance
from pytorch_metric_learning.utils.inference import CustomKNN
knn_func = CustomKNN(SNRDistance())
ac = AccuracyCalculator(knn_func=knn_func)
tester = testers.GlobalEmbeddingSpaceTester(accuracy_calculator=ac)
logging_presets模块包含用于记录数据、模型验证和保存模型,以及在训练期间提前停止的hook钩子。它需要依赖record-keeper和tensorboard两个包,这两个包可以通过pip安装:
pip install record-keeper tensorboard
下面是把logging_presets集成到trainer和tester一起使用的代码示例:
import pytorch_metric_learning.utils.logging_presets as LP log_folder, tensorboard_folder = "example_logs", "example_tensorboard" record_keeper, _, _ = LP.get_record_keeper(log_folder, tensorboard_folder) hooks = LP.get_hook_container(record_keeper) dataset_dict = {"val": val_dataset} model_folder = "example_saved_models" # Create the tester tester = testers.GlobalEmbeddingSpaceTester(end_of_testing_hook=hooks.end_of_testing_hook) end_of_epoch_hook = hooks.end_of_epoch_hook(tester, dataset_dict, model_folder) trainer = trainers.MetricLossOnly(models, optimizers, batch_size, loss_funcs, mining_funcs, train_dataset, sampler=sampler, end_of_iteration_hook=hooks.end_of_iteration_hook, end_of_epoch_hook=end_of_epoch_hook) trainer.train(num_epochs=num_epochs)
当使用PyTorch的 DistributedDataParallel进行分布式训练时,用这些封装来封装 tuple loss或者 miner,来实现分布式训练
utils.distributed.DistributedLossWrapper(loss, efficient=False)
代码示例:
from pytorch_metric_learning import losses
from pytorch_metric_learning.utils import distributed as pml_dist
loss_func = losses.ContrastiveLoss()
loss_func = pml_dist.DistributedLossWrapper(loss_func)
# in each process during training
loss = loss_func(embeddings, labels)
utils.distributed.DistributedMinerWrapper(miner, efficient=False)
代码示例:
from pytorch_metric_learning import miners
from pytorch_metric_learning.utils import distributed as pml_dist
miner = miners.MultiSimilarityMiner()
miner = pml_dist.DistributedMinerWrapper(miner)
# in each process
tuples = miner(embeddings, labels)
# pass into a DistributedLossWrapper
loss = loss_func(embeddings, labels, indices_tuple)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。