CNN utilisant la convolution est relativement bon pour extraire les caractéristiques des images bidimensionnelles en noir et blanc. Un code QR est l'une des images en noir et blanc, voyons donc si la valeur de ce code QR peut être lue par CNN. En fait, quel bit en noir et blanc est la valeur qui peut être lue sur une base de règles, et NN sans convolution est suffisant, mais ici, j'ose utiliser CNN.
La version du code QR dépend de la taille du code QR et du nombre de caractères qu'il peut contenir. Par exemple, version = 1 a une taille de 21 x 21 et peut contenir "www.wikipedia.org" et une chaîne de 17 caractères comme indiqué ci-dessous. Puisque E1 à E7 sont une correction d'erreur, la lecture n'est pas essentielle. En d'autres termes, dans ce cas, chaque caractère est de 8 bits, donc si vous vérifiez la valeur de 136 bits au total, vous pouvez lire ce qui est écrit dans cette taille même sur la base de règles. Pour l'instant, le but est de lire les chiffres inscrits sur ce QR code minimum 21x21.
J'ai écrit ce qui suit à Keras. J'ai converti le nombre à 6 chiffres en une chaîne de caractères et créé 40000 codes QR avec la plus petite taille à utiliser comme données d'entraînement et de test. De plus, s'il s'agit du CNN d'origine, il peut être nécessaire d'utiliser le regroupement pour réduire de moitié la taille de l'image, mais dans ce cas, la taille d'entrée est aussi petite que 21 × 21, donc conv2d constitue à lui seul la partie de convolution.
qr.py
import qrcode
import numpy as np
import random
from keras.utils import np_utils
from keras.layers import Input, Conv2D, MaxPooling2D, AveragePooling2D, BatchNormalization, Concatenate
from keras.models import Model
batch_size = 128
num_classes = 10
epochs = 30
X, Y = [], []
sample_list = random.sample(range(10**6), k=40000)
for i in sample_list:
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=1, border=0 )
qr.add_data('%06d' % (i))
qr.make()
img = qr.make_image()
X.append(np.asarray(img))
Y.append([int(d) for d in format(i, '06d')])
X = np.reshape(np.asarray(X),(-1,21,21,1))/1.0
Y = np.reshape(np_utils.to_categorical(np.asarray(Y)), (-1,1,6,10))
print(X.shape)
print(Y.shape)
inputs = Input((21,21,1))
x = Conv2D(256, (3,3), padding='same', activation='relu')(inputs)
x = BatchNormalization()(x)
x = Conv2D(256, (3,3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = Conv2D(256, (3,3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = Conv2D(256, (3,3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = Conv2D(256, (3,3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = Conv2D(512, (3,3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = Conv2D(512, (3,3), padding='same', activation='relu')(x)
x = MaxPooling2D(pool_size=(21, 21))(x)
y = [Conv2D(10, (1,1), activation='softmax')(x) for i in range(6)]
y = Concatenate(axis=-2)(y)
model = Model(inputs=inputs, outputs=y)
model.summary()
model.compile(loss='categorical_crossentropy',
optimizer='Adam',
metrics=['accuracy'])
history = model.fit(X[:30000], Y[:30000], batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(X[30000:], Y[30000:]))
model.save('qr_model.h5', include_optimizer=False)
ici
qr.py
y = [Conv2D(10, (1,1), activation='softmax')(x) for i in range(6)]
y = Concatenate(axis=-2)(y)
Vous pouvez écrire la partie comme suit.
qr.py
y1 = Conv2D(10, (1,1), activation='softmax')(x)
y2 = Conv2D(10, (1,1), activation='softmax')(x)
y3 = Conv2D(10, (1,1), activation='softmax')(x)
y4 = Conv2D(10, (1,1), activation='softmax')(x)
y5 = Conv2D(10, (1,1), activation='softmax')(x)
y6 = Conv2D(10, (1,1), activation='softmax')(x)
y = Concatenate(axis=-2)([y1,y2,y3,y4,y5,y6])
Le résultat de l'exécution du code à ce moment est le suivant.
(40000, 21, 21, 1)
(40000, 1, 6, 10)
...
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) (None, 21, 21, 1) 0
__________________________________________________________________________________________________
conv2d_1 (Conv2D) (None, 21, 21, 256) 2560 input_1[0][0]
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 21, 21, 256) 1024 conv2d_1[0][0]
__________________________________________________________________________________________________
conv2d_2 (Conv2D) (None, 21, 21, 256) 590080 batch_normalization_1[0][0]
__________________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, 21, 21, 256) 1024 conv2d_2[0][0]
__________________________________________________________________________________________________
conv2d_3 (Conv2D) (None, 21, 21, 256) 590080 batch_normalization_2[0][0]
__________________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, 21, 21, 256) 1024 conv2d_3[0][0]
__________________________________________________________________________________________________
conv2d_4 (Conv2D) (None, 21, 21, 256) 590080 batch_normalization_3[0][0]
__________________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, 21, 21, 256) 1024 conv2d_4[0][0]
__________________________________________________________________________________________________
conv2d_5 (Conv2D) (None, 21, 21, 256) 590080 batch_normalization_4[0][0]
__________________________________________________________________________________________________
batch_normalization_5 (BatchNor (None, 21, 21, 256) 1024 conv2d_5[0][0]
__________________________________________________________________________________________________
conv2d_6 (Conv2D) (None, 21, 21, 512) 1180160 batch_normalization_5[0][0]
__________________________________________________________________________________________________
batch_normalization_6 (BatchNor (None, 21, 21, 512) 2048 conv2d_6[0][0]
__________________________________________________________________________________________________
conv2d_7 (Conv2D) (None, 21, 21, 512) 2359808 batch_normalization_6[0][0]
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D) (None, 1, 1, 512) 0 conv2d_7[0][0]
__________________________________________________________________________________________________
conv2d_8 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_9 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_10 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_11 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_12 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_13 (Conv2D) (None, 1, 1, 10) 5130 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
concatenate_1 (Concatenate) (None, 1, 6, 10) 0 conv2d_8[0][0]
conv2d_9[0][0]
conv2d_10[0][0]
conv2d_11[0][0]
conv2d_12[0][0]
conv2d_13[0][0]
==================================================================================================
Total params: 5,940,796
Trainable params: 5,937,212
Non-trainable params: 3,584
__________________________________________________________________________________________________
Train on 30000 samples, validate on 10000 samples
Epoch 1/30
30000/30000 [==============================] - 66s 2ms/step - loss: 2.7801 - acc: 0.1714 - val_loss: 2.3467 - val_acc: 0.2484
Epoch 2/30
30000/30000 [==============================] - 62s 2ms/step - loss: 1.8426 - acc: 0.3493 - val_loss: 1.6885 - val_acc: 0.3941
Epoch 3/30
30000/30000 [==============================] - 64s 2ms/step - loss: 1.4841 - acc: 0.4555 - val_loss: 1.4549 - val_acc: 0.4547
...
Epoch 28/30
30000/30000 [==============================] - 64s 2ms/step - loss: 0.0401 - acc: 0.9868 - val_loss: 0.3695 - val_acc: 0.9110
Epoch 29/30
30000/30000 [==============================] - 64s 2ms/step - loss: 0.0435 - acc: 0.9853 - val_loss: 0.3403 - val_acc: 0.9184
Epoch 30/30
30000/30000 [==============================] - 63s 2ms/step - loss: 0.0339 - acc: 0.9889 - val_loss: 0.3164 - val_acc: 0.9231
La précision finale est ** acc: 0.9889, val_acc: 0.9231 **. L'époque et le modèle conviennent, donc les ajuster améliorera un peu plus la précision.
Écrivez ce qui suit comme code de vérification. Le résultat est une condition limitée assez petite de 21x21 avec version = 1 du code QR, mais j'ai pu prédire un nombre à 6 chiffres dans une certaine mesure.
qr2.py
import qrcode
import numpy as np
import random
from keras.models import load_model
X, Y = [], []
sample_list = random.sample(range(10**6), k=10)
for i in sample_list:
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=1, border=0 )
qr.add_data('%06d' % (i))
qr.make()
img = qr.make_image()
X.append(np.asarray(img))
Y.append(i)
X = np.reshape(np.asarray(X),(-1,21,21,1))/1.0
model = load_model('qr_model.h5')
Y_pred = model.predict(X)
Y_pred_list = []
for i in range(10):
Y_pred_value = 0
for j in range(6):
Y_pred_value += np.argmax(Y_pred[i,0,j])
Y_pred_value *= 10
Y_pred_list.append(Y_pred_value//10)
print(Y)
print(Y_pred_list)
[89127, 306184, 427806, 501649, 727976, 232504, 427216, 893062, 127368, 100207]
[89127, 306184, 427806, 501649, 727976, 234506, 431222, 893062, 127378, 100207]
Généralement, MaxPooling2D sait s'il y a une fonction dans l'image, mais il n'atteint pas cet emplacement dans les couches suivantes. En parlant de CNN, il est souvent utilisé pour des problèmes de classification tels que les chiens et les chats étant inclus quelque part dans l'image, mais d'un autre côté, j'ai pensé qu'il était possible d'extraire des caractéristiques (sans utiliser d'aplatissement) pour spécifier l'emplacement, mais c'est possible. Et il semble. Cela peut être dû au fait que la quantité de caractéristiques de l'emplacement des données est extraite par la valeur de données dans la convolution à courte distance et la distance du motif carré fixe dans le coin supérieur gauche, supérieur droit et inférieur gauche dans la convolution longue distance. Si vous venez de lire la valeur des données (8 bits de 2,4 ou 4,2), Conv2d avec 2 couches (équivalent à 5,5) devrait suffire, mais en réalité, la précision n'est pas si élevée avec le modèle avec Conv2d à 2 couches. C'était (val_acc: environ 0,5). Par conséquent, si vous lisez le code QR avec CNN, il peut être nécessaire d'extraire la quantité de caractéristiques du lieu par un pliage longue distance. (Ou peut-être qu'il vaut mieux utiliser aplatir rapidement comme modèle ...)
Recommended Posts