[PYTHON] Multi-Class Multi-Label-Klassifizierung von Bildern mit Pytorch

Einführung

――Ich habe versucht, das zu tun, was der Titel sagt. Ich habe alles getan, also als Memo. ――Es gibt einen Grund, warum Sie den Inhalt nicht tief berühren können, und es gibt einige Teile, die etwas chaotisch sind. ――Zum Beispiel, wo haben Sie diese Variable deklariert? Möglicherweise gibt es ein Ziel

Was ich getan habe

Ordnerstruktur

Ich habe folgendes getan. Aber das ist vielleicht nicht das Beste, um ehrlich zu sein. Dies geschah, weil die Untersuchung mäßig lief.

Ordnerstruktur


.
├── data
│   ├── labels        //Bild- und Etikettenkombination json storage
│   │     ├── A.json
│   │     ├── B.json
│ │ └ und viele andere jsons
│   └── images        //jpg Bildspeicher. Zum Lernen und Verifizieren gemischt
│         ├── A.jpg
│         ├── B.jpg
│ └ und viele andere JPGs
├── model             //Modell speichern Ziel
└── predict           //Installiert als Image-Speicherbereich, den Sie vorhersagen möchten

Übrigens ist der Inhalt von json unter Labels wie folgt. Der Schlüssel ist der Bildname und der Wert ist die Klasseninformation (1 oder 0).

Stichprobe


# A.json
{
    "A": {
        "Etikett A.": 1,
        "Etikett B.": 1,
        "Etikett C.": 0
    }
}

# B.json
{
    "B": {
        "Etikett A.": 0,
        "Etikett B.": 0,
        "Etikett C.": 1
    }
}

Code

Verschiedene Vorbereitungen

# ref: https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#sphx-glr-beginner-blitz-cifar10-tutorial-py

from PIL import Image
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import numpy as np
import pathlib
import random


#Verwenden Sie diese Option, wenn Sie eine GPU haben
def check_cuda():
    return 'cuda:0' if torch.cuda.is_available() else 'cpu'
device = torch.device(check_cuda())

#Trainingsdaten, Testdatenaufteilung
image_set = {pathlib.Path(i).stem for i in pathlib.Path('data/images').glob('*.jpg')}
n_data = len(image_set)
traindata_rate = 0.7
train_idx = random.sample(range(n_data), int(n_data*traindata_rate))

_trainset = {}
_testset = {}
for i, _tuple in enumerate(image_set.items()):
    k, v = _tuple    
    if i in train_idx:
        _trainset[k] = v
    else :
        _testset[k] = v

Transform

# ref: https://qiita.com/takurooo/items/e4c91c5d78059f92e76d
trfm = transforms.Compose([
    transforms.Resize((100, 100)),    # image size --> (100, 100)
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
])

Dataset

class MultiLabelDataSet(torch.utils.data.Dataset):
    def __init__(self, labels, image_dir='./data/images', ext='.jpg', transform=None):
        self.labels = labels
        self.image_dir = image_dir
        self.ext = ext
        self.transform = transform

        self.keys = list(labels.keys())
        self.vals = list(labels.values())

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        image_path = f'{self.image_dir}/{self.keys[idx]}{self.ext}'
        image_array = Image.open(image_path)
        if self.transform:
            image = self.transform(image_array)
        else:
            image = torch.Tensor(np.transpose(image_array, (2, 0, 1)))/255  # for 0~1 scaling
            
        label = torch.Tensor(list(self.vals[idx].values()))

        return {'image': image, 'label': label}

DataLoader

batch_size = 8

trainset = MultiLabelDataSet(_trainset, transform=trfm)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,
                                          shuffle=False, num_workers=2)

testset = MultiLabelDataSet(_testset, transform=trfm)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
                                         shuffle=False, num_workers=2)

classes = ['A', 'B', 'C'...]Mögen

Datenprüfung

import matplotlib.pyplot as plt
%matplotlib inline

# functions to show an image
def imshow(img):
    plt.imshow(np.transpose(img, (1, 2, 0)))
    plt.show()

# sample data
dataiter = iter(trainloader)
tmp = dataiter.next()
images = tmp['image']
labels = tmp['label']

# print images
imshow(torchvision.utils.make_grid(images))

Modell-

Die Anzahl der Schichten und Kanäle ist angemessen ... Ich benutze BCEWithLogitsLoss, damit ich keine Sigmoide beiße (das habe ich gesagt, als ich gegoogelt habe)

