1. Başlamadan önce
Bu codelab'de, önceki Mobil metin sınıflandırmasına başlama codelab'lerinde oluşturduğunuz uygulamayı güncelleyeceksiniz.
Ön koşullar
- Bu codelab, makine öğrenimi konusunda yeni olan deneyimli geliştiriciler için tasarlanmıştır.
- Kod laboratuvarı, sıralı bir rotanın parçasıdır. Henüz Temel bir mesajlaşma stili uygulaması oluşturma veya Spam yorum makine öğrenimi modeli oluşturma bölümlerini tamamlamadıysanız lütfen durup şimdi tamamlayın.
[Üretecekleriniz veya öğrenecekleriniz]
- Özel modelinizi önceki adımlarda oluşturduğunuz uygulamanıza nasıl entegre edeceğinizi öğreneceksiniz.
İhtiyacınız olanlar
- Android Studio veya iOS için CocoaPods
2. Mevcut Android Uygulamasını Açma
Bunun için Codelab 1'i uygulayabilir veya bu depoyu klonlayıp uygulamayı TextClassificationStep1 konumundan yükleyebilirsiniz.
git clone https://github.com/googlecodelabs/odml-pathways
Bu bilgiyi TextClassificationOnMobile->Android yolunda bulabilirsiniz.
Tamamlanmış kod, TextClassificationStep2 olarak da kullanılabilir.
Açıldıktan sonra 2. adıma geçebilirsiniz.
3. Model dosyasını ve meta verilerini içe aktarma
Yorumlardaki spam yorumları tespit eden bir makine öğrenimi modeli oluşturma codelab'inde .TFLITE modeli oluşturmuştunuz.
Model dosyasını indirmiş olmanız gerekir. Bu dosyaya sahip değilseniz bu codelab'in depodan alabilirsiniz. Model burada mevcuttur.
Öğeler dizini oluşturarak projenize ekleyin.
- Proje Gezgini'ni kullanarak üstte Android'in seçili olduğundan emin olun.
- Uygulama klasörünü sağ tıklayın. Yeni > Dizin'i seçin.

- Yeni Dizin iletişim kutusunda src/main/assets'i seçin.

Uygulamada yeni bir öğeler klasörünün kullanıma sunulduğunu görürsünüz.

