diff --git a/Sources/MarkBase/Embedding/EmbeddingGemmaModel.swift b/Sources/MarkBase/Embedding/EmbeddingGemmaModel.swift index 4a5c404..6245486 100644 --- a/Sources/MarkBase/Embedding/EmbeddingGemmaModel.swift +++ b/Sources/MarkBase/Embedding/EmbeddingGemmaModel.swift @@ -1,5 +1,6 @@ import Foundation import Metal +import MetalPerformanceShaders import Accelerate /// EmbeddingGemma configuration @@ -80,15 +81,15 @@ public final class EmbeddingGemmaModel: @unchecked Sendable { try loadBuffer("\(p).post_attention_layernorm.weight"), try loadBuffer("\(p).post_feedforward_layernorm.weight"), ]) - qProjs.append(try loadBuffer("\(p).self_attn.q_proj.weight")) - kProjs.append(try loadBuffer("\(p).self_attn.k_proj.weight")) - vProjs.append(try loadBuffer("\(p).self_attn.v_proj.weight")) - oProjs.append(try loadBuffer("\(p).self_attn.o_proj.weight")) + qProjs.append(try loadAndTranspose("\(p).self_attn.q_proj.weight", rows: config.hiddenSize, cols: config.hiddenSize)) + kProjs.append(try loadAndTranspose("\(p).self_attn.k_proj.weight", rows: config.numKeyValueHeads * config.headDim, cols: config.hiddenSize)) + vProjs.append(try loadAndTranspose("\(p).self_attn.v_proj.weight", rows: config.numKeyValueHeads * config.headDim, cols: config.hiddenSize)) + oProjs.append(try loadAndTranspose("\(p).self_attn.o_proj.weight", rows: config.numAttentionHeads * config.headDim, cols: config.hiddenSize)) qNorms.append(try loadBuffer("\(p).self_attn.q_norm.weight")) kNorms.append(try loadBuffer("\(p).self_attn.k_norm.weight")) - gateProjs.append(try loadBuffer("\(p).mlp.gate_proj.weight")) - upProjs.append(try loadBuffer("\(p).mlp.up_proj.weight")) - downProjs.append(try loadBuffer("\(p).mlp.down_proj.weight")) + gateProjs.append(try loadAndTranspose("\(p).mlp.gate_proj.weight", rows: config.intermediateSize, cols: config.hiddenSize)) + upProjs.append(try loadAndTranspose("\(p).mlp.up_proj.weight", rows: config.intermediateSize, cols: config.hiddenSize)) + downProjs.append(try loadAndTranspose("\(p).mlp.down_proj.weight", rows: config.hiddenSize, cols: config.intermediateSize)) } let fnData = try readTensor("norm.weight") finalNorm = engine.device.makeBuffer(bytes: fnData, length: fnData.count * 4)! @@ -161,6 +162,18 @@ public final class EmbeddingGemmaModel: @unchecked Sendable { return engine.device.makeBuffer(bytes: data, length: data.count * 4)! } + private func loadAndTranspose(_ name: String, rows: Int, cols: Int) throws -> MTLBuffer { + // Load [rows, cols] and transpose to [cols, rows] for MPS matmul C = A × B + let data = try readTensor(name) + var transposed = [Float](repeating: 0, count: data.count) + for r in 0.. MTLBuffer { let seqLen = tokens.count, hs = config.hiddenSize let buf = engine.device.makeBuffer(length: seqLen * hs * 4)! @@ -196,22 +209,24 @@ public final class EmbeddingGemmaModel: @unchecked Sendable { } private func matmulSeq(input: MTLBuffer, weight: MTLBuffer, output: MTLBuffer, m: Int, k: Int, n: Int, cmdBuf: MTLCommandBuffer) throws { - let enc = cmdBuf.makeComputeCommandEncoder()! - defer { enc.endEncoding() } - let pso = try engine.pipeline(named: "matmul_f32") - enc.setComputePipelineState(pso) - for i in 0..