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 и её сложно\невозможно в такой постановке решить?