[PYTHON] Implementierte kontinuierliches Lernen unter Verwendung der Maharanobis-Entfernung des Merkmalsraums

Fortlaufendes Lernen

Kontinuierliches Lernen bedeutet, dass das Modell über einen langen Zeitraum kontinuierlich nacheinander neue Daten lernt. Weitere Informationen finden Sie unter Folie, in dem das kontinuierliche Lernen zusammengefasst ist.

Dieses Mal werden wir ein kontinuierliches Lernpapier mit der Methode der Erkennung von Verteilungsfehlern implementieren. Titel: Ein einfaches einheitliches Framework zum Erkennen von Stichproben und Angriffen außerhalb der Verteilung Zusammenfassung des Papiers: Die Daten außerhalb der Verteilung werden als Daten der neuen Klasse angesehen, die Gaußsche Anpassung wird am Merkmalsraum des tiefen Modells durchgeführt, und die Testdaten werden basierend auf dem Maharanobis-Abstand zwischen dem durchschnittlichen Vektor der alten Klasse und der neuen Klasse klassifiziert. Die ausführliche Erläuterung des Papiers wurde im zweiten Teil von Folie verfasst.

Die Stärke dieses Papiers besteht darin, dass ** kontinuierliches Lernen möglich ist, ohne die Genauigkeit der bisher gelernten Klassen zu beeinträchtigen **, und dass die Parameter von DNN anhand der Daten der neu hinzugefügten Klasse neu gelernt werden müssen. Es gibt kein **. Selbst wenn die Anzahl der Klassen zunimmt, kann das Lernen daher sehr schnell durchgeführt werden.

Einstellungen für Implementierungsprobleme

Erklärung zur Implementierung

Erste Vorbereitung

import os 

import numpy as np
from sklearn.metrics import confusion_matrix, f1_score, accuracy_score
import matplotlib.pyplot as plt
from PIL import Image

import torch
import torch.nn.functional as F
from torch.utils.data import DataLoader
import torch.nn as nn
from torchvision import transforms as T
from torchvision.datasets import CIFAR10

# Github repo: https://github.com/lukemelas/EfficientNet-PyTorch
#Fügen Sie Modell und Dienstprogramme in das obige Repository in derselben Hierarchie ein
from model import EfficientNet 
from tqdm import tqdm
plt.style.use("ggplot")

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

Ich habe eine Funktion vorbereitet, die einen Datenlader zurückgibt, damit ich eine Klasse von CIFAR10 hinzufügen kann

def return_data_loader(classes, train=True, batch_size=128):
    transform = []
    transform.append(T.Resize((64, 64))) #Die Größe muss geändert werden, um efmodel verwenden zu können
    transform.append(T.ToTensor())
    transform = T.Compose(transform)

    dataset = CIFAR10("./data", train=train, download=True, transform=transform)
    targets = np.array(dataset.targets)
    mask = np.array([t in classes for t in targets])
    dataset.data = dataset.data[mask]
    dataset.targets = targets[mask]
    
    data_loader = DataLoader(dataset=dataset,
                                  batch_size=batch_size,
                                  shuffle=train)
    return data_loader

Verwenden Sie das kleinste Modell von Efficientnet als Modell

Als Teil des blauen Pfeils in der Abbildung lernen wir normalerweise 5 Klassen von Diskriminierungsmodellen. Als nächstes approximieren Sie als roter Pfeil das vorherige Merkmal mit einer Gaußschen Verteilung für jede Klasse. スクリーンショット 2020-10-05 9.08.56.png

NCLASS = 5 #Anfangsklasse
classes = np.arange(NCLASS)
model = 'efficientnet-b0'
weight_dir = "."

clf = EfficientNet.from_name(model)
clf._fc = torch.nn.Linear(clf._fc.in_features, NCLASS)
clf = clf.to(device)
clf.train()
train_loader = return_data_loader(classes=classes, train=True)

lr = 0.001
epoch_num = 50
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(clf.parameters(), lr=lr)

