Bridgeburners's comment is correct: in this model, `softmax` always predicts 1. I changed to `sigmoid` activation as I had used originally, then found another problem: under `sigmoid`, `model.predict` returns probabilities, not classes, so the predictions need to thresholded first. With these bugs fixed, and `epochs` cranked up to 300, I'm now getting 99% accuracy. <!-- language: python --> import numpy as np X = np.array([[c == '1' for c in line] for line in [ '1101111111011111111111111111111100011010111110010011111111111111011111110111111111111101111111111110', '0011111001000001011010001011000100010001110011011101111111110000111111001001001001110011011101111000', '0111101111010000000000111110001101000010110000101111010111100110010110000110011000001010000000000110']]) X = X.T n_cases = X.shape[0] y = np.sum(X, axis = 1) > 1 np.random.seed(123) n_folds = 10 folds = np.repeat(range(n_folds), np.ceil(n_cases / n_folds))[ range(n_cases)] np.random.shuffle(folds) y_pred = np.zeros_like(y) for fold_i in range(n_folds): print("Fold", fold_i) import os; os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Activation model = Sequential([ Dense(10, activation = "relu"), Dense(1, activation = "sigmoid")]) model.compile( optimizer = "rmsprop", loss = "binary_crossentropy") model.fit( X[folds != fold_i], y[folds != fold_i], verbose = False, epochs = 300, batch_size = np.sum(folds != fold_i) // 5 + 1) print("Training accuracy:", np.mean( y[folds != fold_i] == (model.predict(X[folds != fold_i])[:,0] > .5))) y_pred[folds == fold_i] = (model.predict(X[folds == fold_i])[:,0] > .5) print(np.mean(y_pred == y))