这是一个demo 项目,用于演示如何在 AI Studio 上训练一个“小”模型,而后把它转化成一个能够部署到Paddle派硬件上的模型。html
为了简单起见,在此只训练一个猫猫和狗狗的二分类模型。linux
进入项目时,已经引用了 AI Studio 的公开数据集"猫狗大战数据集"做为训练数据。数据存储在 data/data62/ 目录下,以压缩包的形式存在。执行下面的代码,进入目录,将训练数据解压网络
In[1]dom
!cd /home/aistudio/data/data62 && unzip -q train.zip !cd /home/aistudio/data/data62 && unzip -q test.zip
训练集中的数据按照ide
cat.123.jpg函数
dog.456.jpg工具
的命名方式。因为数据中存在一些破损的图片,因此须要先清洗一下数据。同时,为了方便训练,将数据的一行准备成学习
文件名\t类别fetch
的格式,并输出到和图片同级目录下的label.txt文件中。猫猫的类别是1,狗狗的类别是0。执行如下代码,进行数据的简单清洗优化
In[2]
#数据清洗 import codecs import os from PIL import Image train_file_list = os.listdir('data/data62/train') with codecs.open("data/data62/train/label.txt", 'w') as out_file: for file in train_file_list: try: img = Image.open(os.path.join('data/data62/train', file)) if file.find('cat') != -1: out_file.write("{0}\t{1}\n".format(file, 1)) else: out_file.write("{0}\t{1}\n".format(file, 0)) except Exception as e: pass # 存在一些文件打不开,此处须要稍做清洗
设置基础训练参数,例如
其中类别数量会在读取数据时提早计算,初始为-1,仅用做占位
In[3]
from __future__ import absolute_import from __future__ import division from __future__ import print_function import os import numpy as np import uuid import random import time import six import sys import functools import math import paddle import paddle.fluid as fluid import paddle.dataset.flowers as flowers import argparse import functools import subprocess import codecs import distutils.util from paddle.fluid import core from paddle.fluid.initializer import MSRA from paddle.fluid.param_attr import ParamAttr from PIL import Image, ImageEnhance import logging train_parameters = { "input_size": [3, 224, 224], "class_dim": -1, "data_dir": "data/data62/train", "save_model_dir": "./classify-model", "mode": "train", "num_epochs": 120, "image_count": -1, "train_batch_size": 50, "mean_rgb": [127.5, 127.5, 127.5], "use_gpu": True, # 根据本身的环境,选择适当的设备进行训练 "image_distort_strategy": { "need_distort": True, "expand_prob": 0.5, "expand_max_ratio": 4, "hue_prob": 0.5, "hue_delta": 18, "contrast_prob": 0.5, "contrast_delta": 0.5, "saturation_prob": 0.5, "saturation_delta": 0.5, "brightness_prob": 0.5, "brightness_delta": 0.125 }, "rsm_strategy": { "learning_rate": 0.02, "lr_epochs": [20, 40, 60, 80, 100], "lr_decay": [1, 0.5, 0.25, 0.1, 0.01, 0.002] }, "momentum_strategy": { "learning_rate": 0.005, "lr_epochs": [20, 40, 60, 80, 100], "lr_decay": [1, 0.5, 0.25, 0.1, 0.01, 0.002] } }
设定网络结构,此处定义了三个经常使用的网络结构
为了训练一个小模型,此处使用mobile-net。若是是其余项目或者其余用途,使用其余网络结构亦可
In[4]
class ResNet(): def __init__(self, layers=50): self.layers = layers def name(self): return 'resnet' def net(self, input, class_dim=1000): layers = self.layers supported_layers = [50, 101, 152] assert layers in supported_layers, \ "supported layers are {} but input layer is {}".format(supported_layers, layers) if layers == 50: depth = [3, 4, 6, 3] elif layers == 101: depth = [3, 4, 23, 3] elif layers == 152: depth = [3, 8, 36, 3] num_filters = [64, 128, 256, 512] conv = self.conv_bn_layer( input=input, num_filters=64, filter_size=7, stride=2, act='relu', name="conv1") conv = fluid.layers.pool2d( input=conv, pool_size=3, pool_stride=2, pool_padding=1, pool_type='max') for block in range(len(depth)): for i in range(depth[block]): if layers in [101, 152] and block == 2: if i == 0: conv_name = "res" + str(block + 2) + "a" else: conv_name = "res" + str(block + 2) + "b" + str(i) else: conv_name = "res" + str(block + 2) + chr(97 + i) conv = self.bottleneck_block( input=conv, num_filters=num_filters[block], stride=2 if i == 0 and block != 0 else 1, name=conv_name) pool = fluid.layers.pool2d( input=conv, pool_size=7, pool_type='avg', global_pooling=True) stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0) out = fluid.layers.fc(input=pool, size=class_dim, act='softmax', param_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Uniform(-stdv, stdv))) return out def conv_bn_layer(self, input, num_filters, filter_size, stride=1, groups=1, act=None, name=None): conv = fluid.layers.conv2d( input=input, num_filters=num_filters, filter_size=filter_size, stride=stride, padding=(filter_size - 1) // 2, groups=groups, act=None, param_attr=ParamAttr(name=name + "_weights"), bias_attr=False, name=name + '.conv2d.output.1') if name == "conv1": bn_name = "bn_" + name else: bn_name = "bn" + name[3:] return fluid.layers.batch_norm( input=conv, act=act, name=bn_name + '.output.1', param_attr=ParamAttr(name=bn_name + '_scale'), bias_attr=ParamAttr(bn_name + '_offset'), moving_mean_name=bn_name + '_mean', moving_variance_name=bn_name + '_variance', ) def shortcut(self, input, ch_out, stride, name): ch_in = input.shape[1] if ch_in != ch_out or stride != 1: return self.conv_bn_layer(input, ch_out, 1, stride, name=name) else: return input def bottleneck_block(self, input, num_filters, stride, name): conv0 = self.conv_bn_layer( input=input, num_filters=num_filters, filter_size=1, act='relu', name=name + "_branch2a") conv1 = self.conv_bn_layer( input=conv0, num_filters=num_filters, filter_size=3, stride=stride, act='relu', name=name + "_branch2b") conv2 = self.conv_bn_layer( input=conv1, num_filters=num_filters * 4, filter_size=1, act=None, name=name + "_branch2c") short = self.shortcut( input, num_filters * 4, stride, name=name + "_branch1") return fluid.layers.elementwise_add( x=short, y=conv2, act='relu', name=name + ".add.output.5") class MobileNet(): def __init__(self): pass def name(self): return 'mobile-net' def net(self, input, class_dim=1000, scale=1.0): # conv1: 112x112 input = self.conv_bn_layer( input, filter_size=3, num_filters=int(32 * scale), stride=2, padding=1) # 56x56 input = self.depthwise_separable( input, num_filters1=32, num_filters2=64, num_groups=32, stride=1, scale=scale) input = self.depthwise_separable( input, num_filters1=64, num_filters2=128, num_groups=64, stride=2, scale=scale) # 28x28 input = self.depthwise_separable( input, num_filters1=128, num_filters2=128, num_groups=128, stride=1, scale=scale) input = self.depthwise_separable( input, num_filters1=128, num_filters2=256, num_groups=128, stride=2, scale=scale) # 14x14 input = self.depthwise_separable( input, num_filters1=256, num_filters2=256, num_groups=256, stride=1, scale=scale) input = self.depthwise_separable( input, num_filters1=256, num_filters2=512, num_groups=256, stride=2, scale=scale) # 14x14 for i in range(5): input = self.depthwise_separable( input, num_filters1=512, num_filters2=512, num_groups=512, stride=1, scale=scale) module1 = input # 7x7 input = self.depthwise_separable( input, num_filters1=512, num_filters2=1024, num_groups=512, stride=2, scale=scale) input = self.depthwise_separable( input, num_filters1=1024, num_filters2=1024, num_groups=1024, stride=1, scale=scale) # class_dim x 1 input = paddle.fluid.layers.conv2d( input, num_filters=class_dim, filter_size=1, stride=1) pool = fluid.layers.pool2d( input=input, pool_size=0, pool_stride=1, pool_type='avg', global_pooling=True) output = fluid.layers.fc(input=pool, size=class_dim, act='softmax', param_attr=ParamAttr(initializer=MSRA())) return output def conv_bn_layer(self, input, filter_size, num_filters, stride, padding, num_groups=1, act='relu', use_cudnn=True): conv = fluid.layers.conv2d( input=input, num_filters=num_filters, filter_size=filter_size, stride=stride, padding=padding, groups=num_groups, act=None, use_cudnn=use_cudnn, param_attr=ParamAttr(initializer=MSRA()), bias_attr=False) return fluid.layers.batch_norm(input=conv, act=act) def depthwise_separable(self, input, num_filters1, num_filters2, num_groups, stride, scale): depthwise_conv = self.conv_bn_layer( input=input, filter_size=3, num_filters=int(num_filters1 * scale), stride=stride, padding=1, num_groups=int(num_groups * scale), use_cudnn=True) pointwise_conv = self.conv_bn_layer( input=depthwise_conv, filter_size=1, num_filters=int(num_filters2 * scale), stride=1, padding=0) return pointwise_conv class VGGNet(): def __init__(self, layers=16): self.layers = layers def name(self): return 'vgg-net' def net(self, input, class_dim=1000): layers = self.layers vgg_spec = { 11: ([1, 1, 2, 2, 2]), 13: ([2, 2, 2, 2, 2]), 16: ([2, 2, 3, 3, 3]), 19: ([2, 2, 4, 4, 4]) } assert layers in vgg_spec.keys(), \ "supported layers are {} but input layer is {}".format(vgg_spec.keys(), layers) nums = vgg_spec[layers] conv1 = self.conv_block(input, 64, nums[0]) conv2 = self.conv_block(conv1, 128, nums[1]) conv3 = self.conv_block(conv2, 256, nums[2]) conv4 = self.conv_block(conv3, 512, nums[3]) conv5 = self.conv_block(conv4, 512, nums[4]) fc_dim = 4096 fc1 = fluid.layers.fc( input=conv5, size=fc_dim, act='relu', param_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Normal(scale=0.005)), bias_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Constant(value=0.1))) fc1 = fluid.layers.dropout(x=fc1, dropout_prob=0.5) fc2 = fluid.layers.fc( input=fc1, size=fc_dim, act='relu', param_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Normal(scale=0.005)), bias_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Constant(value=0.1))) fc2 = fluid.layers.dropout(x=fc2, dropout_prob=0.5) out = fluid.layers.fc( input=fc2, size=class_dim, act='softmax', param_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Normal(scale=0.005)), bias_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Constant(value=0.1))) return out def conv_block(self, input, num_filter, groups): conv = input for i in range(groups): if i == groups - 1: act = None else: act = 'relu' conv = fluid.layers.conv2d( input=conv, num_filters=num_filter, filter_size=3, stride=1, padding=1, act=act, param_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Normal(scale=0.01)), bias_attr=fluid.param_attr.ParamAttr( initializer=fluid.initializer.Constant(value=0.0))) conv = fluid.layers.batch_norm(input=conv, act='relu') return fluid.layers.pool2d(input=conv, pool_size=2, pool_type='max', pool_stride=2)
如下是一些工具函数, 例如日志处理, 图片加强处理等等.
In[5]
# 初始化日志 def init_log_config(): global logger logger = logging.getLogger() logger.setLevel(logging.INFO) log_path = os.path.join(os.getcwd(), 'logs') if not os.path.exists(log_path): os.makedirs(log_path) log_name = os.path.join(log_path, 'train.log') fh = logging.FileHandler(log_name, mode='w') fh.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") fh.setFormatter(formatter) logger.addHandler(fh) # 简单的图像加强函数 def resize_img(img, target_size): percent_h = float(target_size[1]) / img.size[1] percent_w = float(target_size[2]) / img.size[0] percent = min(percent_h, percent_w) resized_width = int(round(img.size[0] * percent)) resized_height = int(round(img.size[1] * percent)) w_off = (target_size[1] - resized_width) / 2 h_off = (target_size[2] - resized_height) / 2 img = img.resize((resized_width, resized_height), Image.LANCZOS) array = np.ndarray((target_size[1], target_size[2], target_size[0]), np.uint8) array[:, :, 0] = 127.5 array[:, :, 1] = 127.5 array[:, :, 2] = 127.5 ret = Image.fromarray(array) ret.paste(img, (int(w_off), int(h_off))) return ret def random_brightness(img): prob = np.random.uniform(0, 1) if prob < train_parameters['image_distort_strategy']['brightness_prob']: brightness_delta = train_parameters['image_distort_strategy']['brightness_delta'] delta = np.random.uniform(-brightness_delta, brightness_delta) + 1 img = ImageEnhance.Brightness(img).enhance(delta) return img def random_contrast(img): prob = np.random.uniform(0, 1) if prob < train_parameters['image_distort_strategy']['contrast_prob']: contrast_delta = train_parameters['image_distort_strategy']['contrast_delta'] delta = np.random.uniform(-contrast_delta, contrast_delta) + 1 img = ImageEnhance.Contrast(img).enhance(delta) return img def random_saturation(img): prob = np.random.uniform(0, 1) if prob < train_parameters['image_distort_strategy']['saturation_prob']: saturation_delta = train_parameters['image_distort_strategy']['saturation_delta'] delta = np.random.uniform(-saturation_delta, saturation_delta) + 1 img = ImageEnhance.Color(img).enhance(delta) return img def random_hue(img): prob = np.random.uniform(0, 1) if prob < train_parameters['image_distort_strategy']['hue_prob']: hue_delta = train_parameters['image_distort_strategy']['hue_delta'] delta = np.random.uniform(-hue_delta, hue_delta) img_hsv = np.array(img.convert('HSV')) img_hsv[:, :, 0] = img_hsv[:, :, 0] + delta img = Image.fromarray(img_hsv, mode='HSV').convert('RGB') return img def distort_image(img): prob = np.random.uniform(0, 1) # Apply different distort order if prob < 0.25: img = random_brightness(img) img = random_contrast(img) img = random_saturation(img) img = random_hue(img) elif prob < 0.5: img = random_brightness(img) img = random_saturation(img) img = random_hue(img) img = random_contrast(img) return img # 自定义数据读取器 def custom_image_reader(file_list, data_dir, mode): with codecs.open(file_list) as flist: lines = [line.strip() for line in flist] train_parameters['image_count'] = len(lines) np.random.shuffle(lines) label_set = set() for line in lines: img_path, label = line.split() label_set.add(label) train_parameters['class_dim'] = len(label_set) print("class dim:{0} image count:{1}".format(train_parameters['class_dim'], train_parameters['image_count'])) def reader(): for line in lines: if mode == 'train' or mode == 'val': img_path, label = line.split() img_path = os.path.join(data_dir, img_path) img = Image.open(img_path) try: if img.mode != 'RGB': img = img.convert('RGB') if train_parameters['image_distort_strategy']['need_distort'] == True: img = distort_image(img) mirror = int(np.random.uniform(0, 2)) if mirror == 1: img = img.transpose(Image.FLIP_LEFT_RIGHT) img = resize_img(img, train_parameters['input_size']) # HWC--->CHW && normalized img = np.array(img).astype('float32') img -= train_parameters['mean_rgb'] img = img.transpose((2, 0, 1)) # HWC to CHW img *= 0.007843 yield img, int(label) except Exception as e: pass elif mode == 'test': img_path = os.path.join(data_dir, line) if img.mode != 'RGB': img = img.convert('RGB') img = resize_img(img, train_parameters['input_size']) yield img return reader # 优化器 def optimizer_momentum_setting(): """ 阶梯型的学习率适合比较大规模的训练数据 """ learning_strategy = train_parameters['momentum_strategy'] batch_size = train_parameters["train_batch_size"] iters = train_parameters["image_count"] // batch_size lr = learning_strategy['learning_rate'] boundaries = [i * iters for i in learning_strategy["lr_epochs"]] values = [i * lr for i in learning_strategy["lr_decay"]] learning_rate = fluid.layers.piecewise_decay(boundaries, values) optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9) return optimizer def optimizer_rms_setting(): """ 阶梯型的学习率适合比较大规模的训练数据 """ batch_size = train_parameters["train_batch_size"] iters = train_parameters["image_count"] // batch_size learning_strategy = train_parameters['rsm_strategy'] lr = learning_strategy['learning_rate'] boundaries = [i * iters for i in learning_strategy["lr_epochs"]] values = [i * lr for i in learning_strategy["lr_decay"]] optimizer = fluid.optimizer.RMSProp( learning_rate=fluid.layers.piecewise_decay(boundaries, values), regularization=fluid.regularizer.L2Decay(0.00005)) return optimizer def optimizer_sgd_setting(): """ loss降低相对较慢,可是最终效果不错,阶梯型的学习率适合比较大规模的训练数据 """ learning_strategy = train_parameters['momentum_strategy'] batch_size = train_parameters["train_batch_size"] iters = train_parameters["image_count"] // batch_size lr = learning_strategy['learning_rate'] boundaries = [i * iters for i in learning_strategy["lr_epochs"]] values = [i * lr for i in learning_strategy["lr_decay"]] learning_rate = fluid.layers.piecewise_decay(boundaries, values) optimizer = fluid.optimizer.SGD(learning_rate=learning_rate) return optimizer def optimizer_adam_setting(): """ 可以比较快速的下降 loss,可是相对后期乏力。对于小规模的数据,比较适合 """ optimizer = fluid.optimizer.Adam(learning_rate=0.01) return optimizer # 保存模型 def save_model(base_dir, base_name, feed_var_list, target_var_list, program, exe): fluid.io.save_persistables(dirname=base_dir, filename=base_name + '-retrain', main_program=program, executor=exe) fluid.io.save_inference_model(dirname=base_dir, params_filename=base_name + '-params', model_filename=base_name + '-model', feeded_var_names=feed_var_list, target_vars=target_var_list, main_program=program, executor=exe)
设置读取训练数据,组装模型,检验训练精度。在代码中能够更换想要的模型
In[6]
init_log_config() train_prog = fluid.Program() train_startup = fluid.Program() print("create prog success") logger.info("create prog success") logger.info("train config:%s", train_parameters) logger.info("build input custom reader and data feeder") file_list = os.path.join(train_parameters['data_dir'], "label.txt") mode = train_parameters['mode'] batch_reader = paddle.batch(custom_image_reader(file_list, train_parameters['data_dir'], mode), batch_size=train_parameters['train_batch_size'], drop_last=True) place = fluid.CUDAPlace(0) if train_parameters['use_gpu'] else fluid.CPUPlace() img = fluid.layers.data(name='img', shape=train_parameters['input_size'], dtype='float32') label = fluid.layers.data(name='label', shape=[1], dtype='int64') feeder = fluid.DataFeeder(feed_list=[img, label], place=place) logger.info("build newwork") # ~~~~~~替换模型在此~~~~~~ # model = ResNet(layers=50) # model = VGGNet(layers=16) model = MobileNet() out = model.net(input=img, class_dim=train_parameters['class_dim']) cost = fluid.layers.cross_entropy(out, label) avg_cost = fluid.layers.mean(x=cost) acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1) # optimizer = optimizer_rms_setting() optimizer = optimizer_momentum_setting() # optimizer = optimizer_sgd_setting() # optimizer = optimizer_adam_setting() optimizer.minimize(avg_cost) exe = fluid.Executor(place)
2019-04-24 16:09:22,866 - INFO - create prog success 2019-04-24 16:09:22,867 - INFO - train config:{'input_size': [3, 224, 224], 'class_dim': -1, 'data_dir': 'data/data62/train', 'save_model_dir': './classify-model', 'mode': 'train', 'num_epochs': 120, 'image_count': -1, 'train_batch_size': 50, 'mean_rgb': [127.5, 127.5, 127.5], 'use_gpu': True, 'image_distort_strategy': {'need_distort': True, 'expand_prob': 0.5, 'expand_max_ratio': 4, 'hue_prob': 0.5, 'hue_delta': 18, 'contrast_prob': 0.5, 'contrast_delta': 0.5, 'saturation_prob': 0.5, 'saturation_delta': 0.5, 'brightness_prob': 0.5, 'brightness_delta': 0.125}, 'rsm_strategy': {'learning_rate': 0.02, 'lr_epochs': [20, 40, 60, 80, 100], 'lr_decay': [1, 0.5, 0.25, 0.1, 0.01, 0.002]}, 'momentum_strategy': {'learning_rate': 0.005, 'lr_epochs': [20, 40, 60, 80, 100], 'lr_decay': [1, 0.5, 0.25, 0.1, 0.01, 0.002]}} 2019-04-24 16:09:22,867 - INFO - build input custom reader and data feeder 2019-04-24 16:09:22,881 - INFO - build newwork
create prog success class dim:2 image count:23202
模型训练主体训练,有必定提早中止策略。炼丹开始! 注意观察loss的变化,而后开始不一样任务的不一样调参吧~
In[5]
main_program = fluid.default_main_program() exe.run(fluid.default_startup_program()) # 若是有训练过的参数,能够经过打开这句话来加载接着训练 # fluid.io.load_persistables(dirname=train_parameters['save_model_dir'], filename=model.name() + '-retrain', main_program=main_program, executor=exe) train_fetch_list = [avg_cost.name, acc_top1.name, out.name] successive_count = 0 stop_train = False total_batch_count = 0 for pass_id in range(train_parameters["num_epochs"]): logger.info("current pass: %d, start read image", pass_id) batch_id = 0 for step_id, data in enumerate(batch_reader()): t1 = time.time() loss, acc1, pred_ot = exe.run(main_program, feed=feeder.feed(data), fetch_list=train_fetch_list) t2 = time.time() batch_id += 1 total_batch_count += 1 period = t2 - t1 loss = np.mean(np.array(loss)) acc1 = np.mean(np.array(acc1)) if batch_id % 10 == 0: print("Pass {0}, trainbatch {1}, loss {2}, acc1 {3}, time {4}".format(pass_id, batch_id, loss, acc1, "%2.2f sec" % period)) logger.info("Pass {0}, trainbatch {1}, loss {2}, acc1 {3}, time {4}".format(pass_id, batch_id, loss, acc1, "%2.2f sec" % period)) if acc1 >= 0.93: successive_count += 1 fluid.io.save_inference_model(dirname=train_parameters['save_model_dir'], params_filename=model.name() + '-params', model_filename=model.name() + '-model', feeded_var_names=['img'], target_vars=[out], main_program=main_program, executor=exe) if successive_count >= 5: logger.info("end training") print("end training") stop_train = True break else: successive_count = 0 if total_batch_count % 400 == 0: logger.info("temp save {0} batch train result".format(total_batch_count)) print("temp save {0} batch train result".format(total_batch_count)) fluid.io.save_persistables(dirname=train_parameters['save_model_dir'], filename=model.name() + '-retrain', main_program=main_program, executor=exe) if stop_train: break save_model(train_parameters['save_model_dir'], model.name() + '-final', ['img'], [out], main_program, exe)
2019-04-24 16:09:26,127 - INFO - current pass: 0, start read image 2019-04-24 16:09:33,745 - INFO - Pass 0, trainbatch 10, loss 0.6780937910079956, acc1 0.6200000047683716, time 0.08 sec
Pass 0, trainbatch 10, loss 0.6780937910079956, acc1 0.6200000047683716, time 0.08 sec
将单个模型参数文件拆分红多个模型参数文件,以便后面的操做
In[20]
# -*- coding: UTF-8 -*- """ 模型转换工具,将已经保存的多参数文件合并为单参数文件 """ import os import paddle import paddle.fluid as fluid def muti_to_single(base_name, feeeze_path, out_path): """ param base_name: 模型的基本名字 param feeeze_path: 多文件预测模型所保存的目录 param out_path: 合并后文件的输出路径 """ place = fluid.CPUPlace() exe = fluid.Executor(place) [inference_program, feed_target_names, fetch_targets] = paddle.fluid.io.load_inference_model(feeeze_path, exe) fluid.io.save_inference_model(dirname=out_path, params_filename=base_name + '-params', model_filename=base_name + '-model', feeded_var_names=feed_target_names, target_vars=fetch_targets, main_program=inference_program, executor=exe) def single_to_muti(base_name, feeeze_path, out_path): """ param base_name: 模型的基本名字 param feeeze_path: 多文件预测模型所保存的目录 param out_path: 合并后文件的输出路径 """ place = fluid.CPUPlace() exe = fluid.Executor(place) [inference_program, feed_target_names, fetch_targets] = paddle.fluid.io.load_inference_model(feeeze_path, exe, params_filename=base_name + '-params', model_filename=base_name + '-model', ) fluid.io.save_inference_model(dirname=out_path, feeded_var_names=feed_target_names, target_vars=fetch_targets, main_program=inference_program, executor=exe) if __name__ == '__main__': # muti_to_single('yolov3', 'freeze', '.') single_to_muti('mobile-net', 'classify-model', 'freeze-model')
训练完成以后,咱们可使用训练好的模型来进行预测,看看猫猫的图片是否能预测类别1,狗狗的图片是类别0
In[14]
from __future__ import absolute_import from __future__ import division from __future__ import print_function import scipy.io as sio import os import numpy as np import random import time import six import sys import functools import time import math import paddle import paddle.fluid as fluid import paddle.dataset.flowers as flowers import argparse import functools import subprocess import distutils.util from paddle.fluid import core from paddle.fluid.param_attr import ParamAttr from PIL import Image, ImageEnhance import logging target_size = [3, 224, 224] mean_rgb = [127.5, 127.5, 127.5] place = fluid.CPUPlace() exe = fluid.Executor(place) path = "classify-model" [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model(dirname=path, params_filename ='mobile-net-params', model_filename='mobile-net-model', executor=exe) # print(fetch_targets) def resize_img(img, target_size): percent_h = float(target_size[1]) / img.size[1] percent_w = float(target_size[2]) / img.size[0] percent = min(percent_h, percent_w) resized_width = int(round(img.size[0] * percent)) resized_height = int(round(img.size[1] * percent)) img = img.resize((resized_width, resized_height), Image.ANTIALIAS) w_off = (target_size[1] - resized_width) / 2 h_off = (target_size[2] - resized_height) / 2 array = np.ndarray((target_size[1], target_size[2], target_size[0]), np.uint8) array[:, :, 0] = 127.5 array[:, :, 1] = 127.5 array[:, :, 2] = 127.5 ret = Image.fromarray(array) ret.paste(img, (int(w_off), int(h_off))) return ret def read_image(img_path): img = Image.open(img_path) if img.mode != 'RGB': img = img.convert('RGB') img = resize_img(img, target_size) img = np.array(img).astype('float32') img -= mean_rgb img = img.transpose((2, 0, 1)) # HWC to CHW img *= 0.007843 img = img[np.newaxis,:] return img def infer(image_path): tensor_img = read_image(image_path) t1 = time.time() label = exe.run(inference_program, feed={feed_target_names[0]: tensor_img}, fetch_list=fetch_targets) period = time.time() - t1 print("predict result:{0} cost time:{1}".format(label, "%2.2f sec" % period)) return period, np.argmax(label) # image_path = sys.argv[1] # 1--4是狗狗的图片,5--11是猫猫的图片 image_path = 'data/data62/test/723.jpg' # 这是一张狗狗的照片 period, result = infer(image_path) print(result)
普通的模型并不能很好地运行在开发板等特定硬件上, 为了在特定硬件上部署, 须要借助一些工具.
本次演示的是Paddle派转换工具.
首先先拉取并解压模型转换工具:
In[ ]
!mkdir /home/aistudio/work/ncc !wget "https://platform.bj.bcebos.com/sdk%2Fncc-linux-x86_64.tar.gz" -O ncc-linux-x86_64.tar.gz !tar -zxvf ncc-linux-x86_64.tar.gz -C /home/aistudio/work/ncc
而后进行模型压缩. 咱们须要进行量化。为了保证量化后的精度, 须要使用训练图片调整模型。
In[18]
import codecs import shutil import os target_dir = 'work/images/' if not os.path.exists(target_dir): os.makedirs(target_dir) for i in range(200, 300): source_path = os.path.join('data/data62/train/', str(i) + '.jpg') target_path = os.path.join(target_dir, str(i) + '.jpg') if os.path.exists(source_path): shutil.copy(source_path, target_path)
最后进行进行模型转换, 而后将模型进行下载, 就能够部署到本身的Paddle派开发板上了
In[ ]
!/home/aistudio/work/ncc/ncc -i paddle -o k210model --dataset work/images/ freeze-model mobilenet.kmodel
至此演示所有完成. 若是有问题, 能够邮件至 aistudio@baidu.com咨询
>> 访问 PaddlePaddle 官网,了解更多相关内容。