Hole in the Wall — computer vision-based method for defect detection



Original Source Here

Work Accomplished

Data Preparation

I will be using a dataset that contains images of various concrete surfaces with and without crack from www.kaggle.com. The image data are organized by folders with one folder for each class — into Negative (without crack) and Positive (with crack) for image classification. Later, the dataset will be train-test 80/20 split using the SubsetRandomSampler into a training and testing set before training ML model. The code also converts the image into Pytorch Tensor. A tensor is a container which can house data in N dimensions.

train_dir = 'data/train'#Converts PIL Image in the range [0, 255] to a torch.FloatTensor of #shape (C x H x W) in the range [0.0, 1.0]train_data = datasets.ImageFolder(train_dir,transform=transforms.ToTensor())#Split into 80/20 train and test datasettrain_test_split = 0.2
count_train = len(train_data)
indexes = list(range(count_train))
split = int(np.floor(train_test_split * count_train))
# SubsetRandomSampler takes as input the indices of data, then pass #the samplers to our dataloadernp.random.seed(1337)
np.random.shuffle(indexes)
train_index, test_index = indexes[split:], indexes[:split]
train_sampler = SubsetRandomSampler(train_index)
test_sampler = SubsetRandomSampler(test_index)
trainloader = torch.utils.data.DataLoader(train_data, sampler=train_sampler, batch_size=32)
testloader = torch.utils.data.DataLoader(train_data, sampler=test_sampler, batch_size=32)

Modelling

I am going to use a machine learning method called Transfer Learning where a model developed for a task is reused as the starting point for a model on a second task. I load a pretrained model ResNet 50. ResNet-50 is a convolutional neural network that is 50 layers deep. The network trained on more than a million images from the ImageNet database. I will change the final output layer of ResNet50 Model, including defining activation functions, loss function and optimizer. My output final layers will train with concrete surface images for my own image classification modelling problem.

#check for the GPU availability and load a pretrained model.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = models.resnet50(pretrained=True)
#freeze the pre-trained layers, so don’t backprop through them during trainingfor param in model.parameters():
param.requires_grad = False
#Change the final layers of ResNet50 Model for Transfer Learningmodel.fc = nn.Sequential(nn.Linear(2048, 512),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(512, 10),
nn.LogSoftmax(dim=1))
#create the criterion (the loss function)
#and pick an optimizer (Adam in this case) and learning rate.
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.fc.parameters(), lr=0.003)
model.to(device)

When train the model, the batches of images are fed as input to the network by calling the model( ) function, then, compute the loss function, and use the optimizer to apply gradient descent in back-propagation to finally get the minimum loss or error. Within the same piece of code below, while training is running, the ML model is tested every 10 batches.

The code also calculates the losses and accuracy as well. The model has achieved the low losses and good accuracy.

#load the batches of images and do the feed forward loop. 
#Then calculate the loss function, and use the optimizer to apply #gradient descent in back-propagation.
epochs = 1
steps = 0
running_loss = 0
print_every = 10
train_losses, test_losses = [], []
for epoch in range(epochs):
for inputs, labels in trainloader:
steps += 1

# get the inputs
inputs, labels = inputs.to(device), labels.to(device)

# zero the parameter gradients
optimizer.zero_grad()

#feed input to the network
logps = model(inputs)

# measure value between n elements in the input and output
loss = criterion(logps, labels)

#compute gradient of loss all the parameters
#store them in parameter.grad attribute for every parameter.
loss.backward()

#update parameters to finally get the minimum loss(error).
optimizer.step()

# print statistics
running_loss += loss.item()
if steps % print_every == 0:
test_loss = 0
accuracy = 0
# switch to eval/test mode
model.eval()

# Turn off gradients to speed up this part
with torch.no_grad():
for inputs, labels in testloader:
inputs, labels = inputs.to(device),labels.to(device)
logps = model(inputs)
batch_loss = criterion(logps, labels)
test_loss += batch_loss.item()

ps = torch.exp(logps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class == labels.view(*top_class.shape)
accuracy += torch.mean(equals.type(torch.FloatTensor)).item()
train_losses.append(running_loss/len(trainloader))
test_losses.append(test_loss/len(testloader))
print("Train loss: " + str(round(running_loss/print_every,3)) + '..' + "Test loss: " + str(round(test_loss/len(testloader),3)) + '..' + "Test accuracy: " + str(round(accuracy/len(testloader),3)))

running_loss = 0

#turn back to training mode after eval step:
model.train()

Once training is done and the model named “cracksmodel.pth” is saved for later predictions.

torch.save(model, 'cracksmodel.pth')

Evaluation

Now I am going to introduce a new set of images which are unseen by the trained ML model (‘cracksmodel.pth’). The user defined function “get_random_images” will picks 10 random images from data/eval folder, then “predict_image” transforms each image and use the trained model to makes a prediction.

def predict_image(image):
test_transforms = transforms.Compose([transforms.ToTensor(),])
image_tensor = test_transforms(image).float()
image_tensor = image_tensor.unsqueeze_(0)
input = Variable(image_tensor)
input = input.to(device)
output = model(input)
index = output.data.cpu().numpy().argmax()
return index
def get_random_images(num):val_indexes = list(range(len(val_data)))
np.random.shuffle(val_indexes)
val_idx = val_indexes[:num]
from torch.utils.data.sampler import SubsetRandomSampler
sampler = SubsetRandomSampler(val_idx)
loader = torch.utils.data.DataLoader(val_data,sampler=sampler, batch_size=num)
dataiter = iter(loader)
images, labels = dataiter.next()
return images, labels
from torch.autograd import Variableval_dir = 'data/eval'
val_data = datasets.ImageFolder(val_dir, transform=transforms.ToTensor())
classes = val_data.classes
to_pil = transforms.ToPILImage()
images, labels = get_random_images(10)
fig=plt.figure(figsize=(20,10))
for i in range(len(images)):
image = to_pil(images[i])
index = predict_image(image)
sub = fig.add_subplot(1, len(images), i+1)
res = int(labels[i]) == index
sub.set_title(str(classes[index]) + ":" + str(res))
plt.axis('off')
plt.imshow(image)
plt.show()

AI/ML

Trending AI/ML Article Identified & Digested via Granola by Ramsey Elbasheer; a Machine-Driven RSS Bot

%d bloggers like this: