机器学习迁移模型到IOS

https://paulswith.github.io/2018/02/24/%E8%BF%81%E7%A7%BB%E5%AD%A6%E4%B9%A0InceptionV3/ 上文记录了如何从一个别人训练好的模型, 切入我们自己的图片, 改为我们自己的模型.
本来以为移植到手机很简单, 但是不简单的是我的模型本身就是迁移学习别人的模型,有很多莫名其面的坑, 在CoreML经历了N个坑后,1点14分我搞掂了.

项目源码和转换源码已经上传到git.
https://github.com/Paulswith/machineLearningIntro/tree/master/classification_101

转化为mlmodel

说说转换为mlmodel的工具有两个:

接着往下看:

是否是graph-pb?

如果你跟我一样, 训练的模型, 从tensorflow的代码保存下来的, 调用的:

1
saver.save(sess, MODEL_SAVEPATH, global_step=50)

它并不会保存出一个pb文件, 其中的.meta也需要其他方式转换似乎也可以, 我没有尝试过.
用这个方法, 你需要在上方代码的下面加两行,就可以继续:

1

2
if i %SAVE_EPOCH == 0:

tf.train.write_graph(sess.graph, MODEL_SAVE_DIR, 'model.pbtxt')

pdtxt固化为pd

操作参考链接https://www.jianshu.com/p/091415b114e2
我是直接使用的bezel, 编译tensorflow源码后, 直接使用, 其中参数跟着填, 需要注意的是output_node:

导入化图

导入图和查看图的节点信息:
如果你的图不属于pb文件, 那么就会在导入图的时候报错的.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
with open(TF_MODEL_FILE, 'rb') as f:

serialized = f.read()

tf.reset_default_graph()

original_gdef = tf.GraphDef()

original_gdef.ParseFromString(serialized)

with tf.Graph().as_default() as g:

tf.import_graph_def(original_gdef, name='')

ops = g.get_operations()

try:

for i in range(10000):

print('op id {} : op name: {}, op type: "{}"'.format(str(i),ops[i].name, ops[i].type))

except:

print("全部节点已打印完毕.")

pass

预处理节点

其实这一步个人不是很清楚很知道它做了什么,但确是不得不做的. 最后的大小看着也不像是”减包”
需要注意两点:

  • input_node_names: 这里填写的节点从上方的代码可以打印看得到的, 实际在训练模型的时候, 我们直接喂图片的节点是在”import/DecodeJpeg/contents”, 而这里”必须是import/Mul”.
  • output_node_names: 因为模型是剪切拼接的, 这跟tensorflow直接调用是一样的节点.
    最后生成一个完整的pb文件.
    1
    
    2
    
    3
    
    4
    
    5
    
    6
    
    7
    
    8
    
    9
    
    10
    
    11
    
    12
    
    input_node_names = ['import/Mul', 'BottleneckInputPlaceholder']   # 本来以为是import/DecodeJpeg/contents, 实际上是Mul(tfcoreml-git上说的)
    
    output_node_names = ['import/pool_3/_reshape','final_train_ops/softMax_last']  # 想要保存的节点 , 'final_train_ops/softMax_last'
    
    gdef = strip_unused_lib.strip_unused(
    
    input_graph_def = original_gdef,
    
    input_node_names = input_node_names,
    
    output_node_names = output_node_names,
    
    placeholder_type_enum = dtypes.float32.as_datatype_enum)
    
    with gfile.GFile(FROZEN_MODEL_FILE, "wb") as f:
    
    f.write(gdef.SerializeToString())
    

开始转换

  • input_tensor_shapes: 是placeholder 和 input节点, 方括号的第一个参数是batch大小,代表一张一张的喂给它. 字典里面这两个, 对应生成后的InceptionV3_input的两个属性.
  • output_tensor_names: 训练后得到的节点, 对应生成后的InceptionV3_output的两个属性
    1
    
    2
    
    3
    
    4
    
    5
    
    6
    
    7
    
    8
    
    9
    
    10
    
    11
    
    12
    
    13
    
    14
    
    15
    
    16
    
    17
    
    input_tensor_shapes = {
    
    "import/Mul:0":[1,299,299,3],          # batch size is 1
    
    "BottleneckInputPlaceholder:0":[1,2048],
    
    }
    
    output_tensor_names = ['import/pool_3/_reshape:0','final_train_ops/softMax_last:0']
    
    # Call the converter. This may take a while
    
    coreml_model = tfcoreml.convert(
    
    tf_model_path=FROZEN_MODEL_FILE,
    
    mlmodel_path=COREML_MODEL_FILE,
    
    input_name_shape_dict=input_tensor_shapes,
    
    output_feature_names=output_tensor_names,
    
    image_input_names = ['import/Mul:0'],
    
    red_bias = -1,
    
    green_bias = -1,
    
    blue_bias = -1,
    
    image_scale = 2.0/255.0)
    

