The beginner’s guide to implementing YOLOv3 in TensorFlow 2.0 (part-3)
In part 2, we’ve discovered how to construct the YOLOv3 network. In this part 3, we’ll focus on the file yolov3.weights
.
So, what we’re going to do in part is to load the weights parameters from the file yolov3.weights
, then convert them into the TensorFlow 2.0 weights format. Just to remain you that, the file yolov3.weights
contains the pre-trained CNN’s parameters of YOLOv3.
To begin with, let’s take a look at how the YOLOv3 weights are stored.
How YOLOv3’s weights are stored?
The original YOLOv3 weights file yolov3.weights
is a binary file and the weights are stored in the float data type.
One thing that we need to know that the weights only belong to convolutional layers. As we know, in YOLOv3, there are 2 convolutional layer types, with and without a batch normalization layer. So, the weights are applied differently for different types of convolutional layers. Since we’re reading the only float data, there’s no clue which one belongs to which layer. If we incorrectly associate these weights with their layers properly, we’ll screw up everything, and the weights won’t be converted. So, that’s why understanding how the weights are stored is crucially important.
Here, I tried to make a simple flowchart in order to describe how the weights are stored.
When we re-write these weights to TensorFlow’s format for a convolutional with a batch normalization layer, we need to switch the position of beta
and gamma
. So, they’re ordered like this: beta
, gamma
, means
, variance
and conv weights
. However, the weights’ order remains the same for the convolutional without a batch normalization layer.
All right!!, Now we’re ready to code the weights converter.
Without further ado, let’s do it…
Working with the file convert_weights.py
Open the file convert_weights.py
, then copy and paste the following code to the top of it. Here, we import NumPy library and the two functions that we’ve created previously in part 2, YOLOv3Net
and parse_cfg
.
#convert_weights.py import numpy as np from yolov3 import YOLOv3Net from yolov3 import parse_cfg
Now, let’s create a function called load_weights()
. This function has 3 parameters, model
, cfgfile
, and weightfile
. The parameter model
is a returning parameters of the network’s model after calling the function YOLOv3Net
. Thecfgfile
and weightfile
are respectively refer to the files yolov3.cfg
and yolov3.weights
.
def load_weights(model,cfgfile,weightfile):
Open the file yolov3.weights
and read the first 5 values. These values are the header information. So, we can skip them all.
# Open the weights file fp = open(weightfile, "rb") # The first 5 values are header information np.fromfile(fp, dtype=np.int32, count=5)
Then call parse_cfg()
function.
blocks = parse_cfg(cfgfile)
As we did when building the YOLOv3 network, we need to loop over the blocks
and search for the convolutional layer. Don’t forget to check whether the convolutional is with batch normalization or not. If it is true, go get the relevant values (gamma
, beta
, means
, and variance
), and re-arrange them to the TensorFlow weights order. Otherwise, take the bias values. After that take the convolutional weights and set these weights to the convolutional layer depending on the convolutional type.
for i, block in enumerate(blocks[1:]): if (block["type"] == "convolutional"): conv_layer = model.get_layer('conv_' + str(i)) print("layer: ",i+1,conv_layer) filters = conv_layer.filters k_size = conv_layer.kernel_size[0] in_dim = conv_layer.input_shape[-1] if "batch_normalize" in block: norm_layer = model.get_layer('bnorm_' + str(i)) print("layer: ",i+1,norm_layer) size = np.prod(norm_layer.get_weights()[0].shape) bn_weights = np.fromfile(fp, dtype=np.float32, count=4 * filters) # tf [gamma, beta, mean, variance] bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]] else: conv_bias = np.fromfile(fp, dtype=np.float32, count=filters) # darknet shape (out_dim, in_dim, height, width) conv_shape = (filters, in_dim, k_size, k_size) conv_weights = np.fromfile( fp, dtype=np.float32, count=np.product(conv_shape)) # tf shape (height, width, in_dim, out_dim) conv_weights = conv_weights.reshape( conv_shape).transpose([2, 3, 1, 0]) if "batch_normalize" in block: norm_layer.set_weights(bn_weights) conv_layer.set_weights([conv_weights]) else: conv_layer.set_weights([conv_weights, conv_bias])
Alert if the reading has failed. Then, close the file whether the reading was successful or not.
assert len(fp.read()) == 0, 'failed to read all data' fp.close()
The last part of this code is the main function. Copy and paste the following code of the main function just right after the function load_weights()
.
def main(): weightfile = "weights/yolov3.weights" cfgfile = "cfg/yolov3.cfg" model_size = (416, 416, 3) num_classes = 80 model=YOLOv3Net(cfgfile,model_size,num_classes) load_weights(model,cfgfile,weightfile) try: model.save_weights('weights/yolov3_weights.tf') print('\nThe file \'yolov3_weights.tf\' has been saved successfully.') except IOError: print("Couldn't write the file \'yolov3_weights.tf\'.")
Here is the complete code for the convert_weights.py
#convert_weights.py import numpy as np from yolov3 import YOLOv3Net from yolov3 import parse_cfg def load_weights(model,cfgfile,weightfile): # Open the weights file fp = open(weightfile, "rb") # Skip 5 header values np.fromfile(fp, dtype=np.int32, count=5) # The rest of the values are the weights blocks = parse_cfg(cfgfile) for i, block in enumerate(blocks[1:]): if (block["type"] == "convolutional"): conv_layer = model.get_layer('conv_' + str(i)) print("layer: ",i+1,conv_layer) filters = conv_layer.filters k_size = conv_layer.kernel_size[0] in_dim = conv_layer.input_shape[-1] if "batch_normalize" in block: norm_layer = model.get_layer('bnorm_' + str(i)) print("layer: ",i+1,norm_layer) size = np.prod(norm_layer.get_weights()[0].shape) bn_weights = np.fromfile(fp, dtype=np.float32, count=4 * filters) # tf [gamma, beta, mean, variance] bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]] else: conv_bias = np.fromfile(fp, dtype=np.float32, count=filters) # darknet shape (out_dim, in_dim, height, width) conv_shape = (filters, in_dim, k_size, k_size) conv_weights = np.fromfile( fp, dtype=np.float32, count=np.product(conv_shape)) # tf shape (height, width, in_dim, out_dim) conv_weights = conv_weights.reshape( conv_shape).transpose([2, 3, 1, 0]) if "batch_normalize" in block: norm_layer.set_weights(bn_weights) conv_layer.set_weights([conv_weights]) else: conv_layer.set_weights([conv_weights, conv_bias]) assert len(fp.read()) == 0, 'failed to read all data' fp.close() def main(): weightfile = "weights/yolov3.weights" cfgfile = "cfg/yolov3.cfg" model_size = (416, 416, 3) num_classes = 80 model=YOLOv3Net(cfgfile,model_size,num_classes) load_weights(model,cfgfile,weightfile) try: model.save_weights('weights/yolov3_weights.tf') print('\nThe file \'yolov3_weights.tf\' has been saved successfully.') except IOError: print("Couldn't write the file \'yolov3_weights.tf\'.") if __name__ == '__main__': main()
Finally, we can now execute the convert_weights.py
file . Open Anaconda prompt or terminal in Pycharm, type the following command and press Enter.
python convert_weights.py
Here is the output, I printed all the convolutional layers just to make sure that the weights are loaded correctly until the last convolutional layer.
If you use PyCharm, look at the Project Navigation on the left side as I pointed with the red boxes, you have 4 new files:
- checkpoint
- yolov3_weights.tf.data-00000-of-00002
- yolov3_weights.tf.data-00001-of-00002
- yolov3_weights.tf.index
Those files are the TensorFlow 2.0 weights format. So, anytime we want to use them, just simply call them like the only one file, yolov3_weights.tf
. We’ll see how to do this in the last part of this tutorial.
This is the end of part 3, and we still have several things to do, those are:
- creating a pipeline to read the input image or video/camera,
- computing the prediction,
- and drawing the bounding boxes prediction over the input image/video/camera.
Those are what we will be doing soon in the last part. Let’s go into it…
What others say
Hi, I have tried to execute the “convert_weights.py” but it gave me the error:
“can’t multiply sequence by non-int of the type ‘Tensor'” referring to the line: “box_centers = (box_centers + cxy) * strides” in the “yolov3.py”.
Do you have any suggestion? In my opinion it makes sense that a multiplication between a Tensor and a Tuple is not allowed, but I whould like to know how it is possible to solve it. Thank you.
Hi, If you run the code from my Github repo, normally you won’t have this issue.
Can you please give me detailed info of your system (eg; OS, TensorFlow’s version, anaconda, etc)
I was using Python 3.6 instead of 3.7 as you suggested, I guess this was the problem. Now it works, thank you very much for the support!
I am getting this error when I am trying to use your repo with my customed trained Yolov3:
line 38, in parse_cfg
key, value = line.split(“=”)
ValueError: too many values to unpack (expected 2)
Can you please help me out?
Hi,
I am trying to run YOLO on different YOLO dataset other than COCO.
Can you please let me know what will be the changes in the .weights function if the cfg exists for that dataset
Hi I got this error while running convert_weight.py code
File “C:\Users\Hitesh\anaconda3\lib\site-packages\tensorflow\python\keras\engine\network.py”, line 522, in get_layer
raise ValueError(‘No such layer: ‘ + name)
ValueError: No such layer: conv_0
Hi,I got error
File “C:\Users\Hitesh\anaconda3\lib\site-packages\tensorflow\python\keras\engine\network.py”, line 522, in get_layer
raise ValueError(‘No such layer: ‘ + name)
ValueError: No such layer: conv_0
how to solved it.Please give me suggestion.
Like!! Really appreciate you sharing this blog post.Really thank you! Keep writing.
I like the valuable information you provide in your articles.
I like the valuable information you provide in your articles.
Thanks for fantastic info I was looking for this info for my mission.
Thanks so much for the blog post.
Hi, I have tried to execute the “convert_weights.py” but it gave me the error:
“can’t multiply sequence by non-int of the type ‘Tensor’” referring to the line: “box_centers = (box_centers + cxy) * strides” in the “yolov3.py”.
Do you have any suggestion? In my opinion it makes sense that a multiplication between a Tensor and a Tuple is not allowed, but I whould like to know how it is possible to solve it. Thank you.
Hi, normally, this code works perfectly using Python 3.7 and Tensorflow 2.0 and 2.1.
Just take a look at your Python version. Good luck!
Hi,
I get the following error:
(py3.7.7_tf2_yolo_venv) D:\Technical\Dev\CV\Object_Detection\YOLO\YOLOv3_TF2>python convert_weights.py
Traceback (most recent call last):
File “convert_weights.py”, line 20, in
from yolov3 import YOLOv3Net
File “D:\Technical\Dev\CV\Object_Detection\YOLO\YOLOv3_TF2\yolov3.py”, line 25, in
tf.config.experimental.set_memory_growth(physical_devices[0], True)
IndexError: list index out of range
Details of the Environment:
****************************
python = 3.7.7
opencv = 4.4.0
tensorflow = 2.1.0
Hi, it means you don’t have a GPU card installed on you computer.
Just make those lines as comments like this:
“””
physical_devices = tf.config.experimental.list_physical_devices(‘GPU’)
assert len(physical_devices) > 0, “Not enough GPU hardware devices available”
tf.config.experimental.set_memory_growth(physical_devices[0], True)
“””
Hello, When I am trying to convert the weights after training with my own dataset, I am getting the following error, please help me in resolving this. My input image size is 608 * 608 * 3
ERROR :
Traceback (most recent call last):
File “convert_weights.py”, line 55, in
main()
File “convert_weights.py”, line 48, in main
load_weights(model,cfgfile,weightfile)
File “convert_weights.py”, line 34, in load_weights
conv_shape).transpose([2, 3, 1, 0])
ValueError: cannot reshape array of size 264248 into shape (256,128,3,3)
Hi,
Firstly, thank you for this blog. This is helpful. I have been reading about YOLOv3 for sometime now. This is the best tutorial I have found. Thank you. Please keep writing.
I am trying to run this in Spyder(python 3.7).
I have this error. Please help.
runfile(‘D:/YOLO/YOLOv3_TF2/convert_weights.py’, wdir=’D:/YOLO/YOLOv3_TF2′)
Reloaded modules: yolov3
Traceback (most recent call last):
File “D:\YOLO\YOLOv3_TF2\convert_weights.py”, line 62, in
main()
File “D:\YOLO\YOLOv3_TF2\convert_weights.py”, line 53, in main
model=YOLOv3Net(cfgfile,model_size,num_classes)
File “D:\YOLO\YOLOv3_TF2\yolov3.py”, line 22, in YOLOv3Net
blocks = parse_cfg(cfgfile)
File “D:\YOLO\YOLOv3_TF2\yolov3.py”, line 17, in parse_cfg
key, value = line.split(“=”)
ValueError: not enough values to unpack (expected 2, got 1)
Thank you,
Sumana