- assets klasörünü sağ tıklayın.
- Açılan menüde (Mac'te) Bulucu'da göster'i görürsünüz. Uygulamayı seçin. (Windows'da Gezgin'de göster, Ubuntu'da Dosyalar'da göster yazacaktır.)

Dosyaların konumunu göstermek için Finder başlatılır (Windows'da Dosya Gezgini, Linux'ta Dosyalar).
labels.txt,model.tflitevevocabdosyalarını bu dizine kopyalayın.

- Android Studio'ya döndüğünüzde bu dosyaları öğeler klasörünüzde görebilirsiniz.

4. TensorFlow Lite'ı kullanmak için build.gradle dosyanızı güncelleyin
TensorFlow Lite'ı ve bunu destekleyen TensorFlow Lite görev kitaplıklarını kullanmak için build.gradle dosyanızı güncellemeniz gerekir.
Android projelerinde genellikle birden fazla bulunur. Bu nedenle uygulama düzeyinde olanı bulduğunuzdan emin olun. Android görünümündeki proje gezgininde, Gradle Scripts (Gradle Komut Dosyaları) bölümünde bulabilirsiniz. Doğru olan, burada gösterildiği gibi .app ile etiketlenir:

Bu dosyada iki değişiklik yapmanız gerekir. Birincisi en alttaki bağımlılıklar bölümünde yer alır. TensorFlow Lite görev kitaplığı için şu şekilde bir metin implementation ekleyin:
implementation 'org.tensorflow:tensorflow-lite-task-text:0.1.0'
Sürüm numarası bu belge yazıldığından beri değişmiş olabilir. Bu nedenle, en son sürüm için https://www.tensorflow.org/lite/inference_with_metadata/task_library/nl_classifier adresini kontrol edin.
Görev kitaplıkları için de minimum SDK sürümünün 21 olması gerekir. Bu ayarı android > default config bölümünde bulup 21 olarak değiştirin:

Artık tüm bağımlılıklarınız var. Kodlamaya başlama zamanı!
5. Yardımcı sınıf ekleme
Uygulamanızın modeli kullandığı çıkarım mantığını kullanıcı arayüzünden ayırmak için model çıkarımını işleyecek başka bir sınıf oluşturun. Buna "yardımcı" sınıfı diyelim.
MainActivitykodunuzun bulunduğu paket adını sağ tıklayın.- Yeni > Paket'i seçin.

- Ekranın ortasında, paket adını girmenizi isteyen bir iletişim kutusu gösterilir. Mevcut paket adının sonuna ekleyin. (Burada yardımcılar olarak adlandırılır.)

- Bu işlem tamamlandıktan sonra proje gezgininde helpers klasörünü sağ tıklayın.
- New > Java Class'ı (Yeni > Java Sınıfı) seçin ve
TextClassificationClientolarak adlandırın. Dosyayı bir sonraki adımda düzenleyeceksiniz.
TextClassificationClientYardımcı sınıfınız (paket adınız farklı olabilir) aşağıdaki gibi görünür.
package com.google.devrel.textclassificationstep1.helpers;
public class TextClassificationClient {
}
- Dosyayı bu kodla güncelleyin:
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;
}
}
Bu sınıf, TensorFlow Lite yorumlayıcısı için bir sarmalayıcı sağlar, modeli yükler ve uygulamanız ile model arasındaki veri alışverişini yönetmenin karmaşıklığını soyutlar.
load() yönteminde, model yolundan yeni bir NLClassifier türü oluşturulur. Model yolu, modelin adıdır (model.tflite). NLClassifier türü, metin görevleri kitaplıklarının bir parçasıdır. Dizenizi jetonlara dönüştürerek, doğru sıra uzunluğunu kullanarak, modeli geçirerek ve sonuçları ayrıştırarak size yardımcı olur.
(Bunlarla ilgili daha fazla bilgi için Spam yorum makine öğrenimi modeli oluşturma başlıklı makaleyi tekrar inceleyin.)
Sınıflandırma, bir dize ilettiğiniz ve List döndüren sınıflandırma yönteminde gerçekleştirilir. Bir dizenin spam olup olmadığını belirlemek istediğiniz içerikleri sınıflandırmak için makine öğrenimi modellerini kullanırken tüm yanıtların, atanmış olasılıklarla birlikte döndürülmesi yaygın bir durumdur. Örneğin, spam gibi görünen bir mesaj iletirseniz biri spam olma olasılığı, diğeri ise spam olmama olasılığı ile ilgili olmak üzere 2 yanıt içeren bir liste alırsınız. Spam/Spam Değil kategorilerdir. Bu nedenle, döndürülen List bu olasılıkları içerir. Bunu daha sonra ayrıştıracaksınız.
Yardımcı sınıfı oluşturduktan sonra MainActivity dosyasına geri dönün ve metninizi sınıflandırmak için bu sınıfı kullanacak şekilde güncelleyin. Bunu sonraki adımda göreceksiniz.
6. Metni Sınıflandırma
MainActivity uygulamanızda, yeni oluşturduğunuz yardımcıları içe aktarmanız gerekir.
MainActivity.ktüst kısmına, diğer içe aktarma işlemlerinin yanına şunları ekleyin:
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
- Ardından, yardımcıları yüklemeniz gerekir.
onCreateiçinde,setContentViewsatırından hemen sonra yardımcı sınıfı örneklendirmek ve yüklemek için şu satırları ekleyin:
val client = TextClassificationClient(applicationContext)
client.load()
Şu anda düğmenizin onClickListener simgesi aşağıdaki gibi görünmelidir:
btnSendText.setOnClickListener {
var toSend:String = txtInput.text.toString()
txtOutput.text = toSend
}
- Aşağıdaki gibi görünecek şekilde güncelleyin:
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()
}
Bu değişiklik, işlevin yalnızca kullanıcı girişini vermekten önce sınıflandırmaya geçmesini sağlar.
- Bu satırla, kullanıcının girdiği dizeyi alıp modele ileterek sonuçları geri alırsınız:
var results:List<Category> = client.classify(toSend)
Yalnızca 2 kategori vardır: False ve True
. (TensorFlow bunları alfabetik olarak sıralar. Bu nedenle, False 0. öğe, True ise 1. öğe olur.)
- Değerin
Trueolma olasılığına ilişkin puanı almak için results[1].score değerini şu şekilde inceleyebilirsiniz:
val score = results[1].score
- Bir eşik değeri (bu örnekte 0,8) seçtiniz. Bu değere göre, True kategorisinin puanı eşik değerin (0,8) üzerindeyse mesaj spam olarak kabul edilir. Aksi takdirde, ileti spam değildir ve güvenle gönderilebilir:
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()
}
- Modelin işleyiş şeklini buradan inceleyebilirsiniz. "Ürün satın almak için blogumu ziyaret edin!" iletisi, spam olma olasılığı yüksek olarak işaretlendi:

