Wahyu Adnyana
Wahyu Adnyana

Reputation: 11

TensorFlow Keras Optimise prediction

I'm Using tensorflow and keras to predict handwrting digits. For training I'm using nmist dataset. the accuracy is about 98.8% after training. but sometimes in test its confuse between 4 and 9 , 7 and 3, i'm alerady optimize the image input with opencv, like remove noise, rescale, threshold etc.
What should i do next to improved this prdiction accuracy?

My plan is add more sample, and resize the sample image from 28x28 to 56x56.
Will this affect accuracy?

This my model for training:

epoc=15, batch size=64

def build_model():
    model = Sequential()
    # add Convolutional layers
    model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='same', input_shape=input_shape))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='same'))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='same'))
    model.add(MaxPooling2D(pool_size=(2,2)))    
    model.add(Flatten())
    # Densely connected layers
    model.add(Dense(128, activation='relu'))

    # output layer
    model.add(Dense(10, activation='softmax'))

    # compile with adam optimizer & categorical_crossentropy loss function
    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

    return model

Upvotes: 0

Views: 93

Answers (1)

ldavid
ldavid

Reputation: 2552

You can try to add regularization:

def conv2d_bn(x,
              units,
              kernel_size=(3, 3),
              activation='relu',
              dropout=.5):
    y = Dropout(x)
    y = Conv2D(units, kernel_size=kernel_size, use_bias=False)(y)
    y = BatchNormalization(y)
    y = Activation(activation)(y)

    return y

def build_model(..., dropout=.5):
    x = Input(shape=[...])
    y = conv2d_bn(x, 32)
    y = MaxPooling2D(y)
    ...
    y = Dropout(dropout)(y)
    y = Dense(10, activation='softmax')

    model = Model(x, y)
    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    return model

You can tweak the class weights to force the model to pay more attention to classes 3, 4, 7 and 9 during training:

model.fit(..., class_weights={0: 1, 1: 1, 2:1, 3:2, 4:2, 5:1, 6:1, 7:2, 8:1, 9:2})

If you have some time to burn, you can also try to grid or random-search the models hyperparameters. Something in the lines:

def build(conv_layers, dense_layers, dense_units, activation, dropout):
    y = x = Input(shape=[...])

    kernels = 32
    kernel_size = (2, 2)

    for i in range(conv_layers):
        y = conv2d_bn(y, kernel_size, kernels, activation, dropout)

        if i % 2 == 0:  # or 3 or 4.
            y = MaxPooling2D(y)
            kernels *= 2
            kernel_size = tuple(k+1 for k in kernel_size)

    y = GlobalAveragePooling2D()(y)

    for i in range(dense_layers):
        y = Dropout(dropout)(y)
        y = Dense(dense_units)(y)

    y = Dense(10, activation='softmax')(y)


model = KerasClassifier(build_model,
                        epochs=epochs,
                        validation_split=validation_split,
                        verbose=0,
                        ...)
params = dict(conv_layers=[2, 3, 4],
              dense_layers=[0, 1],
              activation=['relu', 'selu'],
              dropout=[.2, .3, .5],
              callbacks=[callbacks.EarlyStopping(patience=10,
                                                 restore_best_weights=True)])

grid = GridSearchCV(model, params,
                    scoring='balanced_accuracy_score',
                    verbose=2,
                    n_jobs=1)

Now, combining hyperparams searching with the NumpyArrayIterator is a little tricky, because the latter assumes we have all training samples (and targets) at hand before the training steps. It's still doable, though:

g = ImageDataGenerator(...)
cv = StratifiedKFold(n_splits=3)
results = dict(params=[], valid_score=[])

for params in ParameterGrid(params):
    fold_scores = []

    for t, v in cv.split(train_data, train_labels):
        train = g.flow(train_data[t], train_labels[t], subset='training')
        nn_valid = g.flow(train_data[t], train_labels[t], subset='validation')
        fold_valid = g.flow(train_data[v], train_labels[v])

        nn = build_model(**params)
        nn.fit_generator(train, validation_data=nn_valid, ...)

        probabilities = nn.predict_generator(fold_valid, steps=...)
        p = np.argmax(probabilities, axis=1)

        fold_scores += [metrics.accuracy_score(valid.classes_, p)]

    results['params'] += [params]
    results['valid_score'] += [fold_scores]

best_ix = np.argmax(np.mean(results['valid_score'], axis=1))
best_params = results['params'][best_ix]

nn = build_model(**best_params)
nn.fit_generator(...)

Upvotes: 1

Related Questions