.h5文件轉.pb文件(windows+Keras+Yolov3訓練自己數據集)細節處理。

一、權重與網絡結構轉換的環境:

1.win10+cuda9.0

2.tensorflow-gpu==1.10;tensorflow==1.12

3.keras==2.2;python==3.5

二、Keras保存權重有兩種方式:

#方式一:權重的參數和網絡結構分開保存
    1,保存網絡結構到json文件
     model_json=model.to_json()

        with open("mosel.json") as file:
            file.write(model_json)
    2,保存權重參數到h5文件
     model.save_weights('trained.h5')

#方式二:權重參數和網絡結構一起保存到h5文件
    model.save('trained.h5')
     

   項目源碼參考:https://github.com/qqwweee/keras-yolo3,權重保存方式修改在train.py文件中,個人傾向使用save()方式,第一種方式model.to_json()保存尚未解決;

三、.h5文件轉成.pb文件:

       方式1:.h5轉weights,具體代碼參考:h5_to_weights.py ,參考鏈接:https://github.com/amir-abdi/keras_to_tensorflow.git

import argparse
import numpy
import numpy as np
import keras
from keras.models import load_model
from keras import backend as K
#從yolo3中導入網絡結構
from yolo3.model import yolo_head,yolo_body,box_iou
import tensorflow as tf

def parser():
    parser = argparse.ArgumentParser(description="Darknet\'s yolov3.cfg and yolov3.weights \
                                      converted into Keras\'s yolov3.h5!")
    # 修改成自己文件的路徑
    parser.add_argument('-cfg_path', default="./yolov3.cfg",help='yolov3.cfg')
    parser.add_argument('-h5_path', default="./trained.h5",help='yolov3.h5')
    parser.add_argument('-output_path', default="./train.weights",help='yolov3.weights')
    return parser.parse_args()


class WeightSaver(object):

    def __init__(self, h5_path, output_path):
        #self.load=yolo_body(Input(shape=(None, None, 3)), 3, num_classes)
        # 網絡結構加入
        self.model = load_model(h5_path,custom_objects={"yolo_head":yolo_head,'yolo_body':yolo_body,'tf': tf,'box_iou':box_iou},'<lambda>': lambda y_true, output: output)
        # 如果要讀取keras調用save_weights的h5文件,可以先讀取一次save的h5,
        # 然後取消下面的註釋,讀取save_weights的h5
        #        self.model.load_weights('text.h5')
        self.layers = {weight.name: weight for weight in self.model.weights}
        self.sess = K.get_session()
        self.fhandle = open(output_path, 'wb')
        self._write_head()

    def _write_head(self):
        numpy_data = numpy.ndarray(shape=(3,),
                                   dtype='int32',
                                   buffer=np.array([0, 2, 0], dtype='int32'))
        self.save(numpy_data)
        numpy_data = numpy.ndarray(shape=(1,),
                                   dtype='int64',
                                   buffer=np.array([320000], dtype='int64'))
        self.save(numpy_data)

    def get_bn_layername(self, num):
        layer_name = 'batch_normalization_{num}'.format(num=num)
        bias = self.layers['{0}/beta:0'.format(layer_name)]
        scale = self.layers['{0}/gamma:0'.format(layer_name)]
        mean = self.layers['{0}/moving_mean:0'.format(layer_name)]
        var = self.layers['{0}/moving_variance:0'.format(layer_name)]

        bias_np = self.get_numpy(bias)
        scale_np = self.get_numpy(scale)
        mean_np = self.get_numpy(mean)
        var_np = self.get_numpy(var)
        return bias_np, scale_np, mean_np, var_np

    def get_convbias_layername(self, num):
        layer_name = 'conv2d_{num}'.format(num=num)
        bias = self.layers['{0}/bias:0'.format(layer_name)]

        bias_np = self.get_numpy(bias)
        return bias_np

    def get_conv_layername(self, num):
        layer_name = 'conv2d_{num}'.format(num=num)
        conv = self.layers['{0}/kernel:0'.format(layer_name)]

        conv_np = self.get_numpy(conv)
        return conv_np

    def get_numpy(self, layer_name):
        numpy_data = self.sess.run(layer_name)
        return numpy_data

    def save(self, numpy_data):
        bytes_data = numpy_data.tobytes()
        self.fhandle.write(bytes_data)
        self.fhandle.flush()

    def close(self):
        self.fhandle.close()


class KerasParser(object):

    def __init__(self, cfg_path, h5_path, output_path):
        self.block_gen = self._get_block(cfg_path)
        self.weights_saver = WeightSaver(h5_path, output_path)
        self.count_conv = 0
        self.count_bn = 0

    def _get_block(self, cfg_path):

        block = {}
        with open(cfg_path, 'r', encoding='utf-8') as fr:
            for line in fr:
                line = line.strip()
                if '[' in line and ']' in line:
                    if block:
                        yield block
                    block = {}
                    block['type'] = line.strip(' []')
                elif not line or '#' in line:
                    continue
                else:
                    key, val = line.strip().replace(' ', '').split('=')
                    key, val = key.strip(), val.strip()
                    block[key] = val

            yield block

    def close(self):
        self.weights_saver.close()

    def conv(self, block):
        self.count_conv += 1
        batch_normalize = 'batch_normalize' in block
        print('handing.. ', self.count_conv)

        # 如果bn存在,則先處理bn,順序爲bias,scale,mean,var
        if batch_normalize:
            bias, scale, mean, var = self.bn()
            self.weights_saver.save(bias)

            scale = scale.reshape(1, -1)
            mean = mean.reshape(1, -1)
            var = var.reshape(1, -1)
            remain = np.concatenate([scale, mean, var], axis=0)
            self.weights_saver.save(remain)

        # 否則,先處理biase
        else:
            conv_bias = self.weights_saver.get_convbias_layername(self.count_conv)
            self.weights_saver.save(conv_bias)

        # 接着處理weights
        conv_weights = self.weights_saver.get_conv_layername(self.count_conv)
        # 需要將(height, width, in_dim, out_dim)轉換成(out_dim, in_dim, height, width)
        conv_weights = np.transpose(conv_weights, [3, 2, 0, 1])
        self.weights_saver.save(conv_weights)

    def bn(self):
        self.count_bn += 1
        bias, scale, mean, var = self.weights_saver.get_bn_layername(self.count_bn)
        return bias, scale, mean, var


def main():
    args = parser()
    keras_loader = KerasParser(args.cfg_path, args.h5_path, args.output_path)

    for block in keras_loader.block_gen:
        if 'convolutional' in block['type']:
            keras_loader.conv(block)
    keras_loader.close()


if __name__ == "__main__":
    main()

注意點:將文件放置在keras_yolo3項目下,遇到問題點1:“yolo_head is not defind”,或者是“tf is not defind ”...在

self.model = load_model(h5_path,custom_objects={"yolo_head":yolo_head,'yolo_body':yolo_body,'tf': tf,'box_iou':box_iou,'<lambda>': lambda y_true, output: output})中把缺少的東西加進去。

然後運行:python h5_to_weights.py

之後生成的.weights文件大概200多兆: buffer is too small for requested array

未完待續........

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章