Skip to content

Commit

Permalink
Merge pull request #423 from KevinMusgrave/dev
Browse files Browse the repository at this point in the history
v1.1.1
  • Loading branch information
Kevin Musgrave authored Feb 12, 2022
2 parents 537a5f5 + b6bfcff commit df53e74
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 28 deletions.
4 changes: 2 additions & 2 deletions conda_build/pytorch-metric-learning/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{% set name = "pytorch-metric-learning" %}
{% set version = "1.1.0" %}
{% set version = "1.1.1" %}

package:
name: "{{ name|lower }}"
version: "{{ version }}"

source:
url: "https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz"
sha256: d52913eee027746de928bf0e9c031f59a0915cfee4f02cdf81c198a058bd6b21
sha256: 6e572dc54179c762abc333fc4c6f68fcd909e800f9519ca1463235d14b9f5c44

build:
number: 0
Expand Down
2 changes: 1 addition & 1 deletion src/pytorch_metric_learning/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.1.0"
__version__ = "1.1.1"
2 changes: 1 addition & 1 deletion src/pytorch_metric_learning/distances/base_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def forward(self, query_emb, ref_emb=None):
)
mat = self.compute_mat(query_emb_normalized, ref_emb_normalized)
if self.power != 1:
mat = mat ** self.power
mat = mat**self.power
assert mat.size() == torch.Size((query_emb.size(0), ref_emb.size(0)))
return mat

Expand Down
2 changes: 1 addition & 1 deletion src/pytorch_metric_learning/losses/fast_ap_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def compute_loss(self, embeddings, labels, indices_tuple, ref_emb, ref_labels):
return self.zero_losses()
dist_mat = self.distance(embeddings)

