Pull to refresh

Comments 33

А в причине не разобрались?
Очень похоже, что без нормализации (а вы вроде бы её не делаете после перемешивания) функции активации не отрабатывают так как прежде.

Выложите полный код пожалуйста.
Непонятно, обучение происходит на исходных нормализованных данных, а потом вы сети скармливаете ненормализованные данные?
Для случая распознавания чисел после полного перемешивания изображения по точкам у меня в голове есть только одна рабочая идея (гипотеза), — сеть тупо считает количество точек на изображении. И по их числу предсказывает цифру. Никакие другие гипотезы в голову не лезут, т.к. перемещивание не оставляет никакой другой полезной информации.
Но это надо проверять экспериментально.

Причина довольно-таки очевидна — и автор явно её понимал ещё до начала эксперимента. Сеть не знает, как обобщать данные, и выбирает какой-то свой метод. Если бы была огромная обучающая выборка, включающая в себя все виды написания (в т.ч. как во втором примере) — всё было бы хорошо, но раз её нет — надо вносить информацию о желаемых принципах обобщения (явно, строя входные данные для сети, или неявно, создавая искусственную обучающую выборку из искажённых образов исходной).

Причина довольно-таки очевидна

Это да, но у вас она указана неправильно :) Как раз в обучающей выборке были цифры, написанные именно так, как в тестовой — но всё равно распознать их у сети не получилось.

Да, я вообще напутал — почему-то решил, что учили по неиспорченным образцам, а распознавали испорченные.

from keras.datasets import mnist # subroutines for fetching the MNIST dataset
from keras.models import Model # basic class for specifying and training a neural network
from keras.layers import Input, Dense # the two types of neural network layer we will be using
from keras.utils import np_utils # utilities for one-hot encoding of ground truth values
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

batch_size = 128 # in each iteration, we consider 128 training examples at once
num_epochs = 16
hidden_size_1 = 512 # there will be 512 neurons in both hidden layers
hidden_size_2 = 512 # there will be 512 neurons in both hidden layers

height, width, depth = 28, 28, 1 # MNIST images are 28x28 and greyscale
num_classes = 10 # there are 10 classes (1 per digit)

(X_train, y_train), (X_test, y_test) = mnist.load_data() # fetch MNIST data

num_train, width, depth = X_train.shape # there are 50000 training examples in mnist 
num_test = X_test.shape[0] # there are 10000 test examples in mnist
num_classes = np.unique(y_train).shape[0] # there are 10 image classes

#Visualizing
I_train = list()
I_test = list()

fig, axes = plt.subplots(1,10,figsize=(10,10))
for k in range(10):
    i = np.random.choice(range(len(X_train)))
    I_train.append(i)
    axes[k].set_axis_off()
    axes[k].imshow(X_train[i:i+1][0], cmap='gray')
fig, axes = plt.subplots(1,10,figsize=(10,10))
for k in range(10):
    i = np.random.choice(range(len(X_test)))
    I_test.append(i)        
    axes[k].set_axis_off()
    axes[k].imshow(X_test[i:i+1][0], cmap='gray')
    
perm = np.array([153,  17,   7, 148, 191,  15,  73, 109, 180, 129,   2, 218, 122,
       151, 227, 167,  40, 248,  66, 212, 197, 101, 211, 139, 234, 133,
       168, 174,  53, 207, 219,  37, 246, 194, 239, 255, 107,  90,  22,
        44, 215,  84, 102, 201,  61, 176,  72, 125,  56,  99, 156, 161,
       226,   6, 238,  52,  27,  50, 216, 231,  71,   5,  25,  34,  62,
        29, 166, 253, 220,   3,  24, 225, 130, 196, 113,  86, 150, 209,
        65, 195,   1, 200,  41,  81,  69, 163,  33, 147, 230, 202, 232,
       112, 241, 137,  47, 187, 203, 175, 229,  39, 160, 186, 152, 222,
        14,  85,  21,  77, 210, 108, 193, 250,  54,  45,  92, 141,  94,
       208, 110, 192, 228, 115,  91, 143,  26,  88,  96, 170,  78,  87,
       132, 172, 247, 178, 205, 165, 177, 144,  83,  49,  11,  67,  82,
       134, 245, 100,  18,  48, 136, 213, 105, 162, 199, 103, 252, 214,
       158, 189, 149,  95, 164, 111, 233, 181, 142, 249,   9, 236,  38,
       173, 243,  57,  28, 128,  55,  32, 116,  59, 145,  97,  35, 106,
        43, 206, 198,  60, 135,  74,  23,  76, 251, 120, 240,  75,  20,
       169, 179, 121,  80, 217, 123, 235, 126, 114,  16, 155, 146, 119,
        19,   8,  51,  98,  42, 185, 127,  93, 190, 104, 224, 171, 244,
       124,  89,  68,   0,   4, 117, 182,  70,  64, 159, 157, 242, 188,
        10,  30,  36,  58, 138, 118, 204, 184, 221,  13, 254,  31,  12,
       140,  63, 223,  79,  46, 154, 183, 131, 237])
