[PYTHON] Erstellen Sie eine App, die Bilder erkennt, indem Sie auf Android (PyTorch Mobile) Zahlen auf den Bildschirm schreiben [CNN-Netzwerkerstellung]

App diesmal erstellt werden

Erstellen Sie mit Pytorch Mobile und kotlin eine Bilderkennungs-App, die die auf dem Bildschirm geschriebenen Zahlen erkennt. ** Erstellen Sie alle Funktionen des Modells und Android für die Bilderkennung von Grund auf neu. ** ** ** Es wird in zwei Teile unterteilt: ** CNN Network Creation (Python) ** und ** Android Implementation (Kotlin) **.

Wenn Sie ein Android-Ingenieur sind, der keine Python-Umgebung hat, oder wenn Sie Probleme beim Erstellen eines Modells haben, [Erstellen Sie eine Bilderkennungsanwendung, die die mit Android (PyTorch Mobile) auf dem Bildschirm geschriebenen Zahlen unterscheidet [Android-Implementierung]](https: // qiita. Bitte gehen Sie zu com / YS-BETA / items / 15a4a2c64360f91f8b3a) und laden Sie das trainierte Modell im Implementierungsabschnitt herunter, um fortzufahren.

Ich habe diesen Python-Code auf Github aufgelistet Github: https://github.com/SY-BETA/CNN_PyTorch

Dies ↓

Schöpfungsfluss

  1. MNIST herunterladen (* Die Anzahl der Kanäle muss auf 3 Kanäle geändert werden.)
  2. Erstellen Sie ein einfaches CNN-Modell mit Python (PyTorch)
  3. Trainieren Sie das Modell
  4. Speichern Sie das Modell
  5. Implementierte die Fähigkeit, Bilder auf Android zu zeichnen
  6. Implementieren Sie das Modell auf Android für die Weitergabe

Was ist zu diesem Zeitpunkt zu tun?

Mach 1 ~ 4. Speichern Sie das Modell sogar mit Python. Die diesmal verwendete Bibliothek ist "PyTorch". Die Ausführungsumgebung ist "Jupyter Notebook" Laden Sie den MNIST-Datensatz herunter, um ein einfaches CNN-Modell zu erstellen und zu trainieren.

MNIST herunterladen

Laden Sie den handschriftlichen Nummerndatensatz MNIST herunter, den jeder gerne mit torchvision verwendet

import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose([
        transforms.ToTensor()])
train = torchvision.datasets.MNIST(
    root="data/train", train=True, transform=transform, target_transform=None, download=True)
test = torchvision.datasets.MNIST(
    root="data/test", train=False, transform=transform, target_transform=None, download=True)

Schauen Sie sich MNIST an

Mal sehen, welche Art von Datensatz

from matplotlib import pyplot as plt
import numpy as np

print(train.data.size())
print(test.data.size())
img = train.data[0].numpy()
plt.imshow(img, cmap='gray')
print('Label:', train.targets[0])

** Ausführungsergebnis ** キャプチaaaaャ.PNG

Wechseln Sie von Graustufen zu RGB

Ändern Sie die Anzahl der Farbkanäle von MNIST von 1 auf 3.

** Warum verschwenden Sie eine solche Erhöhung des Rechenaufwands? ** -> Wenn Sie mit Bildern auf Android arbeiten, behandeln Sie sie im Bitmap-Format, wenn Sie sie mit Pytorch Mobile in Tensor konvertieren. ** Kann nur mit 3 Kanälen in Tensor konvertiert werden **. (Wird in Zukunft eine Graustufenkonvertierung hinzugefügt oder handelt es sich um eine solche Spezifikation ...) Trainieren wir das Modell, indem wir es in RGB konvertieren.

** Nicht auf diese Zeit beschränkt, muss das in PyTorch Mobile verwendete Modell ein Modell mit 3 Farbkanälen sein. ** ** **

train_data_resized = train.data.numpy()  #vom Fackeltensor bis zum Numpy
test_data_resized = test.data.numpy()

train_data_resized = torch.FloatTensor(np.stack((train_data_resized,)*3, axis=1))  #In RGB konvertieren
test_data_resized =  torch.FloatTensor(np.stack((test_data_resized,)*3, axis=1))
print(train_data_resized.size())

Die Größe des Datensatzes wurde jetzt von "torch.Size ([60000, 28, 28])" in "torch.Size ([60000, 3, 28, 28])" geändert.

Erstellen Sie Ihren eigenen Datensatz

Erstellen Sie eine benutzerdefinierte Dataset-Klasse

Dieses Mal kann der MNIST-Datensatz aufgrund der Anzahl der Kanäle nicht verwendet werden. Erstellen Sie daher einen benutzerdefinierten Datensatz, indem Sie den "Datensatz" von pytorch erben. Außerdem wird hier eine Standardisierungsklasse erstellt, bei der es sich um eine Bildvorverarbeitung handelt.

import torch.utils.data as data

mean = (0.485, 0.456, 0.406)
std = (0.229, 0.224, 0.225)

#Bildvorverarbeitung
class ImgTransform():
    def __init__(self):
        self.transform = transforms.Compose([
            transforms.ToTensor(),  #Tensolumwandlung
            transforms.Normalize(mean, std)  #Standardisierung
        ])

    def __call__(self, img):
        return self.transform(img)

#Erben Sie die Dataset-Klasse
class _3ChannelMnistDataset(data.Dataset):
    def __init__(self, img_data, target, transform):
        #[Die Anzahl der Daten,Höhe,Seite,Anzahl der Kanäle]Zu
        self.data = img_data.numpy().transpose((0, 2, 3, 1)) /255
        self.target = target
        self.img_transform = transform #Instanz der Bildvorverarbeitungsklasse

    def __len__(self):
        #Gibt die Anzahl der Bilder zurück
        return len(self.data)

    def __getitem__(self, index):
        #Bildvorverarbeitung(Standardisierung)Gibt die Daten zurück
        img_transformed = self.img_transform(self.data[index])
        return img_transformed, self.target[index]

Beachten Sie, dass "Mittelwert" und "Standard" die üblichen Werte sind, die häufig für die Standardisierung verwendet werden, z. B. VGG16. Dies ist der Wert zu diesem Zeitpunkt, der bei der Konvertierung in Tensor auf Android immer standardisiert ist. Wenn Sie den Wert nicht kennen, können Sie die ImageUtils von pytroch mobile in Android Studio überprüfen. aaaaキャプチャ.PNG

Erstellen Sie ein Dataset mit der oben erstellten Klasse

train_dataset = _3ChannelMnistDataset(train_data_resized, train.targets, transform=ImgTransform())
test_dataset = _3ChannelMnistDataset(test_data_resized, test.targets, transform=ImgTransform())

#Testen Sie den Datensatz
index = 0
print(train_dataset.__getitem__(index)[0].size())
print(train_dataset.__getitem__(index)[1])
print(train_dataset.__getitem__(index)[0][1]) #Sie können sehen, dass es richtig standardisiert ist

Erstellung des Datenladers

Erstellen Sie einen benutzerdefinierten Datenlader mit dem erstellten Dataset. Die Stapelgröße beträgt 100

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=100, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=100, shuffle=False)

