KERAS Classification only use some of the digits on Mnist dataset - python

# Creating a Sequential Model and adding the layers
input_img = Input(shape=(28, 28, 1))
#63 kernels - Conv of 3X3
conv_1 = Conv2D(63, kernel_size=(3,3),activation='relu', padding='same')(input_img)
#Then pooling of 2X2
encoded = MaxPooling2D((2, 2), padding='same')(conv_1)
#model.add(Dropout(0.2))
###Classification###
# Flattening the 2D arrays for fully connected layers
flatten = Flatten()(encoded)
# Adding dense layer
fc = Dense(1000, activation='relu')(flatten)
fc1 = (Dropout(0.2))(fc)
#A6 = model.add(Dropout(0.2),name = 'A6') #Combat Overfitting, drop random elements
#Softmax layer must have neurons = range of labels, 0-9 for this case
softmax = Dense(5, activation='softmax', name='classification')(fc1)
model = Model(inputs=input_img, outputs=[softmax])
when i run and model.fit the model , the follow error occurs:
ValueError: Data cardinality is ambiguous:
x sizes: 30703
y sizes: 30703, 51660
Please provide data which shares the same first dimension.
What i am trying to achieve is that , i am trying to run keras classification on mnist dataset, and i have removed some of the digits , leaving only 0,1,2,3,9, a total of 5 integers , i need to index the integers so i could output a dense layer of 5 outputs , instead of having to stick to 10 (covering integer 9). I have done the below , but error above occurs, kindly advise thanks
# Transform y_train (and similarly y_test).
uniquetrain, index = np.unique(y_train, return_inverse=True)
y_train = np.arange(len(uniquetrain))[index]
# To get back the original labels, just index into the unique values.
unique[y_train]
# Transform y_train (and similarly y_test).
uniquetest, index1 = np.unique(y_test, return_inverse=True)
y_test = np.arange(len(uniquetest))[index1]
# To get back the original labels, just index into the unique values.
unique[y_test]

You can create a mask to keep the instances of the desired labels and also create a mapping from old labels to new ones. See the code below:
X_train = ... # Tensor of shape [ELEMS, 28, 28, 1]
y_train = ... # Tensor of shape [ELEMS]
X_test = ... # Tensor of shape [TEST_ELEMS, 28, 28, 1]
y_test = ... # Tensor of shape [TEST_ELEMS]
# Labels you want to keep
keep_labels = [0, 1, 2, 3, 9]
# Map old labels to new labels, for instance the label 9 on the new set of labels,
# is going to be 4
labels_to_index = {l: i for i,l in enumerate(keep_labels)}
# Masks to keep training and test instance. Trues keeps the instances
train_keep_mask = np.zeros(y_train.shape[0], dtype=np.bool)
test_keep_mask = np.zeros(y_test.shape[0], dtype=np.bool)
for l in keep_labels:
train_keep_mask |= y_train == l
test_keep_mask |= y_test == l
# Apply masks to filter the training and test instances
X_train = X_train[train_keep_mask]
y_train = y_train[train_keep_mask]
X_test = X_test[test_keep_mask]
y_test = y_test[test_keep_mask]
# From now on X_train, y_train, X_test, y_test only contain the desired labels
# which are defined in `keep_labels`
# Map old labels to new ones
new_y_train = np.array([labels_to_index[l] for l in y_train.tolist()])
new_y_test = np.array([labels_to_index[l] for l in y_test.tolist()])
# To invert the labels use `keep_labels[new_label]`
again_old_y_train = np.array([keep_labels[l] for l in new_y_train.tolist()])
again_old_y_test = np.array([keep_labels[l] for l in new_y_test.tolist()])

Related

ResNet50 base model for multi-label classification predicting only label

