FroSSL
FroSSL (Frobenius norm minimization for Self-Supervised Learning) is a method that avoids representation collapse by maximizing the entropy of each view’s (trace-normalized) covariance matrix through its squared Frobenius norm, while an invariance term pulls the views towards their mean. It uses a VICReg/Barlow Twins style projection head and, unlike two-view objectives, naturally supports any number of views.
This example can be run from the command line with:
python lightly/examples/pytorch/frossl.py
# This example requires the following dependencies to be installed:
# pip install lightly
import torch
import torchvision
from torch import nn
from lightly.loss import FroSSLLoss
## The projection head is the same as the VICReg/Barlow Twins one.
from lightly.models.modules.heads import VICRegProjectionHead
from lightly.transforms.vicreg_transform import VICRegTransform
class FroSSL(nn.Module):
def __init__(self, backbone):
super().__init__()
self.backbone = backbone
self.projection_head = VICRegProjectionHead(
input_dim=512,
hidden_dim=2048,
output_dim=2048,
num_layers=2,
)
def forward(self, x):
x = self.backbone(x).flatten(start_dim=1)
z = self.projection_head(x)
return z
resnet = torchvision.models.resnet18()
backbone = nn.Sequential(*list(resnet.children())[:-1])
model = FroSSL(backbone)
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
transform = VICRegTransform(input_size=32)
dataset = torchvision.datasets.CIFAR10(
"datasets/cifar10", download=True, transform=transform
)
# or create a dataset from a folder containing images or videos:
# dataset = LightlyDataset("path/to/folder", transform=transform)
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=256,
shuffle=True,
drop_last=True,
num_workers=8,
)
# FroSSL supports any number of views; here we use the standard two.
criterion = FroSSLLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.06)
print("Starting Training")
for epoch in range(10):
total_loss = 0
for batch in dataloader:
x0, x1 = batch[0]
x0 = x0.to(device)
x1 = x1.to(device)
z0 = model(x0)
z1 = model(x1)
loss = criterion([z0, z1])
total_loss += loss.detach()
loss.backward()
optimizer.step()
optimizer.zero_grad()
avg_loss = total_loss / len(dataloader)
print(f"epoch: {epoch:>02}, loss: {avg_loss:.5f}")
This example can be run from the command line with:
python lightly/examples/pytorch_lightning/frossl.py
# This example requires the following dependencies to be installed:
# pip install lightly
# Note: The model and training settings do not follow the reference settings
# from the paper. The settings are chosen such that the example can easily be
# run on a small dataset with a single GPU.
import pytorch_lightning as pl
import torch
import torchvision
from torch import nn
from lightly.loss import FroSSLLoss
## The projection head is the same as the VICReg/Barlow Twins one.
from lightly.models.modules.heads import VICRegProjectionHead
from lightly.transforms.vicreg_transform import VICRegTransform
class FroSSL(pl.LightningModule):
def __init__(self):
super().__init__()
resnet = torchvision.models.resnet18()
self.backbone = nn.Sequential(*list(resnet.children())[:-1])
self.projection_head = VICRegProjectionHead(
input_dim=512,
hidden_dim=2048,
output_dim=2048,
num_layers=2,
)
self.criterion = FroSSLLoss()
def forward(self, x):
x = self.backbone(x).flatten(start_dim=1)
z = self.projection_head(x)
return z
def training_step(self, batch, batch_index):
(x0, x1) = batch[0]
z0 = self.forward(x0)
z1 = self.forward(x1)
# FroSSL supports any number of views; here we use the standard two.
loss = self.criterion([z0, z1])
return loss
def configure_optimizers(self):
optim = torch.optim.SGD(self.parameters(), lr=0.06)
return optim
model = FroSSL()
transform = VICRegTransform(input_size=32)
dataset = torchvision.datasets.CIFAR10(
"datasets/cifar10", download=True, transform=transform
)
# or create a dataset from a folder containing images or videos:
# dataset = LightlyDataset("path/to/folder", transform=transform)
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=256,
shuffle=True,
drop_last=True,
num_workers=8,
)
accelerator = "gpu" if torch.cuda.is_available() else "cpu"
trainer = pl.Trainer(max_epochs=10, devices=1, accelerator=accelerator)
trainer.fit(model=model, train_dataloaders=dataloader)