Pull to refresh

Comments 11

Думаю, что этот эффект связан с тем, что сеть затачивается под одну (или несколько цифр) последних цифр, если подавать ей примеры все скопом: сначала все "1", затем все "2" и т.д. Связано это с тем, что у нейронов нет памяти и проблемой затухающего градиента. Если хочется, чтобы сеть корректно работала на всех последовательностях, то, вероятно, нужно смотреть в сторону LSTM. Вопрос только зачем. Разве что как исследование.

Не думаю, что LSTM тут поможет: проблема не сколько в архитектуре сети, сколько в принципе обучения. Условно, сначала нейроны скатываются по максимальному градиенту для одной цифры, а потом для другой. При этом, если цифры идут блоками (сначала 1, потом 2 итд), то пересечения множеств нейронов, используемых для распознавания каждой цифры будут значительно больше, чем при случайном порядке.

В таком случае, LSTM должен чутка вытаскивать. Сложно сказать, насколько сильно он вытащит. Но, вероятно, профит будет. Т.е. нейроны будут скатываться по градиенту для одной цифры. Но потом они будут куда меньше скатывать по градиенту для другой цифры.

Хотел в статье по последней метке провести аналогию 1 курс — MLP, 2 курс -CNN ну и т.д. и посмотреть как можно/нельзя учить «старшекурсников», но реально не хватает мощности карты, очень долго.
На будущее неплохое исследование.

Максимальный градиент — странный предмет. Вроде бы он есть, а вроде и нет

Ничего удивительного, посмотрите внимательно как происходит обучение MLP, а обучать их можно по разному НО, когда примеры не перемешаны, а «потенциал коррекции весов» (назовём это так) убывает, получается что под первый пример она ещё как-то затачивается, а под последующие уже никак, приехали, либо кишка тонка, либо взаимный выпил в зависимости от того как будем учить.
Вообще тема вроде-бы баянистая в популярной литературе не раз перетёртая.
Скорее наоборот, после всего скопа примеров последнего класса веса затачиваются на этот класс, затирая результаты тренировок на остальных предыдущих классах. Проверить просто — оставить в тестовом множестве только первый класс (нули) или только последний (девятки), и посмотреть какие будут ошибки.
Если оставить только последние «9» (5949 шт) MLP учится и результат
step 0
('Test accuracy:', 0.97360000000000002)
('Test accuracy:', 0.9758)
('Test accuracy:', 0.77300000000000002)
step 1
('Test accuracy:', 0.97460000000000002)
('Test accuracy:', 0.97509999999999997)
('Test accuracy:', 0.88370000000000004)
step 2
('Test accuracy:', 0.97550000000000003)
('Test accuracy:', 0.97299999999999998)
('Test accuracy:', 0.83389999999999997)

Эта последовательность «9» в конце приводит к переобучению, но предыдущие результаты не затираются, когда они есть.
Статья, в том числе, о том, что на одинаковой последовательности MLP не учится совсем.
но предыдущие результаты не затираются, когда они есть.

Ваш эксперимент как раз показывает, что это не так.
Test accuracy как раз и равен около 0.1, т.к. на девятках, которые были последними, тестовые примеры проходят успешно, и девятки как раз составляют 1/10 от общего числа тестовых примеров, с учетом равномерного распределения. И если в тестовом наборе (x_test, y_test) оставить только 9ки, то Test accuracy будет стремится к единице, даже при упорядочивании примеров, и к нули, если из тестового множества убрать все девятки.

Если оставить только последние «9» (5949 шт) в интересующем варианте (0..01..12..23...78..89..9) т.е. последнюю последовательность «9», а начало вернуть, как в исходной последовательности, тогда получится результат выше.

PS. Извиняюсь за телеграфный стиль
Проверка гипотезы
from keras import backend as K_B
from keras.datasets import mnist
from keras.layers import Input, Dense, Dropout
from keras.models import Sequential
from keras.optimizers import RMSprop
from keras.utils import np_utils
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

batch_size = 12
epochs = 12
hidden_size = 512

(X_train, y_train), (X_test, y_test) = mnist.load_data()

num_train, width, height = X_train.shape
num_test = X_test.shape[0]
num_classes = np.unique(y_train).shape[0]

X_train = X_train.astype('float32') 
X_test = X_test.astype('float32')
X_train /= 255.
X_test /= 255.
X_train = X_train.reshape(num_train, height * width)
X_test = X_test.reshape(num_test, height * width)

XX_train = np.copy(X_train)
yy_train = np.copy(y_train)
XX_test = np.copy(X_test)
yy_test = np.copy(y_test)

perm = np.arange(num_train, dtype='int')
j = 0
for k in xrange(num_classes):
    for i in xrange(num_train):
        if (yy_train[i] == k):
            perm[j] = i
            j += 1

for k in xrange(num_train):
    X_train[k,...] = XX_train[perm[k],...]
    y_train[k] = yy_train[perm[k]]

