Section 4/171 menit
4. MLMultiArray dan Tipe Data Spesialis
4. MLMultiArray dan Tipe Data Spesialis
MLMultiArray adalah kontainer tensor n-dimensi untuk input/output yang bukan gambar atau string. Penggunaannya yang efisien sangat krusial untuk performa.
Membuat MLMultiArray
swift
import CoreML
// MLMultiArray 1D: vektor 512 elemen
let embedding = try MLMultiArray(shape: [512], dataType: .float32)
// MLMultiArray 2D: batch 8 sequence, panjang 128
let tokenIDs = try MLMultiArray(shape: [8, 128], dataType: .int32)
// MLMultiArray 3D: batch 4, 3 channel, 224x224
let imageTensor = try MLMultiArray(shape: [4, 3, 224, 224], dataType: .float16)
// Akses elemen via subscript (lambat untuk loop besar)
embedding[0] = 0.5
embedding[[0] as [NSNumber]] = 0.5 // ekuivalen
// Akses elemen via pointer (cepat untuk batch)
let pointer = UnsafeMutablePointer<Float>(ObjCBool(false).map { _ in embedding.dataPointer.assumingMemoryBound(to: Float.self) })
Zero-Copy dengan UnsafeMutablePointer
Untuk performa maksimal, langsung akses memory pointer MLMultiArray tanpa konversi:
swift
// Mengisi MLMultiArray dari array Swift tanpa copy
func fillMultiArray(_ array: MLMultiArray, from source: [Float]) {
assert(source.count == array.count)
let pointer = array.dataPointer.assumingMemoryBound(to: Float.self)
source.withUnsafeBufferPointer { buffer in
pointer.initialize(from: buffer.baseAddress!, count: buffer.count)
}
}
// Membaca MLMultiArray ke array Swift tanpa copy
func readMultiArray(_ array: MLMultiArray) -> [Float] {
let pointer = array.dataPointer.assumingMemoryBound(to: Float.self)
return Array(UnsafeBufferPointer(start: pointer, count: array.count))
}
// Operasi in-place pada MLMultiArray dengan Accelerate
import Accelerate
func normalizeMultiArray(_ array: MLMultiArray, mean: Float, std: Float) {
let count = array.count
let pointer = array.dataPointer.assumingMemoryBound(to: Float.self)
// Kurangi mean
var negativeMean = -mean
vDSP_vsadd(pointer, 1, &negativeMean, pointer, 1, vDSP_Length(count))
// Bagi dengan std
var reciprocalStd = 1.0 / std
vDSP_vsmul(pointer, 1, &reciprocalStd, pointer, 1, vDSP_Length(count))
}
Strides dan Non-Contiguous Memory
MLMultiArray menyimpan data dalam row-major order. Untuk operasi yang perlu akses per-channel atau per-batch, strides sangat penting:
swift
// Inspect strides (bytes per elemen di setiap dimensi)
let array = try MLMultiArray(shape: [2, 3, 224, 224], dataType: .float32)
print(array.strides) // [3*224*224, 224*224, 224, 1] × 4 bytes
// Manual indexing dengan strides untuk akses per-channel
func getPixelValue(from array: MLMultiArray, batch: Int, channel: Int, row: Int, col: Int) -> Float {
let strides = array.strides.map { $0.intValue }
let offset = batch * strides[0] + channel * strides[1] + row * strides[2] + col * strides[3]
let pointer = array.dataPointer.assumingMemoryBound(to: Float.self)
return pointer[offset / MemoryLayout<Float>.stride]
}
MLShapedArray (iOS 15+)
MLShapedArray adalah wrapper yang lebih ergonomis dan type-safe di atas MLMultiArray:
swift
import CoreML
// MLShapedArray — type-safe, Sendable, bisa dipakai di Swift Concurrency
let shaped = MLShapedArray<Float>(shape: [1, 512], scalars: Array(repeating: 0, count: 512))
// Akses elemen
print(shaped[[0, 0]]) // elemen pertama
// Slice
let slice = shaped[0...0, 0...255] // sub-array tanpa copy
// Konversi ke MLMultiArray untuk API lama
let multiArray = try MLMultiArray(shaped)
// Dari MLMultiArray ke MLShapedArray
let backToShaped = MLShapedArray<Float>(multiArray)
// Buat dari flat array
let fromFlat = MLShapedArray<Float>(scalars: [1.0, 2.0, 3.0, 4.0], shape: [2, 2])
Tipe Data dan Presisi
swift
// Float32: presisi penuh, kompatibel paling luas
let f32 = try MLMultiArray(shape: [512], dataType: .float32)
// Float16: lebih hemat memori, lebih cepat di ANE/GPU
// Core ML otomatis mengkonversi internal jika model membutuhkan
let f16 = try MLMultiArray(shape: [512], dataType: .float16)
// Int32: untuk token IDs, indeks, label integer
let int32 = try MLMultiArray(shape: [128], dataType: .int32)
// Double: jarang dibutuhkan, lebih lambat di hardware ML
let f64 = try MLMultiArray(shape: [512], dataType: .double)
// Tips: model quantized ke INT8 tetap menerima Float input —
// Core ML yang handle konversi internal