"""
perm = np.array([0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 
        0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 
        0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 
        0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 
        0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 
        0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 
        0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 
        0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 
        0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 
        0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 
        0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 
        0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 
        0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 
        0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 
        0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 
        0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16])
"""

#perm = np.arange(28*28)
#perm = np.random.permutation(256)

X_train = X_train.reshape(num_train, height * width)
X_test = X_test.reshape(num_test, height * width)

XX_test = np.copy(X_test)
XX_train = np.copy(X_train)


for j in xrange(X_test.shape[1]):
    for i in xrange(X_test.shape[0]):
        X_test[i][j] = perm[XX_test[i][j]]
    for i in xrange(X_train.shape[0]):
        X_train[i][j] = perm[XX_train[i][j]]

X_train = X_train.reshape(num_train, height, width)
X_test = X_test.reshape(num_test, height, width)

#Visualizing
fig, axes = plt.subplots(1,10,figsize=(10,10))
for k in range(10):
    i = I_train[k]
    axes[k].set_axis_off()
    axes[k].imshow(X_train[i:i+1][0], cmap='gray')

fig, axes = plt.subplots(1,10,figsize=(10,10))
for k in range(10):
    i = I_test[k]
    axes[k].set_axis_off()
    axes[k].imshow(X_test[i:i+1][0], cmap='gray')

X_train = X_train.reshape(num_train, height * width) # Flatten data to 1D
X_test = X_test.reshape(num_test, height * width) # Flatten data to 1D
X_train = X_train.astype('float32') 
X_test = X_test.astype('float32')
X_train /= np.max(X_train) # Normalise data to [0, 1] range
X_test /= np.max(X_test) # Normalise data to [0, 1] range

Y_train = np_utils.to_categorical(y_train, num_classes) # One-hot encode the labels
Y_test = np_utils.to_categorical(y_test, num_classes) # One-hot encode the labels


inp = Input(shape=(height * width,)) # Our input is a 1D vector of size 784
hidden_1 = Dense(hidden_size_1, activation='relu')(inp) # First hidden ReLU layer
hidden_2 = Dense(hidden_size_2, activation='relu')(hidden_1) # Second hidden ReLU layer
out = Dense(num_classes, activation='softmax')(hidden_2) # Output softmax layer

model = Model(input=inp, output=out) # To define a model, just specify its input and output layers
model.compile(loss='categorical_crossentropy', # using the cross-entropy loss function
              optimizer='adam', # using the Adam optimiser
              metrics=['accuracy']) # reporting the accuracy
model.fit(X_train, Y_train, # Train the model using the training set...
          batch_size=batch_size, nb_epoch=num_epochs,
          verbose=1, validation_split=0.1) # ...holding out 10% of the data for validation
model.evaluate(X_test, Y_test, verbose=1) # Evaluate the trained model on the test set!

