关于TensorFlow Lite Android 端推理结果不一致的问题

项目场景:

刚入坑TensorflowLite,用CNN写了个简单的人脸识别模型,数据集分3类,前两类使用OpenCV通过摄像头采集两个人的人脸,后一类为其他人的人脸。input_shape = (64, 64 , 3), 模型代码如下:

def face_model():
    model = tf.keras.Sequential([
        tf.keras.layers.Conv2D(filters=32, kernel_size=5, padding='same', input_shape=(64, 64, 3), activation=tf.nn.relu),
        tf.keras.layers.Conv2D(filters=64, kernel_size=3, padding='same', activation=tf.nn.relu),
        tf.keras.layers.MaxPool2D(pool_size=2, strides=2, padding='same'),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(3, activation='softmax')
    ])
    return model

加载数据集:

def load_dataset():
    image_list = []
    label_list = []
    user_num = 0
    for user in os.listdir(file_dir):
        user_dir = os.path.join(file_dir, user)
        if not os.listdir(user_dir):
            continue

        for face_file in os.listdir(user_dir):
            img_path = os.path.join(user_dir, face_file)
            cvimg = cv2.imread(img_path)
            image_list.append((cvimg - 128.0 ) / 128.0)
            label_list.append(user_num)
        user_num += 1
   
    image_list = np.array(image_list)
    label_list = np.array(label_list)

    # 打乱数据
    per = np.random.permutation(image_list.shape[0])
    all_image_list = image_list[per]
    all_label_list = label_list[per]

    # 划分训练集,测试集
    total_data_num = all_image_list.shape[0]
    train_num = int(total_data_num * 0.8)

    train_image = all_image_list[:train_num]
    train_label = all_label_list[:train_num]
    test_image = all_image_list[train_num:]
    test_label = all_label_list[train_num:]

    return (train_image, train_label), (test_image, test_label)

训练模型并转换为tflite模型:

def train():
    # 加载数据集
    (train_image, train_label), (test_image, test_label) = load_dataset()
    train_image = train_image.reshape((train_image.shape[0], train_image.shape[1], train_image.shape[2], 3))
    test_image = test_image.reshape((test_image.shape[0], test_image.shape[1], test_image.shape[2], 3))

    model = face_model()
    model.compile(
        optimizer=tf.keras.optimizers.Adam(lr=0.001),
        loss=tf.keras.losses.sparse_categorical_crossentropy,
        metrics=['accuracy']
    )
    history = model.fit(
        train_image, train_label,
        epochs=20,
        validation_freq=0.2,
        #callbacks=[cp_callback]
    )
    model.evaluate(test_image, test_label)
	converter = tf.lite.TFLiteConverter.from_keras_model(model)
    tflite_model = converter.convert()
    with open('../models/model/model.tflite', 'wb') as f:
        f.write(tflite_model)
	

训练过程如下:

...
371/371 [==============================] - 0s 412us/sample - loss: 1.1582e-04 - accuracy: 1.0000
Epoch 20/20

 32/371 [=>............................] - ETA: 0s - loss: 2.2099e-04 - accuracy: 1.0000
160/371 [===========>..................] - ETA: 0s - loss: 1.0828e-04 - accuracy: 1.0000
288/371 [======================>.......] - ETA: 0s - loss: 1.1207e-04 - accuracy: 1.0000
371/371 [==============================] - 0s 407us/sample - loss: 1.0737e-04 - accuracy: 1.0000

32/93 [=========>....................] - ETA: 0s - loss: 3.4331e-04 - accuracy: 1.0000
93/93 [==============================] - 0s 3ms/sample - loss: 0.0030 - accuracy: 1.0000

可以看到,经过几轮训练,精度已经非常高了。找了张人脸预测,也与预期一致。

y_pred = net.predict(face03)
print(np.around(y_pred[1], 3))
[0.000002, 0.000007, 0.999990]
然后开始迁移至Android平台测试。 然后问题来了!!!

问题描述:

为了便于验证结果,在Android中使用了PC端相同的人脸照片测试,然而输出结果完全不一样。

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.face03);

ImageProcessor imageProcessor = new ImageProcessor.Builder()
		.add(new ResizeOp(64, 64, ResizeOp.ResizeMethod.NEAREST_NEIGHBOR))
		.add(new NormalizeOp(IMAGE_MEAN_ARR, IMAGE_STD_ARR))
		.build();
TensorImage tImage = new TensorImage(DataType.FLOAT32);
tImage.load(bitmap);
tImage = imageProcessor.process(tImage);

TensorBuffer probBuffer = TensorBuffer.createFixedSize(new int[]{1, 3}, DataType.FLOAT32);
interpreter.run(tImage.getBuffer(), probBuffer.getBuffer());

预测结果

[1.0, 0.000000, 0.000000]

换了几张其他照片,结果跟PC也是完全不一样,懵逼。


分析过程:

  1. 是不是图片归一化处理有问题?经检测两端归一化处理一致,换了其它几种归一化方法后问题还是存在,所以排除。
  2. 是否模型设计有问题?更换其它主流网络如MobileNet测试,问题依旧。排除。
  3. 是否模型转换有问题?更换不同的转换方式,问题依旧。排除。
  4. 难道是TensorFlow Lite Android 依赖版本的原因?换了各种版本测试,问题依然存在,过。

既然不是框架的问题也不是模型的问题。那问题肯定在输入数据源上,对比PC、Android图片处理过程,并查看对应的doc描述:
OpenCV:

cv2.imread(img_path)

在这里插入图片描述

Android:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.face03);
TensorImage tImage = new TensorImage(DataType.FLOAT32);
tImage.load(bitmap);

在这里插入图片描述

破案了,Bitmap使用主流的RGB通道顺序排列,OpenCV默认使用BGR通道顺序排列。

用BGR数据集训练的模型,通过RGB推理,结果肯定是有不对的。

查了下为什么OpenCV要使用BGR排列,也是由于历史原因,详情有兴趣大家可以自行了解。


解决方案:

一、修改通道顺序

cvimg = cv2.imread(img_path)
cvimg = cvimg[:, :, (2,1,0)]

二、使用PIL库读取图片

from PIL import Image
image = Image.open(img_path)

然后重新训练模型,再次部署到端侧测试,结果一致。

在这里插入图片描述

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值