Во второй части мы увидели, как мы строим сеть YOLOv3. А теперь, в части 3, мы сосредоточимся на файле yolov3.weights
.
Что мы собираемся сделать в этой части, так это загрузить предварительно обученные веса YOLOv3 из файла yolov3.weights
, а затем преобразовать их в формат весов TensorFlow 2.0.
Итак, давайте сначала посмотрим, как хранятся веса YOLOv3.
Как хранятся веса YOLOv3?
Исходный файл весов YOLOv3 yolov3.weights
является двоичным файлом, и веса хранятся в типе данных с плавающей запятой.
Одна вещь, которую нам нужно знать, что веса принадлежат только сверточным слоям. В YOLOv3 есть 2 типа сверточных слоев с пакетной нормализацией и без него. Таким образом, веса применяются по-разному для разных типов сверточных слоев.
Поскольку мы читаем только данные с плавающей запятой, мы не можем понять, какой из них какому слою принадлежит. Если мы неправильно свяжем эти веса с их слоями, мы все испортим, и веса не будут преобразованы. Следовательно, понимание того, как хранятся веса, имеет решающее значение.
На следующем рисунке представлена простая блок-схема, которую я сделал, чтобы описать, как хранятся веса.
Когда мы переписываем эти веса в формат TensorFlow, нам нужно переключить положение beta
и gamma
для сверточного с пакетным слоем нормализации, чтобы они были упорядочены следующим образом beta
, gamma
, means
, variance
и conv weights
. Однако порядок весов остается неизменным для сверточного преобразования без слоя пакетной нормализации.
Хорошо !!, Теперь мы готовы написать код конвертера весов. Итак, без лишних слов, давайте сделаем это ...
convert_weights.py
Откройте файл convert_weights.py
, затем скопируйте и вставьте следующий код в его верхнюю часть. Здесь мы импортируем библиотеку numpy и 2 функции, которые мы создали ранее в части 2, YOLOv3Net
и parse_cfg
.
import numpy as np from yolov3 import YOLOv3Net from yolov3 import parse_cfg
Теперь давайте создадим функцию с именем load_weights()
. Эта функция имеет 3 параметра: model
, cfgfile
и weightfile
. Параметр model
- это модель обратной сети после вызова функции YOLOv3Net
. cfgfile
и weightfile
соответственно относятся к файлам yolov3.cfg
и yolov3.weights
.
def load_weights(model,cfgfile,weightfile):
Откройте файл yolov3.weights
и прочтите первые 5 значений. Эти значения представляют собой информацию заголовка. Итак, мы можем пропустить их все.
# Open the weights file fp = open(weightfile, "rb") # Skip 5 header values np.fromfile(fp, dtype=np.int32, count=5)
Затем вызовите функцию parse_cfg()
.
blocks = parse_cfg(cfgfile)
Как и при построении сети YOLOv3, нам нужно перебрать blocks
и найти сверточный слой. Не забудьте проверить, есть ли сверточная нормализация с пакетной нормализацией или нет. Если это правда, найдите соответствующие значения (gamma
, beta
, means
и variance
) и переставьте их в порядок TensorFlow. В противном случае возьмите значения смещения. После этого возьмите сверточные веса и установите эти веса для сверточного слоя в зависимости от типа свертки. Оповещать, если чтение не удалось. Затем закройте файл независимо от того, было ли чтение успешным.
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()
Последняя часть этого кода - основная функция. Скопируйте и вставьте следующий код сразу после функции 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\'.")
Вот полный код 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()
Наконец, теперь мы можем выполнить weights_converter.py
. Откройте командную строку или терминал Anaconda в Pycharm, введите следующую команду и нажмите Enter.
python convert_weights.py
Вот результат: я распечатал все сверточные слои, чтобы убедиться, что веса загружены правильно до последнего сверточного слоя.
Если вы используете PyCharm, посмотрите на Project Navigation слева, поскольку я указал красными полями на рисунке ниже, у вас есть 4 новых файла:
- контрольно-пропускной пункт
- yolov3_weights.tf.data-00000-of-00002
- yolov3_weights.tf.data-00001-of-00002
- yolov3_weights.tf.index
Эти файлы представляют собой формат весов TensorFlow 2.0. Итак, в любое время, когда мы захотим их использовать, просто назовите их как единственный файл, yolov3_weights.tf
. Мы увидим, как это сделать, в последней части этого руководства.
Конечные примечания:
Это конец части 3, и у нас еще есть несколько дел, а именно:
- создание конвейера для чтения входного изображения или видео / камеры,
- вычисление прогноза,
- и рисование предсказания ограничивающих прямоугольников над входным изображением / видео / камерой.
Итак, это то, чем мы скоро займемся в следующей части. Так что давай ...
Части:
- Часть-1, Краткое знакомство с YOLOv3 и принципами работы алгоритма.
- Часть 2, разбор файла конфигурации YOLOv3 (yolov3. cfg) и создание сети YOLOv3.
- Часть 3, преобразование файла предварительно обученных весов YOLOv3 (yolov3.weights) в формат весов TensorFlow 2.0.
- Часть 4, Кодирование ограничивающих рамок и тестирование этой реализации с изображениями и видео.
Первоначально опубликовано на https://machinelearningspace.com.