Existem muitas publicações na rede sobre a interpretação de uma rede neural particular e a importância e contribuição de certos pontos para a aprendizagem. Existem muitos trabalhos sobre a busca de bigodes, rabos e outras partes e sua importância e significado. Agora não vou substituir bibliotecários e fazer uma lista. Vou apenas contar sobre meu experimento.
Tudo começou com um excelente vídeo Report “Como os robôs pensam. Interpretação de modelos de ML ” , revisado por conselho de uma pessoa inteligente e como qualquer negócio sensato, levantou muitas questões. Por exemplo: - quão únicos são os pontos-chave do conjunto de dados?
Ou outra pergunta: - há muitos artigos na rede sobre como alterar um ponto da imagem pode distorcer significativamente a previsão da rede. Deixe-me lembrá-lo de que, neste artigo, consideraremos apenas problemas de classificação. Quão único é este ponto insidioso? Existem tais pontos na sequência natural do MNIST e se eles forem encontrados e eliminados, a precisão do treinamento da rede neural será maior?
O autor, seguindo seu método tradicional de se livrar de todo o desnecessário, decidiu não interferir no bando e escolheu uma forma simples, confiável e eficaz de estudar as questões colocadas:
como um problema experimental, um exemplo de preparação, escolha o conhecido MNIST ( yann.lecun.com/exdb/mnist ) e sua classificação.
Como rede experimental, escolhi a clássica, recomendada para iniciantes, uma rede exemplar da equipe
KERAS github.com/keras-team/keras/blob/master/examples/mnist_cnn.py
E decidi fazer a pesquisa em si de forma muito simples.
Vamos treinar a rede KERAS com um critério de parada como a ausência de um aumento na precisão na sequência de teste, ou seja, ensine a rede até que test_accuracy se torne significativamente maior do que validation_accuracy e validation_accuracy não melhore por 15 épocas. Em outras palavras, a rede parou de aprender e o retreinamento começou.
A partir do conjunto de dados MNIST, faremos 324 novos conjuntos de dados descartando grupos de pontos e ensinaremos a mesma rede exatamente nas mesmas condições com os mesmos pesos iniciais.
Vamos começar, acho que é certo e certo dispor todo o código, da primeira à última linha. Mesmo que os leitores o tenham visto, obviamente, muitas vezes.
Carregamos as bibliotecas e carregamos o conjunto de dados mnist, se ainda não tiver sido carregado.
Então, nós o convertemos para o formato 'float32' e o normalizamos para o intervalo de 0 a 1.
A preparação acabou.
'''Trains a simple convnet on the MNIST dataset.
Gets to 99.25% test accuracy after 12 epochs
(there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential, load_model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
from keras.optimizers import *
from keras.callbacks import EarlyStopping
import numpy as np
import os
num_classes = 10
# input image dimensions
img_rows, img_cols = 28, 28
# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= np.max(x_train)
x_test /= np.max(x_test)
XX_test = np.copy(x_test)
XX_train = np.copy(x_train)
YY_test = np.copy(y_test)
YY_train = np.copy(y_train)
print('x_train shape:', XX_train.shape)
print('x_test shape:', XX_test.shape)
Lembremos nas variáveis o nome dos arquivos do modelo e pesos, bem como a precisão e perda de nossa rede. Isso não está no código-fonte, mas é necessário para o experimento.
f_model = "./data/mnist_cnn_model.h5"
f_weights = "./data/mnist_cnn_weights.h5"
accu_f = 'accuracy'
loss_f = 'binary_crossentropy'
A própria rede é exatamente a mesma que em
github.com/keras-team/keras/blob/master/examples/mnist_cnn.py .
Salve a rede e as escalas em disco. Executaremos todas as nossas tentativas de treinamento com os mesmos pesos iniciais:
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=[loss_f], optimizer=Adam(lr=1e-4), metrics=[accu_f])
model.summary()
model.save_weights(f_weights)
model.save(f_model)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
conv2d_1 (Conv2D) (None, 24, 24, 64) 18496
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 12, 12, 64) 0
_________________________________________________________________
dropout (Dropout) (None, 12, 12, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 9216) 0
_________________________________________________________________
dense (Dense) (None, 128) 1179776
_________________________________________________________________
dropout_1 (Dropout) (None, 128) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 1290
=================================================================
Total params: 1,199,882
Trainable params: 1,199,882
Non-trainable params: 0
_________________________________________________________________
Vamos começar a treinar no mnist inicial para obter um benchmark, eficiência básica.
x_test = np.copy(XX_test)
x_train = np.copy(XX_train)
s0 = 0
if os.path.isfile(f_model):
model = load_model(f_model)
model.load_weights(f_weights, by_name=False)
step = 0
while True:
fit = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=1,
verbose=0,
validation_data=(x_test, y_test)
)
current_accu = fit.history[accu_f][0]
current_loss = fit.history['loss'][0]
val_accu = fit.history['val_'+accu_f][0]
val_loss = fit.history['val_loss'][0]
print("\x1b[2K","accuracy {0:12.10f} loss {1:12.10f} step {2:5d} val_accu {3:12.10f} val_loss {4:12.10f} ".\
format(current_accu, current_loss, step, val_accu, val_loss), end="\r")
step += 1
if val_accu > max_accu:
s0 = 0
max_accu = val_accu
else:
s0 += 1
if current_accu * 0.995 > val_accu and s0 > 15:
break
else:
print("model not found ")
accuracy 0.9967333078 loss 0.0019656278 step 405 val_accu 0.9916999936 val_loss 0.0054226643
Agora vamos começar o experimento principal. Tiramos para treinamento da sequência original todas as 60.000 fotos marcadas e nelas zeramos tudo, exceto o quadrado 9x9. Vamos obter 324 sequências experimentais e comparar o resultado do treinamento da rede nelas com o treinamento da sequência original. Treinamos a mesma rede com os mesmos pesos iniciais.
batch_size = 5000
s0 = 0
max_accu = 0.
for i in range(28 - 9):
for j in range(28 - 9):
print("\ni= ", i, " j= ",j)
x_test = np.copy(XX_test)
x_train = np.copy(XX_train)
x_train[:,:i,:j,:] = 0.
x_test [:,:i,:j,:] = 0.
x_train[:,i+9:,j+9:,:] = 0.
x_test [:,i+9:,j+9:,:] = 0.
if os.path.isfile(f_model):
model = load_model(f_model)
model.load_weights(f_weights, by_name=False)
else:
print("model not found ")
break
step = 0
while True:
fit = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=1,
verbose=0,
validation_data=(x_test, y_test)
)
current_accu = fit.history[accu_f][0]
current_loss = fit.history['loss'][0]
val_accu = fit.history['val_'+accu_f][0]
val_loss = fit.history['val_loss'][0]
print("\x1b[2K","accuracy {0:12.10f} loss {1:12.10f} step {2:5d} val_accu {3:12.10f} val_loss {4:12.10f} ".\
format(current_accu, current_loss, step, val_accu, val_loss), end="\r")
step += 1
if val_accu > max_accu:
s0 = 0
max_accu = val_accu
else:
s0 += 1
if current_accu * 0.995 > val_accu and s0 > 15:
break
Não faz sentido postar todos os 324 resultados aqui, se alguém tiver interesse, posso enviar pessoalmente. O cálculo leva vários dias, se alguém quiser repeti-lo.
Como se viu, a rede em um recorte 9x9 pode aprender como pior, o que é óbvio, mas também melhor, o que não é nada óbvio.
Por exemplo:
i = 0 j = 14
precisão 0,9972333312 perda 0,0017946947 passo 450 val_accu 0,9922000170 val_loss 0,0054322388
i = 18, j = 1
precisão 0,9973166585 perda 0,0019487827 passo 415 val_accu 0,9922000170 val_loss 0,0053000450
Nós jogamos longe de fotos com números escritos à mão todos, mas o 9x9 quadrado e a qualidade da aprendizagem e o reconhecimento está melhorando conosco!
Também está claro que existe mais de uma área especial para melhorar a qualidade da rede. E não dois, esses dois são dados como exemplo.
O resultado desta experiência e conclusões preliminares.
- Qualquer conjunto de dados natural, não acho que LeCune distorceu algo deliberadamente, contém não apenas pontos que são essenciais para o aprendizado, mas também pontos que interferem no aprendizado. A tarefa de encontrar os pontos "nocivos" torna-se urgente, eles estão lá, mesmo que não sejam visíveis.
- Você pode empilhar e combinar não apenas ao longo do conjunto de dados, selecionando imagens em grupos, mas também ao longo, selecionando áreas de imagens para dividir e, em seguida, como de costume. Nesse caso, tal abordagem melhora a qualidade do treinamento e há esperança de que, em uma tarefa semelhante, o uso de tal empilhamento adicionará qualidade. E no mesmo kaggle.com, alguns dez milésimos às vezes (quase sempre) permitem que você aumente significativamente sua autoridade e classificação.
Obrigado pela atenção.