上方具体的参数可以在方法看得到,后面四个参数就是我们输入图片时候的均值化, 还有个特殊的参数class_labels, output后的模型可以直接索引到标签, 但是在实践过程中, 我这个本身是迁移别人的学习的模型并起不到作用.

执行完成后生成文件:

ios-code调用

了解模型:

首先, 直接将inceptionV3.mlmodel拖入到工程:
导入头文件, inceptionV3.h, 点开查看:

他们之间的关系是, inception_v3_input导入 -> 启动inception_v3.model训练 -> 得到inception_v3_output 分别提供了一个实例化方法.

开始代码

首先确认他们之间的调用方向 层次, 我直接是参考tensorflow加载的顺序, 只要理解了, 就可以直接调用了:
这是Py 大专栏  机器学习迁移模型到IOSthon的调用方法:

1

2
poo3_frist = sess.run(poo3, feed_dict={inpiut_x: image})   # 按照模型的顺序要, 先喂给它图片, 然后图片提取到瓶颈的tensor

result     = sess.run(predict, feed_dict={change_input:poo3_frist}) # 瓶颈的tensor再转入input传入, 得到我们最后的predict

如果参照tensorflow加载模型的做法, 我们直接是一张图片, 得到一个run到pool3, 但实际CoreML只给我们生成了一个实例方法:

1
- (instancetype)initWithBottleneckInputPlaceholder__0:(MLMultiArray *)BottleneckInputPlaceholder__0 import__Mul__0:(CVPixelBufferRef)import__Mul__0;

方法必须要传入一个MLMultiArray, 而且shape必须一致的. 最后我直接调用MLMultiArray的方法,生成一个0值的2048shape

预测部分的, 完整核心代码 均有详细的注释说明

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29
- (inception_v3 *)tfModel {

if (!_tfModel) {

// 1 加载模型, 本身代码会调用init的时候, 方法会调用initWithContentsOfURL, 找到inception文件进行初始化

_tfModel = [[inception_v3 alloc] init];

}

return _tfModel;

}

- (NSString *)predictWithFoodImage:(UIImage *)foodImage

{

// step1: 标准为size, 转为可传入的参数.

UIImage *img = [foodImage scaleToSize:CGSizeMake(299, 299)];             // 转换为可传参的图片大小

CVPixelBufferRef refImage = [[UIImage new] pixelBufferFromCGImage:img];  // 转换为可传参的类型

// step2.1: 由于一开始是没有BottleneckInputPlaceholder, 直接0值初始一个传入

MLMultiArray *holder = [[MLMultiArray alloc] initWithShape:@[@2048] dataType:MLMultiArrayDataTypeDouble error:nil];

// step2.2: 启动预测, 预测完成后得到import__pool_3___reshape__0

inception_v3Output *output = [self.tfModel predictionFromBottleneckInputPlaceholder__0:holder import__Mul__0:refImage error:nil];

// step3: 从第二步, 完整得到了想要的BottleneckInputPlaceholder, 直接代入, 图片也代入.

inception_v3Output *output1 = [self.tfModel predictionFromBottleneckInputPlaceholder__0:output.import__pool_3___reshape__0 import__Mul__0:refImage error:nil];

// step4: 从final_train_ops__softMax_last__0提取预测结果

MLMultiArray *__final = output1.final_train_ops__softMax_last__0;

return [self poAccu:__final];

}

调用摄像头进行图片获取

通过整合代码层次, 代码调用也封装好了, 方便代用:
从简书上拿到别人写好的调用摄像头拍照https://www.jianshu.com/p/62d69d89fa43, 提取了下代码:
主要逻辑:
拍照后重置大小展示到view, 异步进行模型预测, 回到主线程展示label结果.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44
//触发事件:拍照

- (void)addCamera

{

UIImagePickerController *picker = [[UIImagePickerController alloc] init];

picker.delegate = self;

picker.allowsEditing = YES; //可编辑

//判断是否可以打开照相机

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

//摄像头

picker.sourceType = UIImagePickerControllerSourceTypeCamera;

} else { //否则打开照片库

picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

}

[self presentViewController:picker animated:YES completion:nil];

}

//拍摄完成后要执行的代理方法

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

{

NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

if ([mediaType isEqualToString:@"public.image"]) {

//得到照片

UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

image = [image scaleToSize:self.imageView.frame.size];

self.imageView.image = image;

// 异步处理, 不要占用主线程:

dispatch_async(dispatch_queue_create(0, 0), ^{

NSString *preString = [self.prediction predictWithFoodImage:image];

dispatch_async(dispatch_get_main_queue(), ^{

self.preLabel.text = preString;

});

});

}

[self dismissViewControllerAnimated:YES completion:nil];

}

//进入拍摄页面点击取消按钮

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker

{

[self dismissViewControllerAnimated:YES completion:nil];

}

测试结果

制作gif, 直接看图片: 或者上面链接从我的git上下载源码:

[好困, 该睡觉了...]

原文地址:https://www.cnblogs.com/lijianming180/p/12147618.html

