项目场景:
刚入坑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也是完全不一样,懵逼。
分析过程:
- 是不是图片归一化处理有问题?经检测两端归一化处理一致,换了其它几种归一化方法后问题还是存在,所以排除。
- 是否模型设计有问题?更换其它主流网络如MobileNet测试,问题依旧。排除。
- 是否模型转换有问题?更换不同的转换方式,问题依旧。排除。
- 难道是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)
然后重新训练模型,再次部署到端侧测试,结果一致。