Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YOLOv5 Classification Model Training Metrics #11509

Closed
1 task done
Pop469 opened this issue May 10, 2023 · 39 comments
Closed
1 task done

YOLOv5 Classification Model Training Metrics #11509

Pop469 opened this issue May 10, 2023 · 39 comments
Labels
question Further information is requested Stale

Comments

@Pop469
Copy link

Pop469 commented May 10, 2023

Search before asking

Question

Hello, I am unable to find the confusion matrix and other metrics similar to the Detection Model. In the detection model, there are these files as metrics:

image

However, in my runs/train-cls/exp folder, there are only these files:

image

I found out that in the Detection model, there are usage of metrics from utils, but not in Classification model. Is that a yet-to-implement feature?

Additional

No response

@Pop469 Pop469 added the question Further information is requested label May 10, 2023
@glenn-jocher
Copy link
Member

@Pop469 hello, thank you for reaching out. The metrics you are referring to are specific to the detection model in YOLOv5. The classification model does not currently have the same metrics implemented. However, you can still evaluate the performance of the classification model using the standard metrics for classification tasks, such as accuracy, precision, recall, and F1 score. I hope that answers your question. Let us know if you have any further queries.

@Pop469
Copy link
Author

Pop469 commented May 10, 2023

@glenn-jocher Thanks for replying! May I know how can I access the metrics you stated like accuracy, precision, recall, and F1 score? I am only able to get these:

image
Train loss, Validation loss, Top 1 accuracy, and Top 5 accuracy of all epochs from classify/train.py

image
Top 1 accuracy, top 5 accuracy of all classes from classify/val.py

For your info, runs/val-cls/exp are empty, thus I am in a lost finding the metrics that you have mentioned. Please advise on how can I get those metrics, thanks!

Edit: In addition, I also think CometML logging was not implemented for Classification, as I am unable to get logs in the Comet.

@glenn-jocher
Copy link
Member

Hello @Pop469, to get the standard classification metrics such as accuracy, precision, recall, and F1 score, you can use external libraries such as scikit-learn or PyTorch's built-in functions. For example, after training your model on some validation dataset (using val.py), you can make predictions on the same validation dataset and compute the accuracy using scikit-learn's accuracy_score() function. Similarly, PyTorch has functions like precision_score, recall_score, and f1_score to compute precision, recall, and F1 score, respectively.

Regarding CometML logging, you are correct that it is not implemented for Classification yet. We plan to add CometML support for Classification in future releases.

Regarding the empty runs/val-cls/exp folder, it is expected to be empty unless you manually save some results to it using --save-json or --save-txt arguments during training.

I hope that helps, let me know if you have any further questions.

@Pop469
Copy link
Author

Pop469 commented May 10, 2023

@glenn-jocher Well understood, thank you for your explanation!

@Pop469 Pop469 closed this as completed May 10, 2023
@Pop469 Pop469 reopened this May 10, 2023
@Pop469
Copy link
Author

Pop469 commented May 10, 2023

@glenn-jocher Hi again, how do I call the PyTorch functions? I'm having issue with both TorchMetrics' library (issue with dimensions) and Scikit's library (requires numpy instead of tensors).

I'm not sure if I'm doing it correctly or not, but I am editing the classify/val.py file to implement these computations. Please advise, thanks!

@glenn-jocher
Copy link
Member

Hello @Pop469, to compute the PyTorch metrics, you need to first convert your tensors to NumPy arrays. You can do this using the detach() method to break the Tensor's computational graph, followed by the cpu() method to move the tensor to CPU (if it's not already there), and then finally calling .numpy(). An example of computing the accuracy of a classification model using scikit-learn would look like this:

from sklearn.metrics import accuracy_score

# assume model is your trained PyTorch model and val_loader is the validation data loader
model.eval()
y_true = []
y_pred = []

with torch.no_grad():
    for batch, labels in val_loader:
        batch = batch.to(device)
        labels = labels.to(device)
        outputs = model(batch)
        _, predicted = torch.max(outputs.data, 1)
        y_pred.extend(predicted.detach().cpu().numpy())
        y_true.extend(labels.detach().cpu().numpy())

accuracy = accuracy_score(y_true, y_pred)
print('Accuracy:', accuracy)

Similarly, you can use PyTorch's built-in precision_score, recall_score and f1_score functions to compute precision, recall, and F1 score, respectively, on your NumPy arrays. I hope that helps. Let me know if you have any further questions or issues.