时间: 2024-10-13 20:28:36

机器学习迁移模型到IOS的相关文章

ios开发——实用技术篇&Pist转模型详细介绍

Pist转模型详细介绍 关于Plist转模型在iOS开发中是非常常见的,每开一一个项目或者实现一个功能都要用到它,所以今天就给大家讲讲Plist怎么转成模型数据, 前提:必须有一个Plist文件或者通过一定的方式返回的plist数据 一:直接加载Plist数据 1 //定义一个数组属性 2 @property (nonatomic, assign) NSArray *apps; 获取Plist文件 1 //懒加载plist文件,返回一个apps数据,后面直接使用旧可以 2 -(NSArray *

从BSP模型到Apache Hama

? 什么是BSP模型 概述 BSP(Bulk Synchronous Parallel,整体同步并行计算模型)是一种并行计算模型,由英国计算机科学家Viliant在上世纪80年代提出.Google发布的一篇论文(<Pregel: A System for Large-Scale Graph Processing>)使得这一概念被更多人所认识,据说在Google 80%的程序运行在MapReduce上,20%的程序运行在Pregel上.和MapReduce一样,Google并没有开源Pregel

iOS开发设计模式之MVC

本片文章介绍MVC设计模式及其在iOS开发中的使用技巧 什么是MVC设计模式 MVC是设计模式的起源,也是在软件工程领域使用最多的一种设计模式. MVC即:Model模型.View视图.Controller控制器 模型: 在模型对象中封装数据并定义操作数据的特定逻辑行为 模型可以表示复杂的数据结构,可以是屏幕上显示的数据.等待处理的数据等 视图: 使用视图对象向用户展示信息 视图对象可以响应用户的操作,并懂得如何将自己展现在屏幕上 视图对象通常从应用程序的模型对象中获取数据用以展示. 可以跟一个

ios开发小技巧-用宏化简代码

在IOS开发中,要做字典转模型一般情况如下: 1 /** 2 * 声明方法 3 */ 4 - (instancetype) initWithDictionary:(NSDictionary *)dict; 5 + (instancetype) carWithDictionary:(NSDictionary *)dict; 6 7 /** 8 * 实现方法 9 */ 10 - (instancetype)initWithDictionary:(NSDictionary *)dict 11 { 12

iOS Sprite Kit教程之编敲代码以及Xcode的介绍

iOS Sprite Kit教程之编敲代码以及Xcode的介绍 Xcode界面介绍 一个Xcode项目由非常多的文件组成,比如代码文件.资源文件等.Xcode会帮助开发人员对这些文件进行管理.所以,Xcode的界面也比較复杂,如图1.40所看到的. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > 图

ios基础篇(十八)——Delegate 、NSNotification 和 KVO用法及其区别

一.Delegate Delegate本质是一种程序设计模型,iOS中使用Delegate主要用于两个页面之间的数据传递.iphone中常用@protocol和delegate的机制来实现接口的功能.例如想在A的功能要在B中实现,可以在A中定义一个Protocol. protocol用法: @interface ClassA :ClassB<protocol1, protocol2> 1.首先声明一个UIView类: @interface myView  :UIView{  } @end: 2

IOS私有API的使用

首先要使用class-dump 和dumpFrameworks.pl 工具 将ios的framework导出来. 以下是工具的下载地址: class-dump下载地址http://www.codethecode.com/projects/class-dump/ DumpFrameworks.pl原来的下载地址不能用了,我在github上的别人的越狱程序里面找到的. 将下载好的 class-dump 放入usr/local/bin 下. 如果 '/usr/local/bin' 不知道在哪里,可以在

iOS 9应用开发教程之创建iOS 9项目与模拟器介绍

iOS 9应用开发教程之创建iOS 9项目与模拟器介绍 编写第一个iOS 9应用 本节将以一个iOS 9应用程序为例,为开发者讲解如何使用Xcode 7.0去创建项目,以及iOS模拟器的一些功能.编辑界面等内容. 创建iOS 9项目 一个iOS应用的所有文件都在一个Xcode项目下.项目可以帮助用户管理代码文件和资源文件.以下是使用Xcode创建项目的具体操作步骤 (1)打开Xcode,弹出Welcome to Xcode对话框,如图1.4所示. 图1.4  Welcome to Xcode对话

iOS 优化内存(八)持久化

iOS持久化 1.文件系统 不管是Mac OS X 还是iOS的文件系统都是建立在UNIX文件系统基础之上的. 1.1 沙盒模型 在iOS中,一个App的读写权限只局限于自己的沙盒目录中. 沙盒模型到底有哪些好处呢? 安全:别的App无法修改你的程序或数据 保护隐私:别的App无法读取你的程序和数据 方便删除:因为一个App所有产生的内容都在自己的沙盒中,所以删除App只需要将沙盒删除就可以彻底删除程序了 iOS App沙盒中的目录 App Bundle ,如xxx.app 其实是一个目录,里面