class MultiClassifier(nn.Module):
    def __init__(self):
        super(MultiClassifier, self).__init__()

        self.ConvLayer1 = nn.Sequential(
            # ref(H_out & W_out): https://pytorch.org/docs/stable/nn.html#conv2d
            nn.Conv2d(3, 32, 3),
            nn.MaxPool2d(2),
            nn.ReLU(),
            )

        self.ConvLayer2 = nn.Sequential(
            nn.Conv2d(32, 64, 3),
            nn.MaxPool2d(2),
            nn.ReLU(),
            )

        self.ConvLayer3 = nn.Sequential(
            nn.Conv2d(64, 128, 3),
            nn.MaxPool2d(2),
            nn.ReLU(),
            )    

        self.ConvLayer4 = nn.Sequential(
            nn.Conv2d(128, 256, 3),
            nn.MaxPool2d(2),
            nn.ReLU(),
            nn.Dropout(0.2, inplace=True),
            )    

        self.Linear1 = nn.Linear(256 * 4 * 4, 2048)
        self.Linear2 = nn.Linear(2048, 1024)
        self.Linear3 = nn.Linear(1024, 512)
        self.Linear4 = nn.Linear(512, len(classes))


    def forward(self, x):
        x = self.ConvLayer1(x)
        x = self.ConvLayer2(x)
        x = self.ConvLayer3(x)
        x = self.ConvLayer4(x)
#         print(x.size())
        x = x.view(-1, 256 * 4 * 4)
        x = self.Linear1(x)
        x = self.Linear2(x)
        x = self.Linear3(x)
        x = self.Linear4(x)
        return x

def try_gpu(target):
    if check_cuda():
        device = torch.device(check_cuda())
        target.to(device)

model = MultiClassifier()
try_gpu(model)

Ausbildung

Eine Variable namens "pos_weight" erscheint plötzlich im Kriterium, aber dies liegt an der Gewichtung, wenn die positive Klasse korrekt ist. https://pytorch.org/docs/stable/nn.html#torch.nn.BCEWithLogitsLoss

Wenn Sie eine solche Operation nicht benötigen, müssen Sie sie nicht angeben. Ich habe es angegeben, weil ich das Gewicht zum Zeitpunkt der richtigen Antwort erhöhen wollte. Die Details sind als ref verlinkt, also werde ich dort ~~ der Erklärung entkommen ~~

# ref: https://medium.com/@thevatsalsaglani/training-and-deploying-a-multi-label-image-classifier-using-pytorch-flask-reactjs-and-firebase-c39c96f9c427
import numpy as np
from pprint import pprint
from torch.autograd import Variable
import torch.optim as optim

# ref: https://discuss.pytorch.org/t/bceloss-vs-bcewithlogitsloss/33586
# ref: https://discuss.pytorch.org/t/weights-in-bcewithlogitsloss/27452
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
try_gpu(criterion)

optimizer = optim.SGD(model.parameters(), lr = 0.005, momentum = 0.9)

def pred_acc(original, predicted):
    # ref: https://pytorch.org/docs/stable/torch.html#module-torch
    return torch.round(predicted).eq(original).sum().numpy()/len(original)


def fit_model(epochs, model, dataloader, phase='training', volatile = False):
    if phase == 'training':
        model.train()
        
    if phase == 'validataion':
        model.eval()
        volatile = True
        
    running_loss = []
    running_acc = []
    for i, data in enumerate(dataloader):
        inputs, target = Variable(data['image']), Variable(data['label'])
        
        # for GPU
        if device != 'cpu':
            inputs, target = inputs.to(device), target.to(device)

        if phase == 'training':
            optimizer.zero_grad()  #Gradienteninitialisierung

        ops = model(inputs)
         acc_ = []
         for j, d in enumerate(ops):
             acc = pred_acc(torch.Tensor.cpu(target[j]), torch.Tensor.cpu(d))
             acc_.append(acc)

        loss = criterion(ops, target)
        running_loss.append(loss.item())
        running_acc.append(np.asarray(acc_).mean())
        
        if phase == 'training':
            loss.backward()  #Fehler bei der Weitergabe
            optimizer.step() #Parameteraktualisierung

    total_batch_loss = np.asarray(running_loss).mean()
    total_batch_acc = np.asarray(running_acc).mean()

    if epochs % 10 == 0:
        pprint(f"[{phase}] Epoch: {epochs}, loss: {total_batch_loss}.")
        pprint(f"[{phase}] Epoch: {epochs}, accuracy: {total_batch_acc}.")
    
    return total_batch_loss, total_batch_acc


from tqdm import tqdm

num = 50
best_val = 99
trn_losses = []; trn_acc = []
val_losses = []; val_acc = []
for idx in tqdm(range(1, num+1)):
    trn_l, trn_a = fit_model(idx, model, trainloader)
    val_l, val_a = fit_model(idx, model, testloader, phase='validation')
    trn_losses.append(trn_l); trn_acc.append(trn_a)
    val_losses.append(val_l); val_acc.append(val_a)

    if best_val > val_l:
        torch.save(model.state_dict(), f'model/best_model.pth')
        best_val = val_l
        best_idx = idx

Prognose

