Section 11/171 menit
11. Stateful Models (iOS 18)
11. Stateful Models (iOS 18)
iOS 18 memperkenalkan stateful models — model yang mempertahankan state antar prediksi. Ini fundamental untuk sequence models seperti LSTM, autoregressive transformers, dan RNN tanpa harus pass state eksplisit.
swift
// Stateful model — state dikelola oleh Core ML runtime
// Berguna untuk: chatbot, speech recognition, real-time sensor fusion
if #available(iOS 18, *) {
// Load model stateful
let config = MLModelConfiguration()
let model = try MLModel(contentsOf: compiledModelURL, configuration: config)
// Buat MLState — mewakili satu "sesi" dengan state terpisah
// Berbeda state = berbeda conversation thread
let state1 = try model.makeState()
let state2 = try model.makeState() // state terpisah, independen
// Prediksi dengan state — state diperbarui otomatis
let input1 = NextTokenInput(token: 1234)
let output1 = try model.prediction(from: input1, using: state1)
// state1 sekarang berisi KV-cache dari token 1234
let input2 = NextTokenInput(token: 5678)
let output2 = try model.prediction(from: input2, using: state1)
// state1 berisi KV-cache dari token 1234 DAN 5678
// state2 masih kosong (independen dari state1)
let output3 = try model.prediction(from: NextTokenInput(token: 9999), using: state2)
// Reset state (mulai conversation baru)
// Cukup buat state baru: let freshState = try model.makeState()
}
// Placeholder
class NextTokenInput: MLFeatureProvider {
init(token: Int) {}
var featureNames: Set<String> { [] }
func featureValue(for featureName: String) -> MLFeatureValue? { nil }
}
Stateful Model untuk Autoregressive Generation
swift
// LLM autoregressive text generation dengan stateful model (iOS 18)
@available(iOS 18, *)
actor StatefulLLMInference {
private let model: MLModel
private var activeStates: [String: MLState] = [:] // sessionID → state
init(modelURL: URL) throws {
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
self.model = try MLModel(contentsOf: modelURL, configuration: config)
}
func startSession() throws -> String {
let sessionID = UUID().uuidString
activeStates[sessionID] = try model.makeState()
return sessionID
}
func generateNextToken(sessionID: String, currentToken: Int) throws -> Int {
guard let state = activeStates[sessionID] else {
throw GenerationError.sessionNotFound
}
let input = LLMTokenInput(tokenID: currentToken)
let output = try model.prediction(from: input, using: state)
// State sudah di-update dengan KV-cache dari currentToken
guard let logits = output.featureValue(for: "logits")?.multiArrayValue else {
throw GenerationError.invalidOutput
}
return argmax(logits)
}
func endSession(sessionID: String) {
activeStates.removeValue(forKey: sessionID)
// State di-release secara otomatis
}
private func argmax(_ array: MLMultiArray) -> Int {
let ptr = array.dataPointer.assumingMemoryBound(to: Float.self)
var maxIdx = 0
var maxVal = ptr[0]
for i in 1..<array.count {
if ptr[i] > maxVal {
maxVal = ptr[i]
maxIdx = i
}
}
return maxIdx
}
enum GenerationError: Error { case sessionNotFound, invalidOutput }
}
class LLMTokenInput: MLFeatureProvider {
let tokenID: Int
var featureNames: Set<String> { ["input_ids"] }
init(tokenID: Int) { self.tokenID = tokenID }
func featureValue(for featureName: String) -> MLFeatureValue? {
guard featureName == "input_ids" else { return nil }
let array = try? MLMultiArray(shape: [1, 1], dataType: .int32)
array?.dataPointer.assumingMemoryBound(to: Int32.self)[0] = Int32(tokenID)
return array.map { MLFeatureValue(multiArray: $0) }
}
}