I'm building a ConvNet for handwritten digits recognition using the MNIST dataset. My code is written in Keras using the Theano backend.
I want to train my ConvNet so it can identify a subset of classes (for example, digits '1' and '2' only) and output any other as a generic 'unknown' class. I know this can be done on Theano since it's described on "Distributed Neural Networks for Internet of Things: The Big-Little Approach", but I can't find any documentation or examples on this subject.
Modify your objective function outputs (your Y_train) to convert 0, 1, 2, 3, 4, 5, etc., to 0, 1, 2 (where 2 represents "other"). Note that if you actually want to predict, say, digits 5 and 6, and everything else as other, you'll need to re-index your classes to start at 0, so that 0 becomes digit 5, 1 becomes digit 6, and 2 becomes "other".
Here's a modified version of the Keras MNIST example, basically straight from the repo with just a few extra lines:
'''Trains a simple convnet on the MNIST dataset.
Gets to 99.25% test accuracy after 12 epochs
(there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
import numpy as np
batch_size = 128
epochs = 1
# input image dimensions
img_rows, img_cols = 28, 28
# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
###############################
# This is the key... order is important!
y_train[y_train<=4]=2
y_train[y_train==5]=0
y_train[y_train==6]=1
y_train[y_train>=7]=2
y_test[y_test<=4]=2
y_test[y_test==5]=0
y_test[y_test==6]=1
y_test[y_test>=7]=2
num_classes=3
print(np.unique(y_train))
# [0 1 2]
###############################
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
Related
I am trying to train a network where the learning rate for each layer scales with 1/(layer width). Is there a way to do this in pytorch? I tried changing the learning rate in the optimizer and including it in my training loop but that didn't work. I've seen some people talk about this with Adam, but I am using SGD to train. Here are the chunks where I defined my model and training, if thats any help.
class ConvNet2(nn.Module):
def __init__(self):
super(ConvNet2, self).__init__()
self.network = nn.Sequential(
nn.Conv2d(3, 8, 3),
nn.ReLU(),
nn.Conv2d(8,32, 3),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Conv2d(32, 32, 3),
nn.ReLU(),
nn.Conv2d(32,32, 3),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Flatten(),
nn.Linear(800, 10)
)
def forward(self, x):
return self.network(x)
net2 = ConvNet2().to(device)
def train(network, number_of_epochs):
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(network.parameters(), lr=learning_rate)
for epoch in range(number_of_epochs): # loop over the dataset multiple times
running_loss = 0.0
for i, (inputs, labels) in enumerate(trainloader):
# get the inputs
inputs = inputs.to(device)
labels = labels.to(device)
outputs = network(inputs)
loss = criterion(outputs, labels)
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = network(inputs)
loss.backward()
optimizer.step()
In the documentation you can see that you can specify "per-parameter options". Assuming you only want to specify the learning rate for the Conv2d layers (this is easily customizable in the code below) you could do something like this:
import torch
from torch import nn
from torch import optim
from pprint import pprint
class ConvNet2(nn.Module):
def __init__(self):
super(ConvNet2, self).__init__()
self.network = nn.Sequential(
nn.Conv2d(3, 8, 3),
nn.ReLU(),
nn.Conv2d(8,32, 3),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Conv2d(32, 32, 3),
nn.ReLU(),
nn.Conv2d(32,32, 3),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Flatten(),
nn.Linear(800, 10)
)
def forward(self, x):
return self.network(x)
net2 = ConvNet2()
def getParameters(model):
getWidthConv2D = lambda layer: layer.out_channels
parameters = []
for layer in model.children():
paramdict = {'params': layer.parameters()}
if (isinstance(layer, nn.Conv2d)):
paramdict['lr'] = getWidthConv2D(layer) * 0.1 # Specify learning rate for Conv2D here
parameters.append(paramdict)
return parameters
optimizer = optim.SGD(getParameters(net2.network), lr=0.05)
print(optimizer)
You can do that by passing the relevant parameters with associated learning rates.
optimizer = optim.SGD(
[
{"params": network.layer[0].parameters(), "lr": 1e-1},
{"params": network.layer[1].parameters(), "lr": 1e-2},
...
],
lr=1e-3,
)
I tried to build a model that would help me identify images of a multi label classification problem, for example if I had pictures of cats, dogs and cows.
I ran a CNN model but it didnt catch at all (gave a precision of 33%).
Can anyone please share a model that works (even if the accuracy is just reasonable)?
Thanks in advance to everyone!
[attached a my code mentioned above]
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D,
BatchNormalization
from keras.callbacks import LearningRateScheduler
from keras.optimizers import adam, SGD
from keras.preprocessing.image import ImageDataGenerator
from keras.applications.vgg16 import VGG16
# 2 - Create network layers
image_width = 200
image_height = 200
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=(3,3),
activation='relu',input_shape=(
(image_width,image_height,3)))
model.add(BatchNormalization())
model.add(Conv2D(filters=16, kernel_size=(3,3), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPool2D(strides=(2,2)))
model.add(Dropout(0.25))
# Stage II = make it more compex with 'filters = 32'
model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu'))
model.add(BatchNormalization())
model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPool2D(strides=(2,2)))
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3, activation='softmax'))
# We'll Randomize the training set (shuffle), to avoid overfitting
(augmentation)
datagen = ImageDataGenerator(zoom_range = 0.1,
height_shift_range = 0.1,
width_shift_range = 0.1,
rotation_range = 10)
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=
['accuracy'])
# automatically retrieve images and their classes for train and validation
train_generator = datagen.flow_from_directory(
train_dataset,
target_size=(image_width, image_height),
batch_size=32,
class_mode='categorical')
validation_generator = datagen.flow_from_directory(
validation_dataset,
target_size=(image_width, image_height),
batch_size=32,
class_mode='categorical')
# Now let's fit the model on the validation set
model.fit_generator(
train_generator,
steps_per_epoch=50,
epochs=500,
validation_data=validation_generator,
validation_steps=15)
One of the problems I see in your code is that, flow_from_directory does not support multi-label classification. It will only return a single label based on the sub-directories. Link to the docs
This could be a huge problem as your model is not even performing multi-label classification.
I am trying to augment my MNIST dataset in keras but for some reason its not working. Any help will be appreciated.
Part of the code:
x_train = x_train.reshape(x_train.shape[0],28, 28,1)
x_test = x_test.reshape(x_test.shape[0],28, 28,1)
x_train = x_train.reshape(x_train.shape[0],28, 28,1)
x_test = x_test.reshape(x_test.shape[0],28, 28,1)
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2)
model.compile(loss='categorical_crossentropy',
optimizer= adam,
metrics=['accuracy'])
train_gen = datagen.flow(x_train, r_train, batch_size=batch_size)
history2 = model.fit_generator(train_gen,
steps_per_epoch=int(np.ceil(x_train.shape[0] / float(batch_size))),
epochs=epochs)
# history = model.fit(x_train, r_train,
# batch_size=batch_size,
# epochs=epochs,
# verbose=1,
# validation_data=(x_test, r_test))
score = model.evaluate(x_test, r_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
error:
ValueError: Error when checking input: expected dense_218_input to have 2 dimensions, but got array with shape (512, 28, 28, 1)
The dense_218_input should be a numpy 2d-array instead of shape: (512, 28, 28, 1). You can reshape it with numpy.reshape.
I want to save the mnist_cnn.py file from keras/examples to .json file and save it to desktop (on mac).
I know that there is a function for the keras models called to_json, but I don't know how to make it save the model as a .json file on my desktop.
Could you please help me figure this out?
'''Trains a simple convnet on the MNIST dataset.
Gets to 99.25% test accuracy after 12 epochs
(there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
batch_size = 128
num_classes = 10
epochs = 12
# input image dimensions
img_rows, img_cols = 28, 28
# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
model_json = model.to_json()
with open("model.json", "w") as json_file:
json_file.write(model_json)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
So the main lines are the last, except I don't know how to save it to desktop.
Change "model.json" to the path to your desktop directory, which is /Users/<username>/Desktop/model.json, where <username> is your username in the system.
If you run the script from the same user, "~/Desktop/model.json" should work as well.
I'd like to make a prediction for a single image with Keras. I've trained my model so I'm just loading the weights.
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
import numpy as np
import cv2
# dimensions of our images.
img_width, img_height = 150, 150
def create_model():
if K.image_data_format() == 'channels_first':
input_shape = (3, img_width, img_height)
else:
input_shape = (img_width, img_height, 3)
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
return model
img = cv2.imread('./test1/1.jpg')
model = create_model()
model.load_weights('./weight.h5')
model.predict(img)
I'm loading the image using:
img = cv2.imread('./test1/1.jpg')
And using the predict function of the model:
model.predict(img)
But I get the error:
ValueError: Error when checking : expected conv2d_1_input to have 4 dimensions, but got array with shape (499, 381, 3)
How should I proceed to have predictions on a single image ?
Since you trained your model on mini-batches, your input is a tensor of shape [batch_size, image_width, image_height, number_of_channels].
When predicting, you have to respect this shape even if you have only one image. Your input should be of shape: [1, image_width, image_height, number_of_channels].
You can do this in numpy easily. Let's say you have a single 5x5x3 image:
>>> x = np.random.randint(0,10,(5,5,3))
>>> x.shape
>>> (5, 5, 3)
>>> x = np.expand_dims(x, axis=0)
>>> x.shape
>>> (1, 5, 5, 3)
Now x is a rank 4 tensor!
Even though this doesn't solve your error, make sure and rescale your image if you have done that previously. For instance, my training generator looks like:
train_datagen = ImageDataGenerator(
rotation_range=40,
zoom_range=[0.7, 0.9],
horizontal_flip=True,
rescale=1./255
)
So when I go to predict a single image:
from PIL import Image
import numpy as np
from skimage import transform
def load(filename):
np_image = Image.open(filename)
np_image = np.array(np_image).astype('float32')/255
np_image = transform.resize(np_image, (256, 256, 3))
np_image = np.expand_dims(np_image, axis=0)
return np_image
image = load('my_file.jpg')
model.predict(image)
I have to also rescale it by 255.
You can load the image with desired width and height, convert it to a numpy array with the shape of (image_width, image_height, number_of_channels) and then change the shape of the array to (1, image_width, image_height, number_of_channels). (batch_size =1)
import numpy as np
from keras.preprocessing import image
img_width, img_height = 150, 150
img = image.load_img('image_path/image_name.jpg', target_size = (img_width, img_height))
img = image.img_to_array(img)
img = np.expand_dims(img, axis = 0)
model.predict(img)
single_test = model.predict(np.expand_dims(X_test[i], axis=0))
try:
model.predict(img[None,...])