Créez une application de reconnaissance d'image qui reconnaît les nombres écrits sur l'écran avec Pytorch Mobile et kotlin. ** Créez toutes les fonctions du modèle et d'Android pour la reconnaissance d'image à partir de zéro. ** ** Il sera divisé en deux parties, ** CNN Network Creation (Python) ** et ** Android Implementation (kotlin) **.
Si vous êtes un ingénieur Android qui ne possède pas d'environnement Python, ou si vous rencontrez des difficultés pour créer un modèle, [Créez une application de reconnaissance d'image qui discrimine les nombres écrits à l'écran avec android (PyTorch Mobile) [implémentation Android]](https: // qiita. Veuillez aller à com / YS-BETA / items / 15a4a2c64360f91f8b3a) et télécharger le modèle formé dans la section de mise en œuvre pour continuer.
J'ai répertorié ce code python sur Github Github: https://github.com/SY-BETA/CNN_PyTorch
Ce ↓
Faites 1 ~ 4.
Enregistrez même le modèle en utilisant python. La bibliothèque utilisée cette fois est PyTorch
L'environnement d'exécution est jupyter notebook
Téléchargez l'ensemble de données MNIST pour créer et entraîner un modèle CNN simple.
Téléchargez l'ensemble de données numériques manuscrites MNIST que tout le monde aime utiliser torchvision
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)
Voyons quel type d'ensemble de données
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])
** Résultat d'exécution **
Changez le nombre de canaux de couleur de MNIST de 1 à 3.
** Pourquoi vous souciez-vous de gaspiller une telle augmentation du montant du calcul? ** -> Lorsque vous traitez avec des images sur Android, manipulez-les au format bitmap, lors de la conversion en tenseur avec pytorch mobile ** Ne peut être converti qu'en tenseur avec 3 canaux **. (La conversion de l'échelle de gris est-elle ajoutée à l'avenir ou est-ce une telle spécification ...) Entraînons donc le modèle en le convertissant en RVB.
** Non limité à ce moment, le modèle utilisé dans PyTorch Mobile doit être un modèle avec 3 canaux de couleur. ** **
train_data_resized = train.data.numpy() #du tenseur de la torche au numpy
test_data_resized = test.data.numpy()
train_data_resized = torch.FloatTensor(np.stack((train_data_resized,)*3, axis=1)) #Convertir en RVB
test_data_resized = torch.FloatTensor(np.stack((test_data_resized,)*3, axis=1))
print(train_data_resized.size())
La taille du jeu de données est maintenant passée de torch.Size ([60000, 28, 28])
à torch.Size ([60000, 3, 28, 28])
.
Cette fois, le jeu de données MNIST ne peut pas être utilisé car cela est dû au nombre de canaux, donc créez un jeu de données personnalisé en héritant du Dataset
de pytorch.
De plus, une classe de normalisation, qui est un prétraitement d'image, est également créée ici.
import torch.utils.data as data
mean = (0.485, 0.456, 0.406)
std = (0.229, 0.224, 0.225)
#Prétraitement d'image
class ImgTransform():
def __init__(self):
self.transform = transforms.Compose([
transforms.ToTensor(), #Conversion de Tensol
transforms.Normalize(mean, std) #Standardisation
])
def __call__(self, img):
return self.transform(img)
#Hériter de la classe Dataset
class _3ChannelMnistDataset(data.Dataset):
def __init__(self, img_data, target, transform):
#[Le nombre de données,la taille,côté,Nombre de canaux]À
self.data = img_data.numpy().transpose((0, 2, 3, 1)) /255
self.target = target
self.img_transform = transform #Instance de la classe de prétraitement d'image
def __len__(self):
#Renvoie le nombre d'images
return len(self.data)
def __getitem__(self, index):
#Prétraitement d'image(Standardisation)Renvoie les données
img_transformed = self.img_transform(self.data[index])
return img_transformed, self.target[index]
Notez que «mean» et «std» sont les valeurs habituelles qui sont souvent utilisées pour la normalisation, comme VGG16. C'est la valeur à ce moment-là qui est toujours normalisée lors de la conversion en tenseur sur Android. Si vous ne connaissez pas la valeur, vous pouvez vérifier ʻImageUtils` de pytroch mobile dans Android Studio.
train_dataset = _3ChannelMnistDataset(train_data_resized, train.targets, transform=ImgTransform())
test_dataset = _3ChannelMnistDataset(test_data_resized, test.targets, transform=ImgTransform())
#Essayez de tester l'ensemble de données
index = 0
print(train_dataset.__getitem__(index)[0].size())
print(train_dataset.__getitem__(index)[1])
print(train_dataset.__getitem__(index)[0][1]) #Vous pouvez voir qu'il est correctement standardisé
Créez un chargeur de données personnalisé avec l'ensemble de données créé. La taille du lot est de 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)
Créez un réseau simple avec 1 couche de convolution et 3 couches entièrement connectées. (Je déteste prendre le temps d'apprendre)
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) #Vectorisé pour le traitement linéaire de la matrice(view(Hauteur largeur))
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)
Un tel réseau
import tqdm
from torch import optim
#Mode d'inférence
def eval_net(net, data_loader, device="cpu"): #Si vous avez un GPU, allez sur gpu
#En mode inférence
net.eval()
ypreds = [] #Variable de stockage d'étiquette prédite
for x, y in (data_loader):
#Transférer vers l'appareil avec la méthode
x = x.to(device)
y = [y.to(device)]
#Prédire la classe avec la probabilité la plus élevée
#propagation vers l'avant
with torch.no_grad():
_, y_pred = net(x).max(1)
ypreds.append(y_pred)
#Prédiction pour chaque mini-lot en un tenseur
y = torch.cat(y)
ypreds = torch.cat(ypreds)
#Calculer la valeur prévue(Bonne réponse = somme des éléments prédictifs)
acc = (y == ypreds).float().sum()/len(y)
return acc.item()
#Mode entraînement
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): #Tourner 4 fois
runnig_loss = 0.0
#En mode entraînement
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() #Initialiser l'optimiseur
loss.backward() #Fonction de perte(Erreur d'entropie croisée)De la propagation arrière
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)
#Précision des prévisions des données d'entraînement
train_acc.append(n_acc / n)
#Précision des prévisions des données de vérification
eval_acc.append(eval_net(net, test_loader, device))
#Voir les résultats avec cette époque
print("epoch:",epoch, "train_loss:",train_losses[-1], "train_acc:",train_acc[-1],
"eval_acc:",eval_acc[-1], flush=True)
Essayez d'abord de déduire sans apprendre
eval_net(model, test_loader)
Étant donné que la valeur de départ du paramètre aléatoire du réseau n'est pas fixe, elle n'est pas reproductible et change de manière aléatoire, mais dans mon environnement, le score avant l'apprentissage était de «0,0799999982».
Apprentissage à l'aide de la fonction créée précédemment
train_net(model, train_loader, test_loader)
Enfin, la précision de la prédiction est devenue d'environ "0,98000001907". Eh bien, la précision est trop élevée. Je m'inquiète si c'est trop précis ...
Mettez une donnée dans le modèle entraîné et essayez de prédire l'étiquette.
data = train_dataset.__getitem__(0)[0].reshape(1, 3, 28, 28) #Redimensionner (notez la taille du chargeur de données)
print("étiquette",train_dataset.__getitem__(0)[1].data)
model.eval()
output = model(data)
print(output.size())
output
** Résultat d'exécution ** Vous pouvez voir que le score avec un indice de 5 est le plus élevé et peut être prédit.
Enfin, la création et la formation du modèle sont terminées! !!
Enregistrer le modèle pour une utilisation sur Android
#Enregistrer le modèle
model.eval()
#Taille d'entrée d'échantillon
example = torch.rand(1, 3, 28, 28)
traced_script_module = torch.jit.trace(model, example)
traced_script_module.save("./CNNModel.pt")
print(model)
Pour l'instant, c'est la fin de [Création de réseau] !! Ensuite, nous allons implémenter le modèle créé sur Android. Lorsque je l'ai converti en tenseur avec PyTorch Mobile, il est devenu un tenseur RVB, et je ne pouvais pas le faire en niveaux de gris, j'ai donc dû me donner la peine de convertir MNIST en RVB, ce qui était beaucoup de traitement gênant. En conséquence, je ne pouvais pas utiliser le jeu de données MNIST tel quel, et je devais utiliser mon propre jeu de données et mon chargeur de données. Eh bien, je pense qu'il peut difficilement être utilisé à l'échelle de gris ou au niveau commercial. De plus, bien qu'il s'agisse d'un réseau CNN correctement conçu, j'ai été surpris que la précision soit étonnamment élevée, comme prévu CNN Je vais vous donner Github pour le moment.
Ce code Github: https://github.com/SY-BETA/CNN_PyTorch
Modèle formé créé cette fois (.py): https://github.com/SY-BETA/CNN_PyTorch/blob/master/CNNModel.pt
Passons à l'implémentation Android Créez une application de reconnaissance d'image qui discrimine les nombres écrits à l'écran avec Android (PyTorch Mobile) [Implémentation Android]
Recommended Posts