Я гурман и энтузиаст компьютерного зрения. Имеет смысл только то, что я в конечном итоге сделаю что-то подобное. После поиска в Kaggle интересных наборов данных для реализации того, что я узнал из deeplearning.ai's Tensorflow: Data and Deployment on Coursera, я наткнулся на набор данных Food-101 и вуаля!
Набор данных Food-101, как следует из названия, предполагает набор данных, содержащий 1000 изображений каждое из колоссального 101 блюда от яблочного пирога до вафель (и мне немного стыдно сказать, что я не слышал довольно много из них, несмотря на мою склонность к еде) . Учитывая, что вряд ли я наткнусь на фуа-гра или ранчо huevos, набор данных был сокращен с 101 класса до 20 для простоты.
chicken curry fried rice samosa
chicken wings garlic bread sandwich
chocolate cake hamburger soup
cup cake hot-dog spring rolls
donuts ice-cream sushi
dumplings omelette waffles
french fries pizza
С 1000 изображений в каждой из вышеперечисленных категорий данных было слишком мало для обучения точной модели с нуля. На очереди Transfer Learning с использованием слоев из MobileNetV1, предварительно обученных в наборе данных ImageNet.
conv_base = applications.MobileNet(weights = "imagenet", include_top=False, input_shape = (256, 256, 3))
После добавления слоя GlobalMaxPooling2D и Dense архитектура модели выглядела так:
Model: "sequential_8" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= mobilenet_1.00_224 (Model) (None, 8, 8, 1024) 3228864 _________________________________________________________________ global_average_pooling2d_6 ( (None, 1024) 0 _________________________________________________________________ dense_13 (Dense) (None, 1024) 1049600 _________________________________________________________________ dropout_25 (Dropout) (None, 1024) 0 _________________________________________________________________ dense_14 (Dense) (None, 20) 20500 ================================================================= Total params: 4,298,964 Trainable params: 4,277,076 Non-trainable params: 21,888
Чтобы избежать переобучения, к Data Augmentation были применены методы увеличения, включая поворот изображения, линейные сдвиги, масштабирование, изменение освещения и переворачивание.
from keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator(rescale = 1./255, rotation_range=360,width_shift_range=0.2,height_shift_range=0.2, shear_range = 0.2,zoom_range = [0.5, 1.0],brightness_range = [0.2, 1.0],horizontal_flip = True,vertical_flip =False,zca_whitening=True, zca_epsilon=1e-06) training_set=train_datagen.flow_from_directory(path_training,target_size =(256,256),batch_size=64,class_mode='categorical',shuffle=True)
Для начальных эпох перенесенные слои были заморожены.
for layer in conv_base.layers: layer.trainable=False
Первоначальное обучение проводилось с помощью Adam Optimizer со снижающейся скоростью обучения, за которым последовало переключение на SGD в более поздние эпохи. После выхода на плато с потерей валидации перенесенные слои были разморожены, и обучение было возобновлено для дальнейшей точной настройки модели. Была получена точность обучения 91,34% и точность проверки 85,51%.
Тестирование
imn = '/content/test/testimage.jpg' #test image path img = load_img(imn, target_size=(256, 256)) img = np.asarray(img) img = img.astype('float32') img = img/255 img = np.expand_dims(img, axis=0) img = img.reshape(1,256,256,3) res = model2.predict(img) ord = np.argsort(res) ind = np.argmax(res) li = ['chicken curry', 'chicken wings', 'ch cake', 'cup cake', 'donuts', 'dumplings', 'fries','fried rice', 'garlic bread', 'hamburger', 'hot-dog', 'ice-cream', 'omelette', 'pizza', 'samosa', 'sandwich','soup','spring rolls', 'sushi', 'waffle'] lis = [] for i in range(0, 5): lis.append(li[(ord[0][19 - i])]) print(lis) # Top-5 predictions
Теперь можно сохранить модель keras .h5. Модель занимает около 32,9 МБ места.
Преобразование в TensorFlow lite
image_shape = (256, 256, 3) def representative_dataset_gen(): num_calibration_images = 10 for i in range(num_calibration_images): image = tf.random.normal([1] + list(image_shape)) yield [image] converter=lite.TFLiteConverter.from_keras_model_file('model.h5') converter.default_ranges_stats=[0,255] converter.optimizations = [tf.lite.Optimize.DEFAULT] converter.representative_dataset = representative_dataset_gen converter.target_spec.supported_ops =[tf.lite.OpsSet.TFLITE_BUILTINS_INT8] converter.inference_input_type = tf.uint8 converter.inference_output_type = tf.uint8 model = converter.convert() file = open( 'model.tflite' , 'wb' ) file.write(model)
Полученная модель tflite имеет размер 16,29 МБ, что почти вдвое меньше размера модели h5.
Тестирование с tflite
imn = '/content/test/testimage.jpg' img = load_img(imn, target_size=(256, 256)) img = np.asarray(img) img = img.astype('float32') img = img/255 img = np.expand_dims(img, axis=0) img = img.reshape(1,256,256,3) interpreter = lite.Interpreter(model_path="model.tflite") interpreter.allocate_tensors() # Get input and output tensors. input_details = interpreter.get_input_details() output_details = interpreter.get_output_details() interpreter.set_tensor(input_details[0]['index'], img) interpreter.invoke() output_data = interpreter.get_tensor(output_details[0]['index']) ord = np.argsort(output_data) ind = np.argmax(output_data) lis = [] for i in range(0, 5): lis.append(li[(ord[0][19 - i])]) print(lis)
И мы закончили с TFlite!
Android
Клонируйте репозиторий github из здесь.
В каталог ресурсов вашего приложения добавьте модель tflite и файл label.txt с метками. Вот как должен выглядеть текстовый файл:
label 1 label 2 label 3 .. .. ..
В файле Kotlin MainAct.kt отредактируйте переменные mInputSize, mModelPath и mLabelPath в соответствии с вашими потребностями. Мы рассматриваем пороговую оценку 0,40, указанную в Classifier.kt, поиграйте для достижения наилучших результатов!
Вот скриншот приложения: