如何将模型部署到安卓移动端,这⾥有⼀份简单教程
本⽂介绍了如何利⽤ TensorFlow Mobile 将 PyTorch 和 Keras 模型部署到安卓移动端。
截⾄ 2018 年,全球活跃的安卓设备已经超过了 20 亿部。安卓⼿机的迅速普及在很⼤程度上得益于各种各样的智能应⽤,从地图到图⽚编辑器⽆所不有。随着深度学习技术的兴起,移动应⽤注定会变得更加智能。深度学习加持下的下⼀代移动应⽤将专门为你学习和定制功能。微软的「SwiftKey」就是⼀个很好的例⼦,它能够通过学习你常⽤的单词和短语来帮助你更快地打字。
计算机视觉、⾃然语⾔处理、语⾳识别以及语⾳合成等技术可以极⼤地提⾼移动应⽤程序各个⽅⾯的⽤户体验。幸运的是,⼈们现在已经开发出了⼤量⼯具,⽤于简化在移动应⽤中部署和管理深度学习模型的过程。在本⽂中,作者将向⼤家介绍如何使⽤ TensorFlow Mobile 将Pytorch 和 Keras 模型部署到移动设备上。
使⽤ TensorFlow Mobile 将模型部署到安卓设备上包括三个步骤:
将训练好的模型转换成 TensorFlow 格式;
向安卓应⽤添加 TensorFlow Mobile 依赖项;
编写相关的 Java 代码,在你的应⽤中使⽤ TensorFlow 模型执⾏推断。
在本⽂中,我将带你熟悉以上的整个流程,最终完成⼀个嵌⼊图像识别功能的安卓应⽤。
环境设置
在本教程中,我们将使⽤ Pytorch 和 Keras,选择你偏好的机器学习框架,并按照说明进⾏操作。因此,你的环境设置取决于你选择的框架。
第⼀步,安装 TensorFlow:
pip3 install tensorflow
如果你是⼀名 Keras 开发者,你可以使⽤下⾯的命令安装相关开发环境:
pip3 install keras
pip3 install h5py
Android Studio(精简版 3.0)
将 PyTorch 模型转换为 Keras 模型
本节仅针对于 PyTorch 开发者。如果你使⽤的是 Keras 框架,你可以直接跳到「将 Keras 模型转换为 TensorFlow 模型」这⼀节。
在转换权重之前,我们需要在 PyTorch 和 Keras 中定义 Squeezenet 模型。
在两个框架中都定义 Squeezenet,然后使⽤下⾯的⽅法将 PyTorch 框架的权重迁移到 Keras 框架中。
创建⼀个 convert.py ⽂件,引⼊下⾯的代码,并且运⾏脚本。
import torch
as nntensorflow入门教程
from torch.autograd import Variable
import keras.backend as K
dels import *
from keras.layers import *
import torch
dels import squeezenet1_1
class PytorchToKeras(object):
def __init__(self,pModel,kModel):
super(PytorchToKeras,self)
self.__source_layers = []
self.__target_layers = []
self.pModel = pModel
self.kModel = kModel
K.set_learning_phase(0)
def __retrieve_k_layers(self):
for i,layer in enumerate(self.kModel.layers):
if len(layer.weights) > 0:
self.__target_layers.append(i)
def __retrieve_p_layers(self,input_size):
input = torch.randn(input_size)
input = Variable(input.unsqueeze(0))
hooks = []
def add_hooks(module):
def hook(module, input, output):
if hasattr(module,"weight"):
self.__source_layers.append(module)
if not isinstance(module, nn.ModuleList) and not isinstance(module,nn.Sequential) and module != self.pModel:
hooks.ister_forward_hook(hook))
self.pModel.apply(add_hooks)
self.pModel(input)
for hook in hooks:
def convert(self,input_size):
self.__retrieve_k_layers()
self.__retrieve_p_layers(input_size)
for i,(source_layer,target_layer) in enumerate(zip(self.__source_layers,self.__target_layers)):
weight_size = len(source_layer.weight.data.size())
transpose_dims = []
for i in range(weight_size):
transpose_dims.append(weight_size - i - 1)
self.kModel.layers[target_layer].set_weights([source_layer.weight.data.numpy().transpose(transpose_dims), source_layer.bias.data.numpy()])
def save_model(self,output_file):
self.kModel.save(output_file)
def save_weights(self,output_file):
self.kModel.save_weights(output_file)
"""
We explicitly redefine the Squeezent architecture since Keras has no predefined Squeezent
"""
def squeezenet_fire_module(input, input_channel_small=16, input_channel_large=64):
channel_axis = 3
input = Conv2D(input_channel_small, (1,1), padding="valid" )(input)
input = Activation("relu")(input)
input_branch_1 = Conv2D(input_channel_large, (1,1), padding="valid" )(input)
input_branch_1 = Activation("relu")(input_branch_1)
input_branch_2 = Conv2D(input_channel_large, (3, 3), padding="same")(input)
input_branch_2 = Activation("relu")(input_branch_2)
input = concatenate([input_branch_1, input_branch_2], axis=channel_axis)
return input
def SqueezeNet(input_shape=(224,224,3)):
image_input = Input(shape=input_shape)
network = Conv2D(64, (3,3), strides=(2,2), padding="valid")(image_input)
network = Activation("relu")(network)
network = MaxPool2D( pool_size=(3,3) , strides=(2,2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=16, input_channel_large=64)    network = squeezenet_fire_module(input=network, input_channel_small=16, input_channel_large=64)    network = MaxPool2D(pool_size=(3,3), strides=(2,2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=32, input_channel_large=128)    network = squeezenet_fire_module(input=network, input_channel_small=32, input_channel_large=128)    network = MaxPool2D(pool_size=(3, 3), strides=(2, 2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=48, input_channel_large=192)    network = squeezenet_fire_module(input=network, input_channel_small=48, input_channel_large=192)    network = squeezenet_fire_module(input=network, input_channel_small=64, input_channel_large=256)    networ
k = squeezenet_fire_module(input=network, input_channel_small=64, input_channel_large=256)
#Remove layers like Dropout and BatchNormalization, they are only needed in training
#network = Dropout(0.5)(network)
network = Conv2D(1000, kernel_size=(1,1), padding="valid", name="last_conv")(network)
network = Activation("relu")(network)
network = GlobalAvgPool2D()(network)
network = Activation("softmax",name="output")(network)
input_image = image_input
model = Model(inputs=input_image, outputs=network)
return model
keras_model = SqueezeNet()
#Lucky for us, PyTorch includes a predefined Squeezenet
pytorch_model = squeezenet1_1()
#Load the pretrained model
pytorch_model.load_state_dict(torch.load("squeezenet.pth"))
#Time to transfer weights
converter = PytorchToKeras(pytorch_model,keras_model)
#Save the weights of the converted keras model for later use
converter.save_weights("squeezenet.h5")
在完成了上述权重转换⼯作后,你现在只需将 Keras 模型保存为 squeezenet.h5。此时,我们可以将 PyTorch 模型抛在脑后,继续进⾏我们的下⼀步⼯作。
将 Keras 模型转化为 TensorFlow 模型
创建⼀个新的 ConvertToTensorflow.py ⽂件,添加以下代码。
dels import Model
from keras.layers import *
import os
import tensorflow as tf
def keras_to_tensorflow(keras_model, output_dir, model_name,out_prefix="output_", log_tensorboard=True):
if ists(output_dir) == False:
os.mkdir(output_dir)
out_nodes = []
for i in range(len(keras_model.outputs)):
out_nodes.append(out_prefix + str(i + 1))
tf.identity(keras_model.output[i], out_prefix + str(i + 1))
sess = K.get_session()
from tensorflow.python.framework import graph_util, graph_io
init_graph = aph.as_graph_def()
main_graph = vert_variables_to_constants(sess, init_graph, out_nodes)
graph_io.write_graph(main_graph, output_dir, name=model_name, as_text=False)
if log_tensorboard:
from ls import import_pb_to_tensorboard
import_pb_to_tensorboard.import_to_tensorboard(
os.path.join(output_dir, model_name),
output_dir)
"""
We explicitly redefine the Squeezent architecture since Keras has no predefined Squeezenet
"""
def squeezenet_fire_module(input, input_channel_small=16, input_channel_large=64):
channel_axis = 3
input = Conv2D(input_channel_small, (1,1), padding="valid" )(input)
input = Activation("relu")(input)
input_branch_1 = Conv2D(input_channel_large, (1,1), padding="valid" )(input)
input_branch_1 = Activation("relu")(input_branch_1)
input_branch_1 = Activation("relu")(input_branch_1)
input_branch_2 = Conv2D(input_channel_large, (3, 3), padding="same")(input)
input_branch_2 = Activation("relu")(input_branch_2)
input = concatenate([input_branch_1, input_branch_2], axis=channel_axis)
return input
def SqueezeNet(input_shape=(224,224,3)):
image_input = Input(shape=input_shape)
network = Conv2D(64, (3,3), strides=(2,2), padding="valid")(image_input)
network = Activation("relu")(network)
network = MaxPool2D( pool_size=(3,3) , strides=(2,2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=16, input_channel_large=64)
network = squeezenet_fire_module(input=network, input_channel_small=16, input_channel_large=64)
network = MaxPool2D(pool_size=(3,3), strides=(2,2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=32, input_channel_large=128)
network = squeezenet_fire_module(input=network, input_channel_small=32, input_channel_large=128)
network = MaxPool2D(pool_size=(3, 3), strides=(2, 2))(network)
network = squeezenet_fire_module(input=network, input_channel_small=48, input_channel_large=192)
network = squeezenet_fire_module(input=network, input_channel_small=48, input_channel_large=192)
network = squeezenet_fire_module(input=network, input_channel_small=64, input_channel_large=256)
network = squeezenet_fire_module(input=network, input_channel_small=64, input_channel_large=256)
#Remove layers like Dropout and BatchNormalization, they are only needed in training
#network = Dropout(0.5)(network)
network = Conv2D(1000, kernel_size=(1,1), padding="valid", name="last_conv")(network)
network = Activation("relu")(network)
network = GlobalAvgPool2D()(network)
network = Activation("softmax",name="output")(network)
input_image = image_input
model = Model(inputs=input_image, outputs=network)
return model
keras_model = SqueezeNet()
keras_model.load_weights("squeezenet.h5")
output_dir = os.path.wd(),"checkpoint")
keras_to_tensorflow(keras_model,output_dir=output_dir,model_name="squeezenet.pb")
print("MODEL SAVED")
上述代码将 squeezenet.pb 保存到了我们的 output_dir ⽂件夹中。它还在同⼀个⽂件夹中创建了 TensorBoard 事件⽂件。为了对模型有⼀个更清晰的理解,你可以在 TensorBoard 中对其可视化。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。