[Из песочницы] Блеск и нищета Искусственного Интеллекта

На примере простой задачи с простой нейронной сетью. Навеяно вот этой статьей и сеть взята без изменений. Просто выполнить код было неинтересно и пытливый ум решил внести изменения в предмет распознавания. А именно, нужно взять и перемешать точки в 28×28 и посмотреть.

Вот код из статьи с некоторыми изменениями и особо прошу обратить внимание на блок «permutations» — там и будем вносить и удовлетворять свое любопытство.

from keras.datasets import mnist
from keras.models import Model
from keras.layers import Input, Dense 
from keras.utils import np_utils 
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

batch_size = 128 
num_epochs = 16
hidden_size_1 = 512 
hidden_size_2 = 512 

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 
num_test = X_test.shape[0] 
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')

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)


Прочитали исходные картинки (Что за картинки и для чего — прошу прочесть в исходной статье)
И выбрали случайно по 10 шт из Train и Test для показа. Картинки как картинки, если ничего в них не менять (perm = np.arange (28×28)) сеть дает 98% и более. Нормально.

Можете проверить сами.

Но теперь случайно перемешаем картинки. Случайно то случайно, но все картинки мешаем одинаково.

#  Блок перестановок
perm = np.random.permutation((28*28)) # например так

for j in xrange(X_test.shape[1]):
    for i in xrange(X_test.shape[0]):
        X_test[i][j] = XX_test[i][perm[j]]
    for i in xrange(X_train.shape[0]):
        X_train[i][j] = XX_train[i][perm[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!


a1fmhxroeo6bfbqplwzwnb2tdow.png

wesqp57vu0rtqx8wribekxql2de.png

Исходные, случайно выбранные по 10 картинки, и теперь после перемешивания те же.

uxwfbmsumyfrtggy4tijhoeeaak.png

g3rj250n7argzmumqsfdisukrxy.png

Напомню, что перемешивались все картинки одинаково.

Но тут о! мистика, сеть легко справляется с такими картинками и у меня получается [0.082131341451834983, 0.98219999999999996].

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

Но пытливый ум на этом не останавливается и теперь пытается взболтать. Точнее применить другой код:

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]]


Перемешиваются не точки, а значения точек. Можно опять перемешать случайно, но главное одинаково по всей картинкам, а можно выбрать одно очень широко в узких кругах известное перемешивание:

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])


И результат работы сети (тот же код без изменений) сразу насторожил:
[1.0329392189979554, 0.64970000000000006]

kqkpidlnhbcowtbiymepvldsp-e.png

pkz42_ycdskny48zmtdh7dxavkm.png

vjzjw20r6yvpbnqklf8bjalycw8.png

d1jcj7toikvdcpemebv8hzvwui0.png

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

Ну и напоследок ложка дёгтя.

Если

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])


то все совсем плохо.

uqkiajf7naujqt6japkymrq1gg8.png

9chrq3bj1_n2hv82qxpwbdr0yqq.png

mnxxdgn6axibawchc3zjze45sss.png

u-_dg0cwwjp4hdoxxdhvkdlik_a.png

Глазами все видно, но сеть выдает

[2.301039520263672, 0.1135]

Вывод: «Перемешать, но не взбалтывать».

© Habrahabr.ru