1. Sebelum memulai
Dalam codelab ini, Anda akan mengupdate aplikasi yang Anda bangun di codelab Mulai menggunakan klasifikasi teks seluler sebelumnya.
Prasyarat
- Codelab ini telah dirancang untuk developer berpengalaman yang baru menggunakan machine learning.
- Codelab ini adalah bagian dari jalur yang diurutkan. Jika Anda belum menyelesaikan Membangun aplikasi gaya pesan dasar atau Membangun model machine learning spam komentar, berhentilah dan lakukan sekarang.
Yang akan Anda [bangun atau pelajari]
- Anda akan mempelajari cara mengintegrasikan model kustom ke dalam aplikasi, yang dibuat pada langkah sebelumnya.
Yang Anda butuhkan
- Android Studio, atau CocoaPods untuk iOS
2. Membuka Aplikasi Android yang Ada
Anda bisa mendapatkan kode untuk codelab ini dengan mengikuti Codelab 1, atau dengan meng-clone repo ini dan memuat aplikasi dari TextClassificationStep1
.
git clone https://github.com/googlecodelabs/odml-pathways
Anda dapat menemukannya di jalur TextClassificationOnMobile->Android
.
Kode selesai juga tersedia untuk Anda sebagai TextClassificationStep2
.
Setelah terbuka, Anda siap melanjutkan ke langkah 2.
3. Mengimpor File Model dan Metadata
Dalam codelab Membuat model machine learning spam komentar, Anda telah membuat model .TFLITE.
Anda seharusnya telah mendownload file model. Jika tidak memilikinya, Anda bisa mendapatkannya dari repo untuk codelab ini, dan modelnya tersedia di sini.
Tambahkan ke project Anda dengan membuat direktori aset.
- Dengan menggunakan navigator project, pastikan Android dipilih di bagian atas.
- Klik kanan folder app. Pilih New > Directory.
- Dalam dialog New Directory, pilih src/main/assets.
Anda akan melihat folder aset baru yang kini tersedia di aplikasi.
- Klik kanan aset.
- Pada menu yang terbuka, Anda akan melihat (di Mac) Pengungkapan di Finder. Pilih aplikasi. (Di Windows, akan muncul Show in Explorer, di Ubuntu akan muncul Show in Files.)
Finder akan diluncurkan untuk menampilkan lokasi file (File Explorer di Windows, Files di Linux).
- Salin file
labels.txt
,model.tflite
, danvocab
ke direktori ini.
- Kembali ke Android Studio, dan Anda akan melihatnya tersedia di folder aset.
4. Mengupdate build.gradle untuk menggunakan TensorFlow Lite
Untuk menggunakan TensorFlow Lite, dan library tugas TensorFlow Lite yang mendukungnya, Anda harus mengupdate file build.gradle
.
Project Android sering kali memiliki lebih dari satu, jadi pastikan untuk menemukan level satu aplikasi. Di project explorer dalam tampilan Android, temukan di bagian Gradle Scripts. File yang benar akan diberi label .app seperti yang ditunjukkan di sini:
Anda harus membuat dua perubahan pada file ini. Yang pertama ada di bagian dependensi di bagian bawah. Tambahkan implementation
teks untuk library tugas TensorFlow Lite, seperti ini:
implementation 'org.tensorflow:tensorflow-lite-task-text:0.1.0'
Nomor versi mungkin telah berubah sejak ini ditulis, jadi pastikan untuk memeriksa https://www.tensorflow.org/lite/inference_with_metadata/task_library/nl_classifier untuk mengetahui versi terbaru.
Library tugas juga memerlukan versi SDK minimum 21. Temukan setelan ini di android
> default config
, dan ubah menjadi 21:
Sekarang Anda memiliki semua dependensi, jadi saatnya untuk mulai membuat kode.
5. Menambahkan Class Helper
Untuk memisahkan logika inferensi, tempat aplikasi Anda menggunakan model, dari antarmuka pengguna, buat class lain untuk menangani inferensi model. Sebut class ini sebagai "helper".
- Klik kanan nama paket tempat kode
MainActivity
Anda berada. - Pilih New > Package.
- Anda akan melihat dialog di bagian tengah layar yang meminta Anda memasukkan nama paket. Tambahkan di akhir nama paket saat ini. (Di sini, fungsi ini disebut helper.)
- Setelah selesai, klik kanan folder helpers di project explorer.
- Pilih New > Java Class, lalu beri nama
TextClassificationClient
. Anda akan mengedit file di langkah berikutnya.
Class bantuan TextClassificationClient
akan terlihat seperti ini (meskipun nama paket Anda mungkin berbeda.)
package com.google.devrel.textclassificationstep1.helpers;
public class TextClassificationClient {
}
- Perbarui file dengan kode ini:
package com.google.devrel.textclassificationstep2.helpers;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.util.List;
import org.tensorflow.lite.support.label.Category;
import org.tensorflow.lite.task.text.nlclassifier.NLClassifier;
public class TextClassificationClient {
private static final String MODEL_PATH = "model.tflite";
private static final String TAG = "CommentSpam";
private final Context context;
NLClassifier classifier;
public TextClassificationClient(Context context) {
this.context = context;
}
public void load() {
try {
classifier = NLClassifier.createFromFile(context, MODEL_PATH);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
public void unload() {
classifier.close();
classifier = null;
}
public List<Category> classify(String text) {
List<Category> apiResults = classifier.classify(text);
return apiResults;
}
}
Class ini akan menyediakan wrapper ke penafsir TensorFlow Lite, memuat model, dan mengabstraksi kompleksitas pengelolaan pertukaran data antara aplikasi dan model Anda.
Dalam metode load()
, metode ini akan membuat instance jenis NLClassifier
baru dari jalur model. Jalur model hanyalah nama model, model.tflite
. Jenis NLClassifier
adalah bagian dari library tugas teks, dan membantu Anda dengan mengonversi string menjadi token, menggunakan panjang urutan yang benar, meneruskannya ke model, dan mengurai hasilnya.
(Untuk mengetahui detail selengkapnya tentang hal ini, buka kembali model Machine learning spam komentar.)
Klasifikasi dilakukan dalam metode klasifikasi, tempat Anda meneruskan string ke metode tersebut, dan akan menampilkan List
. Saat menggunakan model Machine Learning untuk mengklasifikasikan konten di mana Anda ingin menentukan apakah string merupakan spam atau bukan, biasanya semua jawaban akan ditampilkan, dengan probabilitas yang ditetapkan. Misalnya, jika Anda meneruskan pesan yang terlihat seperti spam, Anda akan mendapatkan daftar 2 jawaban; satu dengan probabilitas bahwa pesan tersebut adalah spam, dan satu lagi dengan probabilitas bahwa pesan tersebut bukan spam. Spam/Bukan Spam adalah kategori, sehingga List
yang ditampilkan akan berisi probabilitas ini. Anda akan mengurainya nanti.
Setelah memiliki class helper, kembali ke MainActivity
dan perbarui untuk menggunakannya guna mengklasifikasikan teks. Anda akan melihatnya di langkah berikutnya.
6. Mengklasifikasikan Teks
Di MainActivity
, pertama-tama Anda perlu mengimpor helper yang baru saja dibuat.
- Di bagian atas
MainActivity.kt
, bersama dengan impor lainnya, tambahkan:
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
- Selanjutnya, Anda harus memuat helper. Di
onCreate
, tepat setelah barissetContentView
, tambahkan baris berikut untuk membuat instance dan memuat class helper:
val client = TextClassificationClient(applicationContext)
client.load()
Saat ini, onClickListener
tombol Anda seharusnya terlihat seperti ini:
btnSendText.setOnClickListener {
var toSend:String = txtInput.text.toString()
txtOutput.text = toSend
}
- Perbarui agar terlihat seperti ini:
btnSendText.setOnClickListener {
var toSend:String = txtInput.text.toString()
var results:List<Category> = client.classify(toSend)
val score = results[1].score
if(score>0.8){
txtOutput.text = "Your message was detected as spam with a score of " + score.toString() + " and not sent!"
} else {
txtOutput.text = "Message sent! \nSpam score was:" + score.toString()
}
txtInput.text.clear()
}
Fungsi ini mengubah fungsi, dari hanya menghasilkan output input pengguna, menjadi mengklasifikasikan input pengguna terlebih dahulu.
- Dengan baris ini, Anda akan mengambil string yang dimasukkan pengguna dan meneruskannya ke model, sehingga mendapatkan hasil:
var results:List<Category> = client.classify(toSend)
Hanya ada 2 kategori, False
dan True
. (TensorFlow mengurutkannya menurut abjad, jadi False adalah item 0, dan True adalah item 1.)
- Untuk mendapatkan skor probabilitas bahwa nilainya adalah
True
, Anda dapat melihat results[1].score seperti ini:
val score = results[1].score
- Memilih nilai minimum (dalam hal ini 0,8), yang menyatakan bahwa jika skor untuk kategori Benar di atas nilai minimum (0,8), pesan tersebut adalah spam. Jika tidak, pesan tersebut bukan spam dan pesan aman untuk dikirim:
if(score>0.8){
txtOutput.text = "Your message was detected as spam with a score of " + score.toString() + " and not sent!"
} else {
txtOutput.text = "Message sent! \nSpam score was:" + score.toString()
}
- Lihat cara kerja model di sini. Pesan "Kunjungi blog saya untuk membeli barang" ditandai sebagai kemungkinan spam yang tinggi:
Dan sebaliknya, "Hei, tutorial seru, terima kasih!" dilihat sebagai kemungkinan yang sangat rendah untuk menjadi spam:
7. Mengupdate Aplikasi iOS untuk menggunakan Model TensorFlow Lite
Anda bisa mendapatkan kode untuk codelab ini dengan mengikuti Codelab 1, atau dengan meng-clone repo ini dan memuat aplikasi dari TextClassificationStep1
. Anda dapat menemukannya di jalur TextClassificationOnMobile->iOS
.
Kode selesai juga tersedia untuk Anda sebagai TextClassificationStep2
.
Dalam codelab Membuat model machine learning spam komentar, Anda telah membuat aplikasi yang sangat sederhana yang memungkinkan pengguna mengetik pesan ke UITextView
dan meneruskannya ke output tanpa pemfilteran apa pun.
Sekarang Anda akan mengupdate aplikasi tersebut untuk menggunakan model TensorFlow Lite guna mendeteksi spam komentar dalam teks sebelum mengirim. Cukup simulasikan pengiriman dalam aplikasi ini dengan merender teks dalam label output (tetapi aplikasi yang sebenarnya mungkin memiliki papan buletin, obrolan, atau sesuatu yang serupa).
Untuk memulai, Anda memerlukan aplikasi dari langkah 1, yang dapat di-clone dari repo.
Untuk menyertakan TensorFlow Lite, Anda akan menggunakan CocoaPods. Jika belum menginstalnya, Anda dapat melakukannya dengan mengikuti petunjuk di https://cocoapods.org/.
- Setelah menginstal CocoaPods, buat file dengan nama Podfile di direktori yang sama dengan
.xcproject
untuk aplikasi TextClassification. Isi file ini akan terlihat seperti ini:
target 'TextClassificationStep2' do
use_frameworks!
# Pods for NLPClassifier
pod 'TensorFlowLiteSwift'
end
Nama aplikasi Anda harus berada di baris pertama, bukan "TextClassificationStep2".
Dengan Terminal, buka direktori tersebut dan jalankan pod install
. Jika berhasil, Anda akan memiliki direktori baru bernama Pods, dan file .xcworkspace
baru akan dibuat untuk Anda. Anda akan menggunakannya di masa mendatang, bukan .xcproject
.
Jika gagal, pastikan Anda memiliki Podfile di direktori yang sama dengan .xcproject
. Podfile di direktori yang salah, atau nama target yang salah, biasanya adalah penyebab utamanya.
8. Menambahkan File Model dan Vocab
Saat membuat model dengan TensorFlow Lite Model Maker, Anda dapat menghasilkan model (sebagai model.tflite
) dan kosakata (sebagai vocab.txt
).
- Tambahkan ke project Anda dengan menarik lalu melepasnya dari Finder ke jendela project. Pastikan tambahkan ke target dicentang:
Setelah selesai, Anda akan melihatnya di project Anda:
- Periksa kembali apakah keduanya telah ditambahkan ke paket (sehingga di-deploy ke perangkat) dengan memilih project Anda (dalam screenshot di atas, ikon biru TextClassificationStep2), dan melihat tab Build Phases:
9. Memuat Kosakata
Saat melakukan klasifikasi NLP, model dilatih dengan kata yang dienkode menjadi vektor. Model ini mengenkode kata dengan kumpulan nama dan nilai tertentu yang dipelajari saat model dilatih. Perhatikan bahwa sebagian besar model akan memiliki kosakata yang berbeda, dan Anda harus menggunakan kosakata untuk model yang dibuat pada saat pelatihan. Ini adalah file vocab.txt
yang baru saja Anda tambahkan ke aplikasi.
Anda dapat membuka file di Xcode untuk melihat encoding. Kata-kata seperti "song" dienkode ke 6 dan "love" ke 12. Urutan sebenarnya adalah urutan frekuensi, sehingga "I" adalah kata yang paling umum dalam set data, diikuti dengan "check".
Saat pengguna mengetik kata, Anda harus mengenkodenya dengan kosakata ini sebelum mengirimkannya ke model untuk diklasifikasikan.
Mari kita pelajari kode tersebut. Mulai dengan memuat kosakata.
- Tentukan variabel tingkat class untuk menyimpan kamus:
var words_dictionary = [String : Int]()
- Kemudian, buat
func
di class untuk memuat kosakata ke dalam kamus ini:
func loadVocab(){
// This func will take the file at vocab.txt and load it into a has table
// called words_dictionary. This will be used to tokenize the words before passing them
// to the model trained by TensorFlow Lite Model Maker
if let filePath = Bundle.main.path(forResource: "vocab", ofType: "txt") {
do {
let dictionary_contents = try String(contentsOfFile: filePath)
let lines = dictionary_contents.split(whereSeparator: \.isNewline)
for line in lines{
let tokens = line.components(separatedBy: " ")
let key = String(tokens[0])
let value = Int(tokens[1])
words_dictionary[key] = value
}
} catch {
print("Error vocab could not be loaded")
}
} else {
print("Error -- vocab file not found")
}
}
- Anda dapat menjalankannya dengan memanggilnya dari dalam
viewDidLoad
:
override func viewDidLoad() {
super.viewDidLoad()
txtInput.delegate = self
loadVocab()
}
10. Mengubah string menjadi urutan token
Pengguna akan mengetik kata sebagai kalimat yang akan menjadi string. Setiap kata dalam kalimat, jika ada dalam kamus, akan dienkode ke dalam nilai kunci untuk kata tersebut seperti yang ditentukan dalam kosakata.
Model NLP biasanya menerima panjang urutan tetap. Ada pengecualian dengan model yang dibuat menggunakan ragged tensors
, tetapi sebagian besar Anda akan melihatnya diperbaiki. Saat membuat model, Anda menentukan panjang ini. Pastikan Anda menggunakan durasi yang sama di aplikasi iOS.
Nilai default di Colab untuk TensorFlow Lite Model Maker yang Anda gunakan sebelumnya adalah 20, jadi siapkan juga di sini:
let SEQUENCE_LENGTH = 20
Tambahkan func
ini yang akan mengambil string, mengonversinya menjadi huruf kecil, dan menghapus tanda baca apa pun:
func convert_sentence(sentence: String) -> [Int32]{
// This func will split a sentence into individual words, while stripping punctuation
// If the word is present in the dictionary it's value from the dictionary will be added to
// the sequence. Otherwise we'll continue
// Initialize the sequence to be all 0s, and the length to be determined
// by the const SEQUENCE_LENGTH. This should be the same length as the
// sequences that the model was trained for
var sequence = [Int32](repeating: 0, count: SEQUENCE_LENGTH)
var words : [String] = []
sentence.enumerateSubstrings(
in: sentence.startIndex..<sentence.endIndex,options: .byWords) {
(substring, _, _, _) -> () in words.append(substring!) }
var thisWord = 0
for word in words{
if (thisWord>=SEQUENCE_LENGTH){
break
}
let seekword = word.lowercased()
if let val = words_dictionary[seekword]{
sequence[thisWord]=Int32(val)
thisWord = thisWord + 1
}
}
return sequence
}
Perhatikan bahwa urutannya adalah Int32. Hal ini sengaja dipilih karena saat meneruskan nilai ke TensorFlow Lite, Anda akan menangani memori tingkat rendah, dan TensorFlow Lite memperlakukan bilangan bulat dalam urutan string sebagai bilangan bulat 32-bit. Hal ini akan memudahkan Anda (sedikit) dalam meneruskan string ke model.
11. Melakukan Klasifikasi
Untuk mengklasifikasikan kalimat, kalimat tersebut harus dikonversi menjadi urutan token terlebih dahulu berdasarkan kata-kata dalam kalimat. Hal ini akan dilakukan pada langkah 9.
Sekarang Anda akan mengambil kalimat dan meneruskannya ke model, meminta model melakukan inferensi pada kalimat, dan mengurai hasilnya.
Tindakan ini akan menggunakan penafsir TensorFlow Lite, yang harus Anda impor:
import TensorFlowLite
Mulai dengan func
yang menggunakan urutan Anda, yang merupakan array jenis Int32:
func classify(sequence: [Int32]){
// Model Path is the location of the model in the bundle
let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
var interpreter: Interpreter
do{
interpreter = try Interpreter(modelPath: modelPath!)
} catch _{
print("Error loading model!")
return
}
Tindakan ini akan memuat file model dari paket, dan memanggil penafsir dengan file tersebut.
Langkah berikutnya adalah menyalin memori dasar yang disimpan dalam urutan ke buffer yang disebut myData,
sehingga dapat diteruskan ke tensor. Saat menerapkan pod TensorFlow Lite serta penafsir, Anda mendapatkan akses ke Tensor Type.
Mulai kode seperti ini (masih dalam func
klasifikasi):
let tSequence = Array(sequence)
let myData = Data(copyingBufferOf: tSequence.map { Int32($0) })
let outputTensor: Tensor
Jangan khawatir jika Anda mendapatkan error pada copyingBufferOf
. Hal ini akan diterapkan sebagai ekstensi nanti.
Sekarang saatnya mengalokasikan tensor di penafsir, menyalin buffer data yang baru saja Anda buat ke tensor input, lalu memanggil penafsir untuk melakukan inferensi:
do {
// Allocate memory for the model's input `Tensor`s.
try interpreter.allocateTensors()
// Copy the data to the input `Tensor`.
try interpreter.copy(myData, toInputAt: 0)
// Run inference by invoking the `Interpreter`.
try interpreter.invoke()
Setelah pemanggilan selesai, Anda dapat melihat output penafsir untuk melihat hasilnya.
Ini akan berupa nilai mentah (4 byte per neuron) yang kemudian harus Anda baca dan konversi. Karena model khusus ini memiliki 2 neuron output, Anda harus membaca 8 byte yang akan dikonversi menjadi Float32 untuk penguraian. Anda menangani memori tingkat rendah, sehingga unsafeData
.
// Get the output `Tensor` to process the inference results.
outputTensor = try interpreter.output(at: 0)
// Turn the output tensor into an array. This will have 2 values
// Value at index 0 is the probability of negative sentiment
// Value at index 1 is the probability of positive sentiment
let resultsArray = outputTensor.data
let results: [Float32] = [Float32](unsafeData: resultsArray) ?? []
Sekarang, mengurai data untuk menentukan kualitas spam relatif mudah. Model ini memiliki 2 output, output pertama dengan kemungkinan bahwa pesan bukan spam, output kedua dengan kemungkinan bahwa pesan bukan spam. Jadi, Anda dapat melihat results[1]
untuk menemukan nilai spam:
let positiveSpamValue = results[1]
var outputString = ""
if(positiveSpamValue>0.8){
outputString = "Message not sent. Spam detected with probability: " + String(positiveSpamValue)
} else {
outputString = "Message sent!"
}
txtOutput.text = outputString
Untuk memudahkan, berikut adalah metode lengkapnya:
func classify(sequence: [Int32]){
// Model Path is the location of the model in the bundle
let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
var interpreter: Interpreter
do{
interpreter = try Interpreter(modelPath: modelPath!)
} catch _{
print("Error loading model!")
Return
}
let tSequence = Array(sequence)
let myData = Data(copyingBufferOf: tSequence.map { Int32($0) })
let outputTensor: Tensor
do {
// Allocate memory for the model's input `Tensor`s.
try interpreter.allocateTensors()
// Copy the data to the input `Tensor`.
try interpreter.copy(myData, toInputAt: 0)
// Run inference by invoking the `Interpreter`.
try interpreter.invoke()
// Get the output `Tensor` to process the inference results.
outputTensor = try interpreter.output(at: 0)
// Turn the output tensor into an array. This will have 2 values
// Value at index 0 is the probability of negative sentiment
// Value at index 1 is the probability of positive sentiment
let resultsArray = outputTensor.data
let results: [Float32] = [Float32](unsafeData: resultsArray) ?? []
let positiveSpamValue = results[1]
var outputString = ""
if(positiveSpamValue>0.8){
outputString = "Message not sent. Spam detected with probability: " +
String(positiveSpamValue)
} else {
outputString = "Message sent!"
}
txtOutput.text = outputString
} catch let error {
print("Failed to invoke the interpreter with error: \(error.localizedDescription)")
}
}
12. Menambahkan Ekstensi Swift
Kode di atas menggunakan ekstensi ke Jenis data untuk memungkinkan Anda menyalin bit mentah array Int32 ke dalam Data
. Berikut kode untuk ekstensi tersebut:
extension Data {
/// Creates a new buffer by copying the buffer pointer of the given array.
///
/// - Warning: The given array's element type `T` must be trivial in that it can be copied bit
/// for bit with no indirection or reference-counting operations; otherwise, reinterpreting
/// data from the resulting buffer has undefined behavior.
/// - Parameter array: An array with elements of type `T`.
init<T>(copyingBufferOf array: [T]) {
self = array.withUnsafeBufferPointer(Data.init)
}
}
Saat menangani memori tingkat rendah, Anda menggunakan data "tidak aman", dan kode di atas mengharuskan Anda menginisialisasi array data yang tidak aman. Ekstensi ini memungkinkan:
extension Array {
/// Creates a new array from the bytes of the given unsafe data.
///
/// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit
/// with no indirection or reference-counting operations; otherwise, copying the raw bytes in
/// the `unsafeData`'s buffer to a new array returns an unsafe copy.
/// - Note: Returns `nil` if `unsafeData.count` is not a multiple of
/// `MemoryLayout<Element>.stride`.
/// - Parameter unsafeData: The data containing the bytes to turn into an array.
init?(unsafeData: Data) {
guard unsafeData.count % MemoryLayout<Element>.stride == 0 else { return nil }
#if swift(>=5.0)
self = unsafeData.withUnsafeBytes { .init($0.bindMemory(to: Element.self)) }
#else
self = unsafeData.withUnsafeBytes {
.init(UnsafeBufferPointer<Element>(
start: $0,
count: unsafeData.count / MemoryLayout<Element>.stride
))
}
#endif // swift(>=5.0)
}
}
13. Menjalankan Aplikasi iOS
Jalankan dan uji aplikasi.
Jika semua berjalan lancar, Anda akan melihat aplikasi di perangkat Anda seperti ini:
Jika pesan "Beli buku saya untuk mempelajari trading online" dikirim, aplikasi akan mengirimkan kembali pemberitahuan deteksi spam dengan probabilitas 0,99%.
14. Selamat!
Sekarang Anda telah membuat aplikasi yang sangat sederhana yang memfilter teks untuk spam komentar menggunakan model yang dilatih dengan data yang digunakan untuk mengirim spam ke blog.
Langkah berikutnya dalam siklus proses developer standar adalah mempelajari hal-hal yang diperlukan untuk menyesuaikan model berdasarkan data yang ditemukan di komunitas Anda sendiri. Anda akan melihat cara melakukannya di aktivitas jalur berikutnya.