I am using a ResNet50 as base model to predict multiple label in an image and sum up the respective values of the labels.
reading the data:
#read the data
data_path = '/content/drive/MyDrive/Notifyer-dataset/dataset'
def load_dataset(folder):
X = [] # create an empty list to store the images
y = [] # create an empty list to store the labels
# get a list of all the files in the folder
filenames = os.listdir(folder)
# iterate over the files
for filename in filenames:
# get the label from the filename
label = filename.split('_')[0]
# open the image file and convert it to a NumPy array
image = Image.open(os.path.join(folder, filename))
image = image.resize((200, 200)) # resize the image to 200x200
image = image.convert('RGB') # convert the image to RGB
image = np.array(image) / 255 # normalize the pixel values
image = image.reshape(-1, 200, 200, 3) # reshape to (batch_size, height, width, channels)
# append the image and label to the list
X.append(image)
y.append(label)
# convert the lists to NumPy arrays
X = np.array(X)
y = np.array(y)
#preprocessing
X = X.reshape(-1, 200, 200, 3) # reshape arrays to 200x200 images with 1 channel
X = X / 255.0 # normalize pixel values
#one hot encoding
num_classes = len(np.unique(y))
y = to_categorical(y, num_classes)
return X, y,num_classes
X, y, num_classes = load_dataset(data_path)
building the model:
def build_r_cnn_model(num_classes):
"""
Build a region-based CNN model.
Parameters:
num_classes (int): number of classes to classify
Returns:
Model: the R-CNN model
"""
# load the ResNet50 model pre-trained on ImageNet
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(200, 200, 3))
# freeze the base model layers
for layer in base_model.layers:
layer.trainable = False
# add a global average pooling layer
x = base_model.output
x = tf.keras.layers.GlobalAveragePooling2D()(x)
# add a fully-connected layer
x = tf.keras.layers.Dense(1024, activation='relu')(x)
# add a dropout layer
x = tf.keras.layers.Dropout(0.5)(x)
# add a classification layer
predictions = tf.keras.layers.Dense(num_classes, activation='softmax')(x)
#build the model
model = Model(inputs=base_model.input, outputs=predictions)
return model
compiling the model:
# build and compile the model
model = build_r_cnn_model(num_classes)
model.compile(loss='binary_crossentropy', optimizer='sgd', metrics=['accuracy'])
training the model:
#train
history = model.fit(X_train, y_train, epochs=10, batch_size=128, validation_data=(X_val, y_val))
function to sum up all label values in the image:
#function to calculate total sum of value of predicted labels
def predict_total_sum(model, image):
y_pred = model.predict(image) # classify the image
# define a lookup table to map class indices to values
value_lookup = {
0: 1, # class 0 corresponds to value 1
1: 2, # class 1 corresponds to value 2
}
total_sum = 0
for prediction in y_pred:
# get the class index with the highest predicted probability
class_index = np.argmax(prediction)
print(class_index)
# add the value of the detected denomination to the total sum
total_sum += value_lookup[class_index]
return total_sum
It gives value 1 or 2 for every image for each model compilation which means it is only predicting only one label even if the image has multiple objects of both the labels.
My dataset is small and every image in it contains object of one of the label, do I need to diversify my dataset to make the model identify both labels in an image or is there something wrong with the model architecture? I have also tried to build a CNN model from scratch but it is giving the same result...
I think the output of model.predict has shape [1, num_of_classes] (you can verify it by printing it's shape once). Hence when you are looping on y_pred then you basically iterate only once and add one of the class index to the total_sum. Even if the shape was [num_of_classes], then also I think that this is not how you should try multi-class classification. Would prefer you to read more about how multiclass classification is done.
You can take help from this link: https://www.kaggle.com/code/prateek0x/multiclass-image-classification-using-keras

ValueError: Input 0 of layer sequential is incompatible with the layer: expected min_ndim=3, found ndim=2. Full shape received: [None, 2]

Here is my block of code:
x_train = []
def preprocess_dataset(batch_size, normalize=True):
#first accessing RSSI columns to train x-axis
col_list = [0, 1]
trainX_data_frame = pd.read_csv('/home/Documents/generated_rssi_dataset.csv', usecols=col_list)
trainX_rows = pd.DataFrame(trainX_data_frame)
for trainX_row in trainX_rows:
train_x1 = trainX_row.loc[0]
#train_x1 = trainX_row[0].loc[trainX_row]
train_x2 = trainX_row.loc[1]
training_x = ((train_x1 + train_x2)/2)
x_train = x_train.append(training_x)
return np.array(x_train), np.array(y_train)
for i in range(training_cycles):
x_train = preprocess_dataset(x_train)
y_train = preprocess_dataset(np.array(y_train))
x_train = x_train.reshape(x_train, time_steps, n_features)
history = model.fit(x_train, y_train,epochs=30,batch_size=10,validation_split=0.2)
I am getting an attribute error caused by a named error. I have checked the other parts of the code, it is not missing any declarations or definitions anywhere. Only in this part of the code 'the function preprocess_dataset' has some error. I understand it is due to the for loop, the possibility is that if the loop isn't executed then x_train won't have any attribute. But I don't know how to resolve this issue. Any help will be greatly appreciated.
Note: y_train is a similar block so I have not added it here in the code section.
%%%%%% update 4th July 2021%%%%
using comment by Nicolas Gervais, changed the below.
x_train = []
y_train = []
def preprocess_dataset_x(batch_size, normalize=True):
#first accessing RSSI columns to train x-axis
col_list_x = [0, 1]
trainX_data_frame = pd.read_csv('/home/kobuki/Documents/generated_rssi_dataset.csv', usecols=col_list_x)
trainX_rows = pd.DataFrame(trainX_data_frame)
for index, row in trainX_rows.iterrows():
train_x1 = trainX_rows.loc[0]
train_x2 = trainX_rows.loc[1]
training_x = ((train_x1 + train_x2)/2)
x_train.append(training_x)
print("x_train calculated and stored in array")
return np.array(x_train)
def preprocess_dataset_y(batch_size, normalize=True):
#accessing loc coordinates to train y-axis
col_list_y = [2, 3]
trainY_data_frame = pd.read_csv('/home/kobuki/Documents/generated_rssi_dataset.csv', usecols = col_list_y)
trainY_rows = pd.DataFrame(trainY_data_frame)
for index, row in trainY_rows.iterrows():
train_y1 = trainY_rows.loc[2]
train_y2 = trainY_rows.loc[3]
training_y = (train_y1, train_y2)
y_train.append(training_y)
print("y_train calculated and stored in array")
return np.array(y_train)
for i in range(training_cycles):
x_train = preprocess_dataset_x(np.array(x_train))
y_train = preprocess_dataset_y(np.array(y_train))
### x_train = tf.data.Dataset.from_tensor_slices(x_train)
### y_train = tf.data.Dataset.from_tensor_slices(y_train)
### x_train = x_train.reshape(x_train, time_steps, n_features)
### y_train = y_train.reshape(y_train, time_steps, n_features)
history = model.fit(x_train, y_train,epochs=30,batch_size=10)
after this point, while fitting the model to a conv1D I am getting the below error now.
I am getting errors with reshaping also and the array created along x_train, y_train is 460, 460, 2. I don't know why it says min_ndim expected as 3. Please advise.
%%% Model %%%
def createCnnLstmModel(time_steps, n_features):
##CorNet architecture
model = Sequential()
model.add(Conv1D(filters=32,kernel_size=5,activation='relu',input_shape=(time_steps, n_features)))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=4))
model.add(Dropout(0.1))
model.add(Conv1D(filters=32,kernel_size=5,activation='relu',input_shape=(time_steps, n_features)))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=4))
model.add(Dropout(0.1))
model.add(LSTM(128,activation='tanh',return_sequences=True))
model.add(LSTM(128,activation='tanh'))
model.add(Dense(1))
model.compile(optimizer='RMSProp',loss='MAE',metrics=['mae','mape',soft_acc])
model.summary()
return model
timesteps = 460 and n_features are 2.