Erstellen Sie ein CNN-Netzwerk

Erstellen Sie ein einfaches Netzwerk mit 1 Faltungsschicht und 3 vollständig verbundenen Schichten. (Ich hasse es, mir Zeit zum Lernen zu nehmen)

from torch import nn
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.relu = nn.ReLU()
        self.pool = nn.MaxPool2d(3)
        self.conv = nn.Conv2d(3, 10, kernel_size=4)
        self.fc1 = nn.Linear(640, 300)
        self.fc2 = nn.Linear(300, 100)
        self.fc3 = nn.Linear(100, 10)

    def forward(self, x):
        x = self.conv(x)
        x = self.relu(x)
        x = self.pool(x)
        x = x.view(x.size()[0], -1) #Vektorisiert für die lineare Verarbeitung der Matrix(view(Höhe Breite))
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        x = self.relu(x)
        x = self.fc3(x)
        return x

model = Model()
print(model)

Ein solches Netzwerk キャプfadsfcvaチャ.PNG

Trainiere das Netzwerk

Erstellen Sie Funktionen für den Trainingsmodus und den Inferenzmodus

import tqdm
from torch import optim

#Inferenzmodus
def eval_net(net, data_loader, device="cpu"): #Wenn Sie eine GPU haben, gehen Sie zu GPU
    #Im Inferenzmodus
    net.eval()
    ypreds = [] #Voraussichtliche Etikettenspeichervariable
    for x, y in (data_loader):
        #Mit der Methode auf das Gerät übertragen
        x = x.to(device)
        y = [y.to(device)]
        #Sagen Sie die Klasse mit der höchsten Wahrscheinlichkeit voraus
        #Vorwärtsausbreitung
        with torch.no_grad():
            _, y_pred = net(x).max(1)
            ypreds.append(y_pred)
            #Vorhersage für jede Mini-Charge in einen Tensor
            y = torch.cat(y)
            ypreds = torch.cat(ypreds)
            #Berechnen Sie den vorhergesagten Wert(Richtige Antwort = Summe der Vorhersageelemente)
            acc = (y == ypreds).float().sum()/len(y)
            return acc.item()