Buna karşılık, "Hey, fun tutorial, thanks!" (Hey, eğlenceli eğitim için teşekkürler!) ifadesinin spam olma olasılığının çok düşük olduğu görülmüştür:

7. TensorFlow Lite modelini kullanmak için iOS uygulamanızı güncelleme
Bunun için Codelab 1'i uygulayabilir veya bu depoyu klonlayıp uygulamayı TextClassificationStep1 konumundan yükleyebilirsiniz. Bu bilgiyi TextClassificationOnMobile->iOS yolunda bulabilirsiniz.
Tamamlanmış kod, TextClassificationStep2 olarak da kullanılabilir.
Build a comment spam machine learning model (Yorum spam'i makine öğrenimi modeli oluşturma) adlı codelab'de, kullanıcının UITextView içine mesaj yazmasına ve mesajın herhangi bir filtreleme olmadan çıkışa aktarılmasına olanak tanıyan çok basit bir uygulama oluşturmuştunuz.
Şimdi bu uygulamayı, gönderilmeden önce metindeki spam yorumu algılamak için TensorFlow Lite modelini kullanacak şekilde güncelleyeceksiniz. Metni bir çıkış etiketinde oluşturarak bu uygulamada gönderme işlemini simüle etmeniz yeterlidir (ancak gerçek bir uygulamada duyuru panosu, sohbet veya benzer bir özellik olabilir).
Başlamak için 1. adımda belirtilen uygulamaya ihtiyacınız var. Bu uygulamayı depodan klonlayabilirsiniz.
TensorFlow Lite'ı dahil etmek için CocoaPods'u kullanırsınız. Bunlar yüklü değilse https://cocoapods.org/ adresindeki talimatları uygulayarak yükleyebilirsiniz.
- CocoaPods'u yükledikten sonra TextClassification uygulaması için
.xcprojectile aynı dizinde Podfile adlı bir dosya oluşturun. Bu dosyanın içeriği şu şekilde olmalıdır:
target 'TextClassificationStep2' do
use_frameworks!
# Pods for NLPClassifier
pod 'TensorFlowLiteSwift'
end
İlk satırda "TextClassificationStep2" yerine uygulamanızın adı olmalıdır.
Terminal'i kullanarak bu dizine gidin ve pod install komutunu çalıştırın. İşlem başarılı olursa Pods adlı yeni bir dizininiz ve sizin için oluşturulmuş yeni bir .xcworkspace dosyanız olur. Gelecekte .xcproject yerine bu değeri kullanacaksınız.
Başarısız olursa lütfen .xcproject dosyasının bulunduğu dizinde Podfile dosyanızın olduğundan emin olun. Genellikle, podfile'ın yanlış dizinde olması veya hedef adının yanlış olması bu soruna neden olur.
8. Model ve Sözlük Dosyalarını Ekleme
TensorFlow Lite Model Maker ile modeli oluşturduğunuzda modeli (model.tflite olarak) ve kelime dağarcığını (vocab.txt olarak) çıkış olarak verebiliyordunuz.
- Bu öğeleri Finder'dan proje pencerenize sürükleyip bırakarak projenize ekleyin. Hedeflere ekle seçeneğinin işaretlendiğinden emin olun:

İşlemi tamamladığınızda bunları projenizde görürsünüz:

- Projenizi seçip (yukarıdaki ekran görüntüsünde mavi simge TextClassificationStep2) Build Phases (Derleme Aşamaları) sekmesine bakarak dosyaların pakete eklenip eklenmediğini (böylece cihazlara dağıtılıp dağıtılmadığını) tekrar kontrol edin:

9. Load the Vocab
Doğal dil işleme sınıflandırması yapılırken model, vektörlere kodlanmış kelimelerle eğitilir. Model, eğitilirken öğrenilen belirli bir ad ve değer grubuyla kelimeleri kodlar. Çoğu modelin farklı kelime dağarcığına sahip olduğunu ve modeliniz için eğitim sırasında oluşturulan kelime dağarcığını kullanmanız gerektiğini lütfen unutmayın. Bu, uygulamanıza yeni eklediğiniz vocab.txt dosyasıdır.
Kodlamaları görmek için dosyayı Xcode'da açabilirsiniz. "Şarkı" gibi kelimeler 6, "aşk" gibi kelimeler ise 12 olarak kodlanır. Sıra aslında sıklık sırasıdır. Bu nedenle, veri kümesinde en sık kullanılan kelime "I" (Ben) olup bunu "check" (kontrol et) kelimesi takip etmektedir.
Kullanıcınız kelimeler yazdığında, bunları sınıflandırılmak üzere modele göndermeden önce bu kelime dağarcığıyla kodlamanız gerekir.
Bu kodu inceleyelim. Öncelikle kelime dağarcığını yükleyin.
- Sözlüğü depolamak için sınıf düzeyinde bir değişken tanımlayın:
var words_dictionary = [String : Int]()
- Ardından, kelime dağarcığını bu sözlüğe yüklemek için sınıfta
funcoluşturun:
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")
}
}
- Bu işlemi
viewDidLoadiçinden çağırarak çalıştırabilirsiniz:
override func viewDidLoad() {
super.viewDidLoad()
txtInput.delegate = self
loadVocab()
}
10. Bir dizeyi jeton dizisine dönüştürme
Kullanıcılarınız kelimeleri cümle olarak girer ve bu cümle dizeye dönüştürülür. Cümledeki her kelime, sözlükte varsa vocab'da tanımlandığı şekilde kelimenin anahtar değerine kodlanır.
Bir NLP modeli genellikle sabit bir dizi uzunluğunu kabul eder. ragged tensors kullanılarak oluşturulan modellerde istisnalar olsa da çoğu durumda bu sorunun düzeltildiğini görürsünüz. Bu uzunluğu modelinizi oluştururken belirttiniz. iOS uygulamanızda aynı uzunluğu kullandığınızdan emin olun.
Daha önce kullandığınız TensorFlow Lite Model Maker için Colab'deki varsayılan değer 20'ydi. Bu nedenle, burada da aynı değeri ayarlayın:
let SEQUENCE_LENGTH = 20
Dizeyi alıp küçük harfe dönüştürecek ve noktalama işaretlerini kaldıracak şu func işlevini ekleyin:
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
}
Dizinin Int32 olacağını unutmayın. Bu, değerleri TensorFlow Lite'a aktarma söz konusu olduğunda düşük düzeyli bellekle uğraşacağınız ve TensorFlow Lite'ın bir dize dizisindeki tam sayıları 32 bitlik tam sayılar olarak ele alacağı için kasıtlı olarak seçilmiştir. Bu, modele dize iletme konusunda hayatınızı (biraz) kolaylaştırır.
11. Sınıflandırma yapma
Bir cümlenin sınıflandırılabilmesi için önce cümledeki kelimelere göre jeton dizisine dönüştürülmesi gerekir. Bu işlem 9. adımda yapılmış olmalıdır.
Şimdi cümleyi alıp modele iletecek, modelin cümle üzerinde çıkarım yapmasını sağlayacak ve sonuçları ayrıştıracaksınız.
Bu işlemde, içe aktarmanız gereken TensorFlow Lite yorumlayıcısı kullanılır:
import TensorFlowLite
Dizinizin (Int32 türlerinden oluşan bir dizi) girişini alan bir func ile başlayın:
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
}
Bu işlem, paketteki model dosyasını yükler ve onunla bir yorumlayıcı çağırır.
Bir sonraki adımda, dizide depolanan temel bellek, tensöre aktarılabilmesi için myData, adlı bir arabelleğe kopyalanır. TensorFlow Lite pod'unu ve yorumlayıcıyı uygularken bir Tensor Türü'ne erişim elde ettiniz.
Kodu şu şekilde başlatın (hâlâ sınıflandırma func bölümündeyken):
let tSequence = Array(sequence)
let myData = Data(copyingBufferOf: tSequence.map { Int32($0) })
let outputTensor: Tensor
copyingBufferOf üzerinde hata alırsanız endişelenmeyin. Bu özellik daha sonra uzantı olarak kullanıma sunulacaktır.
Şimdi yorumlayıcıda tensörleri ayırma, yeni oluşturduğunuz veri arabelleğini giriş tensörüne kopyalama ve ardından çıkarım yapmak için yorumlayıcıyı çağırma zamanı:
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()
Çağırma işlemi tamamlandıktan sonra sonuçları görmek için yorumlayıcının çıkışına bakabilirsiniz.
Bunlar, daha sonra okuyup dönüştürmeniz gereken ham değerler (nöron başına 4 bayt) olacaktır. Bu modelde 2 çıkış nöronu olduğundan, ayrıştırma için Float32'ye dönüştürülecek 8 bayt okumanız gerekir. Düşük düzeyli bellek kullanıyorsunuz. Bu nedenle unsafeData simgesi gösteriliyor.
// 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) ?? []
Artık spam kalitesini belirlemek için verileri ayrıştırmak nispeten kolay. Modelin 2 çıkışı vardır. Birincisi iletinin spam olmama olasılığını, ikincisi ise spam olma olasılığını gösterir. Bu nedenle, spam değerini bulmak için results[1] değerine bakabilirsiniz:
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
Kolaylık olması açısından, yöntemin tamamını aşağıda bulabilirsiniz:
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. Swift uzantılarını ekleme
Yukarıdaki kod, Int32 dizisinin ham bitlerini bir Data'ye kopyalamanıza olanak tanımak için veri türünün bir uzantısını kullanmıştır. İlgili uzantının kodu:
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)
}
}
Düşük düzeyli bellek ile çalışırken "güvenli olmayan" veriler kullanırsınız ve yukarıdaki kod, güvenli olmayan verilerden oluşan bir diziyi başlatmanızı gerektirir. Bu uzantı sayesinde:
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. iOS uygulamasını çalıştırma
Uygulamayı çalıştırın ve test edin.
Her şey yolunda gittiyse uygulamayı cihazınızda aşağıdaki gibi görmeniz gerekir:

"Online ticareti öğrenmek için kitabımı satın alın!" mesajının gönderildiği yerde, uygulama %99 olasılıkla spam tespit edildi uyarısı gönderir.
14. Tebrikler!
Artık, bloglara spam göndermek için kullanılan verilerle eğitilmiş bir modeli kullanarak metni yorum spam'i için filtreleyen çok basit bir uygulama oluşturdunuz.
Tipik geliştirici yaşam döngüsündeki bir sonraki adım, kendi topluluğunuzda bulunan verilere göre modeli özelleştirmek için gerekenleri keşfetmektir. Bunu nasıl yapacağınızı bir sonraki rotadaki etkinlikte öğreneceksiniz.