@Pop469
Copy link
Author

Pop469 commented May 10, 2023

@glenn-jocher Understood. Is there any functions to calculate average precision, or even mean average precision for the classification model?

@glenn-jocher
Copy link
Member

Hello @Pop469, to compute the average precision (AP) for a classification model in PyTorch, you can use the torchmetrics library. Specifically, you can use the AP metric provided by the library. Here's an example of how to use torchmetrics.AP:

import torchmetrics

model.eval()
device = 'cuda' if torch.cuda.is_available() else 'cpu'

ap = torchmetrics.AveragePrecision(num_classes)

with torch.no_grad():
    for batch, labels in val_loader:
        batch = batch.to(device)
        labels = labels.to(device)
        outputs = model(batch)
        probabilities = torch.softmax(outputs, dim=1)
        ap(probabilities, labels)
        
print('Mean AP:', ap.compute())

This should give you the mean AP for all classes. Let me know if that helps or if you have any further questions.

@Pop469
Copy link
Author

Pop469 commented May 11, 2023

@glenn-jocher gotcha! Only issue is that AveragePrecision requires task argument, but that was fixed by adding task=Multiclass into the argument.

Last question, how do you get train loss and validation loss of best.pt? I'm thinking to read from the train.py runs, but I'm not sure which epoch was selected to be the best fitness.

@glenn-jocher
Copy link
Member

Hello @Pop469, glad to hear that adding task=Multiclass solved the issue with AveragePrecision.

Regarding your last question, the epoch number with the best validation loss is saved in the results.txt file in the training runs folder. You can find the corresponding training loss and validation loss for that epoch in the train_batch_*.txt and val.txt files respectively.

You can use the following code snippet to find the epoch number with the best validation loss and the corresponding training and validation loss values:

# assume your runs folder is located at '/path/to/runs'
results_file = '/path/to/runs/train.txt'

with open(results_file, 'r') as f:
    lines = f.readlines()
    
best_val_loss = float('inf')
best_epoch = -1

for i, line in enumerate(lines):
    # get validation loss for current epoch
    val_loss = float(line.split()[1])
    
    # if validation loss is better than current best, update
    if val_loss < best_val_loss:
        best_val_loss = val_loss
        best_epoch = i + 1  # epoch numbers start from 1, not 0
        
# best_epoch is the epoch number with lowest validation loss
print(f'Epoch {best_epoch} had the best validation loss: {best_val_loss:.4f}')

# read corresponding train and val loss values for best_epoch
train_file = f'/path/to/runs/train_results_epoch{best_epoch}.txt'
val_file = f'/path/to/runs/val_results.txt'
    
with open(val_file, 'r') as f:
    val_lines = f.readlines()
    val_loss = float(val_lines[best_epoch-1].split()[1])

with open(train_file, 'r') as f:
    train_lines = f.readlines()
    train_loss = float(train_lines[-1].split()[1])
    
print(f'Train loss: {train_loss:.4f}, Validation loss: {val_loss:.4f}')

Let me know if this helps or if you have any further questions.

@Pop469
Copy link
Author

Pop469 commented May 15, 2023

@glenn-jocher I forgot to update, that worked well, thanks! Will close this issue now.

@Pop469 Pop469 closed this as completed May 15, 2023
@glenn-jocher
Copy link
Member

@Pop469 you're welcome! I'm glad to hear that the solution worked for you. If you have any further questions or issues, don't hesitate to open a new issue. Have a great day!

@Pop469
Copy link
Author

Pop469 commented May 17, 2023

Hi, sorry for reopening the issue, but I saw that classification model records the best epoch as best.pt. However, when validating them, models (best.pt) that were trained for (lets say) 50 epoch is performing significantly weaker than 25 epochs. Is there any early stopping mechanism implemented in YOLOv5-cls?

Another question is what are the learning rate scheduler in the classification model?

@Pop469 Pop469 reopened this May 17, 2023
@glenn-jocher
Copy link
Member

@Pop469 hello, thanks for reaching back to us. The train.py script for YOLOv5-cls supports various learning rate scheduling techniques including "step", "cosine", and "polynomial". You can set these using the --lr-scheduler flag when running the script. Early stopping is not currently implemented in YOLOv5-cls. However, it is easy to implement early stopping on your own. You can use the training and validation loss values to monitor the performance of the model and stop training when the validation loss starts increasing. Here's a simple example of how you can implement early stopping:

