В этом блоге мы увидим, как создать приложение для предсказания цифр почерка с использованием модели машинного обучения в kotlin.
Андроид
- Тензорный поток
- package com.codewithgolap.tflite.mnist
import android.content.Context
import android.graphics.Bitmap
import android.os.SystemClock
import android.util. Журнал
import android.util.Size
import org.tensorflow.lite.Delegate
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.Tensor
> импортировать org.tensorflow.lite.gpu.GpuDelegate
импортировать org.tensorflow.lite.nnapi.NnApiDelegate
импортировать org.tensorflow.lite.support.common.FileUtil
импортировать org.tensorflow. lite.support.tensorbuffer.TensorBuffer
import java.io.Closeable
import java.nio.ByteBuffer
import java.nio.ByteOrder
class Classifier(
context: Контекст,
устройство: Устройство = Устройство. ЦП,
numThreads: Int = 4
) {
private val delegate: Delegate? = when(device) {
Device.CPU -> null
Device.NNAPI -> NnApiDelegate()
Device.GPU -> GpuDelegate()
}
private val интерпретатор: Interpreter = Interpreter(
FileUtil.loadMappedFile(context, MODEL_FILE_NAME),
Interpreter.Options().apply {
setNumThreads(numThreads)
delegate?.let { addDelegate (it) }
}
)
private val inputTensor: Tensor =terpreter.getInputTensor(0)
private val outputTensor: Tensor =terpreter.getOutputTensor(0)
val inputShape: Size = with(inputTensor.shape()) { Size(this[2], this[1]) }
private val imagePixels = IntArray(inputShape.height * inputShape.width)
private val imageBuffer: ByteBuffer =
ByteBuffer.allocateDirect(4 * inputShape.height * inputShape.width).apply {
order(ByteOrder.nativeOrder())
}
private val outputBuffer : TensorBuffer =
TensorBuffer.createFixedSize(outputTensor.shape(), outputTensor.dataType())
init {
Log.v(
LOG_TAG, «[Input] shape = $ {inputTensor.shape()?.contentToString()}, " +
"dataType = ${inputTensor.dataType()}")
Log.v(
LOG_TAG, "[Output] shape = ${outputTensor.shape()?.contentToString()}, “ +
“dataType = ${outputTensor.dataType()}”)
}
fun classify(image: Bitmap ): Распознавание {
convertBitmapToByteBuffer(image)
val start = SystemClock.uptimeMillis()
terpreter.run(imageBuffer, outputBuffer.buffer.rewind())
val end = SystemClock .uptimeMillis()
val timeCost = end — start
val probs = outputBuffer.floatArray
val top = probs.argMax()
Log.v(LOG_TAG, «classify() : timeCost = $timeCost, top = $top, probs = ${probs.contentToString()}")
return Recognition(top, probs[top], timeCost)
}
fun close () {
terpreter.close()
if (делегат Closeable) {
delegate.close()
}
}
private fun convertBitmapToByteBuffer( bitmap: Bitmap) {
imageBuffer.rewind()
bitmap.getPixels(imagePixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
for (i in 0 до inputShape.width * inputShape.height) {
val pixel: Int = imagePixels[i]
imageBuffer.putFloat(convertPixel(pixel))
}
}
private fun convertPixel(color: Int): Float {
return (255 — ((color shr 16 и 0xFF) * 0. 299f
+ (цвет shr 8 и 0xFF) * 0,587f
+ (цвет и 0xFF) * 0,114f)) / 255,0f
}
сопутствующий объект {
private val LOG_TAG: String = Classifier::class.java.simpleName
private const val MODEL_FILE_NAME: String = «mnist.tflite»
}
}
fun FloatArray.argMax() : Int {
return this.withIndex().maxByOrNull { it.value }?.index
?: throw IllegalArgumentException("Не удается найти аргумент max в пустом списке") - package com.codewithgolap.tflite.mnist
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Log
import android.widget. Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import java.io.IOException
class MainActivity : AppCompatActivity() {
// вызов класса классификатора
private lateinit var classifier: Classifier
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView( R.layout.activity_main)
init()
}
private fun init() {
initClassifier()
initView()
}
// если с вашим интернет-соединением и кодом классификатора все в порядке, ошибок не будет msg
private fun initClassifier() {
try {
classifier = Classifier(this)
Log. v(LOG_TAG, «Классификатор инициализирован»)
} catch (e: IOException) {
Toast.makeText(this, R.string.failed_to_create_classifier, Toast.LENGTH_LONG).show()
Журнал .e(LOG_TAG, «init(): не удалось создать классификатор», e)
}
}
// события нажатия кнопок
private fun initView() {
btn_detect.setOnClickListener { onDetectClick() }
btn_clear.setOnClickListener { clearResult() }
}
private fun onDetectClick() {
if (!this::classifier.isInitialized) {
Log.e(LOG_TAG, «onDetectClick(): классификатор не инициализирован»)
return
} else if (fingerPaintView.isEmpty) {
Toast.makeText(this, R.string.please_write_a_digit, Toast.LENGTH_SHORT).show()
return
}
// когда мы рисуем что-то в представлении Finerpaint, вызывается функция renderResult
val image : Bitmap = fingerPaintView.exportToBitmap(
classifier.inputShape.width, classifier.inputShape.height
)
val result = classifier.classify(image)
renderResult(result)
}
// в этой функции мы получим метку, которая является цифрой, соответствие, которое является вероятностью, и стоимость времени
private fun renderResult(result: Recognition) {
tv_prediction. text = java.lang.String.valueOf(result.label)
tv_probability.text = java.lang.String.valueOf(result.confidence)
tv_timecost.text = java.lang.String.format(
getString(R.string.timecost_value),
result.timeCost
)
}
// при нажатии на кнопку очистки все данные будут удалены
приватная забава clearResult() {
fingerPaintView.clear()
tv_prediction.setText(R.string.empty)
tv_probability.setText(R.string.empty)
tv_timecost.setText (R.string.empty)
}
переопределить fun onDestroy() {
super.onDestroy()
// classifier.close()
}
сопутствующий объект {
private val LOG_TAG: String = MainActivity::class.java.simpleName
}
Котлин
Набор данных MNIST используется для создания модели TFLite.
Ссылка на код Python: НАЖМИТЕ ЗДЕСЬ, чтобы открыть код в Google Colab.
Сначала создайте модель TFLite, а затем загрузите ее в папку ресурсов Android Studio.
Машинное обучение
1. Activity_main.xml
‹?xml version=”1.0” encoding="utf-8"?›
‹LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android '
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android: ориентация=”вертикальный”
android:background=”@android:color/black”
tools:context=”com.codewithgolap.tflite.mnist.MainActivity”›
‹TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@android:color/white"›
> ‹TextView
style="@style/ResultText"
android:fontFamily="@font/poppins_bold"
android:text="@string/prediction"
android:textColor =”@android:color/black”
android:textSize=”19sp”
android:letterSpacing=”0.05'/›
‹TextView
android:id=”@+ id/tv_prediction”
style="@style/ResultText"
android:text="@string/empty"
android:textColor="@android:color/black"
android:textSize="24sp"/›
‹TableRow
android:layout_marginTop="16dp"›
‹TextView
style="@style/ResultText"
android :text=”@string/probability”
android:background=”#FFC107’
android:textColor=”@color/colorPrimary”
android:fontFamily=”@font/poppins_medium”< br /> android:letterSpacing=”0.02'
android:layout_marginEnd=”2dp”/›
‹TextView
style=”@style/ResultText”
android:text=” @string/timecost”
android:background=”#FFC107'
android:textColor=”@color/colorPrimary”
android:fontFamily=”@font/poppins_medium”
android :letterSpacing="0,02'
android:layout_marginStart="2dp"/›
‹/TableRow›
‹TableRow›
‹TextView
android:id="@+id/tv_probability"
style="@style/ResultText"
android:text="@string /пусто”
android:background=”#FFEB3B”
android:textColor=”@color/colorPrimary”
android:fontFamily=”@font/poppins_medium”
android:letterSpacing =”0.02'
android:layout_marginEnd=”2dp”/›
‹TextView
android:id=”@+id/tv_timecost”
style=”@style/ResultText”
android:text="@string/empty"
android:background="#FFEB3B"
android:textColor="@color/colorPrimary"
android:fontFamily="@ font/poppins_medium”
android:letterSpacing=”0,02'
android:layout_marginStart=”2dp”/›
‹/TableRow›
‹/TableLayout›
‹com .nex3z.fingerpaintview.FingerPaintView
android:id=”@+id/fingerPaintView”
android:layout_width=”280dp”
android:layout_height=”280dp”
android:layout_marginTop =”48dp”
android:layout_gravity=”center”
android:background=”@android:color/white”
android:foreground=”@drawable/shape_rect_border”/›
‹LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
> android:paddingEnd="16dp"
android:layout_marginTop="48dp"›
‹Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id=”@+id/btn_detect”
android:text=”@string/detect”
android:layout_weight=”1'
android:textSize=”16sp”< br /> android:fontFamily="@font/poppins_medium"/›
‹Кнопка
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id =”@+id/btn_clear”
android:text=”@string/clear”
android:layout_weight=”1'
android:textSize=”16sp”
android: fontFamily="@font/poppins_medium"/›
‹/LinearLayout›
‹/LinearLayout›
2. Classifier.java
3. Устройство.java
package com.codewithgolap.tflite.mnist
enum class Device {
CPU,
NNAPI,
GPU
4. Распознавание.java
package com.codewithgolap.tflite.mnist
класс данных Распознавание (
значение label: Int,
значение достоверности: Float,
значение timeCost: Long
)
5. MainActivity.java
Посмотрите полное видео, чтобы узнать больше шаг за шагом
Android-приложение для предсказания цифр почерка с использованием модели машинного обучения в Kotlin
В этом блоге мы увидим, как создать приложение для предсказания цифр почерка с использованием модели машинного обучения в kotlin.