一、权重与网络结构转换的环境:
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
未完待续........