Add core test suite

This commit is contained in:
Jensun Ravichandran 2021-06-12 20:40:23 +02:00
parent a30672b932
commit 1ba7f5c4f7

552
tests/test_core.py Normal file
View File

@ -0,0 +1,552 @@
"""ProtoTorch core test suite"""
import unittest
import numpy as np
import pytest
import torch
import prototorch as pt
from prototorch.utils import parse_distribution
# Utils
def test_parse_distribution_dict_0():
distribution = {"num_classes": 1, "per_class": 0}
distribution = parse_distribution(distribution)
assert distribution == {0: 0}
def test_parse_distribution_dict_1():
distribution = dict(num_classes=3, per_class=2)
distribution = parse_distribution(distribution)
assert distribution == {0: 2, 1: 2, 2: 2}
def test_parse_distribution_dict_2():
distribution = {0: 1, 2: 2, -1: 3}
distribution = parse_distribution(distribution)
assert distribution == {0: 1, 2: 2, -1: 3}
def test_parse_distribution_tuple():
distribution = (2, 3)
distribution = parse_distribution(distribution)
assert distribution == {0: 3, 1: 3}
def test_parse_distribution_list():
distribution = [1, 1, 0, 2]
distribution = parse_distribution(distribution)
assert distribution == {0: 1, 1: 1, 2: 0, 3: 2}
# Components initializers
def test_shape_aware_raises_error():
with pytest.raises(TypeError):
_ = pt.initializers.ShapeAwareCompInitializer(shape=(2, ))
def test_literal_comp_generate():
protos = torch.rand(4, 3, 5, 5)
c = pt.initializers.LiteralCompInitializer(protos)
components = c.generate(num_components="IgnoreMe!")
assert torch.allclose(components, protos)
def test_zeros_comp_generate():
shape = (3, 5, 5)
c = pt.initializers.ZerosCompInitializer(shape)
components = c.generate(num_components=4)
assert torch.allclose(components, torch.zeros(4, 3, 5, 5))
def test_ones_comp_generate():
c = pt.initializers.OnesCompInitializer(2)
components = c.generate(num_components=3)
assert torch.allclose(components, torch.ones(3, 2))
def test_fill_value_comp_generate():
c = pt.initializers.FillValueCompInitializer(2, 0.0)
components = c.generate(num_components=3)
assert torch.allclose(components, torch.zeros(3, 2))
def test_comp_generate_0_components():
c = pt.initializers.ZerosCompInitializer(2)
_ = c.generate(num_components=0)
def test_stratified_mean_comp_generate():
# yapf: disable
x = torch.Tensor(
[[0, -1, -2],
[10, 11, 12],
[0, 0, 0],
[2, 2, 2]])
y = torch.LongTensor([0, 0, 1, 1])
desired = torch.Tensor(
[[5.0, 5.0, 5.0],
[1.0, 1.0, 1.0]])
# yapf: enable
c = pt.initializers.StratifiedMeanCompInitializer(data=[x, y])
actual = c.generate([1, 1])
assert torch.allclose(actual, desired)
def test_stratified_selection_comp_generate():
# yapf: disable
x = torch.Tensor(
[[0, 0, 0],
[1, 1, 1],
[0, 0, 0],
[1, 1, 1]])
y = torch.LongTensor([0, 1, 0, 1])
desired = torch.Tensor(
[[0, 0, 0],
[1, 1, 1]])
# yapf: enable
c = pt.initializers.StratifiedSelectionCompInitializer(data=[x, y])
actual = c.generate([1, 1])
assert torch.allclose(actual, desired)
# Labels initializers
def test_labels_init_from_list():
l = pt.initializers.LabelsInitializer()
components = l.generate(distribution=[1, 1, 1])
assert torch.allclose(components, torch.LongTensor([0, 1, 2]))
def test_labels_init_from_tuple_legal():
l = pt.initializers.LabelsInitializer()
components = l.generate(distribution=(3, 1))
assert torch.allclose(components, torch.LongTensor([0, 1, 2]))
def test_labels_init_from_tuple_illegal():
l = pt.initializers.LabelsInitializer()
with pytest.raises(AssertionError):
_ = l.generate(distribution=(1, 1, 1))
# Components
def test_components_no_initializer():
with pytest.raises(TypeError):
_ = pt.components.Components(3, None)
def test_components_no_num_components():
with pytest.raises(TypeError):
_ = pt.components.Components(initializer=pt.initializers.OCI(2))
def test_components_none_num_components():
with pytest.raises(TypeError):
_ = pt.components.Components(None, initializer=pt.initializers.OCI(2))
def test_components_no_args():
with pytest.raises(TypeError):
_ = pt.components.Components()
def test_components_zeros_init():
c = pt.components.Components(3, pt.initializers.ZCI(2))
assert torch.allclose(c.components, torch.zeros(3, 2))
# Losses
def test_glvq_loss_int_labels():
d = torch.stack([torch.ones(100), torch.zeros(100)], dim=1)
labels = torch.tensor([0, 1])
targets = torch.ones(100)
batch_loss = pt.losses.glvq_loss(distances=d,
target_labels=targets,
prototype_labels=labels)
loss_value = torch.sum(batch_loss, dim=0)
assert loss_value == -100
def test_glvq_loss_one_hot_labels():
d = torch.stack([torch.ones(100), torch.zeros(100)], dim=1)
labels = torch.tensor([[0, 1], [1, 0]])
wl = torch.tensor([1, 0])
targets = torch.stack([wl for _ in range(100)], dim=0)
batch_loss = pt.losses.glvq_loss(distances=d,
target_labels=targets,
prototype_labels=labels)
loss_value = torch.sum(batch_loss, dim=0)
assert loss_value == -100
def test_glvq_loss_one_hot_unequal():
dlist = [torch.ones(100), torch.zeros(100), torch.zeros(100)]
d = torch.stack(dlist, dim=1)
labels = torch.tensor([[0, 1], [1, 0], [1, 0]])
wl = torch.tensor([1, 0])
targets = torch.stack([wl for _ in range(100)], dim=0)
batch_loss = pt.losses.glvq_loss(distances=d,
target_labels=targets,
prototype_labels=labels)
loss_value = torch.sum(batch_loss, dim=0)
assert loss_value == -100
# Activations
class TestActivations(unittest.TestCase):
def setUp(self):
self.flist = ["identity", "sigmoid_beta", "swish_beta"]
self.x = torch.randn(1024, 1)
def test_registry(self):
self.assertIsNotNone(pt.nn.ACTIVATIONS)
def test_funcname_deserialization(self):
for funcname in self.flist:
f = pt.nn.get_activation(funcname)
iscallable = callable(f)
self.assertTrue(iscallable)
def test_callable_deserialization(self):
def dummy(x, **kwargs):
return x
for f in [dummy, lambda x: x]:
f = pt.nn.get_activation(f)
iscallable = callable(f)
self.assertTrue(iscallable)
self.assertEqual(1, f(1))
def test_unknown_deserialization(self):
for funcname in ["blubb", "foobar"]:
with self.assertRaises(NameError):
_ = pt.nn.get_activation(funcname)
def test_identity(self):
actual = pt.nn.identity(self.x)
desired = self.x
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_sigmoid_beta1(self):
actual = pt.nn.sigmoid_beta(self.x, beta=1.0)
desired = torch.sigmoid(self.x)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_swish_beta1(self):
actual = pt.nn.swish_beta(self.x, beta=1.0)
desired = self.x * torch.sigmoid(self.x)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def tearDown(self):
del self.x
# Competitions
class TestCompetitions(unittest.TestCase):
def setUp(self):
pass
def test_wtac(self):
d = torch.tensor([[2.0, 3.0, 1.99, 3.01], [2.0, 3.0, 2.01, 3.0]])
labels = torch.tensor([0, 1, 2, 3])
actual = pt.competitions.wtac(d, labels)
desired = torch.tensor([2, 0])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_wtac_unequal_dist(self):
d = torch.tensor([[2.0, 3.0, 4.0], [2.0, 3.0, 1.0]])
labels = torch.tensor([0, 1, 1])
actual = pt.competitions.wtac(d, labels)
desired = torch.tensor([0, 1])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_wtac_one_hot(self):
d = torch.tensor([[1.99, 3.01], [3.0, 2.01]])
labels = torch.tensor([[0, 1], [1, 0]])
actual = pt.competitions.wtac(d, labels)
desired = torch.tensor([[0, 1], [1, 0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_knnc_k1(self):
d = torch.tensor([[2.0, 3.0, 1.99, 3.01], [2.0, 3.0, 2.01, 3.0]])
labels = torch.tensor([0, 1, 2, 3])
actual = pt.competitions.knnc(d, labels, k=1)
desired = torch.tensor([2, 0])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def tearDown(self):
pass
# Pooling
class TestPooling(unittest.TestCase):
def setUp(self):
pass
def test_stratified_min(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0], [9.0, 8.0, 0, 1]])
labels = torch.tensor([0, 0, 1, 2])
actual = pt.pooling.stratified_min_pooling(d, labels)
desired = torch.tensor([[0.0, 2.0, 3.0], [8.0, 0.0, 1.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_min_one_hot(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0], [9.0, 8.0, 0, 1]])
labels = torch.tensor([0, 0, 1, 2])
labels = torch.eye(3)[labels]
actual = pt.pooling.stratified_min_pooling(d, labels)
desired = torch.tensor([[0.0, 2.0, 3.0], [8.0, 0.0, 1.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_min_trivial(self):
d = torch.tensor([[0.0, 2.0, 3.0], [8.0, 0, 1]])
labels = torch.tensor([0, 1, 2])
actual = pt.pooling.stratified_min_pooling(d, labels)
desired = torch.tensor([[0.0, 2.0, 3.0], [8.0, 0.0, 1.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_max(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0, 9.0], [9.0, 8.0, 0, 1, 7.0]])
labels = torch.tensor([0, 0, 3, 2, 0])
actual = pt.pooling.stratified_max_pooling(d, labels)
desired = torch.tensor([[9.0, 3.0, 2.0], [9.0, 1.0, 0.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_max_one_hot(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0, 9.0], [9.0, 8.0, 0, 1, 7.0]])
labels = torch.tensor([0, 0, 2, 1, 0])
labels = torch.nn.functional.one_hot(labels, num_classes=3)
actual = pt.pooling.stratified_max_pooling(d, labels)
desired = torch.tensor([[9.0, 3.0, 2.0], [9.0, 1.0, 0.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_sum(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0], [9.0, 8.0, 0, 1]])
labels = torch.LongTensor([0, 0, 1, 2])
actual = pt.pooling.stratified_sum_pooling(d, labels)
desired = torch.tensor([[1.0, 2.0, 3.0], [17.0, 0.0, 1.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_sum_one_hot(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0], [9.0, 8.0, 0, 1]])
labels = torch.tensor([0, 0, 1, 2])
labels = torch.eye(3)[labels]
actual = pt.pooling.stratified_sum_pooling(d, labels)
desired = torch.tensor([[1.0, 2.0, 3.0], [17.0, 0.0, 1.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def test_stratified_prod(self):
d = torch.tensor([[1.0, 0.0, 2.0, 3.0, 9.0], [9.0, 8.0, 0, 1, 7.0]])
labels = torch.tensor([0, 0, 3, 2, 0])
actual = pt.pooling.stratified_prod_pooling(d, labels)
desired = torch.tensor([[0.0, 3.0, 2.0], [504.0, 1.0, 0.0]])
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=5)
self.assertIsNone(mismatch)
def tearDown(self):
pass
# Distances
class TestDistances(unittest.TestCase):
def setUp(self):
self.nx, self.mx = 32, 2048
self.ny, self.my = 8, 2048
self.x = torch.randn(self.nx, self.mx)
self.y = torch.randn(self.ny, self.my)
def test_manhattan(self):
actual = pt.distances.lpnorm_distance(self.x, self.y, p=1)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=1,
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=2)
self.assertIsNone(mismatch)
def test_euclidean(self):
actual = pt.distances.euclidean_distance(self.x, self.y)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=2,
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=3)
self.assertIsNone(mismatch)
def test_squared_euclidean(self):
actual = pt.distances.squared_euclidean_distance(self.x, self.y)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = (torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=2,
keepdim=False,
)**2)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=2)
self.assertIsNone(mismatch)
def test_lpnorm_p0(self):
actual = pt.distances.lpnorm_distance(self.x, self.y, p=0)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=0,
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=4)
self.assertIsNone(mismatch)
def test_lpnorm_p2(self):
actual = pt.distances.lpnorm_distance(self.x, self.y, p=2)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=2,
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=4)
self.assertIsNone(mismatch)
def test_lpnorm_p3(self):
actual = pt.distances.lpnorm_distance(self.x, self.y, p=3)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=3,
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=4)
self.assertIsNone(mismatch)
def test_lpnorm_pinf(self):
actual = pt.distances.lpnorm_distance(self.x, self.y, p=float("inf"))
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=float("inf"),
keepdim=False,
)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=4)
self.assertIsNone(mismatch)
def test_omega_identity(self):
omega = torch.eye(self.mx, self.my)
actual = pt.distances.omega_distance(self.x, self.y, omega=omega)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = (torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=2,
keepdim=False,
)**2)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=2)
self.assertIsNone(mismatch)
def test_lomega_identity(self):
omega = torch.eye(self.mx, self.my)
omegas = torch.stack([omega for _ in range(self.ny)], dim=0)
actual = pt.distances.lomega_distance(self.x, self.y, omegas=omegas)
desired = torch.empty(self.nx, self.ny)
for i in range(self.nx):
for j in range(self.ny):
desired[i][j] = (torch.nn.functional.pairwise_distance(
self.x[i].reshape(1, -1),
self.y[j].reshape(1, -1),
p=2,
keepdim=False,
)**2)
mismatch = np.testing.assert_array_almost_equal(actual,
desired,
decimal=2)
self.assertIsNone(mismatch)
def tearDown(self):
del self.x, self.y