Section 5/151 menit
5. Natural Language Framework — Pemrosesan Teks
5. Natural Language Framework — Pemrosesan Teks
Language Detection
swift
import NaturalLanguage
// Detect bahasa dari teks
func detectLanguage(of text: String) -> String? {
let recognizer = NLLanguageRecognizer()
recognizer.processString(text)
// Bahasa paling probable
guard let language = recognizer.dominantLanguage else { return nil }
return language.rawValue // "en", "id", "ja", dll
// Atau dapatkan probabilitas semua bahasa
// let hypotheses = recognizer.languageHypotheses(withMaximum: 3)
// → ["en": 0.98, "id": 0.01, "ms": 0.01]
}
Tokenization dan Part-of-Speech Tagging
swift
// NaturalLanguage: tokenization dan POS tagging
class TextAnalyzer {
struct Token {
let text: String
let range: Range<String.Index>
let partOfSpeech: NLTag?
}
func tokenize(_ text: String, unit: NLTokenUnit = .word) -> [Token] {
var tokens: [Token] = []
let tokenizer = NLTokenizer(unit: unit)
tokenizer.string = text
tokenizer.enumerateTokens(in: text.startIndex..<text.endIndex) { range, _ in
tokens.append(Token(text: String(text[range]), range: range, partOfSpeech: nil))
return true
}
return tokens
}
func tagPartsOfSpeech(in text: String) -> [Token] {
var tokens: [Token] = []
let tagger = NLTagger(tagSchemes: [.lexicalClass])
tagger.string = text
tagger.enumerateTags(
in: text.startIndex..<text.endIndex,
unit: .word,
scheme: .lexicalClass,
options: [.omitPunctuation, .omitWhitespace]
) { tag, range in
tokens.append(Token(
text: String(text[range]),
range: range,
partOfSpeech: tag
))
return true
}
return tokens
}
}
// NLTag values:
// .noun, .verb, .adjective, .adverb, .pronoun,
// .determiner, .particle, .preposition, .number,
// .conjunction, .interjection, .classifier, .idiom,
// .otherWord, .sentenceTerminator, .openQuote, .closeQuote
Named Entity Recognition (NER)
swift
// NaturalLanguage: Named Entity Recognition
struct NamedEntity {
let text: String
let type: EntityType
enum EntityType: String {
case person = "PersonalName"
case place = "PlaceName"
case organization = "OrganizationName"
case unknown
}
}
func extractNamedEntities(from text: String) -> [NamedEntity] {
var entities: [NamedEntity] = []
let tagger = NLTagger(tagSchemes: [.nameType])
tagger.string = text
let options: NLTagger.Options = [.omitPunctuation, .omitWhitespace, .joinNames]
tagger.enumerateTags(
in: text.startIndex..<text.endIndex,
unit: .word,
scheme: .nameType,
options: options
) { tag, range in
guard let tag else { return true }
let entityText = String(text[range])
let entityType = NamedEntity.EntityType(rawValue: tag.rawValue) ?? .unknown
if entityType != .unknown {
entities.append(NamedEntity(text: entityText, type: entityType))
}
return true
}
return entities
}
// Contoh:
// Input: "Tim Cook mengunjungi Apple Store di Jakarta"
// Output: [
// NamedEntity(text: "Tim Cook", type: .person),
// NamedEntity(text: "Apple Store", type: .organization),
// NamedEntity(text: "Jakarta", type: .place)
// ]
Sentiment Analysis
swift
// NaturalLanguage: sentiment analysis (iOS 13+)
func analyzeSentiment(of text: String) -> (score: Double, label: String) {
let tagger = NLTagger(tagSchemes: [.sentimentScore])
tagger.string = text
let (tag, _) = tagger.tag(
at: text.startIndex,
unit: .paragraph,
scheme: .sentimentScore
)
let score = Double(tag?.rawValue ?? "0") ?? 0.0
let label: String
switch score {
case 0.1...: label = "Positif"
case ..<(-0.1): label = "Negatif"
default: label = "Netral"
}
return (score, label)
// score: -1.0 (sangat negatif) hingga +1.0 (sangat positif)
}
// Sentence-level sentiment
func analyzeSentimentPerSentence(in text: String) -> [(sentence: String, score: Double)] {
var results: [(String, Double)] = []
let tagger = NLTagger(tagSchemes: [.sentimentScore])
tagger.string = text
tagger.enumerateTags(
in: text.startIndex..<text.endIndex,
unit: .sentence,
scheme: .sentimentScore,
options: []
) { tag, range in
let sentence = String(text[range])
let score = Double(tag?.rawValue ?? "0") ?? 0.0
results.append((sentence, score))
return true
}
return results
}
Custom Text Classifier dengan NLModel
swift
// NaturalLanguage: gunakan model custom yang di-train dengan Create ML
class CustomTextClassifier {
private let model: NLModel
init() throws {
// Load model yang di-train dengan Create ML Text Classifier
let url = Bundle.main.url(
forResource: "SupportTicketClassifier",
withExtension: "mlmodelc"
)!
self.model = try NLModel(contentsOf: url)
}
func classify(_ text: String) -> (label: String, confidence: Double)? {
let label = model.predictedLabel(for: text)
let hypotheses = model.predictedLabelHypotheses(for: text, maximumCount: 3)
guard let label,
let confidence = hypotheses[label] else { return nil }
return (label, confidence)
}
// Batch classification
func classifyBatch(_ texts: [String]) -> [(text: String, label: String, confidence: Double)] {
texts.compactMap { text in
guard let result = classify(text) else { return nil }
return (text, result.label, result.confidence)
}
}
}
Word Embedding dan Semantic Similarity
swift
// NaturalLanguage: word embedding untuk semantic similarity
class SemanticAnalyzer {
private let embedding: NLEmbedding?
init(language: NLLanguage = .english) {
self.embedding = NLEmbedding.wordEmbedding(for: language)
}
func similarity(between word1: String, and word2: String) -> Double {
guard let embedding else { return 0 }
return embedding.distance(between: word1, and: word2, distanceType: .cosine)
// 0.0 = identik, 2.0 = berlawanan
// Konversi ke similarity: similarity = 1.0 - distance
}
func findNeighbors(of word: String, count: Int = 5) -> [(word: String, distance: Float)] {
guard let embedding else { return [] }
return embedding.neighbors(for: word, maximumCount: count)
}
// Sentence embedding (lebih powerful dari word embedding)
func sentenceVector(for text: String) -> [Double]? {
guard let sentenceEmbedding = NLEmbedding.sentenceEmbedding(for: .english) else {
return nil
}
return sentenceEmbedding.vector(for: text)
}
}