Visualization of Keras Convolution Layer Outputs - tensorflow
I have written the following code for this question where there are two convolution layers (Conv1 and Conv2 for short) and I would like to plot all the outputs of each layer (it's self-contained). Everything is fine for Conv1, but I am missing something about Conv2.
I am feeding a 1x1x25x25 (num images, num channels, height, width (my convention, neither TF or Theano convention)) image to Conv1 which has FOUR 5x5 filters. That means its output shape is 4x1x1x25x25 (num filters, num images, num channels, height, width), resulting in 4 plots.
Now, this output is being fed to Conv1 which has SIX 3x3 filters. Hence, the output of Conv2 should be 6x(4x1x1x25x25), but it is not! It's rather 6x1x1x25x25. That means, there are only 6 plots rather than 6x4, but why? The following functions also prints the shape of each output which they are
(1, 1, 25, 25, 4)
-------------------
(1, 1, 25, 25, 6)
-------------------
but should be
(1, 1, 25, 25, 4)
-------------------
(1, 4, 25, 25, 6)
-------------------
Right?
import numpy as np
#%matplotlib inline #for Jupyter ONLY
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Conv2D
from keras import backend as K
model = Sequential()
# Conv1
conv1_filter_size = 5
model.add(Conv2D(nb_filter=4, nb_row=conv1_filter_size, nb_col=conv1_filter_size,
activation='relu',
border_mode='same',
input_shape=(25, 25, 1)))
# Conv2
conv2_filter_size = 3
model.add(Conv2D(nb_filter=6, nb_row=conv2_filter_size, nb_col=conv2_filter_size,
activation='relu',
border_mode='same'))
# The image to be sent through the model
img = np.array([
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.]],
[[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[1.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.]],
[[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[1.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[0.],[1.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.]],
[[1.],[1.],[0.],[1.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[1.],[1.]],
[[1.],[1.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.]],
[[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[0.],[0.],[0.],[0.],[0.],[0.],[0.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]],
[[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.],[1.]]])
def get_layer_outputs(image):
'''This function extracts the numerical output of each layer.'''
outputs = [layer.output for layer in model.layers]
comp_graph = [K.function([model.input] + [K.learning_phase()], [output]) for output in outputs]
# Feeding the image
layer_outputs_list = [op([[image]]) for op in comp_graph]
layer_outputs = []
for layer_output in layer_outputs_list:
print(np.array(layer_output).shape, end='\n-------------------\n')
layer_outputs.append(layer_output[0][0])
return layer_outputs
def plot_layer_outputs(image, layer_number):
'''This function handels plotting of the layers'''
layer_outputs = get_layer_outputs(image)
x_max = layer_outputs[layer_number].shape[0]
y_max = layer_outputs[layer_number].shape[1]
n = layer_outputs[layer_number].shape[2]
L = []
for i in range(n):
L.append(np.zeros((x_max, y_max)))
for i in range(n):
for x in range(x_max):
for y in range(y_max):
L[i][x][y] = layer_outputs[layer_number][x][y][i]
for img in L:
plt.figure()
plt.imshow(img, interpolation='nearest')
plot_layer_outputs(img, 1)
The output of a convolution layer is bundled as one image with multiple channels. These could be thought as feature channels, in contrast with color channels. For example, if a convolution layer has F number of filters, it will output an image with F number of channels, no matter how many (color or feature) channels the input image had. This is why Conv2 produces 6 feature maps rather than 6x4.
In more details, a convolution filter will convolve over all input channels and the linear combination of its convolution would be fed to its activation function.
Related
Tensorflow input shape incompatible with layer
I'm trying to build a Sequential model with tensorflow. import tensorflow as tf import keras from tensorflow.keras import layers from keras import optimizers import numpy as np model = keras.Sequential (name="model") model.add(keras.Input(shape=(786,))) model.add(layers.Dense(2048, activation="relu", name="layer1")) model.add(layers.Dense(786, activation="relu", name="layer2")) model.add(layers.Dense(786, activation="relu", name="layer3")) output = model.add(layers.Dense(786, activation="relu", name="output")) model.summary() model.compile( optimizer=tf.optimizers.Adam(), # Optimizer loss=keras.losses.CategoricalCrossentropy(), metrics=[keras.metrics.SparseCategoricalAccuracy()], ) history = model.fit( x_train, y_train, batch_size=1, epochs=5, ) The input shape is a vector with length of 768 (so the input shape is (768,) right?), representing a chess board: def get_dataset(): container = np.load('/content/drive/MyDrive/test_data_vector.npz') b, v = container['arr_0'], container['arr_1'] v = np.asarray(v / abs(v).max() / 2 + 0.5, dtype=np.float32) # normalization (0 - 1) return b, v xtrain, ytrain = get_dataset() print(xtrain.shape) print(ytrain.shape) >> (37, 786) #there are 37 samples >> (37, 786) But I always get the error: ValueError: Input 0 of layer model is incompatible with the layer: expected axis -1 of input shape to have value 786 but received input with shape (1, 1, 768) I tried with np.expand_dims(), which ended in the same Error.
The error is just a typo, as the user mentioned the issue is resolved by changing the output shape from 786 to 768 and the issue is resolved. One suggestion based on the model structure. The number of units are not related to your input shape, you don't have to match that number. The number of units like 2048 and 786 in dense layer is too large and this may not help the model to learn better. Try with smaller numbers like 32,64 etc, you can refer some of the examples in the tensorflow document.
Neural network output issue
I built a neural network with tensorflow, here the code : class DQNetwork: def __init__(self, state_size, action_size, learning_rate, name='DQNetwork'): self.state_size = state_size self.action_size = action_size self.learning_rate = learning_rate with tf.variable_scope(name): # We create the placeholders self.inputs_ = tf.placeholder(tf.float32, shape=[state_size[1], state_size[0]], name="inputs") self.actions_ = tf.placeholder(tf.float32, [None, self.action_size], name="actions_") # Remember that target_Q is the R(s,a) + ymax Qhat(s', a') self.target_Q = tf.placeholder(tf.float32, [None], name="target") self.fc = tf.layers.dense(inputs = self.inputs_, units = 50, kernel_initializer=tf.contrib.layers.xavier_initializer(), activation = tf.nn.elu) self.output = tf.layers.dense(inputs = self.fc, units = self.action_size, kernel_initializer=tf.contrib.layers.xavier_initializer(), activation=None) # Q is our predicted Q value. self.Q = tf.reduce_sum(tf.multiply(self.output, self.actions_)) # The loss is the difference between our predicted Q_values and the Q_target # Sum(Qtarget - Q)^2 self.loss = tf.reduce_mean(tf.square(self.target_Q - self.Q)) self.optimizer = tf.train.AdamOptimizer(self.learning_rate).minimize(self.loss) But i have an issue with the output, the output should normaly be at the same size than "action_size", and action_size value is 3 but i got an output like [[5][3]] instead of just [[3]] and i realy don't understand why... This network got 2 dense layers, one with 50 perceptrons and the other with 3 perceptrons (= action_size). state_size is format : [[9][5]] If someone know why my output is two dimensions i will be very thankful
Your self.inputs_ placeholder has shape (5, 9). You perform the matmul(self.inputs_, fc1.w) operation in dense layer fc1 which has shape (9, 50) and it results in shape (5, 50). You then apply another dense layer with shape (50, 3) which results in output shape (5, 3). The same schematically: matmul(shape(5, 9), shape(9, 50)) ---> shape(5, 50) # output of 1st dense layer matmul(shape(5, 50), shape(50, 3)) ---> shape(5, 3) # output of 2nd dense layer Usually, the first dimension of the input placeholder represents batch size and the second dimension is the dimension of inputs feature vector. So for each sample in a batch you (batch size is 5 in your case) you get the output shape 3. To get probabilities, use this: import tensorflow as tf import numpy as np inputs_ = tf.placeholder(tf.float32, shape=(None, 9)) actions_ = tf.placeholder(tf.float32, shape=(None, 3)) fc = tf.layers.dense(inputs=inputs_, units=2) output = tf.layers.dense(inputs=fc, units=3) reduced = tf.reduce_mean(output, axis=0) probs = tf.nn.softmax(reduced) # <--probabilities inputs_vals = np.ones((5, 9)) actions_vals = np.ones((1, 3)) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print(probs.eval({inputs_:inputs_vals, actions_:actions_vals})) # [0.01858923 0.01566187 0.9657489 ]
Add Placeholder to layer
I have a Tensorflow layer with 2 nodes. These are the output nodes of another 2 larger hidden layers. Now I want to add 2 new nodes to this layer, so I end up with 4 nodes in total, and do some last computation. The added nodes are implemented as Placeholders so far, and have a dynamic shape depending on the batch size. Here is a sketch of the net: Now I want to concatenate Nodes 3 and 4 to the nodes 1 and 2 of the previously computed layer. I know there is tf.concat for this, but I don't understand how to do this correctly. How do I add Placeholders of the same batchsize as the original net input to a specific layer? EDIT: When I use tf.concat over axis=1, I end up with the following problem: z = tf.placeholder(tf.float32, shape=[None, 2]) Weight_matrix = weight_variable([4, 2]) bias = bias_variable([4, 2]) concat = tf.concat((dnn_out, z), 1) h_fc3 = tf.nn.relu(tf.matmul(concat, Weight_matrix) + bias) Adding the bias to the tf.matmul result throws an InvalidArgumentError: Incompatible shapes: [20,2] vs. [4,2].
Since your data is batched, probably over the first dimension, you need to concatenate over the second (axis=1): import tensorflow as tf import numpy as np dnn_output = tf.placeholder(tf.float32, (None, 2)) # replace with your DNN(input) result additional_nodes = tf.placeholder(tf.float32, (None, 2)) concat = tf.concat((dnn_output, additional_nodes), axis=1) print(concat) # > Tensor("concat:0", shape=(?, 4), dtype=float32) dense_output = tf.layers.dense(concat, units=2) print(dense_output) # > Tensor("dense/BiasAdd:0", shape=(?, 2), dtype=float32) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print(sess.run(dense_output, feed_dict={dnn_output: np.ones((5, 2)), additional_nodes: np.zeros((5, 2))}))
Data Preprocessing - Input Shape for TimeDistributed CNN (LRCN) & ConvLSTM2D for Video Classification
I'm trying to do binary classification for labeled data for 300+ videos. The goal is to extract features using a ConvNet and feed into to an LSTM for sequencing with a binary output after evaluating all the frames in the video. I've preprocessed each video to have exactly 200 frames with each image being 256 x 256 so that it would be easier to feed into a DNN and split the dataset into two folders as labels. (e.g. dog and cat) However, after searching stackoverflow for hours, I'm still unsure how to reshape the dataset of video frames so that the model accounts for the number of frames. I'm trying to feed the video frames into a 3D ConvNets and TimeDistributed (2DConvNets) + LSTM, (e.g. (300, 200, 256, 256, 3) ) with no luck. I'm able to perform 2D ConvNet classification (data is a 4D Tensor, need to add a time step dimension to make it a 5D Tensor ) pretty easily but now having issues wrangling with the temporal aspect. I've been using Keras ImageDataGenerator and train_datagen.flow_from_directory to read in the images and have been running into shape mismatch errors when I attempt to feed it to a TimeDistributed ConvNet. I know hypothetically if I have a X_train dataset I can potentially do X_train = X_train.reshape(...). Any example code would be very much appreciated.
I think you could use ConvLSTM2D in Keras for your purpose. ImageDataGenerator is very good for CNN with images, but may be not convenient for CRNN with videos. You have already transformed your 300 videos data in the same shape (200, 256, 256, 3), each video 200 frames, each frame 256x256 rgb. Next, you need to load them in a numpy array in shape (300, 200, 256, 256, 3). For reading videos in numpy arrays see this answer. Then you can feed the data in a CRNN. Its first ConvLSTM2D layer should have input_shape = (None, 200, 256, 256, 3). A sample according to your data: (only illustrated and not tested) from keras.models import Sequential from keras.layers import Dense from keras.layers.convolutional_recurrent import ConvLSTM2D model = Sequential() model.add(ConvLSTM2D(filters = 32, kernel_size = (5, 5), input_shape = (None, 200, 256, 256, 3))) ### model.add(...more layers) model.add(Dense(units = num_of_categories, # num of your vedio categories kernel_initializer = 'Orthogonal', activation = 'softmax')) model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy']) # then train it model.fit(video_data, # shape (300, 200, 256, 256, 3) [list of categories], batch_size = 20, epochs = 50, validation_split = 0.1) I hope this could be a little helpful.
ValueError: Error when checking target: expected activation_6 to have shape(None,2) but got array with shape (5760,1)
I am trying to adapt Python code for a Convolutional Neural Network (in Keras) with 8 classes to work on 2 classes. My problem is that I get the following error message: ValueError: Error when checking target: expected activation_6 to have shape(None,2) but got array with shape (5760,1). My Model is as follows (without the indentation issues): class MiniVGGNet: #staticmethod def build(width, height, depth, classes): # initialize the model along with the input shape to be # "channels last" and the channels dimension itself model = Sequential() inputShape = (height, width, depth) chanDim = -1 # if we are using "channels first", update the input shape # and channels dimension if K.image_data_format() == "channels_first": inputShape = (depth, height, width) chanDim = 1 # first CONV => RELU => CONV => RELU => POOL layer set model.add(Conv2D(32, (3, 3), padding="same", input_shape=inputShape)) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(32, (3, 3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) # second CONV => RELU => CONV => RELU => POOL layer set model.add(Conv2D(64, (3, 3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(64, (3, 3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) # first (and only) set of FC => RELU layers model.add(Flatten()) model.add(Dense(512)) model.add(Activation("relu")) model.add(BatchNormalization()) model.add(Dropout(0.5)) # softmax classifier model.add(Dense(classes)) model.add(Activation("softmax")) # return the constructed network architecture return model Where classes = 2, and inputShape=(32,32,3). I know that my error has something to do with my classes/use of binary_crossentropy and occurs in the model.fit line below, but haven't been able to figure out why it is problematic, or how to fix it. By changing model.add(Dense(classes)) above to model.add(Dense(classes-1)) I can get the model to train, but then my labels size and target_names are mismatched, and I have only one category which everything is categorized as. # import the necessary packages from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from pyimagesearch.nn.conv import MiniVGGNet from pyimagesearch.preprocessing import ImageToArrayPreprocessor from pyimagesearch.preprocessing import SimplePreprocessor from pyimagesearch.datasets import SimpleDatasetLoader from keras.optimizers import SGD #from keras.datasets import cifar10 from imutils import paths import matplotlib.pyplot as plt import numpy as np import argparse # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-d", "--dataset", required=True, help="path to input dataset") ap.add_argument("-o", "--output", required=True, help="path to the output loss/accuracy plot") args = vars(ap.parse_args()) # grab the list of images that we'll be describing print("[INFO] loading images...") imagePaths = list(paths.list_images(args["dataset"])) # initialize the image preprocessors sp = SimplePreprocessor(32, 32) iap = ImageToArrayPreprocessor() # load the dataset from disk then scale the raw pixel intensities # to the range [0, 1] sdl = SimpleDatasetLoader(preprocessors=[sp, iap]) (data, labels) = sdl.load(imagePaths, verbose=500) data = data.astype("float") / 255.0 # partition the data into training and testing splits using 75% of # the data for training and the remaining 25% for testing (trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42) # convert the labels from integers to vectors trainY = LabelBinarizer().fit_transform(trainY) testY = LabelBinarizer().fit_transform(testY) # initialize the label names for the items dataset labelNames = ["mint", "used"] # initialize the optimizer and model print("[INFO] compiling model...") opt = SGD(lr=0.01, decay=0.01 / 10, momentum=0.9, nesterov=True) model = MiniVGGNet.build(width=32, height=32, depth=3, classes=2) model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"]) # train the network print("[INFO] training network...") H = model.fit(trainX, trainY, validation_data=(testX, testY), batch_size=64, epochs=10, verbose=1) print ("Made it past training") # evaluate the network print("[INFO] evaluating network...") predictions = model.predict(testX, batch_size=64) print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=labelNames)) # plot the training loss and accuracy plt.style.use("ggplot") plt.figure() plt.plot(np.arange(0, 10), H.history["loss"], label="train_loss") plt.plot(np.arange(0, 10), H.history["val_loss"], label="val_loss") plt.plot(np.arange(0, 10), H.history["acc"], label="train_acc") plt.plot(np.arange(0, 10), H.history["val_acc"], label="val_acc") plt.title("Training Loss and Accuracy on items dataset") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend() plt.savefig(args["output"]) I have looked at these questions already, but cannot workout how to get around this problem based on the responses. Stackoverflow Question 1 Stackoverflow Question 2 Stackoverflow Question 3 Any advice or help would be much appreciated, as I've spent the last couple of days on this.
Matt's comment was absolutely correct in that the problem lay with using LabelBinarizer and this hint led me to a solution that did not require me to give up using softmax, or change the last layer to have classes = 1. For posterity and for others, here's the section of code that I altered and how I was able to avoid LabelBinarizer: from keras.utils import np_utils from sklearn.preprocessing import LabelEncoder # load the dataset from disk then scale the raw pixel intensities # to the range [0,1] sp = SimplePreprocessor (32, 32) iap = ImageToArrayPreprocessor() # encode the labels, converting them from strings to integers le=LabelEncoder() labels = le.fit_transform(labels) data = data.astype("float") / 255.0 labels = np_utils.to_categorical(labels,2) # partition the data into training and testing splits using 75% of # the data for training and the remaining 25% for testing ....
I believe the problem lies in the use of LabelBinarizer. From this example: >>> lb = preprocessing.LabelBinarizer() >>> lb.fit_transform(['yes', 'no', 'no', 'yes']) array([[1], [0], [0], [1]]) I gather that the output of your transformation has the same format, i. e. a single 1 or 0 encoding "is new" or "is used". If your problem only calls for classification among these two classes, that format is preferable because it contains all the information and uses less space than the alternative, i. e. [1,0], [0,1], [0,1], [1,0]. Therefore, using classes = 1 would be correct, and the output should be a float indicating the network's confidence in a sample being in the first class. Since these values have to sum to one, the probability of it being in the second class could easily be inferred by subtracting from 1. You would need to replace softmax with any other activation, because softmax on a single value always returns 1. I'm not completely sure about the behaviour of binary_crossentropy with a single-valued result, and you may want to try mean_squared_error as the loss. If you are looking to expand your model to cover more than two classes, you would want to convert your target vector to a One-hot encoding. I believe inverse_transform from LabelBinarizer would do this, although that would seem to be quite a roundabout way to get there. I see that sklearn also has OneHotEncoder which may the more appropriate replacement. NB: You can specify the activation function for any layer more easily with, for example: Dense(36, activation='relu') This may be helpful in keeping your code to a manageable size.