def get_tensor(img):
    tfms = transforms.Compose([
        transforms.Resize((100, 100)),
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
        ])
    return tfms(Image.open(img)).unsqueeze(0)

def predict(img, label_lst, model):
    tnsr = get_tensor(img)
    op = model(tnsr)  # Predict result(float)
    op_b = torch.round(op) # Rounding result(0 or 1)
    op_b_np = torch.Tensor.cpu(op_b).detach().numpy()
    preds = np.where(op_b_np == 1)[1]  # result == 1
    
    sigs_op = torch.Tensor.cpu(torch.round((op)*100)).detach().numpy()[0]
    o_p = np.argsort(torch.Tensor.cpu(op).detach().numpy())[0][::-1]  # label index order by score desc
    
    # anser label
    label = [label_lst[i] for i in preds]
    
    # all result
    arg_s = {label_lst[int(j)] : sigs_op[int(j)] for j in o_p}

    return label, dict(arg_s.items())


model = MultiClassifier()
model.load_state_dict(torch.load(f'model/best_model.pth', map_location=torch.device('cpu')))
model = model.eval()    #Wechseln Sie in den Inferenzmodus

target = 'XXXXXX'
img = Image.open(f'predict/{target}.jpg').resize((100, 100))
plt.imshow(img)

_, all_result = predict(f'predict/{target}.jpg', classes, model)
print('predict top5: ', *sorted(all_result.items(), key=lambda x: x[1], reverse=True)[:5])

Schließlich

Das ist alles für die Implementierung.

Datenerweiterung (Es scheint einfach zu implementieren), Modellpolieren, Ich denke, es gibt noch Raum für Verbesserungen der Genauigkeit, wenn während der Bewertung entsprechende Gewichtseinstellungen vorgenommen werden.

Ich war zufrieden mit dem, was ich vorerst machen wollte.









… Als Referenz. Ich habe etwas gemacht, um es so vorherzusagen.

sample.png

Recommended Posts

Multi-Class Multi-Label-Klassifizierung von Bildern mit Pytorch
[PyTorch] Bildklassifizierung von CIFAR-10
Vorhersage des Nikkei-Durchschnitts mit Pytorch 2
Vorhersage des Nikkei-Durchschnitts mit Pytorch
Vorhersage des Nikkei-Durchschnitts mit Pytorch ~ Makuma ~
Multi-Label-Klassifizierung nach Random Forest mit Scikit-Learn
Algorithmus für maschinelles Lernen (Implementierung einer Klassifizierung mit mehreren Klassen)
Transkription von Bildern mit der Vision API von GCP
Dokumentklassifizierung mit toch Text von PyTorch
Mischen Sie Hunderttausende von Bildern gleichmäßig mit Tensorflow.
Klassifizierung von Gitarrenbildern durch maschinelles Lernen Teil 1
Spiele mit PyTorch
Kategorisieren Sie Gesichtsbilder von Anime-Charakteren mit Chainer
SVM (Mehrklassenklassifikation)
Die Geschichte des Versuchs, Tensorboard mit Pytorch zu verwenden
Kreuzvalidierung mit PyTorch
Beginnend mit PyTorch
Klassifizierung von Gitarrenbildern durch maschinelles Lernen Teil 2
Wavelet-Konvertierung von Bildern mit PyWavelets und OpenCV
Verwendung von xgboost: Mehrklassenklassifizierung mit Irisdaten
Zeigen Sie eingebettete Bilder von MP3 und Flac mit Mutagen an
Versuchen Sie, die Bildkonvertierung mit OpenCV mit Python zu projizieren
[PyTorch] Ein wenig Verständnis von CrossEntropyLoss mit mathematischen Formeln
Erstellen Sie einen Stapel von Bildern und blasen Sie sie mit ImageDataGenerator auf
Zusammenfassung der Probleme bei der semantischen Segmentierung mit Pytorch
Vorbereiten der Ausführungsumgebung von PyTorch mit Docker November 2019
Ich habe die einfachste Methode zur Klassifizierung von Dokumenten mit mehreren Etiketten ausprobiert
Ich habe versucht, das Bild mit Python + OpenCV "morphologisch zu konvertieren"
Mehrklassen-SVM mit Scikit-Learn
Typischer Klassifizierungsindex
Naive Buchten (Mehrklassenklassifikation)
Keras Multiklassenklassifikation Iris
Installieren Sie Fackelstreuung mit PyTorch 1.7
Bild mit Python-Pptx zentrieren
Speichern Sie die Ausgabe von GAN nacheinander ~ Mit der Implementierung von GAN durch PyTorch ~
Bildklassifizierung mit selbst erstelltem neuronalen Netzwerk von Keras und PyTorch
Die Geschichte der Anzeige von Bildern mit OpenCV oder PIL (nur)
Echtzeitklassifizierung mehrerer Objekte in Kamerabildern mit tiefem Erlernen von Raspberry Pi 3 B + & PyTorch