Dataset.from_generator cannot replicate functionality of numpy arrays as input to 1D Convnet

I am feeding many time series of length 100 and 3 features into a 1D Convnet. I have too many of these to use numpy arrays, therefore I need to use Dataset.from_generator().
The problem is that when I train the model on the dataset, it gives the error:
expected conv1d_input to have 3 dimensions, but got array with shape (100, 3)
The code below demonstrates the problem. The generator produces each element as an expected (100,3) array. Why does the model not recognise the generator output as valid?
Many thanks for any help. Julian
import numpy as np
import tensorflow as tf
def create_timeseries_element():
# returns a random time series of 100 intervals, each with 3 features,
# and a random one-hot array of 5 entries
data = np.random.rand(100,3)
label = np.eye(5, dtype='int')[np.random.choice(5)]
return data, label
def data_generator():
d, l = create_timeseries_element()
yield (d, l)
model = tf.keras.models.Sequential([
tf.keras.layers.Conv1D(128, 9, activation='relu', input_shape=(100, 3)),
tf.keras.layers.Conv1D(128, 9, activation='relu'),
tf.keras.layers.MaxPooling1D(2),
tf.keras.layers.Conv1D(256, 5, activation='relu'),
tf.keras.layers.Conv1D(256, 5, activation='relu'),
tf.keras.layers.GlobalAveragePooling1D(),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(5, activation='softmax')])
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
x_train = []
y_train = []
for _ in range(1000):
d, l = create_timeseries_element()
x_train.append(d)
y_train.append(l)
x_train = np.array(x_train)
y_train = np.array(y_train)
# train model with numpy arrays - this works
model.fit(x=x_train, y=y_train)
ds = tf.data.Dataset.from_generator(data_generator, output_types=(tf.float32, tf.int32),
output_shapes=(tf.TensorShape([100, 3]), tf.TensorShape([5])))
# train model with dataset - this fails
model.fit(ds)
Model expects a batch/list of samples. You can do that by simply setting batch property while creating your dataset as follows:
ds = tf.data.Dataset.from_generator(data_generator, output_types=(tf.float32, tf.int32),
output_shapes=(tf.TensorShape([100, 3]), tf.TensorShape([5])))
ds = ds.batch(16)
You can also do that another way when you prepare sample. In this way, You need to expand the sample dimension so that a sample acts as a batch (you can pass a list of samples too) and you have to do the following modifications in your output_shapes of dataset and create_timeseries_element function
def create_timeseries_element():
# returns a random time series of 100 intervals, each with 3 features,
# and a random one-hot array of 5 entries
# Expand dimensions to create a batch of single sample
data = np.expand_dims(np.random.rand(100, 3), axis=0)
label = np.expand_dims(np.eye(5, dtype='int')[np.random.choice(5)], axis=0)
return data, label
ds = tf.data.Dataset.from_generator(data_generator, output_types=(tf.float32, tf.int32), output_shapes=(tf.TensorShape([None, 100, 3]), tf.TensorShape([None, 5])))
The above changes will supply only a single batch (sample for first solution) for each epochs of your dataset. You can generate as much batches (samples for first solution) you want (e.g. 25) by passing a parameter to data_generator function while you define your dataset like follows:
def data_generator(count=1):
for _ in range(count):
d, l = create_timeseries_element()
yield (d, l)
ds = tf.data.Dataset.from_generator(data_generator, args=[25], output_types=(tf.float32, tf.int32), output_shapes=(tf.TensorShape([None, 100, 3]), tf.TensorShape([None, 5])))