from itertools import tee

def pairwise(iterable):
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

model = ...  # your PyTorch model
train_loader = ...  # your PyTorch DataLoader
val_loader = ...  # your PyTorch DataLoader
n_epochs = ...  # number of epochs to run for
patience = ...  # number of epochs with no improvement after which training will be stopped

best_val_loss = float('inf')
best_state_dict = None

for epoch in range(n_epochs):
    # train the model for one epoch
    train_one_epoch(...)
    
    # evaluate the model on validation set
    model.eval()
    val_loss = 0
    
    with torch.no_grad():
        for images, targets in val_loader:
            images = images.to(device)
            targets = [target.to(device) for target in targets]
            loss_dict = model(images, targets)
            losses = sum(loss for loss in loss_dict.values())
            val_loss += losses.cpu().item()
            
        val_loss /= len(val_loader)
        
    print(f'Epoch {epoch}: Validation loss: {val_loss:.4f}')
    
    # check if validation loss has improved
    if val_loss < best_val_loss:
        best_val_loss = val_loss
        best_state_dict = model.state_dict()
        
    # check if training should be stopped
    if epoch == 0:
        continue  # don't stop early on first epoch
        
    if all(val_loss >= vl for vl in val_losses[-patience:]):
        print(f'Early stopping: validation loss did not improve for {patience} epochs')
        break
        
    val_losses.append(val_loss)
    
# load the best model state dict
model.load

@Pop469
Copy link
Author

Pop469 commented May 22, 2023

Hello, @glenn-jocher.

I think --lr-scheduler flag has yet to be implemented in classification model. Can you point out where and how should I add it into classify/train.py?

@glenn-jocher
Copy link
Member

Hello @Pop469, to set the learning rate scheduler for YOLOv5 classification model in train.py, you can use the --lr-scheduler command line argument followed by the type of scheduler to use. Here's an example of how you can add the --lr-scheduler flag to train.py and set it to use the cosine scheduler:

python train.py --img 640 --batch 16 --epochs 50 --data path/to/data.yaml --cfg path/to/yolov5s.yaml --weights '' --name my_experiment --lr-scheduler cosine

This will use the cosine learning rate scheduler. You can use other supported learning rate scheduling techniques like step, polynomial, etc., by replacing cosine with the name of the scheduler you want to use.

Alternatively, you can also modify the train.py script to set the learning rate scheduling technique in code. To do this, you can modify the torch.optim.lr_scheduler object after initializing the optimizer. Here's an example:

optimizer = torch.optim.SGD(model.parameters(), lr=lr, momentum=momentum, weight_decay=weight_decay)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)

This will set the learning rate scheduler to the cosine annealing scheduler with T_max set to epochs. You can modify this to use other learning rate scheduling techniques supported by PyTorch. Let me know if you have any further questions or issues.

@github-actions
Copy link
Contributor

👋 Hello there! We wanted to give you a friendly reminder that this issue has not had any recent activity and may be closed soon, but don't worry - you can always reopen it if needed. If you still have any questions or concerns, please feel free to let us know how we can help.

For additional resources and information, please see the links below:

Feel free to inform us of any other issues you discover or feature requests that come to mind in the future. Pull Requests (PRs) are also always welcomed!

Thank you for your contributions to YOLO 🚀 and Vision AI ⭐

@github-actions github-actions bot added the Stale label Jun 22, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jul 2, 2023
@TeenaJO
Copy link

TeenaJO commented Aug 26, 2023

hello @glenn-jocher ,can we perform model ensembling for yolov5 classification models? I have 2 classsifcation models and i want the output to be the predictions from both the models

@glenn-jocher
Copy link
Member

@TeenaJO hello! Yes, it is indeed possible to perform model ensembling with YOLOv5 classification models. You can combine the predictions from two classification models by averaging their output probabilities or by using more advanced techniques like weighted averaging or majority voting. By combining the predictions from multiple models, you can potentially improve the overall accuracy and robustness of your classification system. Let me know if you have any further questions or need assistance with implementing model ensembling.

@TeenaJO
Copy link

TeenaJO commented Aug 31, 2023

Is model ensembling possible for yolov8 classification,can u also provide the code for that.I couldnt find any sources for that