for epoch in tqdm(range(epoch_num)):
    train_loss = 0
    for x, y in train_loader:
        x = x.to(device)
        y = y.to(device)
        logit = clf(x)
        loss = criterion(logit, y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        train_loss += loss.item()
    train_loss /= len(train_loader.dataset)
torch.save(clf.state_dict(), os.path.join(weight_dir, 'weight.pth'))

test_loader = return_data_loader(range(10), train=False)
clf.load_state_dict(torch.load(os.path.join(weight_dir, 'weight.pth')))
clf.eval()

pred = []
true = []
for x, y in test_loader:
    with torch.no_grad():
            pred.extend(clf(x.to(device)).max(1)[1].detach().cpu().numpy())
            true.extend(y.numpy())
print(accuracy_score(true, pred))
print(confusion_matrix(true, pred))

Zuerst geben wir die gemischte Matrix und die Genauigkeitsrate aus, wenn das Diskriminierungsmodell normalerweise trainiert wurde. Da nur die Klassen 0 bis 4 zum Lernen verwendet werden, ist es nicht möglich, die Klassen 5 bis 9 vorherzusagen, und die Klassen 0 bis 4 werden zwangsweise vorhergesagt.

0.4279 #Richtige Antwortrate
[[877  27  47  35  14   0   0   0   0   0]
 [ 14 972   3   8   3   0   0   0   0   0]
 [ 51   7 785  81  76   0   0   0   0   0]
 [ 20  18 107 780  75   0   0   0   0   0]
 [ 13   2  58  62 865   0   0   0   0   0]
 [ 13  12 226 640 109   0   0   0   0   0]
 [ 26  55 232 477 210   0   0   0   0   0]
 [ 47  21 188 230 514   0   0   0   0   0]
 [604 214  53  95  34   0   0   0   0   0]
 [160 705  43  78  14   0   0   0   0   0]]

Berechnen Sie als Nächstes den Mittelwert und die Kovarianz der Merkmale für die Implementierung des roten Pfeilteils in der obigen Abbildung. スクリーンショット 2020-10-05 9.29.18.png

def ext_feature(x):
    z = clf.extract_features(x)
    z = clf._avg_pooling(z)
    z = z.flatten(start_dim=1)
    return z.detach().cpu().numpy()

train_loaders = [return_data_loader(classes=[c], train=True) for c in range(10)]

z_mean = []
z_var = 0
target_count = []

for c in tqdm(range(NCLASS)): #Bestehende Klasse
    N = len(train_loaders[c].dataset) #Halten Sie die Nummer jeder Klasse
    target_count.append(N)
    
    with torch.no_grad():
        #Durchschnittliche Berechnung
        new_z_mean = 0
        for x, _ in train_loaders[c]:
            x = x.to(device)
            new_z_mean += ext_feature(x).sum(0) / N
        z_mean.append(new_z_mean)

        #Berechnung der Varianz
        for x, _ in train_loaders[c]:
            x = x.to(device)    
            z_var += (ext_feature(x) - new_z_mean).T.dot(ext_feature(x) - new_z_mean) / N

C = len(z_mean)
z_var /=  C
z_mean = np.array(z_mean)
target_count = np.array(target_count)

Sobald der Mittelwert und die Co-Dispersion erhalten sind, ist es möglich, ohne die vollständig verbundene Schicht der letzten Schicht unter Verwendung des Maharanobis-Abstands zu klassifizieren. Die Implementierung verwendet den Bayes'schen Satz スクリーンショット 2020-10-05 9.32.07.png Dabei ist $ \ beta_c $ die Anzahl der Klassendaten

z_var_inv = np.linalg.inv(z_var + np.eye(z_mean.shape[1])*1e-6)  
#Fügen Sie eine Regularisierung hinzu, um zu verhindern, dass die inverse Matrix instabil wird
A = z_mean.dot(z_var_inv) #Ein Punkt des Inhalts der Exp des Moleküls
B = (A*z_mean).sum(1) * 0.5 #2 Gegenstände
beta = np.log(target_count) #3 Artikel

accs = []
pred = []
true = []
with torch.no_grad():
    for x, y in test_loader:
        x = x.to(device)
        pred.extend((A.dot(ext_feature(x).T) - B[:, None] + beta[:, None]).argmax(0))
        true.extend(y.numpy())
acc = accuracy_score(true, pred)        
print(acc)
accs.append(acc)
confusion_matrix(true, pred)

Aus den folgenden Ergebnissen wurde herausgefunden, dass fast die gleiche Genauigkeitsrate erreicht werden kann, ohne die vollständig verbundene Schicht zu verwenden.

0.4273 #Richtige Antwortrate
array([[899,  17,  43,  29,  12,   0,   0,   0,   0,   0],
       [ 25, 958,   6,   9,   2,   0,   0,   0,   0,   0],
       [ 55,   6, 785,  86,  68,   0,   0,   0,   0,   0],
       [ 29,  15, 109, 773,  74,   0,   0,   0,   0,   0],
       [ 23,   2,  55,  62, 858,   0,   0,   0,   0,   0],
       [ 22,   6, 227, 641, 104,   0,   0,   0,   0,   0],
       [ 34,  39, 256, 468, 203,   0,   0,   0,   0,   0],
       [ 71,  16, 199, 214, 500,   0,   0,   0,   0,   0],
       [653, 182,  53,  84,  28,   0,   0,   0,   0,   0],
       [221, 645,  42,  78,  14,   0,   0,   0,   0,   0]])

Implementierung von kontinuierlichem Lernen

Ziel ist es, die Testdaten für alle Klassen anhand des Mittelwerts und der Varianz der neuen Daten zu klassifizieren, ohne die Parameter des Modells zu trainieren.

Der Umriss des Algorithmus ist wie folgt スクリーンショット 2020-10-05 9.39.07.png

for c in tqdm(range(NCLASS, 10)): #Neue Klasse
    N = len(train_loaders[c].dataset)

    with torch.no_grad():
        #Durchschnittliche Berechnung
        new_z_mean = 0        
        for x, _ in train_loaders[c]:
            x = x.to(device)
            new_z_mean += ext_feature(x).sum(0) / N 


        #Berechnung der Varianz
        new_z_var = 0
        for x, _ in train_loaders[c]:
            x = x.to(device)    
            new_z_var += (ext_feature(x) - new_z_mean).T.dot(ext_feature(x) - new_z_mean) / N

    #Durchschnitt und Varianz aktualisieren
    C = len(target_count)
    z_mean = np.concatenate([z_mean, new_z_mean[None, :]])
    z_var = z_var*C/(C+1) + new_z_var/(C+1)
    target_count = np.append(target_count, N)

    z_var_inv = np.linalg.inv(z_var + np.eye(z_mean.shape[1])*1e-6)
    A = z_mean.dot(z_var_inv) 
    B = (A*z_mean).sum(1) * 0.5
    beta = np.log(target_count)
    pred = []
    true = []
    with torch.no_grad():
        for x, y in test_loader:
            x = x.to(device)
            pred.extend((A.dot(ext_feature(x).T) - B[:, None] + beta[:, None]).argmax(0))
            true.extend(y.numpy())
    acc = accuracy_score(true, pred)
    accs.append(acc)
    print(acc)

Das Endergebnis ist

0.4974 #Richtige Antwortrate
array([[635,   1,  18,   4,   2,  14,   9,  36, 260,  21],
       [  1, 761,   0,   1,   0,   0,   8,   3,  21, 205],
       [ 20,   0, 581,  12,   8,  97, 105, 135,  35,   7],
       [  5,   0,  22, 450,  13, 256, 147,  60,  29,  18],
       [  2,   1,  16,  10, 555,  30,  63, 302,  20,   1],
       [  1,   0,  57, 288,  22, 325, 173, 106,  22,   6],
       [  5,   0,  49, 139,  36, 182, 350, 161,  35,  43],
       [  5,   2,  34,  50, 131, 104, 158, 446,  58,  12],
       [226,  26,  13,  11,   3,  22,  58,  41, 430, 170],
       [ 17, 250,   6,   5,   0,   8,  69,  16, 188, 441]])
plt.title("accuracy")
plt.plot(accs)
plt.show()

Die x-Achse bedeutet die Anzahl der hinzugefügten Klassen. Es ist ersichtlich, dass die korrekte Antwortrate, wenn 10 Klassen von Trainingsdaten schließlich gegeben werden, ungefähr 0,1 höher ist als wenn nur 5 Klassen gegeben werden. download-6.png

Recommended Posts

Implementierte kontinuierliches Lernen unter Verwendung der Maharanobis-Entfernung des Merkmalsraums
Berechnen Sie die Maharanobis-Entfernung unter Berücksichtigung der Korrelation der Merkmalsgrößen mit scipy
In Python implementierte Widrow-Hoff-Lernregeln
Implementierte Perceptron-Lernregeln in Python
Bilderkennungsmodell mit Deep Learning im Jahr 2016
Datenversorgungstricks mit deque beim maschinellen Lernen