发新帖

锤子科技--利用TensorflowLite分辨用户手握手机的姿势

[复制链接]
1249 5

快来加入 TensorFlowers 大家庭!

您需要 登录 才可以下载或查看,没有帐号?加入社区

x
本帖最后由 congguohua 于 2018-6-23 13:51 编辑

利用TensorflowLite区分用户手握手机姿势工程师版本
  各位好,随着机器学习逐步成熟,AI落地的场景也越来越多。AI大方向的资料大家也看过不少,往往落地实际项目时候,要解决的问题往往是相对具体的一个细节问题,而恰恰这些细节问题往往会让我们耽误很多时间。最近刚好做完了一个TensorflowLite的落地项目,其中涉及到了如何利用Estimator创建模型,使用exporter导出模型,利用toco转化模型,以及如何在移动端部署模型的全部过程。过程中也遇到了一些相对坑的地方,一并总结出来,希望给大家一个参考。接下来就给大家分享一下锤子科技如何利用TensorflowLite帮助区分用户手握手机的姿势问题。


问题背景介绍:

  锤子科技在几个月前发布了一款手机,由于摄像头是在手机屏幕的下边,因此有一个“倒置拍摄”功能。这个功能初始的目的是:当用户站立,手持手机正对自己,此时,将手机旋转180度,就可以激活拍摄了。
  通过一段时间的用户反馈,我们发现这个功能的误判率非常高,最终导致无法正常使用,并产生了一些不好的影响。看到这些反馈后我们决定优化一下这个功能。

分析问题:

  如何判断手握姿势? 首先我们想到的就是利用手机上的传感器,利用重力和加速度传感器,我们可以很方便的判断出手机当前的姿势。通过两种传感器我们可以得到3种数值。第一种是方位:即,东南西北。第二种是仰角(pitch):即,手机平放时候“抬头”或者“翘尾”的角度,第三种是翻滚角(roll):即,手机平放时候左右翻滚手机的角度。

  通过实验,我们发现当用户做一个标准的倒置动作时,pitch会有较大变化(180度左右),roll的变化并不大。当我们做一些其他动作时比如从起始位置到双手自然下垂,此时roll的变化非常大。有了以上实验结果,我们就可以进行数据的收集了,通过一定的数据标准化,我们就可以利用TensorflowLite进行模型训练了。

解决流程:
  从github下载好最新代码,当前是1.9rc1。我们这个问题本身并不复杂,是一个分类问题,主要思想就是通过2种传感器的一系列取值,来进行分类。
我们选用Estimator,Estimator隐藏了一些tf的细节,让我们更专注与业务逻辑有关的部分。这里我们并没有选择使用feature_column特性,是因为在导出时候每个feature_column都会生成一个placeholder,而我们这里会有120个feature_column导出120个placeholder感觉不太合适。因此我们选择了自定义的Estimator方式。
Estimator好处是把机器学习问题抽象成了“训练”,“评估”,“预测结果”,开发人员只需要按照要求填补好这几步骤种自己要做的事情,按要求返回必要的值就可以了。省去了很多模式化的代码,减少了出错的可能。

以下代码大家可以参考鸢尾花例子进行对照。

创建Estimator:
classifier = tf.estimator.Estimator(
        model_fn=my_model,
        config=runconfig,
        params={
           'hidden_units': [130, 130],   #两个隐层,每层130个神经元,具体可以按需调整,这里仅举例用
            'n_classes':2,                     #结果分2类
        })

写一个给train方法传递数据的例子,推荐大家用DataSet来保存训练数据,这样可以事先把要训练的数据都准备好,这样就不用在训练的代码附近加一些循环来控制epoch了:
  
  def train_input_fn(features, labels, batch_size):
    """An input function for training"""
    # Convert the inputs to a Dataset.
    tensor = (features, labels)                           # 把从csv读出来的DataFrame传进来
    dataset = tf.data.Dataset.from_tensor_slices(tensor)          #由于DataFrame是一个大表,所以我们先按照行切开


    # Shuffle, repeat, and batch the examples.
    dataset = dataset.shuffle(1000).batch(batch_size)            #重新打乱行,并按照batch_size重新组合成一个新的表

    dataset = dataset.repeat()                            #无限次重复,直到step数量到达预设值停止

    # iterator = dataset.make_one_shot_iterator()                # 这里可以打印一下自己组合完后的表是什么样子的,看看是否是自己需要的。
    # one_element = iterator.get_next()
    # with tf.Session() as sess:
    #     try:
    #         while True:
    #             print(sess.run(one_element))
    #     except tf.errors.OutOfRangeError:
    #         print("end!")

    # Return the dataset.
    return dataset

model_fn的定义:
def my_model(features, labels, mode, params):
  # net = tf.feature_column.input_layer(features, params['feature_columns']) #重点这里,不用feature_column了,直接用[-1,120]的一个tensor来表示,省去了导出时候的麻烦,这里其实就是上面那个方法返回的dataset,这样模块的耦合度会很低。
    net = features  # 直接用一个简单的Tensor来代替

    if isinstance(features, dict):
        net = features[inputSigName]

    for units in params['hidden_units']:
        net = tf.layers.dense(net, units=units, activation=tf.nn.relu)

    # Compute logits (1 per class).
    logits = tf.layers.dense(net, params['n_classes'], activation=None)

    # Compute predictions.
    predicted_classes = tf.argmax(logits, 1)
。。。。。。。。。。。。。

  我们这里只有2个隐含层,每层有130个神经元,输出2类。剩下的需要定义,训练,评估,预测的函数,这里并没有什么特别的大家参考tf的例子就可以完成了。