#Trainingsmodus
def train_net(net, train_loader, test_loader,optimizer_cls=optim.Adam, 
              loss_fn=nn.CrossEntropyLoss(),n_iter=3, device="cpu"):
    train_losses = []
    train_acc = []
    eval_acc = []
    optimizer = optimizer_cls(net.parameters())
    for epoch in range(n_iter):  #4 mal drehen
        runnig_loss = 0.0
        #Im Trainingsmodus
        net.train()
        n = 0
        n_acc = 0
    
        for i, (xx, yy) in tqdm.tqdm(enumerate(train_loader),
                                     total=len(train_loader)):
            xx = xx.to(device)
            yy = yy.to(device)
            output = net(xx)
            
            loss = loss_fn(output, yy)
            optimizer.zero_grad()   #Optimierer initialisieren
            loss.backward()   #Verlustfunktion(Kreuzentropiefehler)Von der Rückausbreitung
            optimizer.step()
            
            runnig_loss += loss.item()
            n += len(xx)
            _, y_pred = output.max(1)
            n_acc += (yy == y_pred).float().sum().item()
            
        train_losses.append(runnig_loss/i)
        #Vorhersagegenauigkeit von Trainingsdaten
        train_acc.append(n_acc / n)
        #Vorhersagegenauigkeit von Verifizierungsdaten
        eval_acc.append(eval_net(net, test_loader, device))

        #Zeigen Sie Ergebnisse mit dieser Epoche
        print("epoch:",epoch, "train_loss:",train_losses[-1], "train_acc:",train_acc[-1],
              "eval_acc:",eval_acc[-1], flush=True)

Versuchen Sie zuerst zu schließen, ohne zu lernen

eval_net(model, test_loader)

Da der Startwert des Zufallsparameters des Netzwerks nicht festgelegt ist, ist er nicht reproduzierbar und ändert sich zufällig. In meiner Umgebung betrug die Punktzahl vor dem Lernen jedoch "0,0799999982".

Lernen

Lernen mit der zuvor erstellten Funktion

train_net(model, train_loader, test_loader)

Schließlich wurde die Vorhersagegenauigkeit ungefähr "0,98000001907". Nun, die Genauigkeit ist zu hoch. Ich mache mir Sorgen, wenn es zu genau ist ...

Ich werde tatsächlich einen schließen

Fügen Sie eine Daten in das trainierte Modell ein und versuchen Sie, das Etikett vorherzusagen.

data = train_dataset.__getitem__(0)[0].reshape(1, 3, 28, 28) #Größe ändern (Größe des Datenladers beachten)
print("Etikette",train_dataset.__getitem__(0)[1].data)
model.eval()
output = model(data)
print(output.size())
output

** Ausführungsergebnis ** キafdfafdaャプチャ.PNG Sie können sehen, dass die Punktzahl mit einem Index von 5 die höchste ist und vorhergesagt werden kann.

Endlich ist die Modellerstellung und Schulung abgeschlossen! !!

Speichern Sie das Modell

Speichern Sie das Modell für die Verwendung auf Android

#Modell speichern
model.eval()
#Beispiel-Eingabegröße
example = torch.rand(1, 3, 28, 28)
traced_script_module = torch.jit.trace(model, example)
traced_script_module.save("./CNNModel.pt")
print(model)

Ende

Vorerst ist dies das Ende von [Network Creation] !! Als nächstes werden wir das erstellte Modell auf Android implementieren. Als ich es mit PyTorch Mobile in einen Tensor konvertierte, wurde es zu einem RGB-Tensor, und ich konnte es nicht in Graustufen umwandeln, sodass ich mir die Mühe machen musste, MNIST in RGB zu konvertieren, was eine Menge mühsamer Verarbeitung war. Infolgedessen konnte ich den MNIST-Datensatz nicht so verwenden, wie er war, und ich musste meinen eigenen Datensatz und Datenlader verwenden. Nun, ich denke, es kann kaum auf Graustufen- oder kommerzieller Ebene verwendet werden. Obwohl es sich um ein ordnungsgemäß hergestelltes CNN-Netzwerk handelte, war ich überrascht, dass die Genauigkeit wie erwartet unerwartet hoch war Ich gebe dir vorerst Github.

Dieser Code Github: https://github.com/SY-BETA/CNN_PyTorch

Dieses Mal erstelltes geschultes Modell (.py): https://github.com/SY-BETA/CNN_PyTorch/blob/master/CNNModel.pt

Gehen wir zur Android-Implementierung Erstellen Sie eine Bilderkennungsanwendung, die die mit Android auf dem Bildschirm geschriebenen Zahlen unterscheidet (PyTorch Mobile) [Android-Implementierung]

Recommended Posts

Erstellen Sie eine App, die Bilder erkennt, indem Sie auf Android (PyTorch Mobile) Zahlen auf den Bildschirm schreiben [CNN-Netzwerkerstellung]
Erstellen Sie eine Bilderkennungs-App, die mit Android (PyTorch Mobile) die auf dem Bildschirm geschriebenen Zahlen unterscheidet [Android-Implementierung]
Erstellen Sie eine Web-App, die Zahlen mit einem neuronalen Netzwerk erkennt
[kotlin] Erstellen Sie eine App, die Fotos erkennt, die mit einer Kamera auf Android aufgenommen wurden
Über den kürzesten Weg, um ein Bilderkennungsmodell durch maschinelles Lernen zu erstellen und eine Android-Anwendung zu implementieren