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 ↓
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.
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)
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 **
Ä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.
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.
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
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 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
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 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 ...
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 ** 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 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)
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