histogram_max = 2 ** self.distance.power
histogram_max = 2**self.distance.power
histogram_delta = histogram_max / self.num_bins
mid_points = torch.linspace(
0.0, histogram_max, steps=self.num_edges, device=device, dtype=dtype
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def get_cos_with_margin(self, cosine):
cosine = cosine.unsqueeze(1)
for attr in ["n_range", "margin_choose_n", "cos_powers", "alternating"]:
setattr(self, attr, c_f.to_device(getattr(self, attr), cosine))
cos_powered = cosine ** self.cos_powers
sin_powered = (1 - cosine ** 2) ** self.n_range
cos_powered = cosine**self.cos_powers
sin_powered = (1 - cosine**2) ** self.n_range
terms = (
self.alternating * self.margin_choose_n * cos_powered * sin_powered
) # Equation 7 in the paper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def mine(self, embeddings, labels, ref_emb, ref_labels):

# See the first equation from Section 4 of the paper
log_weights = (2.0 - d) * torch.log(mat) - ((d - 3) / 2) * torch.log(
1.0 - 0.25 * (mat ** 2.0)
1.0 - 0.25 * (mat**2.0)
)

inf_or_nan = torch.isinf(log_weights) | torch.isnan(log_weights)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def compute_loss(self, weights):
deviations_from_mean = squared_weight_norms - torch.mean(squared_weight_norms)
return {
"loss": {
"losses": (deviations_from_mean ** 2) / 4,
"losses": (deviations_from_mean**2) / 4,
"indices": c_f.torch_arange_from_size(weights),
"reduction_type": "element",
}
Expand Down
2 changes: 1 addition & 1 deletion src/pytorch_metric_learning/regularizers/lp_regularizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, p=2, power=1, **kwargs):
def compute_loss(self, embeddings):
reg = torch.norm(embeddings, p=self.p, dim=1)
if self.power != 1:
reg = reg ** self.power
reg = reg**self.power
return {
"loss": {
"losses": reg,
Expand Down
15 changes: 4 additions & 11 deletions src/pytorch_metric_learning/utils/accuracy_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ def r_precision(
matches_per_row = torch.sum(same_label * relevance_mask, dim=1)
max_possible_matches_per_row = torch.sum(relevance_mask, dim=1)
accuracy_per_sample = (
c_f.to_dtype(matches_per_row, dtype=torch.float64)
/ max_possible_matches_per_row
matches_per_row.type(torch.float64) / max_possible_matches_per_row
)
return maybe_get_avg_of_avgs(
accuracy_per_sample, gt_labels, avg_of_avgs, return_per_class
Expand Down Expand Up @@ -99,9 +98,7 @@ def mean_average_precision(
equality = is_same_label * relevance_mask
cumulative_correct = torch.cumsum(equality, dim=1)
k_idx = torch.arange(1, num_k + 1, device=device).repeat(num_samples, 1)
precision_at_ks = (
c_f.to_dtype(cumulative_correct * equality, dtype=torch.float64) / k_idx
)
precision_at_ks = (cumulative_correct * equality).type(torch.float64) / k_idx
summed_precision_per_row = torch.sum(precision_at_ks * relevance_mask, dim=1)
if at_r:
max_possible_matches_per_row = torch.sum(relevance_mask, dim=1)
Expand Down Expand Up @@ -172,9 +169,7 @@ def precision_at_k(
):
curr_knn_labels = knn_labels[:, :k]
same_label = label_comparison_fn(gt_labels, curr_knn_labels)
accuracy_per_sample = (
c_f.to_dtype(torch.sum(same_label, dim=1), dtype=torch.float64) / k
)
accuracy_per_sample = torch.sum(same_label, dim=1).type(torch.float64) / k
return maybe_get_avg_of_avgs(
accuracy_per_sample, gt_labels, avg_of_avgs, return_per_class
)
Expand Down Expand Up @@ -209,9 +204,7 @@ def get_lone_query_labels(
unique_labels, match_counts = label_counts
if embeddings_come_from_same_source:
label_matches_itself = label_comparison_fn(unique_labels, unique_labels)
lone_condition = (
match_counts - c_f.to_dtype(label_matches_itself, dtype=torch.long) <= 0
)
lone_condition = match_counts - label_matches_itself.type(torch.long) <= 0
else:
lone_condition = match_counts == 0
lone_query_labels = unique_labels[lone_condition]
Expand Down
11 changes: 7 additions & 4 deletions src/pytorch_metric_learning/utils/common_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ def get_hierarchy_label(batch_labels, hierarchy_level):
def map_labels(label_map, labels):
labels = to_numpy(labels)
if labels.ndim == 2:
new_labels = np.zeros(labels.shape, dtype=int)
for h in range(labels.shape[1]):
labels[:, h] = label_map(labels[:, h], h)
new_labels[:, h] = label_map(labels[:, h], h)
else:
labels = label_map(labels, 0)
return labels
new_labels = label_map(labels, 0)
return new_labels


def process_label(labels, hierarchy_level, label_map):
Expand Down Expand Up @@ -235,7 +236,9 @@ def make_label_to_rank_dict(label_set):
Returns:
A dictionary mapping each label to its numeric rank in the original set
"""
ranked = scipy.stats.rankdata(label_set) - 1
if len(set(label_set)) != len(label_set):
raise ValueError("label set must not have duplicates")
ranked = scipy.stats.rankdata(label_set).astype(int) - 1
return {k: v for k, v in zip(label_set, ranked)}


Expand Down
4 changes: 2 additions & 2 deletions tests/losses/test_large_margin_softmax_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ def test_large_margin_softmax_and_sphereface_loss(self):
curr_valA = (
c
* (curr_cosineA ** (margin - (2 * z)))
* ((1 - curr_cosineA ** 2) ** z)
* ((1 - curr_cosineA**2) ** z)
)
curr_valB = (
c
* (curr_cosineB ** (margin - (2 * z)))
* ((1 - curr_cosineB ** 2) ** z)
* ((1 - curr_cosineB**2) ** z)
)
if z % 2 == 1:
curr_valA *= -1
Expand Down
2 changes: 1 addition & 1 deletion tests/regularizers/test_center_invariant_regularizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_center_invariant_regularizer(self):
torch.norm(loss_func.W[:, i], p=2) ** 2
- average_squared_weight_norms
)
correct_reg_loss += (deviation ** 2) / 4
correct_reg_loss += (deviation**2) / 4
correct_reg_loss /= num_classes

correct_total_loss = correct_class_loss + (correct_reg_loss * reg_weight)
Expand Down
11 changes: 11 additions & 0 deletions tests/utils/test_calculate_accuracies.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,3 +789,14 @@ def test_custom_knn(self):

for k, v in acc1.items():
self.assertTrue(np.isclose(v, acc2[k], rtol=1e-3))


class TestWithinAutocast(unittest.TestCase):
def test_within_autocast(self):
if TEST_DEVICE == torch.device("cpu"):
return
AC = accuracy_calculator.AccuracyCalculator()
embeddings = torch.randn(1000, 32)
labels = torch.randint(0, 10, size=(1000,))
with torch.autocast(device_type="cuda", dtype=torch.float16):
acc = AC.get_accuracy(embeddings, embeddings, labels, labels, True)
102 changes: 102 additions & 0 deletions tests/utils/test_common_functions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import unittest

import numpy as np
import torch
from sklearn.preprocessing import StandardScaler

Expand All @@ -10,6 +11,41 @@
from .. import WITH_COLLECT_STATS


def process_label_helper(
cls,
dataset_labels,
batch_labels,
correct_mapped_batch_labels,
correct_unmapped_batch_labels,
hierarchy_level,
input_is_string=False,
):
for set_min_label_to_zero in [False, True]:
if input_is_string and not set_min_label_to_zero:
continue
label_mapper = c_f.LabelMapper(set_min_label_to_zero, dataset_labels)
x = c_f.process_label(batch_labels, hierarchy_level, label_mapper.map)
if set_min_label_to_zero:
cls.assertTrue(np.array_equal(x, correct_mapped_batch_labels))
else:
cls.assertTrue(np.array_equal(x, correct_unmapped_batch_labels))


def process_label_helper2d(
cls, dataset_labels, batch_labels, mapped_batch_labels, input_is_string=False
):
for h in [0, 1]:
process_label_helper(
cls,
dataset_labels,
batch_labels,
mapped_batch_labels[h],
batch_labels[:, h],
h,
input_is_string=input_is_string,
)


class TestCommonFunctions(unittest.TestCase):
def test_torch_standard_scaler(self):
torch.manual_seed(56987)
Expand Down Expand Up @@ -58,6 +94,72 @@ def test_logger(self):
self.assertTrue(c_f.LOGGER.level == level)
self.assertTrue(logging.getLogger().level == logging.WARNING)

def test_process_label(self):
# 1D number labels
dataset_labels = np.array([1, 1, 10, 13, 13, 13, 15])
batch_labels = np.array([1, 10, 1, 13, 10, 15, 15, 15])
mapped_batch_labels = np.array([0, 1, 0, 2, 1, 3, 3, 3])
process_label_helper(
self, dataset_labels, batch_labels, mapped_batch_labels, batch_labels, 0
)

# 1D string labels
# These work only with set_min_label_to_zero=True
# The labels will be sorted alphabetically. So in the following, "bear" becomes 0
dataset_labels = np.array(["dog", "dog", "cat", "bear", "bear", "bear", "lion"])
batch_labels = np.array(
["dog", "cat", "dog", "bear", "cat", "lion", "lion", "lion"]
)
mapped_batch_labels = np.array([2, 1, 2, 0, 1, 3, 3, 3])
process_label_helper(
self,
dataset_labels,
batch_labels,
mapped_batch_labels,
batch_labels,
0,
input_is_string=True,
)

# 2D number labels
dataset_labels = np.array(
[[1, 1, 10, 13, 13, 13, 15], [10, 20, 30, 40, 50, 60, 70]]
).transpose()
batch_labels = np.array(
[[1, 10, 1, 13, 10, 15, 15, 15], [30, 70, 40, 10, 70, 60, 50, 40]]
).transpose()
mapped_batch_labels0 = np.array([0, 1, 0, 2, 1, 3, 3, 3])
mapped_batch_labels1 = np.array([2, 6, 3, 0, 6, 5, 4, 3])
process_label_helper2d(
self,
dataset_labels,
batch_labels,
[mapped_batch_labels0, mapped_batch_labels1],
)

# 2D string labels
dataset_labels = np.array(
[
["dog", "dog", "cat", "bear", "bear", "bear", "lion"],
["A.1", "A.2", "A.3", "A.4", "A.5", "A.6", "A.7"],
]
).transpose()
batch_labels = np.array(
[
["dog", "cat", "dog", "bear", "cat", "lion", "lion", "lion"],
["A.3", "A.7", "A.4", "A.1", "A.7", "A.6", "A.5", "A.4"],
]
).transpose()
mapped_batch_labels0 = np.array([2, 1, 2, 0, 1, 3, 3, 3])
mapped_batch_labels1 = np.array([2, 6, 3, 0, 6, 5, 4, 3])
process_label_helper2d(
self,
dataset_labels,
batch_labels,
[mapped_batch_labels0, mapped_batch_labels1],
input_is_string=True,
)


if __name__ == "__main__":
unittest.main()

0 comments on commit df53e74

Please sign in to comment.