Перейти к содержимому
Compvision.ru
mrgloom

Keras: 2 heads -> 2 losses

Recommended Posts

import cv2
import numpy as np
import pandas as pd

import keras
from keras.models import Sequential, Model
from keras.layers import Dense, Dropout, Activation, Flatten, GlobalAveragePooling2D
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Input
from keras.optimizers import SGD, RMSprop
from keras.callbacks import ModelCheckpoint, EarlyStopping

import sys

#Two head - one for number and one for digit with constant positions

#params
list_a= ['0','1','2','3','4','5','6','7','8','9']
list_b= ['A','B','C']
dx= 20
dy= 50
font_scale= 1
batch_size= 32
epochs= 20
patience = 10
model_name= 'model.h5'
input_dim= (80,120,3)

def generate_random_plate_number():
    img= np.zeros(input_dim, np.uint8)
    
    rand_indx_b= np.random.randint(len(list_b))
    letter_str1= list_b[rand_indx_b]
    #print('letter_str',letter_str) #
    cv2.putText(img, letter_str1, (0,dy), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255,255,255), thickness=2)
    
    rand_indx_a= np.random.randint(len(list_a))
    number_str1= list_a[rand_indx_a]
    #print('number_str',number_str) #
    cv2.putText(img, number_str1, (2*dx,dy), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255,255,255), thickness=2)
       
    y1= np.zeros((len(list_b)), np.int32)
    y2= np.zeros((len(list_a)), np.int32)
    
    y1[list_b.index(letter_str1)]= 1
    y2[list_a.index(number_str1)]= 1
    
    #print('y1', y1) #
    #print('y2', y2) #
    
    return img,y1,y2

def batch_generator(batch_size):
    while True:
        image_list = []
        y1_list = []
        y2_list = []
        for i in range(batch_size):
            img,y1,y2= generate_random_plate_number()
            image_list.append(img)
            y1_list.append(y1)
            y2_list.append(y2)
            
        image_arr = np.array(image_list, dtype=np.float32)
        y1_arr = np.array(y1_list, dtype=np.int32)
        y2_arr = np.array(y2_list, dtype=np.int32)
        
        #print('image_arr.shape', image_arr.shape) #
        #print('y1_arr.shape', y1_arr.shape) #
        #print('y2_arr.shape', y2_arr.shape) #
        
        yield(image_arr, {'output1': y1_arr, 'output2': y2_arr} )
         
def get_model():

    network_input = Input(shape=input_dim)
    
    conv1= Conv2D(32, (3, 3), padding='same')(network_input)
    pool1= MaxPooling2D(pool_size=(2, 2))(conv1)
    act1= Activation('relu')(pool1)
    
    conv2= Conv2D(32, (3, 3), padding='same')(act1)
    pool2= MaxPooling2D(pool_size=(2, 2))(conv2)
    act2= Activation('relu')(pool2)
    
    conv3= Conv2D(64, (3, 3), padding='same')(act2)
    pool3= MaxPooling2D(pool_size=(2, 2))(conv3)
    act3= Activation('relu')(pool3)
    
    conv4= Conv2D(64, (3, 3), padding='same')(act3)
    pool4= MaxPooling2D(pool_size=(2, 2))(conv4)
    act4= Activation('relu')(pool4)
    
    tail= GlobalAveragePooling2D()(act4)
    #global_pool= GlobalAveragePooling2D()(act4)
    #tail= Dense(128)(global_pool)
    
    #add heads
    head1= Dense(len(list_b))(tail)
    softmax1= Activation('softmax', name='output1')(head1)
    head2= Dense(len(list_a))(tail)
    softmax2= Activation('softmax', name='output2')(head2)
    
    model = Model(input = network_input, output = [softmax1,softmax2])
    model.compile(optimizer = RMSprop(1e-3),
        loss = {'output1': 'categorical_crossentropy', 'output2': 'categorical_crossentropy'})
    
    #print(model.summary()) #
    #sys.exit() #
    
    return model
    
def train():
    
    model= get_model()
    
    callbacks = [
        EarlyStopping(monitor='val_loss', patience=patience, verbose=0),
        ModelCheckpoint('model_temp.h5', monitor='val_loss', save_best_only=True, verbose=0),
    ]
    
    history = model.fit_generator(
        generator=batch_generator(batch_size),
        nb_epoch=epochs,
        samples_per_epoch=1*batch_size,
        validation_data=batch_generator(batch_size),
        nb_val_samples=1*batch_size,
        verbose=2,
        callbacks=callbacks)

    model.save_weights(model_name)
    pd.DataFrame(history.history).to_csv('train_history.csv', index=False)

 

Задача (не реальная) на картинке (80,120,3) в константных местах стоят(разные) цифра и буква, соответственно создаем общее 'тело' и 2 'головы'(для буквы и цифры), но чего то так не учится. Есть какие то соображения или типа задача weakly supervised и её сложно\невозможно в такой постановке решить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Модель

7a9d0d2bb108401aabfbbbac8734ffe9.png

Сэмпл

db5e18cdfc2249b589108168c069e622.png

Что то похожее делается тут на mxnet, но непонятно причем там конкат и какой там в итоге аутпут - матрица 10x4 ? как быть если у нас буквы и цифры, т.е. длины множеств разные (3 и 10 в изначальном примере)?

https://github.com/apache/incubator-mxnet/blob/master/example/captcha/mxnet_captcha.R#L13

и по сути изначальная синтетическая задача расширяется на номера машин и капчи.

Тоже самое тут только уже на python, а не на R и для plate recognition.

https://github.com/szad670401/end-to-end-for-chinese-plate-recognition/blob/master/train.py#L108

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Похоже несколько аутпутов не надо, просто можно всё лепить в один вектор судя по этому проекту:

https://github.com/matthewearl/deep-anpr/blob/db823ebe283f4e226a80dbbe4cb5b45867bf0485/common.py#L39

https://github.com/matthewearl/deep-anpr/blob/db823ebe283f4e226a80dbbe4cb5b45867bf0485/train.py#L172

https://github.com/matthewearl/deep-anpr/blob/db823ebe283f4e226a80dbbe4cb5b45867bf0485/train.py#L125

http://matthewearl.github.io/2016/05/06/cnn-anpr/

 

Хотя в пейпере от гугла судя по картинке таки несколько аутпутов:

http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42241.pdf

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Проверил работает, но ограничение константная длина надписи.

 

Теперь интересно какой нибудь cnn+lstm накрутить как тут:

https://github.com/synckey/tensorflow_lstm_ctc_ocr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

По результатам разговора самого с собой запилил примеры

https://github.com/mrgloom/Char-sequence-recognition

 

С LSTM пока не разобрался, т.к. непонятно как подавать аутпут блоб [rows,cols,channels] из сверточной части в LSTM

Тут есть пример для Keras

Но как то не очевидно почему так и будет ли это эквивалентно подаче по колонке как на картинке ниже

https://github.com/fchollet/keras/blob/master/examples/image_ocr.py#L440

 

687474703a2f2f63732e636d752e6564752f2537

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

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

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×