Click here to Skip to main content
15,883,814 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am trying to train a triple loss model using a fit_generator. it requires three input and no output. so i have a function that generates hard triplets. the output from the triplets generator has a shape of (3,5,279) which is 3 inputs(anchor,positive and negative) for 5 batches and a total of 279 features. When i run the fit_generator it throws this error that "the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 3 array(s), but instead got the following list of 1 arrays" meanwhile i have passed a list of three arrays. the code is below. it works when i use the fit, however, i want to always call the generator function to generate my triplets as my batches. thanks in advance..this has taken me three days


<pre lang="Python">
def load_data():
    path = "arrhythmia_data.txt"
    f = open( path, "r")
    data = []

    #remove line breaker, comma separate and store in array
    for line in f:
        line = line.replace('\n','').replace('?','0')
        line = line.split(",")

        data.append(line)
    f.close()

    data = np.array(data).astype(np.float64)
    #print(data.shape)


    #create the class labels for input data
    Y_train = data[:,-1:]
    train = data[:,:-1]
    normaliser = preprocessing.MinMaxScaler()
    train = normaliser.fit_transform(train)

    val = train[320:,:]
    train = train[:320,:]

    #create one hot encoding of the class labels of the data and separate them into train and test data

    lb = LabelBinarizer()
    encode = lb.fit_transform(Y_train)
    nb_classes = int(len(encode[0]))

    #one_hot_labels = keras.utils.to_categorical(labels, num_classes=10) this could also be used for one hot encoding
    Y_val_e = encode[320:,:]
    Y_train_e = encode[:320,:]
    print(Y_train_e[0])
    print(np.argmax(Y_train_e[0]))


    val_in = []
    train_in = []

    #grouping and sorting the input data based on label id or name
    for n in range(nb_classes):
        images_class_n = np.asarray([row for idx,row in enumerate(train) if np.argmax(Y_train_e[idx])==n])
        train_in.append(images_class_n)


        images_class_n = np.asarray([row for idx,row in enumerate(val) if np.argmax(Y_val_e[idx])==n])
        val_in.append(images_class_n)
    #print(train_in[0].shape)


    return train_in,val_in,Y_train_e,Y_val_e,nb_classes

train_in,val,Y_train,Y_val,nb_classes = load_data()
input_shape = (train_in[0].shape[1],)


    def build_network(input_shape , embeddingsize):
    '''
    Define the neural network to learn image similarity
    Input : 
            input_shape : shape of input images
            embeddingsize : vectorsize used to encode our picture   
    '''


    #in_ = Input(train.shape)
    net = Sequential()
    net.add(Dense(128,  activation='relu', input_shape=input_shape))
    net.add(Dense(128, activation='relu'))
    net.add(Dense(256, activation='relu'))
    net.add(Dense(4096, activation='sigmoid'))
    net.add(Dense(embeddingsize, activation= None))
     #Force the encoding to live on the d-dimentional hypershpere
    net.add(Lambda(lambda x: K.l2_normalize(x,axis=-1)))


    return net


class TripletLossLayer(Layer):
    def __init__(self, alpha, **kwargs):
        self.alpha = alpha
        super(TripletLossLayer, self).__init__(**kwargs)

    def triplet_loss(self, inputs):
        anchor, positive, negative = inputs
        p_dist = K.sum(K.square(anchor-positive), axis=-1)
        n_dist = K.sum(K.square(anchor-negative), axis=-1)
        return K.sum(K.maximum(p_dist - n_dist + self.alpha, 0), axis=0)

    def call(self, inputs):
        loss = self.triplet_loss(inputs)
        self.add_loss(loss)
        return loss