@glenn-jocher
Copy link
Member

@TeenaJO yes, model ensembling is possible for YOLOv5 classification as well. You can combine the predictions from multiple classification models by averaging their output probabilities or using more advanced techniques like weighted averaging or majority voting. Unfortunately, YOLOv5 does not have built-in support for model ensembling. However, you can achieve it by loading the weights of multiple models, forwarding the images through each model, and then combining the predictions. Here's an example of how you can implement model ensembling:

import torch
from torchvision.transforms import functional as F
from PIL import Image

# Load the weights of multiple models
model1 = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/model1.pt')
model2 = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/model2.pt')

# Set the models to evaluation mode
model1.eval()
model2.eval()

# Load and preprocess an image
image_path = 'path/to/image.jpg'
image = Image.open(image_path)
image_tensor = F.to_tensor(image)
image_tensor = image_tensor.unsqueeze(0)

# Forward the image through each model
with torch.no_grad():
    output1 = model1(image_tensor)
    output2 = model2(image_tensor)

# Combine the predictions
ensemble_output = (output1 + output2) / 2

# Retrieve the predicted class
predicted_class = ensemble_output.argmax(dim=1).item()

print(f"The predicted class is {predicted_class}")

Please note that you need to replace 'path/to/model1.pt' and 'path/to/model2.pt' with the actual paths to your model weights. Also, make sure to install the required dependencies and have the YOLOv5 repository cloned or available in your project.

Let me know if you have any further questions or need more assistance with model ensembling.

@saikrishna0203
Copy link

@glenn-jocher please give me full code with detailed explaination for finding accuracy f1 square etc in classification model,i didnt understand your code above please help

@glenn-jocher
Copy link
Member

@saikrishna0203 hello,

To calculate accuracy, F1 score, and other metrics in a YOLOv5 classification model, you can use the following steps:

  1. Load the trained model:
model = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/model.pt')
  1. Set the model to evaluation mode:
model.eval()
  1. Load your validation dataset and create a DataLoader:
# Assuming you have a validation dataset called validation_dataset
val_loader = torch.utils.data.DataLoader(validation_dataset, batch_size=batch_size, shuffle=False)
  1. Perform inference on the validation dataset and calculate the metrics:
num_classes = len(validation_dataset.classes)
confusion_matrix = torch.zeros(num_classes, num_classes)

with torch.no_grad():
    for images, targets in val_loader:
        images = images.to(device)
        targets = targets.to(device)

        # Forward pass
        outputs = model(images)

        # Calculate metrics
        predictions = outputs.argmax(dim=1)
        labels = targets
        accuracy = (predictions == labels).sum().item() / labels.numel()
        
        # Update confusion matrix
        for t, p in zip(labels.view(-1), predictions.view(-1)):
            confusion_matrix[t.long(), p.long()] += 1

    # Calculate F1 score
    tp = confusion_matrix.diag()
    fp = confusion_matrix.sum(dim=0) - tp
    fn = confusion_matrix.sum(dim=1) - tp
    tn = confusion_matrix.sum() - (tp + fp + fn)
    
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    f1_score = 2 * (precision * recall) / (precision + recall)

In the above code, you need to replace 'path/to/model.pt' with the actual path to your trained model weights. It assumes that you have a validation dataset and it has the classes attribute that contains the list of class names.

Please note that this is just a basic example, and you may need to modify it according to your specific requirements. Additionally, make sure to import the necessary libraries and set the appropriate device before running the code.

Let me know if you need any further clarification or have additional questions.

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 yes, the code provided in my previous response is separate from YOLOv8. The code example I provided is specifically for YOLOv5.

To calculate accuracy, F1 score, and other metrics in a YOLOv8 classification model, you can follow similar steps. Here's a modified version of the code:

  1. Load the trained model:
model = torch.hub.load('ultralytics/yolov5', 'custom', path='path/to/model.pt')
  1. Set the model to evaluation mode:
model.eval()
  1. Load your validation dataset and create a DataLoader:
# Assuming you have a validation dataset called validation_dataset
val_loader = torch.utils.data.DataLoader(validation_dataset, batch_size=batch_size, shuffle=False)
  1. Perform inference on the validation dataset and calculate the metrics:
num_classes = len(validation_dataset.classes)
confusion_matrix = torch.zeros(num_classes, num_classes)