接下来说说可能有坑的地方。

如果大家都是在PC端进行AI的开发的话,是没什么问题的,但是如果需要向移动设备部署的时候会有一些限制,而这些限制在实际使用时候非常容易忽视,导致在部署移动设备端的时候被卡住。

实际遇到的一个坑:
        在写Estimator的model_fn函数时候我们需要按照要求返回一些值,此时要特别注意如果这个模型是要被TFLite使用的时候,要注意尽量只返回必要的值。并做好不支持的op的剔除工作。

def my_model(features, labels, mode, params):
。。。。。。
if mode == tf.estimator.ModeKeys.PREDICT:
   predictions = {
     # 'class_ids': predicted_classes[:, tf.newaxis],
     outputSigName:tf.nn.softmax(logits),
   }
   Return tf.estimator.EstimatorSpec(
mode,
predictions=predictions,
  export_outputs={
。。。
  }
  )
这段代码相信大家都比较熟悉,是TF的例子中的一段,其中红色的那一行“class_ids…”是为了在后面打印结果考虑的一个输出,对于问题本身并没有什么影响,但如果你带着这行导出了模型,并且在移动端load的时候就会报错,错误原因是不支持new_axis操作符。顺便说一下,导出时候需要在model_fn函数里的predict部分的return里,加上export_output项,否则导出时候会报错。大概就是这个样子:

    if mode == tf.estimator.ModeKeys.PREDICT:
         。。。。。。
         return tf.estimator.EstimatorSpec(mode,
                           predictions=predictions,
                           export_outputs={
                                      outputSig: tf.estimator.export.PredictOutput(predictions)
                                      }
           )

导出代码利用Estimator的export_savedMode就可以了,以下是我的导出代码:

my_input_holder = tf.placeholder(dtype=tf.float32, shape=[None, 120])   #我的输入是一系列120维的向量,就是每个动作的传感器值
serving_input_receiver_fn = tf.estimator.export.build_raw_serving_input_receiver_fn({inputSigName : my_input_holder})      #我不需要serving,所以直接按照Raw导出了
classifier.export_savedmodel(export_dir_base="/home/pc/export", serving_input_receiver_fn=serving_input_receiver_fn, as_text=True)  # 导出

  如果一切都运行良好,这时应该可以得到可以正确分类的模型了。下一步就是模型导出和转化了。模型转化为TFLite格式的工具叫TOCO。TOCO支持从.pb文件到.tflite转化。TF导出的模型中,扩展名同为.pb的文件,还是有不同的。这里容易混淆,其中有固化了参数的,还有没有固化参数的。Toco支持的是固化了参数的.pb文件。

  这里给大家推荐一条模型导出转化的相对好的路线:Estimator支持导出SaveModel格式的模型,而1.9版本的Toco进行了更新可以利用TocoConverter直接将SavedModel格式导出为.tflite格式,省去了很多模型格式转化的步骤。
我的转化代码:
signature_key = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY
input_key = tf.saved_model.signature_constants.CLASSIFY_INPUTS
output_key = tf.saved_model.signature_constants.PREDICT_OUTPUTS

saved_model_dir = "/home/pc/export/saved/"
converter = lite.TocoConverter.from_saved_model(saved_model_dir) # 就这一句非常方便,1.9新的接口,一大福音。。。。
tflite_model = converter.convert()

open("/home/pc/export/tflite/converted_model.tflite", "wb").write(tflite_model)

到这里重点工作就给大家介绍完了,部署到移动端后,就可以根据业务逻辑进行推理了。

总结:
  大家知道在PC端,我们可以利用GPU进行AI计算的加速,而移动端往往智能靠CPU勉强支撑。其实在移动端也是有计算加速手段的,向GPU,DSP,XPU,这些资源在移动设别中,多数时候并没有被充分利用起来,导致大家都在怀疑移动设备能否担当AI计算的重任。TFLite支持NNAPI,而NNAPI就是把之前说的那些移动设备的计算资源整合起来的桥梁。NNAPI是在安卓底层的一个模块,只要平台实现了NNAPI的对应接口,那么TFLite就可以直接利用这些设备进行AI计算的加速了。有了这些加速手段,各种应用场景就可以直接在设备端落地,而不用非得在云端,省去了“断网即变傻”的尴尬状况。

  众所周知,手机已经代替电脑成为了和人交互次数最多的电子设备,人类对AI智能的需求理所当然的应该由手机承接。随着智能设备的发展,在设备端进行AI推理的需求也会越来越大。TFLite的出现正好满足这个需求,它的小巧精悍将来可以进入各种智能设备终端,在不同维度帮助人们更“智能”的生活。

精彩评论5

舟3332  TF芽芽  发表于 2018-6-14 09:35:33 | 显示全部楼层
太赞了~ 用的什么模型呀?
congguohua  TF荚荚  发表于 2018-6-15 10:19:03 | 显示全部楼层
舟3332 发表于 2018-6-14 09:35
太赞了~ 用的什么模型呀?

对不起,时间匆忙没有写的很详细,模型就是用的简单的全连接,稍后会补全一些细节。
congguohua  TF荚荚  发表于 2018-6-23 18:51:49 来自手机  | 显示全部楼层
更新了具体细节
neverchange  TF豆豆  发表于 2018-7-5 11:04:23 | 显示全部楼层
TFLite很好的进阶,目前的TFLite才是开始推广应用的阶段,小伙伴们可以参考学习。
xjzdsw  TF豆豆  发表于 2018-7-10 14:02:40 | 显示全部楼层
学习
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

快速回复 返回顶部 返回列表