Y_train = np_utils.to_categorical(y_train, num_classes)
Y_test = np_utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(width * height,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(), # RMSprop(),'adam'
              metrics=['accuracy'])

history = model.fit(X_train, Y_train,
                    shuffle = False,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=2,
                    validation_data=(X_test, Y_test))

for n in xrange(10):    
    j = 0
    i = 0
    for k in xrange(num_test):
        if (yy_test[k] >= 8 ):
            X_test[j,...] = XX_test[k,...]
            y_test[j] = yy_test[k]
        else:
            X_test[j,...] = X_test[i,...]
            y_test[j] = y_test[i]
            i += 1
        j += 1
    Y_test = np_utils.to_categorical(y_test, num_classes)

    score = model.evaluate(X_test, Y_test, verbose=0)
    print ' remove ', 9 - n
    print('Test accuracy:', score[1])

fedorro

Если сделать всё тоже, но в тестовой последовательности оставить только «8» и «9» то сеть совсем ничего не понимает. Т.е. тренируем на упорядоченной последовательности, а результат получаем на test, но без некоторых цифр.
листинг
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
22s — loss: 12.7203 — acc: 0.2105 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 2/12
22s — loss: 13.0790 — acc: 0.1885 — val_loss: 14.4487 — val_acc: 0.1028
Epoch 3/12
22s — loss: 11.0910 — acc: 0.3115 — val_loss: 14.4550 — val_acc: 0.1028
Epoch 4/12
22s — loss: 11.5997 — acc: 0.2798 — val_loss: 14.6790 — val_acc: 0.0892
Epoch 5/12
22s — loss: 10.4851 — acc: 0.3490 — val_loss: 14.6563 — val_acc: 0.0892
Epoch 6/12
22s — loss: 9.7669 — acc: 0.3937 — val_loss: 14.5344 — val_acc: 0.0982
Epoch 7/12
22s — loss: 9.6686 — acc: 0.3999 — val_loss: 14.5332 — val_acc: 0.0982
Epoch 8/12
22s — loss: 9.5976 — acc: 0.4043 — val_loss: 14.5219 — val_acc: 0.0982
Epoch 9/12
22s — loss: 7.6871 — acc: 0.5226 — val_loss: 14.4882 — val_acc: 0.1009
Epoch 10/12
22s — loss: 9.9656 — acc: 0.3814 — val_loss: 14.5188 — val_acc: 0.0982
Epoch 11/12
22s — loss: 9.6053 — acc: 0.4038 — val_loss: 14.5128 — val_acc: 0.0982
Epoch 12/12
22s — loss: 9.5696 — acc: 0.4060 — val_loss: 14.4895 — val_acc: 0.0982
remove 9
('Test accuracy:', 0.00020000000000000001)
remove 8
('Test accuracy:', 0.00020000000000000001)
remove 7
('Test accuracy:', 0.00020000000000000001)
remove 6
('Test accuracy:', 0.00020000000000000001)
remove 5
('Test accuracy:', 0.00020000000000000001)
remove 4
('Test accuracy:', 0.00020000000000000001)
remove 3
('Test accuracy:', 0.00020000000000000001)
remove 2
('Test accuracy:', 0.00020000000000000001)
remove 1
('Test accuracy:', 0.00020000000000000001)
remove 0
('Test accuracy:', 0.00020000000000000001)

По очереди удаляем по одной цифре из test последовательности
for n in xrange(10):    
    j = 0
    i = 0
    for k in xrange(num_test):
        if (yy_test[k] != 9 - n ):
            X_test[j,...] = XX_test[k,...]
            y_test[j] = yy_test[k]
        else:
            X_test[j,...] = X_test[i,...]
            y_test[j] = y_test[i]
            i += 1
        j += 1
    Y_test = np_utils.to_categorical(y_test, num_classes)

    score = model.evaluate(X_test, Y_test, verbose=0)
    print ' remove ', 9 - n
    print('Test accuracy:', score[1])


листинг
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
22s — loss: 12.7232 — acc: 0.2104 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 2/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 3/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 4/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 5/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 6/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 7/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 8/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 9/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 10/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 11/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
Epoch 12/12
22s — loss: 14.3070 — acc: 0.1124 — val_loss: 14.2887 — val_acc: 0.1135
remove 9
('Test accuracy:', 0.1278)
remove 8
('Test accuracy:', 0.12670000000000001)
remove 7
('Test accuracy:', 0.12839999999999999)
remove 6
('Test accuracy:', 0.12659999999999999)
remove 5
('Test accuracy:', 0.1258)
remove 4
('Test accuracy:', 0.12759999999999999)
remove 3
('Test accuracy:', 0.12790000000000001)
remove 2
('Test accuracy:', 0.1285)
remove 1
('Test accuracy:', 0.0)
remove 0
('Test accuracy:', 0.12709999999999999)
Sign up to leave a comment.

Articles