with torch.no_grad():
    for images, targets in val_loader:
        images = images.to(device)
        targets = targets.to(device)

        # Forward pass
        outputs = model(images)

        # Calculate metrics
        predictions = outputs.argmax(dim=1)
        labels = targets
        accuracy = (predictions == labels).sum().item() / labels.numel()
        
        # Update confusion matrix
        for t, p in zip(labels.view(-1), predictions.view(-1)):
            confusion_matrix[t.long(), p.long()] += 1

    # Calculate F1 score
    tp = confusion_matrix.diag()
    fp = confusion_matrix.sum(dim=0) - tp
    fn = confusion_matrix.sum(dim=1) - tp
    tn = confusion_matrix.sum() - (tp + fp + fn)
    
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    f1_score = 2 * (precision * recall) / (precision + recall)

In the above code, you need to replace 'path/to/model.pt' with the actual path to your YOLOv8 trained model weights. It assumes that you have a validation dataset and it has the classes attribute that contains the list of class names.

Please note that this is just a basic example, and you may need to modify it according to your specific requirements. Additionally, make sure to import the necessary libraries

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 to modify the number of epochs in YOLOv8, you need to locate the training script or code that specifies the number of epochs. Look for a line of code that sets the value of the epochs parameter and change it to the desired value. Typically, the epochs parameter is either passed as an argument when running the training script or set directly in the code. You can refer to the specific documentation or codebase you are using for YOLOv8 to find the exact location to make this change.

Regarding the number of classes, you mentioned that it is currently set to 10 but you want it to be 2. To change the number of classes, you need to modify the relevant part of your code where the model is defined or instantiated. Look for a line of code that sets the value of the num_classes parameter and change it to 2. This will ensure that the model is configured to correctly recognize and classify your 2 classes.

I hope this helps! Let me know if you have any further questions.

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 hi saikrishna0203,

Unfortunately, I'm unable to provide you with the full code for YOLOv8 as it is a separate model from YOLOv5. YOLOv8 is not an official version of YOLO, and it may not have a well-defined codebase like YOLOv5.

However, you can try searching for open-source implementations of YOLOv8 on GitHub or other code repositories. You may find some examples that you can use as a starting point for your project. Make sure to follow the instructions provided in the repository and adapt the code to your specific requirements.

If you encounter any specific errors or issues while working with YOLOv8, feel free to ask for assistance. I'll do my best to help you resolve them.

Best regards,

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 thank you for your suggestion to change the metrics.py of YOLOv8. However, please note that YOLOv8 is not an official version of YOLO, and it may not have a well-defined codebase like YOLOv5. Unfortunately, I cannot provide you with the specific code changes for YOLOv8 as it is not readily available.

I recommend searching for open-source implementations of YOLOv8 on GitHub or other code repositories. There might be examples or repositories that provide the specific modifications you are looking for. You can use those as a starting point for your project.

If you have any other questions or need further assistance, please let me know. I'll be happy to help to the best of my abilities.

Thank you.

@saikrishna0203
Copy link

saikrishna0203 commented Oct 16, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 thank you for your inquiry about modifications to the metrics.py file in YOLOv8. However, please note that YOLOv8 is not an official version of YOLO, and it may not have a well-defined codebase like YOLOv5.

I recommend searching for open-source implementations of YOLOv8 on GitHub or other code repositories. You may find examples or repositories that provide the specific modifications you are looking for. These can serve as a useful starting point for your project.

If you have any other questions or need further assistance, please feel free to ask. I'm here to help to the best of my abilities.

Thank you.

@saikrishna0203
Copy link

saikrishna0203 commented Nov 25, 2023 via email

@glenn-jocher
Copy link
Member

@saikrishna0203 you want to hide the confidence scores during object detection in YOLOv8 and only display the object names. In YOLOv8 or any similar object detection model, this can usually be achieved by adjusting the visualization code to only display the class names without the associated confidence scores. The specifics of how to achieve this depend on the visualization implementation within the codebase you are using.

If you are using a specific YOLOv8 implementation, I recommend checking the visualization or inference code where the bounding boxes and labels are drawn on the images. Look for the part of the code that displays the confidence scores and modify it to only show the class names.

If you encounter any specific challenges or need further guidance, please feel free to ask for help.

I hope this information is helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested Stale
Projects
None yet
Development

No branches or pull requests

4 participants