ใน ep นี้ เราจะสอน สร้างแอพ Android เขียน App AI บนมือถือ ที่มีความสามารถ Image Classification ด้วย TensorFlow Lite โดยใช้ Transfer Learning โมเดล Inception v3 มาเป็น Feature Extractor และเพิ่ม Custom Head 3 Class มา Convert ประกอบเป็น App สำหรับรันบนมือถือ Android ด้วยภาษา Kotlin

สอนเขียน App มือถือ Image Classification ด้วย Transfer Learning

เราสามารถพัฒนา App บนมือถือ Android ให้มีความสามารถด้าน AI / Machine Learning มีฟังก์ชัน Image Classification จากรูป ที่ถ่ายจากกล้องมือถือ แบบ Real-time ด้วยโมเดลของเราเอง ที่ทำ Transfer Learning มาจาก Inception v3 เพิ่ม Custom Head 3 Class ได้แก่ Rock ฆ้อน, Paper กระดาษ และ Scissors กรรไกร

ขั้นตอนการสร้างแอพพลิเคชั่น จะเป็นดังนี้

  1. Transfer Learning บน Server (ในที่นี้คือ Notebook / Colab)
  2. Export to SavedMovel และ Convert to FlatBuffer ไฟล์ .tflite
  3. Download นำไปใส่ใน App
  4. เขียน App เตรียมข้อมูล Input/Output ตามที่โมเดลต้องการ

เรามาเริ่มกันเลยดีกว่า ขั้นที่ 1-2

Open In Colab

เราจะ Download ได้ไฟล์ มา 2 ไฟล์ ชื่อว่า converted_model.tflite และ labels.txt

TensorFlow Lite on Android Code Example ขั้นที่ 3-4

ให้นำไฟล์ FlatBuffer และ Label จำนวน 2 ไฟล์จากด้านบน ให้ Rename เป็น inception_v3_rps_299.tflite และ labels.txt แล้วไปใส่ไว้ใน Folder ของ App Android ชื่อ rps_classification/app/src/main/assets

RPS Classification Android App Assets Path
RPS Classification Android App Assets Path

ใน Project โค้ดตัวอย่างจะมีโครงสร้างเหมือนกับ tflite ep.6 แต่จะมีแตกต่างกันอยู่ตรงที่ขนาดของ Input/Output ของโมเดล และโมเดลนี้เป็นโมเดลที่รับ Float32 ไม่ใช่ Int8

Camera2BasicFragment.kt

ใน Camera2BasicFragment.kt เราจะกำหนดตัวเแปร mModelPath ชื่อไฟล์ FlatBuffer, mLabelPath ชื่อไฟล์ Label และกำหนด mInputSize ขนาดกว้างยาวของรูป Input ให้ตรงกับโมเดล

    private val mInputSize = 299            // Depend on model

    // RPS Inception v3 299
    private val mModelPath = "inception_v3_rps_299.tflite"
    private val mLabelPath = "labels_rps.txt"

Classifier.kt

ใน Classifier.kt เราต้อง Allocate ByteBuffer มากขึ้น 4 เท่า เนื่องจาก Int8 ใช้ 1 Byte แต่ Float32 ใช้ 4 Byte และใช้ putFloat() แทน put()

    private fun addPixelValue(byteBuffer: ByteBuffer, intValue: Int): ByteBuffer {

        byteBuffer.putFloat((intValue.shr(16) and 0xFF).toFloat() / 256f)
        byteBuffer.putFloat((intValue.shr(8) and 0xFF).toFloat() / 256f)
        byteBuffer.putFloat((intValue and 0xFF).toFloat() / 256f)
        return byteBuffer
    }

    private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
        val imgData = ByteBuffer.allocateDirect( 4 * INPUT_SIZE * INPUT_SIZE * PIXEL_SIZE)
        imgData.order(ByteOrder.nativeOrder())
        val intValues = IntArray(INPUT_SIZE * INPUT_SIZE)

        imgData.rewind()
        bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
        // Convert the image to floating point.
        var pixel = 0
        val startTime = SystemClock.uptimeMillis()
        for (i in 0 until INPUT_SIZE) {
            for (j in 0 until INPUT_SIZE) {
                val `val` = intValues[pixel++]
                addPixelValue(imgData, `val`)
            }
        }
        return imgData;
    }

ในฟังก์ชัน getSortedResult ค่า confidence ออกมาเป็น Float 0-1 อยู่แล้ว (ไม่ใช่ Int ที่ต้องหาร 255 ให้เป็นเ Float)

    private fun getSortedResult(labelProbArray: Array<FloatArray>): List<Recognition> {
        ...
        for (i in LABEL_LIST.indices) {
            val confidence = labelProbArray[0][i]
            if (confidence >= THRESHOLD) {
                Log.d("confidence value:", "" + confidence);
                pq.add(Recognition("" + i,
                        if (LABEL_LIST.size > i) LABEL_LIST[i] else "Unknown",
                        (confidence)
                ))
            }
        }

Build

Compile และ Build Project

RPS Classification Android App Build Completed Successfully
RPS Classification Android App Build Completed Successfully

Run on Android Mobile Phone

Deploy บน มือถือ Android

เนื่องจากใน RPS Dataset สภาพแสง พื้นหลัง และสีผิว อาจจะแตกต่างกับคนเอเชีย เราสามารถเก็บข้อมูลตัวอย่างเพิ่มเติม ไปใส่ Dataset แล้วเทรนต่อ เพื่อเพิ่มประสิทธิภาพความแม่นยำให้กับโมเดลได้

แชร์ให้เพื่อน:

Surapong Kanoktipsatharporn on Linkedin
Surapong Kanoktipsatharporn
CTO at Bua Labs
The ultimate test of your knowledge is your capacity to convey it to another.

Published by Surapong Kanoktipsatharporn

The ultimate test of your knowledge is your capacity to convey it to another.