Это третья статья по анализу и изучению эллипсов, треугольников и других геометрических фигур.
Предыдущие статьи вызвали у читателей несколько очень интересных вопросов, в частности о сложности или простоте тех или иных обучающих последовательностей. Вопросы на самом деле очень интересные, например насколько треугольник сложнее для обучения, чем четырехугольник или другой многоугольник?
Попробуем сравнить, и для сравнения у нас есть отличная, проверенная поколениями студентов, идея — чем короче шпаргалка, тем легче экзамен.
Статья эта тоже есть просто результат любопытства и праздного интереса, ничего из нее в практике не встречается и для практических задач тут есть пара отличных идей, но нет почти ничего для копипастинга. Это небольшое исследование сложности обучающих последовательностей — рассуждения автора и код изложены, можно все проверить/дополнить/изменить самим.
Итак, попробуем выяснить, какая геометрическая фигура сложнее или проще для сегментации, какой курс лекций для ИИ понятней и лучше усваивается.
Геометрических фигур много разных, но мы будем сравнивать только треугольники, четырехугольники и пятиконечные звезды. Мы применим простой метод построения трейн последовательности — мы разделим 128х128 одноцветной картинки на четыре части и случайным образом будем помещать в эти четверти эллипс и, например, треугольник. Будем детектить треугольник того же цвета, что и эллипс. Т.е. задача состоит в том, что бы обучить сеть отличать, например четырехугольный полигон от эллипса, окрашенного в тот же цвет. Вот примеры картинок, которые будем изучать
Мы не будем детектить на одной картинке треугольник и четырехугольник, мы будем детектить их отдельно, в разных трейн, на фоне помехи в виде эллипса.
Возьмем для исследования классическую U-net и три вида обучающих последовательностей с треугольниками, четырехугольниками и звездами.
Итак, дано:
Идея для проверки:
Начнем, выберем 10 000 пар картинок четырехугольников с эллипсами и масок и рассмотрим их внимательно. Нам интересно, насколько короткой получится шпаргалка и от чего её длина зависит.
Мы будем использовать метрику из первой статьи. Напомню читателям, что будем предсказывать маску пикселя — это «фон» или «четырехугольник» и оценивать истинность или ложность предсказания. Т.е. возможны следующие четыре варианта — мы правильно предсказали, что пиксель это фон, правильно предсказали, что пиксель это четырехугольник или ошиблись в предсказании «фон» или «четырехугольник». И так по всем картинкам и всем пикселям оцениваем количество всех четырех вариантов и подсчитываем результат — это и будет результат работы сети. И чем меньше ошибочных предсказаний и больше истинных, то тем точнее полученный результат и лучше работа сети.
Мы исследуем сеть как «черный ящик», мы не станем смотреть, что происходит с сетью внутри, как меняются веса и как выбираются градиенты — заглянем в недра сети попозже, когда будем сравнивать сети.
Функция генерации пар картинка/маска. На черно-белой картинке 128х128 заполненной случайным шумом со случайно выбранным из двух диапазонов, или 0.0...0.75 или 0.25..1.0. Случайным образом выбираем четверть на картинке и размещаем случайно ориентированный эллипс и в другой четверти размещаем четырехугольник и одинаково раскрашиваем случайным шумом.
Создадим обучающую последовательность пар, посмотрим случайные 10. Напомню, что картинки монохромные, градации серого.
Первый шаг нашего эксперимента простой, мы пробуем обучить сеть предсказывать всего 11 первых картинок.
Мы выбрали из исходной последовательности первые 11 и обучили сеть на них. Сейчас не важно, заучивает сеть конкретно эти картинки или обобщает, главное, что эти 11 картинок она может распознать так, как нам нужно. В зависимости от выбранного датасета и точности, обучение сети может продолжаться долго, очень долго. Но у нас всего несколько итераций. Повторю, что нам сейчас не важно как и что заучила или выучила сеть, главное, что она достигла установленной точности предсказания.
Построим шпаргалку, будем строить такие шпаргалки раздельно для всех трех обучающих последовательностей и сравнивать их длину. Мы будем брать новые пары картинка/маска из построенной последовательности и будем пытаться предсказать их сетью обученной на уже отобранной последовательности. В начале это всего 11 пар картинка/маска и сеть обучена, возможно и не очень корректно. Если в новой паре маска по картинке предсказывается с приемлемой точностью, то эту пару выбрасываем, в ней нет новой информации для сети, она уже знает и может вычислить по этой картинке маску. Если же точность предсказания недостаточна, то эту картинку с маской добавляем в нашу последовательность и начинаем тренировать сеть до достижения приемлемого результата точности на отобранной последовательности. Т.е. эта картинка содержит новую информацию и мы её добавляем в наш обучающую последовательность и извлекаем тренировкой содержащуюся в ней информацию.
Здесь accuracy используется в смысле «точность», а не как стандартная метрика keras и для вычисления точности используется подпрограмма «my_iou_metric».
А теперь сравним работу той же самой сети с теми же самыми параметрами на другой последовательности, на треугольниках
И получим совсем другой результат
Сеть выбрала 1913 картинок с «новой» информацией, т.е. содержательность картинок с треугольниками получается в два раза ниже, чем с четырехугольниками!
Проверим то же самое на звездах и запустим сеть на третьей последовательности
получим
Как видим, звезды оказались наиболее информативными, всего 476 картинок в шпаргалке.
У нас появились основания судить о сложности геометрических фигур для восприятия их нейронной сетью. Самая простая это звезда, всего 476 картинок в шпаргалке, далее четырехугольник с его 1007 и самым сложным оказался треугольник — для обучения нужно 1913 картинок.
Учтите, это для нас, для людей это картинки, а для сети это курс лекций по распознаванию и курс про треугольники оказался самым сложным.
На первый взгляд все эти эллипсы и треугольники кажутся баловством, куличи из песка и лего. Но вот конкретный и серьезный вопрос: если к исходной последовательности применить какую нибудь предобработку, фильтр, то как изменится сложность последовательности? Например, возьмем всё те же эллипсы и четырехугольники и применим к ним вот такую предобработку
На первый взгляд всё то же самое, такие же эллипсы, такие же полигоны, но сеть стала работать совсем по-другому:
Тут необходимо небольшое пояснение, мы не используем аугментацию, т.к. форма полигона и форма эллипса изначально выбираются случайно. Поэтому аугментаци не даст новой информации и не имеет смысла с данном случае.
Но, как видно из результата работы, простой gaussian_filter создал сети много проблем, породил много новой, и наверно лишней, информации.
Ну и для любителей простоты в чистом виде, возьмем те же самые эллипсы с полигонами, но без какой бы то ни было случайности в цвете
результат говорит о том, что случайный цвет совсем не простая добавка.
Сеть вполне обошлась информацией, извлеченной из 251 картинки, почти в четыре раза меньше, чем из множества картинок раскрашенных шумом.
Цель статьи показать некоторый инструмент и примеры его работы на несерьезных примерах, лего в песочнице. Мы получили инструмент сравнения двух обучающих последовательностей, мы можем оценить насколько наша предобработка усложняет или упрощает обучающую последовательность, насколько тот или иной примитив в обучайющей последовательности прост для детекции.
Возможность применения этого лего примера в реальных делах очевидна, но реальные трейны и сети читателей дело самих читателей.
Предыдущие статьи вызвали у читателей несколько очень интересных вопросов, в частности о сложности или простоте тех или иных обучающих последовательностей. Вопросы на самом деле очень интересные, например насколько треугольник сложнее для обучения, чем четырехугольник или другой многоугольник?
Попробуем сравнить, и для сравнения у нас есть отличная, проверенная поколениями студентов, идея — чем короче шпаргалка, тем легче экзамен.
Статья эта тоже есть просто результат любопытства и праздного интереса, ничего из нее в практике не встречается и для практических задач тут есть пара отличных идей, но нет почти ничего для копипастинга. Это небольшое исследование сложности обучающих последовательностей — рассуждения автора и код изложены, можно все проверить/дополнить/изменить самим.
Итак, попробуем выяснить, какая геометрическая фигура сложнее или проще для сегментации, какой курс лекций для ИИ понятней и лучше усваивается.
Геометрических фигур много разных, но мы будем сравнивать только треугольники, четырехугольники и пятиконечные звезды. Мы применим простой метод построения трейн последовательности — мы разделим 128х128 одноцветной картинки на четыре части и случайным образом будем помещать в эти четверти эллипс и, например, треугольник. Будем детектить треугольник того же цвета, что и эллипс. Т.е. задача состоит в том, что бы обучить сеть отличать, например четырехугольный полигон от эллипса, окрашенного в тот же цвет. Вот примеры картинок, которые будем изучать
Мы не будем детектить на одной картинке треугольник и четырехугольник, мы будем детектить их отдельно, в разных трейн, на фоне помехи в виде эллипса.
Возьмем для исследования классическую U-net и три вида обучающих последовательностей с треугольниками, четырехугольниками и звездами.
Итак, дано:
- три обучающие последовательности пар картинка/маска;
- сеть. Обыкновенная U-net, которая широко используются для сегментации.
Идея для проверки:
- определим, какая из обучающих последовательностей «сложнее» для обучения;
- как влияют на обучение некоторые приемы предобработки.
Начнем, выберем 10 000 пар картинок четырехугольников с эллипсами и масок и рассмотрим их внимательно. Нам интересно, насколько короткой получится шпаргалка и от чего её длина зависит.
Загружаем библиотеки, определяем размеры массива картинок
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import math
from tqdm import tqdm
from skimage.draw import ellipse, polygon
from keras import Model
from keras.optimizers import Adam
from keras.layers import Input,Conv2D,Conv2DTranspose,MaxPooling2D,concatenate
from keras.layers import BatchNormalization,Activation,Add,Dropout
from keras.losses import binary_crossentropy
from keras import backend as K
import tensorflow as tf
import keras as keras
w_size = 128
train_num = 10000
radius_min = 10
radius_max = 20
определяем функции потерь и точности
def dice_coef(y_true, y_pred):
y_true_f = K.flatten(y_true)
y_pred = K.cast(y_pred, 'float32')
y_pred_f = K.cast(K.greater(K.flatten(y_pred), 0.5), 'float32')
intersection = y_true_f * y_pred_f
score = 2. * K.sum(intersection) / (K.sum(y_true_f) + K.sum(y_pred_f))
return score
def dice_loss(y_true, y_pred):
smooth = 1.
y_true_f = K.flatten(y_true)
y_pred_f = K.flatten(y_pred)
intersection = y_true_f * y_pred_f
score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) +
K.sum(y_pred_f) + smooth)
return 1. - score
def bce_dice_loss(y_true, y_pred):
return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)
def get_iou_vector(A, B):
# Numpy version
batch_size = A.shape[0]
metric = 0.0
for batch in range(batch_size):
t, p = A[batch], B[batch]
true = np.sum(t)
pred = np.sum(p)
# deal with empty mask first
if true == 0:
metric += (pred == 0)
continue
# non empty mask case. Union is never empty
# hence it is safe to divide by its number of pixels
intersection = np.sum(t * p)
union = true + pred - intersection
iou = intersection / union
# iou metrric is a stepwise approximation of the real iou over 0.5
iou = np.floor(max(0, (iou - 0.45)*20)) / 10
metric += iou
# teake the average over all images in batch
metric /= batch_size
return metric
def my_iou_metric(label, pred):
# Tensorflow version
return tf.py_func(get_iou_vector, [label, pred > 0.5], tf.float64)
from keras.utils.generic_utils import get_custom_objects
get_custom_objects().update({'bce_dice_loss': bce_dice_loss })
get_custom_objects().update({'dice_loss': dice_loss })
get_custom_objects().update({'dice_coef': dice_coef })
get_custom_objects().update({'my_iou_metric': my_iou_metric })
Мы будем использовать метрику из первой статьи. Напомню читателям, что будем предсказывать маску пикселя — это «фон» или «четырехугольник» и оценивать истинность или ложность предсказания. Т.е. возможны следующие четыре варианта — мы правильно предсказали, что пиксель это фон, правильно предсказали, что пиксель это четырехугольник или ошиблись в предсказании «фон» или «четырехугольник». И так по всем картинкам и всем пикселям оцениваем количество всех четырех вариантов и подсчитываем результат — это и будет результат работы сети. И чем меньше ошибочных предсказаний и больше истинных, то тем точнее полученный результат и лучше работа сети.
Мы исследуем сеть как «черный ящик», мы не станем смотреть, что происходит с сетью внутри, как меняются веса и как выбираются градиенты — заглянем в недра сети попозже, когда будем сравнивать сети.
простая U-net
def build_model(input_layer, start_neurons):
# 128 -> 64
conv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(input_layer)
conv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(conv1)
pool1 = MaxPooling2D((2, 2))(conv1)
pool1 = Dropout(0.25)(pool1)
# 64 -> 32
conv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(pool1)
conv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(conv2)
pool2 = MaxPooling2D((2, 2))(conv2)
pool2 = Dropout(0.5)(pool2)
# 32 -> 16
conv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(pool2)
conv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(conv3)
pool3 = MaxPooling2D((2, 2))(conv3)
pool3 = Dropout(0.5)(pool3)
# 16 -> 8
conv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(pool3)
conv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(conv4)
pool4 = MaxPooling2D((2, 2))(conv4)
pool4 = Dropout(0.5)(pool4)
# Middle
convm = Conv2D(start_neurons * 16, (3, 3), activation="relu", padding="same")(pool4)
convm = Conv2D(start_neurons * 16, (3, 3), activation="relu", padding="same")(convm)
# 8 -> 16
deconv4 = Conv2DTranspose(start_neurons * 8, (3, 3), strides=(2, 2), padding="same")(convm)
uconv4 = concatenate([deconv4, conv4])
uconv4 = Dropout(0.5)(uconv4)
uconv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(uconv4)
uconv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(uconv4)
# 16 -> 32
deconv3 = Conv2DTranspose(start_neurons * 4, (3, 3), strides=(2, 2), padding="same")(uconv4)
uconv3 = concatenate([deconv3, conv3])
uconv3 = Dropout(0.5)(uconv3)
uconv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(uconv3)
uconv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(uconv3)
# 32 -> 64
deconv2 = Conv2DTranspose(start_neurons * 2, (3, 3), strides=(2, 2), padding="same")(uconv3)
uconv2 = concatenate([deconv2, conv2])
uconv2 = Dropout(0.5)(uconv2)
uconv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(uconv2)
uconv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(uconv2)
# 64 -> 128
deconv1 = Conv2DTranspose(start_neurons * 1, (3, 3), strides=(2, 2), padding="same")(uconv2)
uconv1 = concatenate([deconv1, conv1])
uconv1 = Dropout(0.5)(uconv1)
uconv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(uconv1)
uconv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(uconv1)
uncov1 = Dropout(0.5)(uconv1)
output_layer = Conv2D(1, (1,1), padding="same", activation="sigmoid")(uconv1)
return output_layer
# model
input_layer = Input((w_size, w_size, 1))
output_layer = build_model(input_layer, 26)
model = Model(input_layer, output_layer)
model.compile(loss=bce_dice_loss, optimizer=Adam(lr=1e-4), metrics=[my_iou_metric])
model.summary()
Функция генерации пар картинка/маска. На черно-белой картинке 128х128 заполненной случайным шумом со случайно выбранным из двух диапазонов, или 0.0...0.75 или 0.25..1.0. Случайным образом выбираем четверть на картинке и размещаем случайно ориентированный эллипс и в другой четверти размещаем четырехугольник и одинаково раскрашиваем случайным шумом.
def next_pair():
img_l = (np.random.sample((w_size, w_size, 1))*
0.75).astype('float32')
img_h = (np.random.sample((w_size, w_size, 1))*
0.75 + 0.25).astype('float32')
img = np.zeros((w_size, w_size, 2), dtype='float')
i0_qua = math.trunc(np.random.sample()*4.)
i1_qua = math.trunc(np.random.sample()*4.)
while i0_qua == i1_qua:
i1_qua = math.trunc(np.random.sample()*4.)
_qua = np.int(w_size/4)
qua = np.array([[_qua,_qua],[_qua,_qua*3],[_qua*3,_qua*3],[_qua*3,_qua]])
p = np.random.sample() - 0.5
r = qua[i0_qua,0]
c = qua[i0_qua,1]
r_radius = np.random.sample()*(radius_max-radius_min) + radius_min
c_radius = np.random.sample()*(radius_max-radius_min) + radius_min
rot = np.random.sample()*360
rr, cc = ellipse(
r, c,
r_radius, c_radius,
rotation=np.deg2rad(rot),
shape=img_l.shape
)
p0 = np.rint(np.random.sample()*(radius_max-radius_min) + radius_min)
p1 = qua[i1_qua,0] - (radius_max-radius_min)
p2 = qua[i1_qua,1] - (radius_max-radius_min)
p3 = np.rint(np.random.sample()*radius_min)
p4 = np.rint(np.random.sample()*radius_min)
p5 = np.rint(np.random.sample()*radius_min)
p6 = np.rint(np.random.sample()*radius_min)
p7 = np.rint(np.random.sample()*radius_min)
p8 = np.rint(np.random.sample()*radius_min)
poly = np.array((
(p1, p2),
(p1+p3, p2+p4+p0),
(p1+p5+p0, p2+p6+p0),
(p1+p7+p0, p2+p8),
(p1, p2),
))
rr_p, cc_p = polygon(poly[:, 0], poly[:, 1], img_l.shape)
if p > 0:
img[:,:,:1] = img_l.copy()
img[rr, cc,:1] = img_h[rr, cc]
img[rr_p, cc_p,:1] = img_h[rr_p, cc_p]
else:
img[:,:,:1] = img_h.copy()
img[rr, cc,:1] = img_l[rr, cc]
img[rr_p, cc_p,:1] = img_l[rr_p, cc_p]
img[:,:,1] = 0.
img[rr_p, cc_p,1] = 1.
return img
Создадим обучающую последовательность пар, посмотрим случайные 10. Напомню, что картинки монохромные, градации серого.
_txy = [next_pair() for idx in range(train_num)]
f_imgs = np.array(_txy)[:,:,:,:1].reshape(-1,w_size ,w_size ,1)
f_msks = np.array(_txy)[:,:,:,1:].reshape(-1,w_size ,w_size ,1)
del(_txy)
# смотрим на случайные 10 с масками
fig, axes = plt.subplots(2, 10, figsize=(20, 5))
for k in range(10):
kk = np.random.randint(train_num)
axes[0,k].set_axis_off()
axes[0,k].imshow(f_imgs[kk])
axes[1,k].set_axis_off()
axes[1,k].imshow(f_msks[kk].squeeze())
Первый шаг. Обучаем на минимальном стартовом множестве
Первый шаг нашего эксперимента простой, мы пробуем обучить сеть предсказывать всего 11 первых картинок.
batch_size = 10
val_len = 11
precision = 0.85
m0_select = np.zeros((f_imgs.shape[0]), dtype='int')
for k in range(val_len):
m0_select[k] = 1
t = tqdm()
while True:
fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0],
batch_size=batch_size,
epochs=1,
verbose=0
)
current_accu = fit.history['my_iou_metric'][0]
current_loss = fit.history['loss'][0]
t.set_description("accuracy {0:6.4f} loss {1:6.4f} ". format(current_accu, current_loss))
t.update(1)
if current_accu > precision:
break
t.close()
accuracy 0.8545 loss 0.0674 lenght 11 : : 793it [00:58, 14.79it/s]
Мы выбрали из исходной последовательности первые 11 и обучили сеть на них. Сейчас не важно, заучивает сеть конкретно эти картинки или обобщает, главное, что эти 11 картинок она может распознать так, как нам нужно. В зависимости от выбранного датасета и точности, обучение сети может продолжаться долго, очень долго. Но у нас всего несколько итераций. Повторю, что нам сейчас не важно как и что заучила или выучила сеть, главное, что она достигла установленной точности предсказания.
Теперь начнем главный эксперимент
Построим шпаргалку, будем строить такие шпаргалки раздельно для всех трех обучающих последовательностей и сравнивать их длину. Мы будем брать новые пары картинка/маска из построенной последовательности и будем пытаться предсказать их сетью обученной на уже отобранной последовательности. В начале это всего 11 пар картинка/маска и сеть обучена, возможно и не очень корректно. Если в новой паре маска по картинке предсказывается с приемлемой точностью, то эту пару выбрасываем, в ней нет новой информации для сети, она уже знает и может вычислить по этой картинке маску. Если же точность предсказания недостаточна, то эту картинку с маской добавляем в нашу последовательность и начинаем тренировать сеть до достижения приемлемого результата точности на отобранной последовательности. Т.е. эта картинка содержит новую информацию и мы её добавляем в наш обучающую последовательность и извлекаем тренировкой содержащуюся в ней информацию.
batch_size = 50
t_batch_size = 1024
raw_len = val_len
t = tqdm(-1)
id_train = 0
#id_select = 1
while True:
t.set_description("Accuracy {0:6.4f} loss {1:6.4f} selected img {2:5d} tested img {3:5d} ".
format(current_accu, current_loss, val_len, raw_len))
t.update(1)
if id_train == 1:
fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0],
batch_size=batch_size,
epochs=1,
verbose=0
)
current_accu = fit.history['my_iou_metric'][0]
current_loss = fit.history['loss'][0]
if current_accu > precision:
id_train = 0
else:
t_pred = model.predict(
f_imgs[raw_len: min(raw_len+t_batch_size,f_imgs.shape[0])],
batch_size=batch_size
)
for kk in range(t_pred.shape[0]):
val_iou = get_iou_vector(
f_msks[raw_len+kk].reshape(1,w_size,w_size,1),
t_pred[kk].reshape(1,w_size,w_size,1) > 0.5)
if val_iou < precision*0.95:
new_img_test = 1
m0_select[raw_len+kk] = 1
val_len += 1
break
raw_len += (kk+1)
id_train = 1
if raw_len >= train_num:
break
t.close()
Accuracy 0.9338 loss 0.0266 selected img 1007 tested img 9985 : : 4291it [49:52, 1.73s/it]
Здесь accuracy используется в смысле «точность», а не как стандартная метрика keras и для вычисления точности используется подпрограмма «my_iou_metric».
А теперь сравним работу той же самой сети с теми же самыми параметрами на другой последовательности, на треугольниках
И получим совсем другой результат
Accuracy 0.9823 loss 0.0108 selected img 1913 tested img 9995 : : 6343it [2:11:36, 3.03s/it]
Сеть выбрала 1913 картинок с «новой» информацией, т.е. содержательность картинок с треугольниками получается в два раза ниже, чем с четырехугольниками!
Проверим то же самое на звездах и запустим сеть на третьей последовательности
получим
Accuracy 0.8985 loss 0.0478 selected img 476 tested img 9985 : : 2188it [16:13, 1.16it/s]
Как видим, звезды оказались наиболее информативными, всего 476 картинок в шпаргалке.
У нас появились основания судить о сложности геометрических фигур для восприятия их нейронной сетью. Самая простая это звезда, всего 476 картинок в шпаргалке, далее четырехугольник с его 1007 и самым сложным оказался треугольник — для обучения нужно 1913 картинок.
Учтите, это для нас, для людей это картинки, а для сети это курс лекций по распознаванию и курс про треугольники оказался самым сложным.
Теперь о серьезном
На первый взгляд все эти эллипсы и треугольники кажутся баловством, куличи из песка и лего. Но вот конкретный и серьезный вопрос: если к исходной последовательности применить какую нибудь предобработку, фильтр, то как изменится сложность последовательности? Например, возьмем всё те же эллипсы и четырехугольники и применим к ним вот такую предобработку
from scipy.ndimage import gaussian_filter
_tmp = [gaussian_filter(idx, sigma = 1) for idx in f_imgs]
f1_imgs = np.array(_tmp)[:,:,:,:1].reshape(-1,w_size ,w_size ,1)
del(_tmp)
fig, axes = plt.subplots(2, 5, figsize=(20, 7))
for k in range(5):
kk = np.random.randint(train_num)
axes[0,k].set_axis_off()
axes[0,k].imshow(f1_imgs[kk].squeeze(), cmap="gray")
axes[1,k].set_axis_off()
axes[1,k].imshow(f_msks[kk].squeeze(), cmap="gray")
На первый взгляд всё то же самое, такие же эллипсы, такие же полигоны, но сеть стала работать совсем по-другому:
Accuracy 1.0575 loss 0.0011 selected img 7963 tested img 9999 : : 17765it [29:02:00, 12.40s/it]
Тут необходимо небольшое пояснение, мы не используем аугментацию, т.к. форма полигона и форма эллипса изначально выбираются случайно. Поэтому аугментаци не даст новой информации и не имеет смысла с данном случае.
Но, как видно из результата работы, простой gaussian_filter создал сети много проблем, породил много новой, и наверно лишней, информации.
Ну и для любителей простоты в чистом виде, возьмем те же самые эллипсы с полигонами, но без какой бы то ни было случайности в цвете
результат говорит о том, что случайный цвет совсем не простая добавка.
Accuracy 0.9004 loss 0.0315 selected img 251 tested img 9832 : : 1000it [06:46, 1.33it/s]
Сеть вполне обошлась информацией, извлеченной из 251 картинки, почти в четыре раза меньше, чем из множества картинок раскрашенных шумом.
Цель статьи показать некоторый инструмент и примеры его работы на несерьезных примерах, лего в песочнице. Мы получили инструмент сравнения двух обучающих последовательностей, мы можем оценить насколько наша предобработка усложняет или упрощает обучающую последовательность, насколько тот или иной примитив в обучайющей последовательности прост для детекции.
Возможность применения этого лего примера в реальных делах очевидна, но реальные трейны и сети читателей дело самих читателей.