На примере простой задачи с простой нейронной сетью. Навеяно вот этой статьей и сеть взята без изменений. Просто выполнить код было неинтересно и пытливый ум решил внести изменения в предмет распознавания. А именно, нужно взять и перемешать точки в 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!





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





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

Но тут о! мистика, сеть легко справляется с такими картинками и у меня получается [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]









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

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

Если

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

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









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

[2.301039520263672, 0.1135]

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

Комментарии (32)


  1. Sovetnikov
    11.11.2017 23:33

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

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


    1. dolgov_vv
      12.11.2017 15:53
      +1

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


    1. aamonster
      12.11.2017 15:58

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


      1. chersanya
        12.11.2017 20:35

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

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


        1. aamonster
          13.11.2017 06:12

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


    1. ChePeter Автор
      14.11.2017 19:05

      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!

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


  1. Hazactam
    11.11.2017 23:34

    Можно попробовать предварительно пофильтровать Фильтром Габора:
    ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80_%D0%93%D0%B0%D0%B1%D0%BE%D1%80%D0%B0
    до скармливания данных сетке. Думаю, результат существенно улучшится.


  1. Sergey6661313
    11.11.2017 23:39

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


    1. michael_vostrikov
      12.11.2017 12:52

      Видя с десяток цифр рядом с друг другом мы «морально» повышаем шансы того что любой знак из этого ряда тоже является цифрой

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


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

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


      1. Sergey6661313
        12.11.2017 14:48

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

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


        1. chersanya
          12.11.2017 15:03

          Сеть же тренировали на простом белом изображении знаков — как ей догадаться что речь идёт об одних и тех же цифрах когда буквально мы им поменяли цвет?

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


        1. michael_vostrikov
          12.11.2017 16:06

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


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

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


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

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


  1. vanyaagent
    12.11.2017 06:58

    Можно создать нейросеть, которая будет проверять, есть ли на картинке число. Либо добавить на выход такую вещь


  1. erwins22
    12.11.2017 08:40

    зачем Dense? почему не свертка?


    1. QtRoS
      12.11.2017 14:22

      Потому что MNIST можно «взять» даже без сверточных сетей, 28x28 -> Flatten и вперед.


    1. brickerino
      13.11.2017 15:54

      Потому что тогда после перемешивания в первом случае тоже ничего не будет учиться.


  1. DenerKup
    12.11.2017 08:57

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

    PS. Заголовок, как обычно, очень желтушный.


    1. erwins22
      12.11.2017 10:29

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


      1. alex4321
        12.11.2017 11:33

        Т.е. скоро возникнет проблема с нехваткой данных

        Аугментация, не?


        1. erwins22
          12.11.2017 14:07

          Простите, не знаком с этим термином.


          1. alex4321
            12.11.2017 21:00

            Можно внести в выборку искажения (для изображений — например, сжатия, повороты, шум, их комбинации). Хотя — сомневаюсь, что тут нам это даст профит.


      1. Sly_tom_cat
        12.11.2017 14:15

        Не понял только как размер и число слоев связаны с размером обучающего набора...?


        1. erwins22
          12.11.2017 14:31
          +1

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


  1. Sly_tom_cat
    12.11.2017 14:21
    +1

    В первом случае вы по сути перемешали входы (что для обучаемой нейронки просто никакого значения не имеет). В других случаях вы мешали уровни сигналов нарушая их линейность (т.е. переход от фона к изображению становился нелинейным). Таки образом вы фактически «смазывали» изображения. Ну с-но результат получился хуже.

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


    1. sshikov
      12.11.2017 15:15

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


      1. zagayevskiy
        12.11.2017 20:39

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


        1. sshikov
          12.11.2017 21:28

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


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

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


      1. dmandreev
        13.11.2017 00:23

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


  1. AlexeyR
    12.11.2017 17:41

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


  1. japan007
    12.11.2017 19:46

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


  1. yleo
    14.11.2017 18:31

    Нашел для себя хороший кейс-задачку для собеседований.

    Если закрыть глаза на то, что теперь это знают «почти все» или поменять условия, то ответ на «объясните почему так» выглядит хорошей проверкой как на знание принципов, так и на умение соображать.


  1. ChePeter Автор
    14.11.2017 19:17

    Хорошая задачка для всех. ( подсказал yleo )
    Если есть перестановки ухудшающие, то, наверно, найдутся и улучшающие.
    Я проверил, перестановка обратная приведенной из rijndael ухудшает ровно на столько же, что приведенная. Обратную из rijndael не проверял.