[PYTHON] Tentative de classification des polices de pictogrammes avec Keras

L'artiste manga Masayuki Kitamichi a produit et publié la police "Kitamiji 222" pour commémorer "Cat Day" le 22 février.

http://kitamichi.sub.jp/Sites/iblog/C513573485/E937677024/ (Article d'introduction: http://www.forest.impress.co.jp/docs/review/20160303_746474.html)

Tout d'abord, vérifiez la licence.

◆ À propos de l'utilisation

"Kitamiji 222" est un logiciel gratuit. S'il n'est pas destiné à un usage commercial, vous pouvez l'utiliser librement sans aucune restriction. Cependant, veuillez vous abstenir de vendre, redistribuer ou traiter le "corps du fichier de police".

Le copyright de "Kitamiji 222" appartient à Masayuki Kitado. Nous ne sommes pas responsables de toute machine ou d'autres problèmes causés par l'utilisation de polices.

(Cette fois, j'ai découpé l'image de la police et l'ai utilisée comme données d'apprentissage automatique, mais je pense qu'elle ne correspond pas au traitement du "corps du fichier de police". → M. Kitamichi a accepté d'utiliser cette police.)

Par exemple, "aiueo" et "kakikukeko" sont affichés comme suit. ** AIUEO ** a_gyo.PNG

** Kakikukeko ** ka_gyo.PNG

De cette façon, la différence de voyelles est exprimée par la direction du visage du chat, et la différence de consonnes est exprimée par la différence de motif de poils. Intuitivement, j'ai senti que la reconnaissance de la voyelle "aiueo" pouvait se faire relativement facilement avec un classificateur composé d'un réseau neuronal, j'ai donc essayé ceci. ("A" et "e" ("ka" et "ke") se ressemblent un peu, mais cela semble un peu difficile à classer.)

Puisque «Kitamiji 222» est fourni dans la police TrueType, le travail a été effectué dans le flux suivant.

  1. Traitez jusqu'au point de conversion en données Numpy avec "Jupyter (ipython) Notebook" + noyau python 3.
  2. Backend "Keras" + "TensorFlow" (par python 2) où les données Numpy sont lues et classées.

(Environnement de programmation: IPython-notebook 4.0.4, python 3.5.1, numpy 1.10.4, oreiller 3.1.1, python 2.7.11, keras 0.3.0, tensorflow 0.7.0)

Découpez l'image de la police et enregistrez le cornichon

Comme je n'avais que peu d'expérience avec les polices 2 octets et les images de polices, ce travail de prétraitement, y compris la recherche, a pris un temps considérable. Le travail principal a été réalisé avec la bibliothèque de classes Image de Pillow (PIL Fork).

Tout d'abord, importez la bibliothèque et chargez la police.

import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image
%matplotlib inline

font = './kitamiji222_ver101.ttf'
font = ImageFont.truetype(font, 36)

Ensuite, créez une instance de l'image requise et écrivez-y le texte.

text = u'AIUEO'
siz = font.getsize(text)
img1 = Image.new('RGB', siz, (255, 255, 255))
draw = ImageDraw.Draw(img1)
orig = (0, 0)
draw.text(orig, text, (0, 0, 0), font=font)

Affichez-le et confirmez.

plt.imshow(img1)
plt.xticks([])
plt.yticks([])
plt.show()

a_gyo_RGB.PNG

Étant donné que la police n'a pas besoin d'informations de couleur, elle est en niveaux de gris. De plus, il est converti en une matrice Numpy.

images = []
siz = (36, 36)
for hira_ch in hiralist:
    img_ch = Image.new('RGB', siz, (255, 255, 255))
    draw = ImageDraw.Draw(img_ch)
    orig = (0, 0)
    draw.text(orig, hira_ch, (0, 0, 0), font=font)
    img_ch_g = img_ch.convert('L')
    images.append(img_ch_g)

def PIL2npmat(img):
    return np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0])

imgmats = [PIL2npmat(img) for img in images]

Enfin, enregistrez-le dans un fichier au format pickle.

import pickle

mydata = [imgmats, codelist]
filename = 'kitamiji222.pkl'
outputfp = open(filename, 'wb')
pickle.dump(mydata, outputfp, protocol=2)
outputfp.close()

Dans la liste ci-dessus, mydata est l'objet python que vous souhaitez enregistrer. Cette fois, la procédure est de sauvegarder le pickle dans l'environnement python3 et de le charger dans l'environnement python2, mais afin de maintenir la compatibilité du fichier pickle, protocol = 2 est spécifié dans pickle.dump (). ..

