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

Problem of replacing Conv with LightConv in yolov8.yaml #12803

Open
1 task done
Malvinlam opened this issue May 18, 2024 · 6 comments
Open
1 task done

Problem of replacing Conv with LightConv in yolov8.yaml #12803

Malvinlam opened this issue May 18, 2024 · 6 comments
Labels
question Further information is requested

Comments

@Malvinlam
Copy link

Search before asking

Question

I was going to train a Yolov8 model by using LightConv as feature extractor, but I can't seem to proceed of building the model, similar attempt is fine for the case of DWConv. The code and output on Colab is as follows:

Additional

Custom YOLO model initialization

model = YOLO("yolov8n.yaml")

Check the model architecture

print(model)

Output:

KeyError Traceback (most recent call last)
in <cell line: 2>()
1 # Custom YOLO model initialization
----> 2 model = YOLO("yolov8n.yaml")
3
4 # Check the model architecture
5 print(model)

4 frames
/usr/local/lib/python3.10/dist-packages/ultralytics/models/yolo/model.py in init(self, model, task, verbose)
21 else:
22 # Continue with default YOLO initialization
---> 23 super().init(model=model, task=task, verbose=verbose)
24
25 @Property

/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py in init(self, model, task, verbose)
148 # Load or create new YOLO model
149 if Path(model).suffix in {".yaml", ".yml"}:
--> 150 self._new(model, task=task, verbose=verbose)
151 else:
152 self._load(model, task=task)

/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py in _new(self, cfg, task, model, verbose)
217 self.cfg = cfg
218 self.task = task or guess_model_task(cfg_dict)
--> 219 self.model = (model or self._smart_load("model"))(cfg_dict, verbose=verbose and RANK == -1) # build model
220 self.overrides["model"] = self.cfg
221 self.overrides["task"] = self.task

/usr/local/lib/python3.10/dist-packages/ultralytics/nn/tasks.py in init(self, cfg, ch, nc, verbose)
285 if nc and nc != self.yaml["nc"]:
286 LOGGER.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
--> 287 self.yaml["nc"] = nc # override YAML value
288 self.model, self.save = parse_model(deepcopy(self.yaml), ch=ch, verbose=verbose) # model, savelist
289 self.names = {i: f"{i}" for i in range(self.yaml["nc"])} # default names dict

/usr/local/lib/python3.10/dist-packages/ultralytics/nn/tasks.py in parse_model(d, ch, verbose)
853 ch = [ch]
854 layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out
--> 855 for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]): # from, number, module, args
856 m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m] # get module
857 for j, a in enumerate(args):

KeyError: 'LightConv'

@Malvinlam Malvinlam added the question Further information is requested label May 18, 2024
Copy link

👋 Hello @Malvinlam, thank you for your interest in Ultralytics YOLOv8 🚀! We recommend a visit to the Docs for new users where you can find many Python and CLI usage examples and where many of the most common questions may already be answered.

If this is a 🐛 Bug Report, please provide a minimum reproducible example to help us debug it.

If this is a custom training ❓ Question, please provide as much information as possible, including dataset image examples and training logs, and verify you are following our Tips for Best Training Results.

Join the vibrant Ultralytics Discord 🎧 community for real-time conversations and collaborations. This platform offers a perfect space to inquire, showcase your work, and connect with fellow Ultralytics users.

Install

Pip install the ultralytics package including all requirements in a Python>=3.8 environment with PyTorch>=1.8.

pip install ultralytics

Environments

YOLOv8 may be run in any of the following up-to-date verified environments (with all dependencies including CUDA/CUDNN, Python and PyTorch preinstalled):

Status

Ultralytics CI

If this badge is green, all Ultralytics CI tests are currently passing. CI tests verify correct operation of all YOLOv8 Modes and Tasks on macOS, Windows, and Ubuntu every 24 hours and on every commit.

@glenn-jocher
Copy link
Member

Hello,

Thanks for reaching out with your issue! It looks like the error you're experiencing with the KeyError is because LightConv isn't recognized within the YOLOv8 model definitions you are using.

If LightConv is not a predefined module in the Ultralytics framework, you would need to define this module yourself within your custom .yaml configuration. Here's a quick example of how you might define a custom module:

# Define in your yolov8n.yaml
backbone:
  # Existing layers
  ...
  - [from, number, LightConv, args]  # Ensure LightConv is properly defined in your code
  ...

