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.
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.
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.
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 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]])
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
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.