Перемешивание производится на исходных и нормализация происходит.
Я не переоцениваю свои знания в области ИИ, но объяснения у меня нет. Для данной сети все точки равноправны и все значения равноправны в исходном сигнале.
Наверное я искусственный… я ни в одном примере не смог разглядеть тройку в верхнем ряду :( И если бы я в первые встретил «взболтанную» четвёрку я бы её тоже не распознал… Но мы распознаём, и дело тут не в том что мы догадливые, а в том что нам ОЧЕНЬ помогает контекст. Видя с десяток цифр рядом с друг другом мы «морально» повышаем шансы того что любой знак из этого ряда тоже является цифрой, а затем дело остаётся за малым — подключаются специально натренированные нейронные сети которые занимаются не тем чтобы угадать что там, а тем чтобы исключить то чего там нет. Я хочу сказать что ваша статья конечно интересная, но распознание циферок это не только ценный мех, но и 3-4 килограмма нюансов, которые не учтены в строении нейросейтей по тем или иным причинам… не даром детей обучают счёту с шести лет, а не раньше. И всё равно им для этого нужно пару лет. Всё потому что сама структура, сложность нейронных сетей сформировываются достаточно хорошо для этой задачи лишь по прошествии ОЧЕНЬ длительных тренировок. И Очень большого количества примеров…
Видя с десяток цифр рядом с друг другом мы «морально» повышаем шансы того что любой знак из этого ряда тоже является цифрой

Ну так эта нейросеть и настроена специально на разгадывание циферок. У нее контекст всегда один.


не даром детей обучают счёту с шести лет

Да, только как выглядят цифры и буквы они запоминают гораздо раньше. И чтобы запомнить, им достаточно увидеть их несколько раз.

Ну значит я точно искусственный… Меня оставляли на второй год — я не мог выучить алфавит…
>> «гораздо раньше.»
вот именно по этому у моих сверстников было большое преимущество надо мной. Мне лично в детстве не приходила в голову разглядывать значки написанные на упаковках и других местах и придавать им значения — хотя меня они буквально окружали…
>> настроена специально
— на паре примеров то? Человек в силу того что его голова не статична — вынужден видеть знаки под всеми возможными углами и в сочетании со всеми возможными вариантами расцветок. Сеть же тренировали на простом белом изображении знаков — как ей догадаться что речь идёт об одних и тех же цифрах когда буквально мы им поменяли цвет?
Умение не только видеть, но и различать, сопоставлять, отсеивать — этим мозг и отличается. А делает он это потому что у него большой запас таких знаний. Вот в самом последнем напримере самая первая четвёрка является именно четвёркой а не единицей, несмотря на то что там тоже есть вертикальная черта… И на чёрном фоне я это хорошо определяю, но в «взбалтанном» режиме я сначала предположил что это единица с шумом.

У вас вообще были дети? если нет — посмотрите видео ролики в интернете о детях которые учатся читать… Многие дети буквально «залипают» на некоторых буквах… Но потом всё же часто называют правильную. А всё почему?
Вы пробывали изучить язык символы которого вы почти не видели за свою жизнь? например японские. Я с легкостью выучил с десяток символов потому что они мне напоминали что либо… т.е. я построил ассоциации. Но с другой стороны я постоянно путаю многие другие символы. Просто не могу запомнить и всё. И не надо тут говорить «достаточно увидеть их несколько раз.» — несколько раз это только для пары символов. И то не факт что вы его запомните. И даже научившись читать хирагану и увидать вдруг её же но в тетрадке какого нибудь японского школьника вы всё равно встанете в ступор. И единственный способ понять что написано — искать какие то приметы, отсеивать неподходящие, и конечно контекст. Только так…
Сеть же тренировали на простом белом изображении знаков — как ей догадаться что речь идёт об одних и тех же цифрах когда буквально мы им поменяли цвет?

Нет, её тренировали на картинках, обработанных таким же образом, как и тестовые.

Давайте не путать буквы, обозначающие звуки, которых десятки, и иероглифы, обозначающие слова, которых тысячи. Речь идет о 10 цифрах.


Человек в силу того что его голова не статична — вынужден видеть знаки под всеми возможными углами и в сочетании со всеми возможными вариантами расцветок.

Нет. Запомнив черную цифру на белом фоне, человек узнает ее нарисованную синим на зеленом, даже если он раньше вообще не видел ни ее ни такое сочетание цветов, а также в виде разного размера и деформации.


А делает он это потому что у него большой запас таких знаний.

Нет. Потому что у него другие принципы работы. Которые впрочем частично пересекаются с используемыми в нейросетях.

Можно создать нейросеть, которая будет проверять, есть ли на картинке число. Либо добавить на выход такую вещь
Потому что MNIST можно «взять» даже без сверточных сетей, 28x28 -> Flatten и вперед.
Потому что тогда после перемешивания в первом случае тоже ничего не будет учиться.
В используемой сети, судя по ссылке на ту статью, откуда взят код, первые два скрытых слоя имеют ReLU активации. Такая функция может хорошо обучиться отделять белые пиксели от черных, но после случайного перемешивания значений теряется линейная зависимость «что-то нарисовано» от значения. Так как слоев всего 2, то даже на одном пикселе, как сходу мне кажется, не получится сделать простой классификатор на черный и белый пиксель, не говоря о каких-то более сложных образах. Сигмоида тоже не поможет. Думаю, что большее число слоев может помочь решить проблему.

PS. Заголовок, как обычно, очень желтушный.
больше слоев не поможет. Так как каждый слой 28*28*28*28 = 614 656 коэффициентов. Т.е. скоро возникнет проблема с нехваткой данных. mnist 60000 картинок. Т.е. даже при одном слое у нас бардак с коэффициентами. Вопрос как тут сделана регуляризация. Без нее результат вообще должен быть на тесте плохой.
Т.е. скоро возникнет проблема с нехваткой данных

Аугментация, не?
Простите, не знаком с этим термином.
Можно внести в выборку искажения (для изображений — например, сжатия, повороты, шум, их комбинации). Хотя — сомневаюсь, что тут нам это даст профит.
Не понял только как размер и число слоев связаны с размером обучающего набора...?
очень большая сеть может просто запомнить все примеры и не заниматься упрощением модели. Т.е. на обучающей выборке все будет идеально, а вот на тестовой очень плохо. Переобучение.
В первом случае вы по сути перемешали входы (что для обучаемой нейронки просто никакого значения не имеет). В других случаях вы мешали уровни сигналов нарушая их линейность (т.е. переход от фона к изображению становился нелинейным). Таки образом вы фактически «смазывали» изображения. Ну с-но результат получился хуже.

Не понял где тут блеск, и уж тем более не понял в чем нищета…

Ну, про блеск как раз в посте написано, вполне очевидными словами — в том, что человек не может распознать некоторые виды изображений ("карты звездного неба") на таком же уровне, как данный ИИ.

Вы, наверное, аудиоплееры тоже ИИ называете? Человек же не может распознать звук в том наборе байт, что приходит им на вход, верно?

Я лишь пояснил, что автор назвал блеском. Вот точная цитата:


Никакой человек с такой задачей на таких перемешанных картинках никогда не справится. Это блеск!!! Это феноменально по сравнению с человеком. Искать большую медведицу и стрельца нужно тысячелетиями.

Каким боком тут мое мнение о том, что такое ИИ?

И человек сможет, если ему так же "рецепторы переставить в глазе" так как сделал автор. По сути данные не поменялись. Просто договорились о том что их порядок поменяется, сеть спокойно и обучилась выдав тот же результат что и без пермутаций. А вторые два случая: причины такого результата в Relu (пороговая функция) которую пытаются использовать на одномерном слое, без учета соседних пикселей.

Человек воспринимает картинку имея представление о взаимном расположении точек. мы знаем, какие точки соседние. Для нейронной сети каждая точка независимый элемент вектора. В процессе обучения сеть «учится вычислять» близость точек. Это происходит за счет того, что каждая картинка говорит о том какие точки могу встречаться совместно. Собственно в этом и есть основная суть обучения на мнисте. Если точки перемешать, то для сети ничего не меняется. Другое дело если поиграться с яркостью точек. По таким картинкам сеть уже не можеть восстановить картину их близости.
очередной мыльный пузырь. подмена понятий для распиливания бюджета. если назвать не кодинг, а бигдата или машинное обучение, это дает возможность подогреть чсв
Нашел для себя хороший кейс-задачку для собеседований.

Если закрыть глаза на то, что теперь это знают «почти все» или поменять условия, то ответ на «объясните почему так» выглядит хорошей проверкой как на знание принципов, так и на умение соображать.
Хорошая задачка для всех. ( подсказал yleo )
Если есть перестановки ухудшающие, то, наверно, найдутся и улучшающие.
Я проверил, перестановка обратная приведенной из rijndael ухудшает ровно на столько же, что приведенная. Обратную из rijndael не проверял.
Sign up to leave a comment.

Articles