def build_model(input_shape, network, margin=0.2):
    '''
    Define the Keras Model for training 
        Input : 
            input_shape : shape of input images
            network : Neural network to train outputing embeddings
            margin : minimal distance between Anchor-Positive and Anchor-Negative for the lossfunction (alpha)

    '''
     # Define the tensors for the three input images
    anchor_input = Input(input_shape, name="anchor_input")
    positive_input = Input(input_shape, name="positive_input")
    negative_input = Input(input_shape, name="negative_input") 

    # Generate the encodings (feature vectors) for the three images
    encoded_a = network(anchor_input)
    encoded_p = network(positive_input)
    encoded_n = network(negative_input)

    #TripletLoss Layer
    loss_layer = TripletLossLayer(alpha=margin,name='triplet_loss_layer')([encoded_a,encoded_p,encoded_n])

    # Connect the inputs with the outputs
    network_train = Model(inputs=[anchor_input,positive_input,negative_input],outputs=loss_layer)

    # return the model
    return network_train

def get_batch_random(batch_size,s="train"):

    # initialize result
    triplets=[np.zeros((batch_size,m)) for i in range(3)]

    for i in range(batch_size):
        #Pick one random class for anchor
        anchor_class = np.random.randint(0, nb_classes)
        nb_sample_available_for_class_AP = X[anchor_class].shape[0]

        #Pick two different random pics for this class => A and P. You can use same anchor as P if there is one one element for anchor
        if nb_sample_available_for_class_AP<=1:
            continue
        [idx_A,idx_P] = np.random.choice(nb_sample_available_for_class_AP,size=2 ,replace=False)

        #Pick another class for N, different from anchor_class
        negative_class = (anchor_class + np.random.randint(1,nb_classes)) % nb_classes
        nb_sample_available_for_class_N = X[negative_class].shape[0]

        #Pick a random pic for this negative class => N
        idx_N = np.random.randint(0, nb_sample_available_for_class_N)

        triplets[0][i,:] = X[anchor_class][idx_A,:]
        triplets[1][i,:] = X[anchor_class][idx_P,:]
        triplets[2][i,:] = X[negative_class][idx_N,:]

    return np.array(triplets)

def get_batch_hard(draw_batch_size,hard_batchs_size,norm_batchs_size,network,s="train"):

    if s == 'train':
        X = train_in
    else:
        X = val

    #m, features = X[0].shape

    #while True:
    #Step 1 : pick a random batch to study
    studybatch = get_batch_random(draw_batch_size,X)

        #Step 2 : compute the loss with current network : d(A,P)-d(A,N). The alpha parameter here is omited here since we want only to order them
    studybatchloss = np.zeros((draw_batch_size))

        #Compute embeddings for anchors, positive and negatives
    A = network.predict(studybatch[0])
    P = network.predict(studybatch[1])
    N = network.predict(studybatch[2])

        #Compute d(A,P)-d(A,N)
    studybatchloss = np.sum(np.square(A-P),axis=1) - np.sum(np.square(A-N),axis=1)

        #Sort by distance (high distance first) and take the 
    selection = np.argsort(studybatchloss)[::-1][:hard_batchs_size]

        #Draw other random samples from the batch
    selection2 = np.random.choice(np.delete(np.arange(draw_batch_size),selection),norm_batchs_size,replace=False)

    selection = np.append(selection,selection2)

    triplets = [studybatch[0][selection,:], studybatch[1][selection,:],studybatch[2][selection,:]]
    triplets = triplets.reshape(triplets.shape[0],triplets.shape[1],triplets.shape[2])    
    yield triplets

network = build_network(input_shape,embeddingsize=10)
hard = get_batch_hard(5,4,1,network,s="train")   
network_train = build_model(input_shape,network)
optimizer = Adam(lr = 0.00006)
network_train.compile(loss=None,optimizer=optimizer)
#this works
#history = network_train.fit(hard,epochs=100,steps_per_epoch=1, verbose=2)

history = network_train.fit_generator(hard,epochs=10,steps_per_epoch=16, verbose=2)
# error:: the list of Numpy arrays that you are passing to your model is not the size the model 
expected. Expected to see 3 array(s), but instead got the following list of 1 arrays:
tensorflow keras conv-neural-network numpy-ndarray loss-function





What I have tried:

i tried yielding separately into different varables but it still didnt work.
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900