Shape of data and LSTM Input for varying timesteps

For my master thesis, I want to predict the price of a stock in the next hour using a LSTM model. My X data contains 30.000 rows with 6 dimensions (= 6 features), my Y data contains 30.000 rows and only 1 dimension (=target variable). For my first LSTM model, I reshaped the X data to (30.000x1x6), the Y data to (30.000x1) and determined the input like this:
input_nn = Input(shape=(1, 6))
I am not sure how to reshape the data and to determine the input shape for the model if I want to increase the timesteps. I still want to predict the stock price in the next hour, but include more previous time steps.
Do I have to add the data from previous timesteps in my X data in the second dimension?
Can you explain what the number of units of a LSTM exactly refers to? Should it be the same as the number of timesteps in my case?
You are on the right track but confusing the number of units with timesteps. The units is a hyper-parameter that controls the output dimension of the LSTM. It is the dimension of the LSTM output vector, so if input is (1,6) and you have 32 units you will get (32,) as in the LSTM will traverse the single timestep and produce a vector of size 32.
Timesteps refers to the size of the history you can your LSTM to consider. So it isn't the same as units at all. Instead of processing the data yourself, Keras has a handy TimeseriesGenerator which will take a 2D data like yours and use a sliding window of some timestep size to generate timeseries data. From the documentation:
from keras.preprocessing.sequence import TimeseriesGenerator
import numpy as np
data = np.array([[i] for i in range(50)])
targets = np.array([[i] for i in range(50)])
data_gen = TimeseriesGenerator(data, targets,
length=10, sampling_rate=2,
batch_size=2)
assert len(data_gen) == 20
batch_0 = data_gen[0]
x, y = batch_0
assert np.array_equal(x,
np.array([[[0], [2], [4], [6], [8]],
[[1], [3], [5], [7], [9]]]))
assert np.array_equal(y,
np.array([[10], [11]]))
which you can use directory in model.fit_generator(data_gen,...) giving you the option to try out different sampling_rates, timesteps etc. You should probably investigate these parameters and how they affect the result in your thesis.
Update with code that is roughly 5 times quicker than the last one:
x = np.load(nn_input + "/EOAN" + "/EOAN_X" + ".npy")
y = np.load(nn_input + "/EOAN" + "/EOAN_Y" + ".npy")
num_features = x.shape[1]
num_time_steps = 500
for train_index, test_index in tscv.split(x):
# Split into train and test set
print("Fold:", fold_counter, "\n" + "Train Index:", train_index, "Test Index:", test_index)
x_train_raw, y_train, x_test_raw, y_test = x[train_index], y[train_index], x[test_index], y[test_index]
# Scaling the data
scaler = StandardScaler()
scaler.fit(x_train_raw)
x_train_raw = scaler.transform(x_train_raw)
x_test_raw = scaler.transform(x_test_raw)
# Creating Input Data with variable timesteps
x_train = np.zeros((x_train_raw.shape[0] - num_time_steps + 1, num_time_steps, num_features), dtype="float32")
x_test = np.zeros((x_test_raw.shape[0] - num_time_steps + 1, num_time_steps, num_features), dtype="float32")
for row in range(len(x_train)):
for timestep in range(num_time_steps):
x_train[row][timestep] = x_train_raw[row + timestep]
for row in range(len(x_test)):
for timestep in range(num_time_steps):
x_test[row][timestep] = x_test_raw[row + timestep]
y_train = y_train[num_time_steps - 1:]
y_test = y_test[num_time_steps - 1:]