And make sure to implement LightConv in Python:

import torch
from torch import nn

class LightConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride):
        super(LightConv, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride)
        self.activ = nn.ReLU()

    def forward(self, x):
        return self.activ(self.conv(x))

# Register the module
torch.nn.LightConv = LightConv

Ensure that this custom module is correctly recognized and placed within your project structure. This will allow Ultralytics YOLO to load and use LightConv correctly in your model configuration.

Please double-check that you integrate your custom layers properly and feel free to update here if there are more issues or concerns!

@Malvinlam
Copy link
Author

I tried to add the LightConv implementation as you mentioned in yolov8.yaml and conv.py. But I still get error and cannot declare the model.
I noticed that in conv.py the LightConv was already declared. But seems like in task.py the LightConv is not declared.
Here is what I get when pasting the implementation in yolov8.yaml:

---------------------------------------------------------------------------
ScannerError                              Traceback (most recent call last)
[<ipython-input-8-36e1cb01366a>](https://localhost:8080/#) in <cell line: 2>()
      1 # Custom YOLO model initialization
----> 2 model = YOLO("yolov8n.yaml")
      3 
      4 # Check the model architecture
      5 print(model)

18 frames
[/usr/local/lib/python3.10/dist-packages/ultralytics/models/yolo/model.py](https://localhost:8080/#) in __init__(self, model, task, verbose)
     21         else:
     22             # Continue with default YOLO initialization
---> 23             super().__init__(model=model, task=task, verbose=verbose)
     24 
     25     @property

[/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py](https://localhost:8080/#) in __init__(self, model, task, verbose)
    148         # Load or create new YOLO model
    149         if Path(model).suffix in {".yaml", ".yml"}:
--> 150             self._new(model, task=task, verbose=verbose)
    151         else:
    152             self._load(model, task=task)

[/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py](https://localhost:8080/#) in _new(self, cfg, task, model, verbose)
    214             verbose (bool): display model info on load
    215         """
--> 216         cfg_dict = yaml_model_load(cfg)
    217         self.cfg = cfg
    218         self.task = task or guess_model_task(cfg_dict)

[/usr/local/lib/python3.10/dist-packages/ultralytics/nn/tasks.py](https://localhost:8080/#) in yaml_model_load(path)
    955     unified_path = re.sub(r"(\d+)([nslmx])(.+)?$", r"\1\3", str(path))  # i.e. yolov8x.yaml -> yolov8.yaml
    956     yaml_file = check_yaml(unified_path, hard=False) or check_yaml(path)
--> 957     d = yaml_load(yaml_file)  # model dict
    958     d["scale"] = guess_model_scale(path)
    959     d["yaml_file"] = str(path)

[/usr/local/lib/python3.10/dist-packages/ultralytics/utils/__init__.py](https://localhost:8080/#) in yaml_load(file, append_filename)
    379 
    380         # Add YAML filename to dict and return
--> 381         data = yaml.safe_load(s) or {}  # always return a dict (yaml.safe_load() may return None for empty files)
    382         if append_filename:
    383             data["yaml_file"] = str(file)

[/usr/local/lib/python3.10/dist-packages/yaml/__init__.py](https://localhost:8080/#) in safe_load(stream)
    123     to be safe for untrusted input.
    124     """
--> 125     return load(stream, SafeLoader)
    126 
    127 def safe_load_all(stream):

[/usr/local/lib/python3.10/dist-packages/yaml/__init__.py](https://localhost:8080/#) in load(stream, Loader)
     79     loader = Loader(stream)
     80     try:
---> 81         return loader.get_single_data()
     82     finally:
     83         loader.dispose()

[/usr/local/lib/python3.10/dist-packages/yaml/constructor.py](https://localhost:8080/#) in get_single_data(self)
     47     def get_single_data(self):
     48         # Ensure that the stream contains a single document and construct it.
---> 49         node = self.get_single_node()
     50         if node is not None:
     51             return self.construct_document(node)

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in get_single_node(self)
     34         document = None
     35         if not self.check_event(StreamEndEvent):
---> 36             document = self.compose_document()
     37 
     38         # Ensure that the stream contains no more documents.

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in compose_document(self)
     53 
     54         # Compose the root node.
---> 55         node = self.compose_node(None, None)
     56 
     57         # Drop the DOCUMENT-END event.

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in compose_node(self, parent, index)
     82             node = self.compose_sequence_node(anchor)
     83         elif self.check_event(MappingStartEvent):
---> 84             node = self.compose_mapping_node(anchor)
     85         self.ascend_resolver()
     86         return node

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in compose_mapping_node(self, anchor)
    131             #    raise ComposerError("while composing a mapping", start_event.start_mark,
    132             #            "found duplicate key", key_event.start_mark)
--> 133             item_value = self.compose_node(node, item_key)
    134             #node.value[item_key] = item_value
    135             node.value.append((item_key, item_value))

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in compose_node(self, parent, index)
     80             node = self.compose_scalar_node(anchor)
     81         elif self.check_event(SequenceStartEvent):
---> 82             node = self.compose_sequence_node(anchor)
     83         elif self.check_event(MappingStartEvent):
     84             node = self.compose_mapping_node(anchor)

[/usr/local/lib/python3.10/dist-packages/yaml/composer.py](https://localhost:8080/#) in compose_sequence_node(self, anchor)
    108             self.anchors[anchor] = node
    109         index = 0
--> 110         while not self.check_event(SequenceEndEvent):
    111             node.value.append(self.compose_node(node, index))
    112             index += 1

[/usr/local/lib/python3.10/dist-packages/yaml/parser.py](https://localhost:8080/#) in check_event(self, *choices)
     96         if self.current_event is None:
     97             if self.state:
---> 98                 self.current_event = self.state()
     99         if self.current_event is not None:
    100             if not choices:

[/usr/local/lib/python3.10/dist-packages/yaml/parser.py](https://localhost:8080/#) in parse_block_sequence_entry(self)
    382         if self.check_token(BlockEntryToken):
    383             token = self.get_token()
--> 384             if not self.check_token(BlockEntryToken, BlockEndToken):
    385                 self.states.append(self.parse_block_sequence_entry)
    386                 return self.parse_block_node()

[/usr/local/lib/python3.10/dist-packages/yaml/scanner.py](https://localhost:8080/#) in check_token(self, *choices)
    113     def check_token(self, *choices):
    114         # Check if the next token is one of the given types.
--> 115         while self.need_more_tokens():
    116             self.fetch_more_tokens()
    117         if self.tokens:

[/usr/local/lib/python3.10/dist-packages/yaml/scanner.py](https://localhost:8080/#) in need_more_tokens(self)
    150         # The current token may be a potential simple key, so we
    151         # need to look further.
--> 152         self.stale_possible_simple_keys()
    153         if self.next_possible_simple_key() == self.tokens_taken:
    154             return True

[/usr/local/lib/python3.10/dist-packages/yaml/scanner.py](https://localhost:8080/#) in stale_possible_simple_keys(self)
    289                     or self.index-key.index > 1024:
    290                 if key.required:
--> 291                     raise ScannerError("while scanning a simple key", key.mark,
    292                             "could not find expected ':'", self.get_mark())
    293                 del self.possible_simple_keys[level]

ScannerError: while scanning a simple key
  in "<unicode string>", line 48, column 1:
    import torch
    ^
could not find expected ':'
  in "<unicode string>", line 49, column 1:
    from torch import nn
    ^

@glenn-jocher
Copy link
Member

Hi there!

It looks like the error you're encountering is related to the YAML syntax in your yolov8n.yaml file. The ScannerError typically indicates a problem with the structure or syntax of your YAML file.

Make sure that your YAML file is correctly formatted. YAML is sensitive to indentation and requires spaces (not tabs) for nesting. Each nested level should be indented by two spaces relative to its parent. Here's a quick example:

backbone:
  - from: -1
    number: 1
    module: LightConv
    args: [256, 3, 1]

Ensure there are no tabs or misplaced characters in your YAML configuration. Also, double-check that the LightConv module is correctly defined and imported in the Python files where it's being used.

If LightConv is already declared in conv.py but not recognized, make sure that the file where it's defined is correctly referenced and imported in any script or module that tries to use it.

If the problem persists, you might want to validate your YAML file with a YAML linter to catch any subtle syntax issues.

@Malvinlam
Copy link
Author

I tried to add two space but its not working also. Here's my yolov8.yaml file :

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect

# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
  s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, LightConv, [64, 3, 2]] # 0-P1/2
  - [-1, 1, LightConv, [128, 3, 2]] # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, LightConv, [256, 3, 2]] # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, LightConv, [512, 3, 2]] # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, LightConv, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2f, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9

# YOLOv8.0n head
head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  - [-1, 3, C2f, [512]] # 12

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  - [-1, 3, C2f, [256]] # 15 (P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]] # cat head P4
  - [-1, 3, C2f, [512]] # 18 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]] # cat head P5
  - [-1, 3, C2f, [1024]] # 21 (P5/32-large)

  - [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)

I even try

from ultralytics.nn.modules.conv import LightConv

which shows no error for this line on Colab terminal, but I still cannot declare the model:

model = YOLO("yolov8n.yaml")

it still shows me the same error:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
[<ipython-input-13-36e1cb01366a>](https://localhost:8080/#) in <cell line: 2>()
      1 # Custom YOLO model initialization
----> 2 model = YOLO("yolov8n.yaml")
      3 
      4 # Check the model architecture
      5 print(model)

4 frames
[/usr/local/lib/python3.10/dist-packages/ultralytics/models/yolo/model.py](https://localhost:8080/#) in __init__(self, model, task, verbose)
     21         else:
     22             # Continue with default YOLO initialization
---> 23             super().__init__(model=model, task=task, verbose=verbose)
     24 
     25     @property

[/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py](https://localhost:8080/#) in __init__(self, model, task, verbose)
    148         # Load or create new YOLO model
    149         if Path(model).suffix in {".yaml", ".yml"}:
--> 150             self._new(model, task=task, verbose=verbose)
    151         else:
    152             self._load(model, task=task)

[/usr/local/lib/python3.10/dist-packages/ultralytics/engine/model.py](https://localhost:8080/#) in _new(self, cfg, task, model, verbose)
    217         self.cfg = cfg
    218         self.task = task or guess_model_task(cfg_dict)
--> 219         self.model = (model or self._smart_load("model"))(cfg_dict, verbose=verbose and RANK == -1)  # build model
    220         self.overrides["model"] = self.cfg
    221         self.overrides["task"] = self.task

[/usr/local/lib/python3.10/dist-packages/ultralytics/nn/tasks.py](https://localhost:8080/#) in __init__(self, cfg, ch, nc, verbose)
    285             LOGGER.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
    286             self.yaml["nc"] = nc  # override YAML value
--> 287         self.model, self.save = parse_model(deepcopy(self.yaml), ch=ch, verbose=verbose)  # model, savelist
    288         self.names = {i: f"{i}" for i in range(self.yaml["nc"])}  # default names dict
    289         self.inplace = self.yaml.get("inplace", True)

[/usr/local/lib/python3.10/dist-packages/ultralytics/nn/tasks.py](https://localhost:8080/#) in parse_model(d, ch, verbose)
    853     layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    854     for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):  # from, number, module, args
--> 855         m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m]  # get module
    856         for j, a in enumerate(args):
    857             if isinstance(a, str):

KeyError: 'LightConv'

I tried the yamllint, which only shows warning for the comment on my yolov8.yaml file for too much character in one lines and too few space before coment

@glenn-jocher
Copy link
Member

Hi there! It seems like the LightConv module isn't being recognized when the model is being built from the YAML configuration. Since you mentioned that LightConv is already declared in conv.py, you need to ensure it's correctly imported and accessible in the script where the model is being parsed and built.

Here’s a quick suggestion:

  1. Make sure that LightConv is correctly imported in the file where the model is being initialized. If LightConv is in conv.py, you might need to adjust the import statement in the model parsing script to correctly reference this module.

  2. Instead of using globals()[m] which looks for LightConv in the global scope, ensure that LightConv is registered in a way that it's recognized by PyTorch's nn.Module. For example, you can modify the registration like this:

    import torch.nn as nn
    from ultralytics.nn.modules.conv import LightConv
    
    nn.LightConv = LightConv  # This registers LightConv to be recognized as a nn module
  3. After modifying the import, try reloading the model. If the issue persists, there might be a need to debug the YAML file parsing or the way modules are dynamically loaded.

If these steps don’t resolve the issue, it might be helpful to provide more details about the modifications you've made to conv.py and any other scripts involved in model initialization. This will help in pinpointing the exact cause of the issue. 🛠️

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

No branches or pull requests

2 participants