RSR AutoEncoder

RSR AutoEncoder

Robust Space Recovery AutoEncoder
Lai, C. H., Zou, D., & Lerman, G. (2019). Robust subspace recovery layer for unsupervised anomaly detection. arXiv preprint arXiv:1904.00152.

In Short

  1. AutoEncoder๋ฅผ unsupervised anomaly detection์— ์‚ฌ์šฉํ•œ ๋ชจ๋ธ
  2. Encoder์™€ Decoder ์‚ฌ์ด์— RSR layer๋ฅผ ์ถ”๊ฐ€ํ•œ ๋ชจ๋ธ
  3. Reconstruction loss ์™ธ์— RSR loss๋ฅผ ์ถ”๊ฐ€ํ•œ ๋ชจ๋ธ

1. Introduction

It maps one representation (embedding obtained from the encoder) into another low-dimensional representation that is outlier-robust.

Letโ€™s say that D (upper case D) is the dimension of the embedding from the encoder. The assumption in the paper is that the โ€œnormalโ€ data lies within d-dimensional (lower case d) manifold (โ€œsubspaceโ€) of the original embedding, which means that d < D.

2. RSR Layer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class RSRLayer(nn.Module):
    def __init__(self, d:int, D: int):
        super().__init__()
        self.d = d
        self.D = D
        self.A = nn.Parameter(torch.nn.init.orthogonal_(torch.empty(d, D)))

    def forward(self, z):
        # z is the output from the encoder
        z_hat = self.A @ z.view(z.size(0), self.D, 1)
        return z_hat.squeeze(2)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class RSRAutoEncoder(nn.Module):
    def __init__(self, input_dim, d, D):
        super().__init__()
        # Put your encoder network here, remember about the output D-dimension
        self.encoder = nn.Sequential(
          nn.Linear(input_dim, input_dim // 2),
          nn.LeakyReLU(),
          nn.Linear(input_dim // 2, input_dim // 4),
          nn.LeakyReLU(),
          nn.Linear(input_dim // 4, D)
        )

        self.rsr = RSRLayer(d, D)

        # Put your decoder network here, rembember about the input d-dimension
        self.decoder = nn.Sequential(
          nn.Linear(d, D),
          nn.LeakyReLU(),
          nn.Linear(D, input_dim // 2),
          nn.LeakyReLU(),
          nn.Linear(input_dim // 2, input_dim)
        )
    
    def forward(self, x):
        enc = self.encoder(x) # obtain the embedding from the encoder
        latent = self.rsr(enc) # RSR manifold
        dec = self.decoder(latent) # obtain the representation in the input space
        return enc, dec, latent, self.rsr.A

์œ„ ์ฝ”๋“œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, A๋ผ๋Š” ํ–‰๋ ฌ์„ ๊ณฑํ•ด์ฃผ๋Š”๊ฒŒ ํฌ์ธํŠธ์ด๋‹ค.

์ฐธ๊ณ ๋กœ ์—ฌ๊ธฐ์„œ RSR Layer์—์„œ D๊ฐ€ input dimension์ด๊ณ , d๊ฐ€ output dimension์ด๋‹ค.

3. Loss

$$
L_{RSRAE}(Enc,A,Dec) = L^p_{AE}(Enc, A, Dec) + L^q_{RSR}(A)
$$

3-1. Reconstruction Loss

$$
L^p_{AE}(Enc, A, Dec) = \sum^N_{t=1} \bigg|\bigg| \bf{x}^{(t)} - \tilde{\bf{x}}^{(t)} \bigg|\bigg|^p_2
$$

3-2. RSR Loss

$$
\begin{align} L^q_{RSR}(A) &= \lambda_1 L_{RSR_1}(A) + \lambda_2 L_{RSR_2}(A) \\ :&= \lambda_1 \sum^N_{t=1} \Bigg|\Bigg|z^{(t)} - A^T \underbrace{Az^{(t)}}_{\tilde{z}^{(t)}}\Bigg|\Bigg|^q_2 + \lambda_2\bigg|\bigg|AA^T-I_d\bigg|\bigg|^2_F \end{align}
$$

ํฌ๊ฒŒ ๋‘ ๊ฐœ์˜ Loss๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋‹ค. ํ•˜๋‚˜๋Š” Reconstruction loss์ด๊ณ , ๋‚˜๋จธ์ง€ ํ•˜๋‚˜๋Š” RSR loss์ด๋‹ค. ๊ฐ๊ฐ์€ ์œ„์™€ ๊ฐ™์ด ์ •์˜๋œ๋‹ค.

RSR loss ์ค‘์—์„œ ์ฒซ๋ฒˆ์งธ๋Š” ์–ผ๋งˆ๋‚˜ RSR layer๋ฅผ ์‹ค์ œ latent vector์™€ ์ตœ๋Œ€ํ•œ ๋น„์Šทํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ํ”„๋กœ์ ์…˜์„ ์ตœ๋Œ€ํ•œ orthogonalํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•จ์ด๋‹ค.

The first term enforces the RSR Layer projection to be robust and the second term enforces the projection to be orthogonal.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class RSRAE(pl.LightningModule):
    def __init__(self, hparams):
        super().__init__()
        self.hparams = hparams
        self.ae = RSRAutoEncoder(
            self.hparams.input_dim, 
            self.hparams.d, 
            self.hparams.D)
        self.reconstruction_loss = L2p_Loss(p=1.0)
        self.rsr_loss = RSRLoss(self.hparams.lambda1, self.hparams.lambda2, self.hparams.d, self.hparams.D)
  
    def forward(self, x):
        return self.ae(x)
  
    def training_step(self, batch, batch_idx):
        X, _ = batch
        x = X.view(X.size(0), -1)
        enc, dec, latent, A = self.ae(x)

        rec_loss = self.reconstruction_loss(torch.sigmoid(dec), x)
        rsr_loss = self.rsr_loss(enc, A)
        loss = rec_loss + rsr_loss
        
        # log some usefull stuff
        self.log("reconstruction_loss", rec_loss.item(), on_step=True, on_epoch=False, prog_bar=True)
        self.log("rsr_loss", rsr_loss.item(), on_step=True, on_epoch=False, prog_bar=True)
        return {"loss": loss}

    def configure_optimizers(self):
        opt = torch.optim.AdamW(self.parameters(), lr=self.hparams.lr)
        # Fast.AI's best practices :)
        scheduler = torch.optim.lr_scheduler.OneCycleLR(opt, max_lr=self.hparams.lr, 
                                                        epochs=self.hparams.epochs, 
                                                        steps_per_epoch=self.hparams.steps_per_epoch)
        return [opt], [{
            "scheduler": scheduler,
            "interval": "step"
        }]
        
dl = DataLoader(ds, batch_size=64, shuffle=True, drop_last=True)

hparams = dict(
    d=16,
    D=128,
    input_dim=28*28,
    # Peak learning rate
    lr=0.01,
    # Configuration for the OneCycleLR scheduler
    epochs=150,
    steps_per_epoch=len(dl),
    # lambda coefficients from RSR Loss
    lambda1=1.0,
    lambda2=1.0,
)

model = RSRAE(hparams)
model

4. Experiment Result

4-1. Dataset

  1. Caltech 101
  2. Fashion-Mnist
  3. Tiny Imagenet
  4. Reuters-21578
  5. 20 Newsgroups

4-2. Measure

AUC (area under curve) and AP (average precision) scores

Critical Point (MY OWN OPINION)

  1. semi-supervised์ด๋ฉด์„œ unsupervised๋ผ๊ณ  ์†์ด๋Š” ๋‹ค๋ฅธ ๋ชจ๋ธ์— ๋น„ํ•ด, ์ด ๋ชจ๋ธ์€ ์ง„์งœ๋กœ unsupervised์ด๋‹ค.
  2. RSR loss์™€ Reconstruction loss์˜ ๊ฐ€์ค‘์น˜๋„ ์กฐ์ ˆํ•ด์•ผํ•˜์ง€ ์•Š์„๊นŒ?
  3. semi-supervised๋‚˜ supervised์— ๋น„ํ•ด์„œ๋Š” (๋‹น์—ฐํžˆ) ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง€์ง€๋งŒ, ์ด๊ฒƒ์— ๋Œ€ํ•ด์„œ ์†”์งํ•˜๊ฒŒ ๋ฐํžˆ๊ณ  ์žˆ๋‹ค๋Š” ์ ๋„ ์ข‹์•˜๋‹ค.
  4. PyTorch๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋„ ๊ณต๊ฐœ๋˜์–ด ์žˆ์–ด์„œ ์ข‹์•˜๋‹ค.

Reference

[1] https://zablo.net/blog/post/robust-space-recovery-rsrlayer-pytorch/