Sequence-to-sequence classification with variable sequence lengths in Keras

I would like to train an LSTM or GRU network in TensorFlow/Keras to continuously recognize whether a user is walking or not based on input from motion sensors (accelerometer and gyroscope). I have 50 input sequences with lengths varying from 581 to 5629 time steps and 6 features and 50 corresponding output sequences of boolean values. My problem is that I don't know how to feed the training data to the fit() method.
I know approximately what I need to do: I'd like to train with 5 batches of 10 sequences each, and for each batch I have to pad all but the longest sequence so all 10 sequences have the same lengths and apply masking. I just don't know how to build the data structures. I know that I can make one big 3D tensor of size (50,5629,6) and that works, but it's painfully slow, so I'd really like to make the sequence length of each batch as small as possible.
Here's the problem in code:
import tensorflow as tf
import numpy as np
# Load data from file
x_list, y_list = loadSequences("train.csv")
# x_list is now a list of arrays (n,6) of float64, where n is the timesteps
# and 6 is the number of features, sorted by increasing sequence lengths.
# y_list is a list of arrays (n,1) of Boolean.
x_train = # WHAT DO I WRITE HERE?
y_train = # AND HERE?
model = tf.keras.models.Sequential([
tf.keras.layers.Masking(),
tf.keras.layers.LSTM(32, return_sequences=True),
tf.keras.layers.Dense(2, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train, batch_size=10, epochs=100)
You can do some thing like this
use generator function take a look at this link fit_generator look for fit_generator method.
def data_generater(batch_size):
print("reading data")
training_file = 'data_location', 'r')
# assuming data is in json format so feels free to change accordingly
training_set = json.loads(training_file.read())
training_file.close()
batch_i = 0 # Counter inside the current batch vector
batch_x = [] # The current batch's x data
batch_y = [] # The current batch's y data
while True:
for obj in training_set:
batch_x.append(your input sequences one by one)
if obj['val'] == True:
batch_y.append([1])
elif obj['val'] == False:
batch_y.append([0])
batch_i += 1
if batch_i == batch_size:
# Ready to yield the batch
# pad input to max length in the batch
batch_x = pad_txt_data(batch_x)
yield batch_x, np.array(batch_y)
batch_x = []
batch_y = []
batch_i = 0
def pad_txt_data(arr):
# expecting arr to be in the shape of (10, m, 6)
paded_arr = []
prefered_len = len(max(arr, key=len))
# Now pad all your sequences to preferred length in the batch(arr)
return np.array(paded_arr)
and in the model
model = keras.Sequential()
model.add(keras.layers.Masking(mask_value=0., input_shape=(None,6)))
model.add(keras.layers.LSTM(32))
model.add(keras.layers.Dense(1, activation="softmax"))
model.compile(optimizer="Adam", loss='categorical_crossentropy', metrics=['categorical_accuracy'])
model.fit_generator(data_generater(10), steps_per_epoch=5, epochs=10)
Batch_size, steps_per_epoch, epoch can be different.
Generally
steps_per_epoch = (number of sequences/batch_size)
Note: Form reading your description your task appears to be Binary classification problem not like an Sequence to sequence problem. A good example for sequence to sequence is a language translation. Just google around you will find what i mean.
And if you really want to see the difference in training times I suggest using a GPU if available and CuDNNLSTM.
In case it helps someone, here's how I ended up implementing a solution:
import tensorflow as tf
import numpy as np
# Load data from file
x_list, y_list = loadSequences("train.csv")
# x_list is now a list of arrays (m,n) of float64, where m is the timesteps
# and n is the number of features.
# y_list is a list of arrays (m,1) of Boolean.
assert len(x_list) == len(y_list)
num_sequences = len(x_list)
num_features = len(x_list[0][0])
batch_size = 10
batches_per_epoch = 5
assert batch_size * batches_per_epoch == num_sequences
def train_generator():
# Sort by length so the number of timesteps in each batch is minimized
x_list.sort(key=len)
y_list.sort(key=len)
# Generate batches
while True:
for b in range(batches_per_epoch):
longest_index = (b + 1) * batch_size - 1
timesteps = len(x_list[longest_index])
x_train = np.zeros((batch_size, timesteps, num_features))
y_train = np.zeros((batch_size, timesteps, 1))
for i in range(batch_size):
li = b * batch_size + i
x_train[i, 0:len(x_list[li]), :] = x_list[li]
y_train[i, 0:len(y_list[li]), 0] = y_list[li]
yield x_train, y_train
model = tf.keras.models.Sequential([
tf.keras.layers.Masking(mask_value=0., input_shape=(None,num_features)),
tf.keras.layers.LSTM(32, return_sequences=True),
tf.keras.layers.Dense(2, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit_generator(train_generator(), steps_per_epoch=batches_per_epoch, epochs=100)

Categories