-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuzoigwe_chiderah_plant_seedling_classification using Computer Vision.py
806 lines (582 loc) · 31.6 KB
/
uzoigwe_chiderah_plant_seedling_classification using Computer Vision.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
# -*- coding: utf-8 -*-
"""Uzoigwe_Chiderah_Plant_Seedling_Classification.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/1SkgV3iHTqzpbHTTcKApDqcIM4if9vadw
# Introduction to Computer Vision: Plant Seedlings Classification
## Problem Statement
### Context
In recent times, the field of agriculture has been in urgent need of modernizing, since the amount of manual work people need to put in to check if plants are growing correctly is still highly extensive. Despite several advances in agricultural technology, people working in the agricultural industry still need to have the ability to sort and recognize different plants and weeds, which takes a lot of time and effort in the long term. The potential is ripe for this trillion-dollar industry to be greatly impacted by technological innovations that cut down on the requirement for manual labor, and this is where Artificial Intelligence can actually benefit the workers in this field, as **the time and energy required to identify plant seedlings will be greatly shortened by the use of AI and Deep Learning.** The ability to do so far more efficiently and even more effectively than experienced manual labor, could lead to better crop yields, the freeing up of human inolvement for higher-order agricultural decision making, and in the long term will result in more sustainable environmental practices in agriculture as well.
### Objective
The aim of this project is to Build a Convolutional Neural Netowrk to classify plant seedlings into their respective categories.
### Data Dictionary
The Aarhus University Signal Processing group, in collaboration with the University of Southern Denmark, has recently released a dataset containing **images of unique plants belonging to 12 different species.**
- The dataset can be download from Olympus.
- The data file names are:
- images.npy
- Label.csv
- Due to the large volume of data, the images were converted to the images.npy file and the labels are also put into Labels.csv, so that you can work on the data/project seamlessly without having to worry about the high data volume.
- The goal of the project is to create a classifier capable of determining a plant's species from an image.
**List of Species**
- Black-grass
- Charlock
- Cleavers
- Common Chickweed
- Common Wheat
- Fat Hen
- Loose Silky-bent
- Maize
- Scentless Mayweed
- Shepherds Purse
- Small-flowered Cranesbill
- Sugar beet
####**Note: Please use GPU runtime to execute the code efficiently**
## Importing necessary libraries
"""
import os
import numpy as np # Importing numpy for Matrix Operations
import pandas as pd # Importing pandas to read CSV files
import matplotlib.pyplot as plt # Importting matplotlib for Plotting and visualizing images
import math # Importing math module to perform mathematical operations
import cv2 # Importing openCV for image processing
import seaborn as sns # Importing seaborn to plot graphs
# Tensorflow modules
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator # Importing the ImageDataGenerator for data augmentation
from tensorflow.keras.models import Sequential,Model # Importing the sequential module to define a sequential model
from tensorflow.keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D,BatchNormalization # Defining all the layers to build our CNN Model
from tensorflow.keras.optimizers import Adam,SGD # Importing the optimizers which can be used in our model
from sklearn import preprocessing # Importing the preprocessing module to preprocess the data
from sklearn.model_selection import train_test_split # Importing train_test_split function to split the data into train and test
from sklearn.metrics import confusion_matrix # Importing confusion_matrix to plot the confusion matrix
from sklearn.preprocessing import LabelBinarizer
# Display images using OpenCV
from google.colab.patches import cv2_imshow # Importing cv2_imshow from google.patches to display images
from sklearn.model_selection import train_test_split
from tensorflow.keras import backend
from keras.callbacks import ReduceLROnPlateau,EarlyStopping
from keras.applications.vgg16 import VGG16
import random
# Ignore warnings
import warnings
warnings.filterwarnings('ignore')
"""## Loading the dataset"""
#Connecting Google drive with Google colab
from google.colab import drive
drive.mount('/content/drive')
images = np.load('/content/drive/My Drive/images.npy')
# Load the labels file of dataset
labels = pd.read_csv('/content/drive/My Drive/Labels.csv')
"""## Data Overview
### Understand the shape of the dataset
"""
print(images.shape)
"""**Observation:**
The number of samples is 4750, indicating the quantity of images in the dataset. The height of each image is 128 pixels, representing the number of rows. The width of each image is 128 pixels, representing the number of columns. The number of channels is 3, denoting the color channels in each pixel, typically representing the Red, Green, and Blue (RGB) color channels. Each channel provides information about the intensity of a particular color.
"""
print(labels.shape)
"""There are 4750 labels"""
labels.head()
labels.info()
"""**Observation:** The output indicates that the labels DataFrame has one column named "Label," containing 4750 non-null entries with an object data type. There are no missing values in the "Label" column.Total memory usage is approximately 37.2+ MB."""
labels.describe()
"""**Observation:** The dataset does not contain any missing values. The "Label" column encompasses 12 unique values, representing different labels. Among these, "Loose Silky-bent" is the most prevalent label, appearing 654 times out of the total 4750 entries.
## Exploratory Data Analysis
- EDA is an important part of any project involving data.
- It is important to investigate and understand the data better before building a model with it.
- A few questions have been mentioned below which will help you understand the data better.
- A thorough analysis of the data, in addition to the questions mentioned below, should be done.
1. How are these different category plant images different from each other?
2. Is the dataset provided an imbalance? (Check with using bar plots)
**Visualizing images randomly from each of the classes.**
"""
def plot_images(images,labels):
num_classes=10 # Number of Classes
categories=np.unique(labels)
keys=dict(labels['Label']) # Obtaing the unique classes from y_train
rows = 3 # Defining number of rows=3
cols = 4 # Defining number of columns=4
fig = plt.figure(figsize=(10, 8)) # Defining the figure size to 10x8
for i in range(cols):
for j in range(rows):
random_index = np.random.randint(0, len(labels)) # Generating random indices from the data and plotting the images
ax = fig.add_subplot(rows, cols, i * rows + j + 1) # Adding subplots with 3 rows and 4 columns
ax.imshow(images[random_index, :]) # Plotting the image
ax.set_title(keys[random_index])
plt.show()
plot_images(images,labels)
"""**Observation:**
The images exhibit a common theme, featuring young seedlings in a setting of small granular material or rocks. Additionally, certain images include a scale, possibly depicted as a black and white barred ruler. Considering the possibility that the images might be in the BGR color space and displayed as RGB, there is a plan to convert them to assess potential improvements in visual representation. Additionally, a crucial step involves checking the dataset's balance to ensure an equitable distribution of images, enhancing the effectiveness of subsequent analyses or machine learning tasks.
### Checking the distribution of the target variable
"""
sns.countplot(x=labels['Label'])
plt.xticks(rotation='vertical')
"""**Observation:**We observe that the dataset appears to be imbalanced, with each category having different number of images.
"""
# number of labels for each plant type
labels['Label'].value_counts()
"""**observation**
Loose Silky-bent: 654
This label appears 654 times in the dataset, making it the most frequent class.
Common Chickweed: 611
This label appears 611 times, making it the second most frequent class.
Scentless Mayweed: 516
This label appears 516 times, making it the third most frequent class.
Small-flowered Cranesbill: 496
This label appears 496 times.
Fat Hen: 475
This label appears 475 times.
Charlock: 390
This label appears 390 times.
Sugar beet: 385
This label appears 385 times.
Cleavers: 287
This label appears 287 times.
Black-grass: 263
This label appears 263 times.
Shepherds Purse: 231
This label appears 231 times.
Common wheat: 221
This label appears 221 times.
Maize: 221
This label also appears 221 times.
"""
# check the percentage of each label value
for i in labels['Label'].unique():
print(i, ": ", (labels[labels['Label']==i].count()/labels.shape[0])*100, "\n")
def labeled_barplot(data, feature, perc=False, n=None):
'''
Barplot with percentage at the top
data: dataframe
feature: dataframe column
perc:
whether to display percentages instead of count (default is False)
n: displays the top n category levels (defaul is None, i.e., display all levels)
'''
total = len(data[feature]) #length of the column
count = data[feature].nunique()
if n is None:
plt.figure(figsize=(count + 2, 6))
else:
plt.figure(figsize=(n + 2, 6))
plt.xticks(rotation=90, fontsize=15)
ax = sns.countplot(
data=data,
x=feature,
palette='Paired',
order=data[feature].value_counts().index[:n]
)
for p in ax.patches:
if perc == True:
label = '{:.1f}%'.format(
100*p.get_height()/total
) # percentage of each class of the category
else:
label = p.get_height() # count of each level of the category
x = p.get_x() + p.get_width()/2 # width of the plot
y = p.get_height() # height of the plot
#annotate the percentage
ax.annotate(
label,
(x, y),
ha='center',
va='center',
size=12,
xytext=(0,5),
textcoords='offset points',
)
plt.show()
# Visualize the balance/distribution of the label values.
labeled_barplot(labels, "Label", perc=True)
"""Loose Silky-bent has the highest percentage
## Data Pre-Processing
### Convert the BGR images to RGB images.
"""
# Converting the images from BGR to RGB using cvtColor function of OpenCV
for i in range(len(images)):
images[i] = cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB)
plot_images(images,labels)
"""This coloration looks much more natural, will continue with the images converted to RGB.
### Resize the images
As the size of the images is large, it may be computationally expensive to train on these larger images; therefore, it is preferable to reduce the image size from 128 to 64.
"""
images_decreased = []
height = 64
width = 64
dimensions = (width, height)
for i in range(len(images)):
images_decreased.append(cv2.resize(images[i], dimensions, interpolation=cv2.INTER_LINEAR))
plt.imshow(images[9])
plt.imshow(images_decreased[9])
"""### Data Preparation for Modeling
- Before you proceed to build a model, you need to split the data into train, test, and validation to be able to evaluate the model that you build on the train data
- You'll have to encode categorical features and scale the pixel values.
- You will build a model using the train data and then check its performance
**Split the dataset**
"""
# Splitting the data into training and test sets
X_temp, X_test, y_temp, y_test = train_test_split(np.array(images_decreased), labels, test_size=0.1, random_state=42, stratify=labels)
# Split the remaining data into training and validation sets
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.1, random_state=42, stratify=y_temp)
print("X_train:", X_train.shape)
print("y_train:", y_train.shape)
print("X_val:", X_val.shape)
print("y_val:", y_val.shape)
print("X_test:", X_test.shape)
print("y_test:", y_test.shape)
"""### Encode the target labels"""
enc = LabelBinarizer()
y_train_encoded = enc.fit_transform(y_train)
y_val_encoded = enc.transform(y_val)
y_test_encoded = enc.transform(y_test)
print(y_train_encoded.shape, y_val_encoded.shape, y_test_encoded.shape)
"""### Data Normalization"""
# normalize the pixel values, convert from 0-255 to 0-1
X_train_normalized = X_train.astype('float32') / 255.0
X_val_normalized = X_val.astype('float32') / 255.0
X_test_normalized = X_test.astype('float32') / 255.0
"""## Model Building
### Model1
"""
# Clearing backend
backend.clear_session()
# Fixing the seed for random number generators
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
# initialize the model as sequential
model1 = Sequential()
# Adding the first conv layer with 128 filters and kernel size 3x3, padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension of images
model1.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Adding max pooling to reduce the size of the output of the first conv layer
model1.add(MaxPooling2D((2, 2), padding='same'))
# Adding two similar convolution and max-pooling layers with activation = relu
model1.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding='same'))
model1.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding='same'))
# Flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model1.add(Flatten())
# Adding a fully connected dense layer with 16 neurons
model1.add(Dense(16, activation='relu'))
model1.add(Dropout(0.3))
# Adding the output layer with 12 neurons and activation function as softmax since this is a multi-class classification problem
model1.add(Dense(12, activation='softmax'))
# Using the Adam Optimizer
opt = Adam()
# Compiling the model using suitable metric for the loss function
model1.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model1.summary()
"""<b> Fitting the model on the train data:"""
history_1 = model1.fit(
X_train_normalized, # Features of the training data
y_train_encoded, # Labels of the training data
epochs=30,
validation_data=(X_val_normalized, y_val_encoded), # Validation data
batch_size=32,
verbose=2
)
"""**Model Evaluation**"""
plt.plot(history_1.history['accuracy'])
plt.plot(history_1.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
"""The validation accuracy was found to be consistently higher than the training accuracy, this suggests that the model may not be learning as well as expected from the training data.
**Evaluate the model on test data**
"""
accuracy = model1.evaluate(X_test_normalized, y_test_encoded, verbose=2)
"""**Plotting the Confusion Matrix**"""
y_pred = model1.predict(X_test_normalized)
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg = np.argmax(y_pred, axis=1)
y_test_arg = np.argmax(y_test_encoded, axis=1)
# Plotting the Confusion Matrix using confusion_matrix() function from sklearn
conf_matrix = confusion_matrix(y_test_arg, y_pred_arg)
# Plotting the Confusion Matrix using seaborn
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
conf_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Setting the labels to both the axes
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True labels')
ax.set_title('Confusion Matrix')
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=20)
plt.show()
"""**We observe that most of the classes are predicted correctly**, but the misclassification occurs mostly with the black grass and loose silky-bent 25, and also common wheat which is sometimes confused with Loose Silky-bent 15, also the model is not predicting instances of black grass.
**Plotting Classification Report**
"""
from sklearn import metrics
# Assuming you have the predicted and true labels
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
"""**Class 0 (label 0):** This class has a precision, recall, and F1-Score of 0. The model is not predicting any instances of this class correctly. This could be due to issues such as imbalanced data or a problem with the model's ability to learn this particular class.
**Class 1 (label 1):**
The model achieved relatively high precision (0.79), recall (0.69), and F1-score (0.74) for this class.
**Class 6 (label 6):**
This class has high recall (0.94), suggesting the model is effective at capturing instances of this class. However, precision (0.58) is relatively lower, indicating a potential for false positives.
**Class 10 (label 10):**
This class has high precision (0.92), recall (0.88), and F1-score (0.90), indicating strong performance.
**Accuracy:** The overall accuracy of the model is reported as 71%, indicating the proportion of correctly classified instances out of the total.
**Conclusion:**The model appears to perform well for some classes (e.g., class 10) but struggles with others (e.g., class 0). It might be worthwhile to investigate why certain classes are more challenging for the model and consider strategies to improve performance, such as adjusting class weights, collecting more data for under-represented classes, or fine-tuning the model architecture.
## Model Performance Improvement
**Reducing the Learning Rate:**
**Hint**: Use **ReduceLRonPlateau()** function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
"""
# Code to monitor val_accuracy
learning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy',
patience=3,
verbose=1,
factor=0.5,
min_lr=0.00001)
"""### **Data Augmentation**
Remember, **data augmentation should not be used in the validation/test data set**.
"""
# Clearing backend
from tensorflow.keras import backend
backend.clear_session()
# Fixing the seed for random number generators
import random
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
train_datagen = ImageDataGenerator(
rotation_range=20,
fill_mode='nearest'
)
# Initializing a sequential model
model2 = Sequential()
# Adding the first conv layer with 64 filters and kernel size 3x3, padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension images
model2.add(Conv2D(64, (3, 3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Adding max pooling to reduce the size of the output of the first conv layer
model2.add(MaxPooling2D((2, 2), padding='same'))
model2.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model2.add(MaxPooling2D((2, 2), padding='same'))
model2.add(BatchNormalization())
# Flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model2.add(Flatten())
# Adding a fully connected dense layer with 16 neurons
model2.add(Dense(16, activation='relu'))
# Adding dropout with dropout_rate=0.3
model2.add(Dropout(0.3))
# Adding the output layer with 12 neurons and activation function as softmax since this is a multi-class classification problem
model2.add(Dense(12, activation='softmax'))
# Initializing Adam Optimizer
opt = Adam()
# Compiling the model
model2.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model2.summary()
"""<b> Fitting the model on the train data"""
# Epochs
epochs = 30
# Batch size
batch_size = 64
history_2 = model2.fit(
train_datagen.flow(X_train_normalized, y_train_encoded, batch_size=batch_size, shuffle=False),
epochs=epochs,
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
validation_data=(X_val_normalized, y_val_encoded),
verbose=1,
callbacks=[learning_rate_reduction]
)
"""**Model Evaluation**"""
plt.plot(history_2.history['accuracy'])
plt.plot(history_2.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
"""The model seems to have trained nicely, with an accuracy of 76.84 on unseen data.
**Evaluate the model on test data**
"""
accuracy = model2.evaluate(X_test_normalized, y_test_encoded, verbose=2)
"""**Plotting the Confusion Matrix**"""
y_pred = model2.predict(X_test_normalized)
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg = np.argmax(y_pred, axis=1)
y_test_arg = np.argmax(y_test_encoded, axis=1)
# Obtaining the confusion matrix using the confusion_matrix() function
confusion_matrix = tf.math.confusion_matrix(y_test_arg, y_pred_arg)
# Plotting the Confusion Matrix using seaborn
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Setting the labels to both the axes
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True labels')
ax.set_title('Confusion Matrix')
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=20)
plt.show()
"""**We observe that most of the classes are predicted correctly**, but the misclassification occurs mostly with the black grass and loose silky-bent 16, and also common wheat which is sometimes confused with Loose Silky-bent 9.
**Plotting Classification Report**
"""
# Plotting the classification report
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
"""**Observation:**
**Class 1 (label 1):**
High precision (0.92), recall (0.92), and F1-score (0.92), indicating excellent performance for this class.
**Class 3 (label 3)** also shows high precision (0.93), recall (0.90), and F1-score (0.92), suggesting strong performance.
**Class 4 (label 4)** has low precision (0.75) and recall (0.14), suggesting that the model struggles with this class, possibly due to imbalanced data or other challenges.
**Class 6 (label 6):**
Decent precision (0.64), high recall (0.82), and a balanced F1-score (0.72), suggesting effective performance with room for improvement in precision.
**Class 7 (label 7)** has high recall (0.95) but lower precision (0.64) indicating that the model is effective at capturing instances of this class but may also have false positives.
**Class 10 (label 10):**
High precision (0.91), recall (0.84), and F1-score (0.87), indicating strong performance.
The overall accuracy of the model is reported as 77%, indicating the proportion of correctly classified instances out of the total.
Model2 seems to perform better than Model1, lets see if we can improve on the model
### Model3
"""
# Clearing backend
backend.clear_session()
# Fixing the seed for random number generators
import random
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
# initialize the model as sequential
model3 = Sequential()
# start with a conv layer
model3.add(Conv2D(64, (3,3), activation='relu', padding='same', input_shape=(64,64,3)))
model3.add(MaxPooling2D(2,2))
model3.add(BatchNormalization())
model3.add(Conv2D(32, (3,3), activation='relu', padding='same'))
model3.add(MaxPooling2D(2,2))
model3.add(BatchNormalization())
model3.add(Conv2D(16, (3,3), activation='relu', padding='same'))
model3.add(MaxPooling2D(2,2))
model3.add(BatchNormalization())
# flatten
model3.add(Flatten())
# ANN layers
model3.add(Dense(64, activation='relu'))
model3.add(Dropout((0.25)))
model3.add(Dense(32, activation='relu'))
model3.add(Dropout((0.25)))
model3.add(Dense(16, activation='relu'))
#output layer
model3.add(Dense(12, activation='softmax'))
model3.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model3.summary()
"""**Reducing the Learning Rate:**
**Hint**: Use **ReduceLRonPlateau()** function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
"""
early_stop = EarlyStopping(monitor='val_loss', min_delta=0.00001, patience=5,) #restore_best_weights=True)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, mode='auto', min_delta=0.00001)
callbacks = (early_stop, reduce_lr)
history_3 = model3.fit(X_train_normalized, y_train_encoded,
validation_data=(X_val_normalized, y_val_encoded),
epochs=50,
batch_size=32,
verbose=2,
callbacks=callbacks
)
plt.plot(history_3.history['accuracy'])
plt.plot(history_3.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
accuracy = model3.evaluate(X_test_normalized, y_test_encoded, verbose=2)
y_pred4 = model3.predict(X_test_normalized)
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred4_arg=np.argmax(y_pred4,axis=1)
y_test_arg=np.argmax(y_test_encoded,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred4_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
"""**We observe that most of the classes are predicted correctly**, but the misclassification occurs mostly with the black grass and loose silky-bent 20."""
cr3 =metrics.classification_report(y_test_arg,y_pred4_arg, output_dict=True)
f1_3 = cr3['macro avg']['f1-score']
acc_3 = cr3['accuracy']
print('f1-score:',f1_3)
print('Accuracy:', acc_3)
cr3 =metrics.classification_report(y_test_arg,y_pred4_arg)
print(cr3)
"""**Observation**
**Class 1 (label 1):**
High precision (0.90), recall (0.92), and F1-score (0.91), indicating excellent performance for this class.
**Class 3 (label 3)** also shows high precision (0.92), recall (0.93), and F1-score (0.93), suggesting strong performance.
**Class 5 (label 5):**
Very high precision (0.98) and recall (0.94), resulting in an outstanding F1-score (0.96), indicating excellent performance.
**Class 6 (label 6)** has a high recall (0.88), but a slightly lower precision (0.69), leading to a balanced F1-score (0.77).
**Class 9 (label 9)** has relatively lower precision (0.85) and recall (0.48), resulting in a moderate F1-score (0.61).
**Class 10 (label 10):**
High precision (0.90), recall (0.92), and F1-score (0.91), indicating strong performance.
**Accuracy:**
The overall accuracy of the model is reported as 82%, indicating the proportion of correctly classified instances out of the total.
In summary, the classification report provides a detailed evaluation of the model's performance for each class and overall. The macro and weighted averages offer insights into the model's generalization across all classes, considering both class-wise performance and class imbalance. An overall accuracy of 82% suggests that the model is performing well.
## Final Model
**Comment:**
Model 3 has higher accuracies across all three sets, indicating better overall performance. This suggests that Model 2 is likely a more robust and effective model.
Also there is less misclassification when it comes to the labels in model 3, model 3 has strong performance for most of the classes
Training Accuracy: 0.8807
Validation Accuracy: 0.8341
Test Accuracy: 0.8232
The model demonstrates strong performance across multiple metrics (precision, recall, f1-score).
### Visualizing the prediction
"""
# Visualizing the predicted and correct label of images from test data
plt.figure(figsize=(2,2))
plt.imshow(X_test[2])
plt.show()
## Predicting the test data using the best model
print('Predicted Label', enc.inverse_transform(model3.predict((X_test_normalized[2].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', enc.inverse_transform(y_test_encoded)[2]) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2,2))
plt.imshow(X_test[33])
plt.show()
## Predicting the test data using the best model
print('Predicted Label', enc.inverse_transform(model3.predict((X_test_normalized[33].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', enc.inverse_transform(y_test_encoded)[33]) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2,2))
plt.imshow(X_test[59],)
plt.show()
## Predicting the test data using the best model
print('Predicted Label', enc.inverse_transform(model3.predict((X_test_normalized[59].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', enc.inverse_transform(y_test_encoded)[59]) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2,2))
plt.imshow(X_test[36])
plt.show()
## Predicting the test data using the best model
print('Predicted Label', enc.inverse_transform(model3.predict((X_test_normalized[36].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', enc.inverse_transform(y_test_encoded)[36]) # using inverse_transform() to get the output label from the output vector
"""## Actionable Insights and Business Recommendations
**Insights**
CNN with Batch Normalization, Maxpooling, dropouts + Dense layers is a good combination for image classification.
We have built a CNN-model to predict the class of a plant, which works quite well. (Increasing number of epochs and/or adding layers to a model can even increase the performance)
**Recommendation**
I recommend using model 3. It is accurate to between 80 and 82%, which will drastically reduce the amount of manual classification work for farmers. This model has the added benefit of requiring little image preprocessing and no costly data augmentation.
_____
"""