Essai avec le modèle MLP (Multi-layer Perceptron)

Comme mentionné ci-dessus, le Deep Learning Framework «Keras» a été utilisé, mais le modèle de réseau a été défini comme suit en référence à cet exemple de code «mnist_mlp.py».

    trXshape = trainX[0].shape
    nclass = trainY.shape[1]
    hidden_units = 800
    
    model = Sequential()                                    #Instanciation séquentielle du modèle
    model.add(Dense(hidden_units, input_shape=trXshape))    #Définition de la couche cachée
    model.add(Activation('relu'))
    model.add(Dropout(0.3))
    model.add(Dense(hidden_units))                          #Définition de la couche cachée
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    model.add(Dense(nclass))                                #Définition de la couche de sortie
    model.add(Activation('softmax'))

    optimizer = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08) #Définition de l'optimiseur
    model.compile(loss='categorical_crossentropy', optimizer=optimizer) #Sélectionnez le coût et compilez le modèle

Puisqu'il existe cinq types de voyelles classées cette fois, les données d'étiquette trainY contiennent des informations correspondant à ['a', 'i', 'u', 'e', 'o'] calculées à partir du code de police ajouté à l'avance. Est inclus. (Par conséquent, nclass = 5.)

Le modèle utilise le type séquentiel de Keras et est défini dans l'ordre du côté entrée au côté sortie. Du côté de l'entrée, un total de trois modèles MLP, couche cachée 1 → couche cachée 2 → couche de sortie, ont été utilisés. Étant donné que l'image de la police est 36x36 (= 1296), le nombre d'unités de calque masquées est défini sur 800. Dropout () est utilisé pour la régularisation. Adam () a été utilisé comme optimiseur pour l'apprentissage.

Dans Keras récent, "Theano" ou "TensorFlow" peut être sélectionné comme backend, mais cette fois "TensorFlow" a été utilisé. Vous pouvez spécifier la fin de la banque Keras en définissant une variable d'environnement.

export KERAS_BACKEND=tensorflow

Tout d'abord, le nombre requis de données d'entraînement (données de train) et de données de test (données de test) a été échantillonné au hasard à partir de "Hiragana" et calculé. L'état, le coût et la précision du calcul sont indiqués dans la figure ci-dessous.

Fig. MLP model, Loss and Accuracy mlp_t_1.png

Après avoir terminé le nombre d'époques prédéterminé, la perte converge vers près de 0 et la précision converge vers près de 1,0. De plus, la précision était de près de 1,0 (100%) dans la classification ultérieure utilisant les données de test. Même s'il a été échantillonné au hasard, il n'y a pas de différence de précision entre Train et Test, car les données de train et les données de test sont tirées du même ensemble et de la même population.

Tester "Katakana" avec le modèle MLP

Il n'est pas intéressant d'apprendre simplement, j'ai donc décidé de préparer les données de test séparément des données d'entraînement. Heureusement, les hiragana et katakana ont été préparés pour "Kitamiji 222", nous avons donc décidé d'utiliser un ensemble de hiragana comme données d'entraînement et un ensemble de katakana comme données de test.

Comparons chaque image. ** Fig. Hiragana, de ligne en ligne ** hira_20ch.PNG

** Fig. Katakana, de la ligne A à la ligne ** kata_20ch.PNG

On observe que les personnages avec le même son en hiragana et en katakana sont de forme assez similaire. Il a été constaté que la différence entre hiragana et katakana s'exprime par le fait que la bouche est «fermée» ou «ouverte».

Par conséquent, les données de train et les données de test ont été préparées séparément et le calcul a été effectué.

Fig. MLP model, Loss and Accuracy mlp1.png

Fig. MLP model, Validation Loss and Validation Accuracy mlp2.png

La situation d'apprentissage en utilisant Hiragana (données de train) est presque la même que la figure précédente. En ce qui concerne la perte de validation et la précision en utilisant Katakana (données de test), la perte diminue et la précision augmente comme prévu. La précision finale était de 75%.

J'ai changé les paramètres de calcul pour améliorer la précision, mais le plus efficace est la valeur de décrochage. En ajustant le degré de conformité du hiragana à l'emblème, la précision de la classification des katakana peut être améliorée. J'ai pu réaliser ce que j'ai appris dans le manuel sous la forme des lettres faciales. (À propos, les bons résultats de divers essais sont indiqués dans la liste ci-dessus, et le taux d'abandon dans la première étape est de 0,3 et le taux d'abandon dans la deuxième étape est de 0,5.

Testé avec CNN (Convolitional Neural Network)

J'ai essayé le modèle CNN (Convolutional Neural Network) pour une meilleure précision. L'avantage des bibliothèques de classes de haut niveau comme Keras est que vous pouvez facilement modifier le modèle avec quelques modifications de code. Les principales parties du code sont présentées ci-dessous.

    nb_classes = trainY.shape[1]
    img_rows, img_cols = 36, 36     # image dimensions
    trainX = trainX.reshape(trainX.shape[0], 1, img_rows, img_cols)
    testX = testX.reshape(testX.shape[0], 1, img_rows, img_cols)
    nb_filters = 32                 # convolutional filters to use
    nb_pool = 2                     # size of pooling area for max pooling
    nb_conv = 3                     # convolution kernel size
    
    model = Sequential()                                     #Instanciation séquentielle du modèle
    model.add(Convolution2D(nb_filters, nb_conv, nb_conv,    #Définition de la couche convolutive
                        border_mode='valid',
                        input_shape=(1, img_rows, img_cols)))
    model.add(Activation('relu'))
    model.add(Convolution2D(nb_filters, nb_conv, nb_conv))   #Définition de la couche convolutive
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(nb_pool, nb_pool)))    #Définition de la couche de pooling
    model.add(Dropout(0.3))

    model.add(Flatten())
    model.add(Dense(128))                                    #Définition de la couche de liaison complète
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    model.add(Dense(nb_classes))                             #Définition de la couche de sortie
    model.add(Activation('softmax'))

    optimizer = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08)  #Définition de l'optimiseur
    model.compile(loss='categorical_crossentropy', optimizer=optimizer)  #Sélectionnez le coût et compilez le modèle

Du côté de l'entrée, il a une structure à cinq couches: couche de convolution 1 → couche de convolution 2 → couche de regroupement → couche entièrement connectée → couche de sortie. (Comme vous pouvez l'imaginer, cette structure de couche suit l'exemple mnist_cnn.py.) De plus, l'optimiseur utilise Adam comme auparavant.

Le résultat du calcul est le suivant.

Fig. CNN model, Loss and Accuracy cnn1.png

Fig. CNN model, Validation Loss and Validation Accuracy cnn2.png

Comme prévu, la précision de la classification s'est améliorée. La précision de classification finale des données de test (Katakana) était de 89%. J'ai fait de mon mieux en ajustant les paramètres, mais je ne pouvais pas l'obtenir au niveau de 90%. (J'ai vérifié principalement le taux d'abandon.)

Par rapport à la précision de 98 .. 99% de la tâche de classification des nombres manuscrits "MNIST", 89% est un peu décevant, mais le nombre d'échantillons de jeux de données est nettement différent entre MNIST et cette fois. (Dans ce cas, l'échantillonnage est effectué de manière aléatoire, mais les types sont limités au jeu de polices.) Pour améliorer encore la précision de cette classification de pictogrammes, traitez l'image de la police (déformation, ajout de bruit) Etc., il est nécessaire d'avoir du mal à augmenter le nombre d'échantillons de données. (Sinon, il peut être assez efficace de changer la méthode de régularisation.)

Enfin, je voudrais remercier M. Kitamichi pour avoir publié un sujet intéressant sur l'apprentissage automatique. (Je sais que je n'ai pas créé la police avec l'apprentissage automatique à l'esprit, mais j'ai pu la jouer !!!)

Références (site Web)

-Police de pictogramme de chat "Kitamiji 222" réalisée par le créateur du dessin animé à 4 images "Pu-Neko" http://www.forest.impress.co.jp/docs/review/20160303_746474.html

Recommended Posts

Tentative de classification des polices de pictogrammes avec Keras
J'ai essayé d'intégrer Keras dans TFv1.1
[Keras] Mémo personnel pour classer les images par dossier [Python]
Classer les numéros mnist par keras sans apprentissage par l'enseignant [Auto Encoder Edition]
Créer une fonction pour décrire les polices japonaises avec OpenCV
Classer les visages d'anime par suite / apprentissage profond avec Keras
J'ai essayé d'implémenter Grad-CAM avec keras et tensorflow
Convertir 202003 en 2020-03 avec les pandas
Tutoriel CIFAR-10 avec Keras
LSTM multivarié avec Keras
[TensorFlow 2 / Keras] Comment exécuter l'apprentissage avec CTC Loss dans Keras
J'ai essayé de classer MNIST par GNN (avec PyTorch géométrique)
Pour les débutants, comment gérer les erreurs courantes dans les keras