Nurymanau's picture
Release v0.1.0: two fully trained text decision models, inference and audited results
0999838 verified
Raw History Blame Contribute Delete
3.52 kB
// Derived from EmbeddingGemma2Swift (MIT). Inference weight loader only.
import Foundation
import MLX
struct QuantizationMetadata: Codable {
let version: Int
let mode: String
let bits, groupSize: Int
let shapes: [String:[Int]]
static func load(_ directory:URL) throws -> Self? {
let url=directory.appendingPathComponent("quantization.json")
guard FileManager.default.fileExists(atPath:url.path) else { return nil }
let q=try JSONDecoder().decode(Self.self,from:Data(contentsOf:url))
guard q.version==1,q.mode=="affine",[4,8].contains(q.bits),q.groupSize==64,!q.shapes.isEmpty else { throw EmbeddingError.invalid("Unsupported quantization metadata") }
return q
}
}
/// Keeps packed matrices resident; only requested embedding rows are dequantized.
struct WeightStore {
let values: [String:MLXArray]
let quantization: QuantizationMetadata?
let dtype: DType
init(loaded:[String:MLXArray], shapes:[String:[Int]], directory:URL, dtype:DType) throws {
self.dtype=dtype
let q=try QuantizationMetadata.load(directory);quantization=q
let packed=q?.shapes ?? [:]
guard Set(packed.keys).isSubset(of:Set(shapes.keys)) else { throw EmbeddingError.invalid("Unknown quantized tensors") }
var keys=Set(shapes.keys),selected=[String:MLXArray]()
for key in packed.keys { keys.insert(key+".scales");keys.insert(key+".biases") }
guard Set(loaded.keys)==keys else { throw EmbeddingError.invalid("Weight keys mismatch") }
for (key,shape) in shapes {
let value=loaded[key]!
if let original=packed[key],let q {
guard original==shape,shape.count==2,shape[1]%q.groupSize==0,
value.shape==[shape[0],shape[1]*q.bits/32],value.dtype == .uint32 else { throw EmbeddingError.invalid("Invalid packed tensor: \(key)") }
selected[key]=value
for suffix in [".scales",".biases"] {
let a=loaded[key+suffix]!
guard a.shape==[shape[0],shape[1]/q.groupSize],a.dtype == .bfloat16 || a.dtype == .float32 else { throw EmbeddingError.invalid("Invalid quantization scales/biases: \(key)") }
selected[key+suffix]=a.asType(dtype)
}
} else {
guard value.shape==shape,value.dtype == .bfloat16 || value.dtype == .float32 else { throw EmbeddingError.invalid("Invalid tensor shape/precision: \(key)") }
selected[key]=value.asType(dtype)
}
}
values=selected;eval(Array(selected.values))
}
subscript(_ key:String) -> MLXArray? { values[key] }
func linear(_ x:MLXArray,_ key:String) -> MLXArray {
if let q=quantization,q.shapes[key] != nil {
return quantizedMM(x,values[key]!,scales:values[key+".scales"]!,biases:values[key+".biases"]!,transpose:true,groupSize:q.groupSize,bits:q.bits)
}
return matmul(x,values[key]!.T)
}
func embedding(_ ids:MLXArray,_ key:String) -> MLXArray {
if let q=quantization,let shape=q.shapes[key] {
let rows=ids.reshaped(-1)
return dequantized(values[key]![rows],scales:values[key+".scales"]![rows],biases:values[key+".biases"]![rows],groupSize:q.groupSize,bits:q.bits,dtype:dtype).reshaped(ids.shape+[shape[1]])
}
return values[key]![ids]
}
var format:String { quantization.map { "affine\($0.bits)-g\($0.groupSize)" } ?? "unquantized" }
}