diff options
| author | Michael Peter Christen <mc@yacy.net> | 2025-11-23 15:35:46 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2025-11-23 15:35:46 +0100 |
| commit | f1d17d94608ae50d9f3139a6f63c9435abd7d6ea (patch) | |
| tree | aec637459bf250e81921ae246781cd85d8b228f5 | |
| parent | df6e72b83ce15a631a512376febfcbc4463bb831 (diff) | |
refactoring
| -rw-r--r-- | source/net/yacy/ai/llama3/Llama.java | 101 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Llama3.java | 2 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java | 11 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Model/GGUF.java | 8 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Model/ModelLoader.java | 8 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Model/Vocabulary.java | 4 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Sampler.java | 9 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java | 269 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java | 26 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java | 2 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java | 6 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java | 2 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/FloatTensor.java | 248 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java | 12 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java | 12 | ||||
| -rw-r--r-- | source/net/yacy/ai/llama3/Tensor/Tensor.java | 100 |
16 files changed, 409 insertions, 411 deletions
diff --git a/source/net/yacy/ai/llama3/Llama.java b/source/net/yacy/ai/llama3/Llama.java index fa35ff418..6735d1d3e 100644 --- a/source/net/yacy/ai/llama3/Llama.java +++ b/source/net/yacy/ai/llama3/Llama.java @@ -32,8 +32,9 @@ import java.util.stream.Stream; import net.yacy.ai.llama3.Model.Arch; import net.yacy.ai.llama3.Model.Tokenizer; +import net.yacy.ai.llama3.Tensor.AbstractFloatTensor; import net.yacy.ai.llama3.Tensor.DirectBufferFloatTensor; -import net.yacy.ai.llama3.Tensor.Tensor; +import net.yacy.ai.llama3.Tensor.FloatTensor; public final class Llama { @@ -108,36 +109,36 @@ public final class Llama { public static final class Weights { // token embedding table - public final Tensor token_embedding_table; // (vocab_size, dim) + public final FloatTensor token_embedding_table; // (vocab_size, dim) // weights for rmsnorms public final FloatBuffer[] rms_att_weight; // (layer, dim) rmsnorm weights // weights for matmuls - public final Tensor[] wq; // (layer, n_heads * head_size) - public final Tensor[] wk; // (layer, n_kv_heads, head_size) - public final Tensor[] wv; // (layer, n_kv_heads * head_size) - public final Tensor[] wo; // (layer, n_heads * head_size, dim) - public final Tensor[] q_bias; // (layer, dim) - public final Tensor[] k_bias; // (layer, kv_dim) - public final Tensor[] v_bias; // (layer, kv_dim) + public final FloatTensor[] wq; // (layer, n_heads * head_size) + public final FloatTensor[] wk; // (layer, n_kv_heads, head_size) + public final FloatTensor[] wv; // (layer, n_kv_heads * head_size) + public final FloatTensor[] wo; // (layer, n_heads * head_size, dim) + public final FloatTensor[] q_bias; // (layer, dim) + public final FloatTensor[] k_bias; // (layer, kv_dim) + public final FloatTensor[] v_bias; // (layer, kv_dim) public final FloatBuffer[] rms_ffn_weight; // (layer, dim) // weights for ffn - public final Tensor[] w1; // (layer, hidden_dim, dim) - public final Tensor[] w2; // (layer, dim, hidden_dim) - public final Tensor[] w3; // (layer, hidden_dim, dim) + public final FloatTensor[] w1; // (layer, hidden_dim, dim) + public final FloatTensor[] w2; // (layer, dim, hidden_dim) + public final FloatTensor[] w3; // (layer, hidden_dim, dim) // public final rmsnorm public final FloatBuffer rms_final_weight; // (dim,) // freq_cis for RoPE relatively positional embeddings public final FloatBuffer freq_cis_real; // (seq_len, head_size/2) public final FloatBuffer freq_cis_imag; // (seq_len, head_size/2) // (optional) classifier weights for the logits, on the last layer - public final Tensor wcls; // (vocab_size, dim) - - public Weights(Tensor token_embedding_table, FloatBuffer[] rms_att_weight, Tensor[] wq, - Tensor[] wk, Tensor[] wv, - Tensor[] q_bias, Tensor[] k_bias, Tensor[] v_bias, - Tensor[] wo, FloatBuffer[] rms_ffn_weight, - Tensor[] w1, Tensor[] w2, Tensor[] w3, FloatBuffer rms_final_weight, - FloatBuffer freq_cis_real, FloatBuffer freq_cis_imag, Tensor wcls) { + public final FloatTensor wcls; // (vocab_size, dim) + + public Weights(FloatTensor token_embedding_table, FloatBuffer[] rms_att_weight, FloatTensor[] wq, + FloatTensor[] wk, FloatTensor[] wv, + FloatTensor[] q_bias, FloatTensor[] k_bias, FloatTensor[] v_bias, + FloatTensor[] wo, FloatBuffer[] rms_ffn_weight, + FloatTensor[] w1, FloatTensor[] w2, FloatTensor[] w3, FloatBuffer rms_final_weight, + FloatBuffer freq_cis_real, FloatBuffer freq_cis_imag, FloatTensor wcls) { this.token_embedding_table = token_embedding_table; this.rms_att_weight = rms_att_weight; this.wq = wq; @@ -163,20 +164,20 @@ public final class Llama { // current wave of activations public final int batchsize; - public final Tensor[] x; // activation at current time stamp (dim,) - public final Tensor[] xb; // same, but inside a residual branch (dim,) - public final Tensor[] xb2; // an additional buffer just for convenience (dim,) - public final Tensor[] hb; // buffer for hidden dimension in the ffn (hidden_dim,) - public final Tensor[] hb2; // buffer for hidden dimension in the ffn (hidden_dim,) - public final Tensor[] q; // query (dim,) - public final Tensor[] k; // key (dim,) - public final Tensor[] v; // value (dim,) - public final Tensor[] att; // buffer for scores/attention values (n_heads, seq_len) - public final Tensor logits; // output logits + public final FloatTensor[] x; // activation at current time stamp (dim,) + public final FloatTensor[] xb; // same, but inside a residual branch (dim,) + public final FloatTensor[] xb2; // an additional buffer just for convenience (dim,) + public final FloatTensor[] hb; // buffer for hidden dimension in the ffn (hidden_dim,) + public final FloatTensor[] hb2; // buffer for hidden dimension in the ffn (hidden_dim,) + public final FloatTensor[] q; // query (dim,) + public final FloatTensor[] k; // key (dim,) + public final FloatTensor[] v; // value (dim,) + public final FloatTensor[] att; // buffer for scores/attention values (n_heads, seq_len) + public final FloatTensor logits; // output logits // kv cache - public final Tensor[] keyCache; // (n_layer, seq_len, kv_dim) - public final Tensor[] valueCache; // (n_layer, seq_len, kv_dim) + public final FloatTensor[] keyCache; // (n_layer, seq_len, kv_dim) + public final FloatTensor[] valueCache; // (n_layer, seq_len, kv_dim) /** last index in previous block */ int idxPrevBlock; @@ -198,19 +199,19 @@ public final class Llama { this.logits = DirectBufferFloatTensor.allocate(config.vocabularySize); int kvDim = (config.dim * config.numberOfKeyValueHeads) / config.numberOfHeads; - this.keyCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(Tensor[]::new); - this.valueCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(Tensor[]::new); + this.keyCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(FloatTensor[]::new); + this.valueCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(FloatTensor[]::new); } - private static Tensor[] allocate(int numTokens, int... dims) { + private static FloatTensor[] allocate(int numTokens, int... dims) { return IntStream.range(0, numTokens) .mapToObj(i -> DirectBufferFloatTensor.allocate(dims)) - .toArray(Tensor[]::new); + .toArray(FloatTensor[]::new); } } - static void rmsnorm(Tensor out, Tensor x, FloatBuffer weight, int size, float rmsNormEps) { + static void rmsnorm(FloatTensor out, FloatTensor x, FloatBuffer weight, int size, float rmsNormEps) { // calculate sum of squares float ss = x.reduce(0, size, 0f, (acc, xi) -> acc + xi * xi); ss /= size; @@ -222,7 +223,7 @@ public final class Llama { } } - static Tensor forward(Llama model, State state, int[] tokens, int position, boolean computeLogits) { + static FloatTensor forward(Llama model, State state, int[] tokens, int position, boolean computeLogits) { // a few convenience variables Configuration config = model.configuration(); Weights weights = model.weights(); @@ -234,7 +235,7 @@ public final class Llama { final int nTokens = tokens.length; // copy the token embedding into x - Tensor.parallelFor(0, nTokens, t -> + AbstractFloatTensor.parallelFor(0, nTokens, t -> weights.token_embedding_table.copyTo(tokens[t] * dim, state.x[t], 0, dim) ); @@ -243,7 +244,7 @@ public final class Llama { // attention rmsnorm final int curLayer = l; - Tensor.parallelFor(0, nTokens, t -> + AbstractFloatTensor.parallelFor(0, nTokens, t -> rmsnorm(state.xb[t], state.x[t], weights.rms_att_weight[curLayer], dim, config.rmsNormEps) ); @@ -253,14 +254,14 @@ public final class Llama { weights.wv[l].matmul(nTokens, state.xb, state.v, kvDim, dim); // RoPE relative positional encoding: complex-valued rotate q and k in each head - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { for (int i = 0; i < dim; i += 2) { int head_dim = i % headSize; float fcr = weights.freq_cis_real.get((position + t) * (headSize / 2) + (head_dim / 2)); float fci = weights.freq_cis_imag.get((position + t) * (headSize / 2) + (head_dim / 2)); int rotn = i < kvDim ? 2 : 1; // how many vectors? 2 = q & k, 1 = q only for (int vi = 0; vi < rotn; vi++) { - Tensor vec = vi == 0 ? state.q[t] : state.k[t]; // the vector to rotate (query or key) + FloatTensor vec = vi == 0 ? state.q[t] : state.k[t]; // the vector to rotate (query or key) float v0 = vec.getFloat(i); float v1 = vec.getFloat(i + 1); vec.setFloat(i, v0 * fcr - v1 * fci); @@ -270,7 +271,7 @@ public final class Llama { }); // save key,value at this time step (position) to our kv cache - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { state.k[t].copyTo(0, state.keyCache[curLayer], (position + t) * kvDim, kvDim); state.v[t].copyTo(0, state.valueCache[curLayer], (position + t) * kvDim, kvDim); }); @@ -282,7 +283,7 @@ public final class Llama { } // multihead attention. iterate over all heads - Tensor.parallelForLong(0, (long) nTokens * (long) config.numberOfHeads, ht -> { + AbstractFloatTensor.parallelForLong(0, (long) nTokens * (long) config.numberOfHeads, ht -> { int token = (int) (ht / config.numberOfHeads); int h = (int) (ht % config.numberOfHeads); int qOffset = h * headSize; @@ -311,12 +312,12 @@ public final class Llama { weights.wo[l].matmul(nTokens, state.xb, state.xb2, dim, dim); // residual connection back into x - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { state.x[t].addInPlace(state.xb2[t]); }); // ffn rmsnorm - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { rmsnorm(state.xb[t], state.x[t], weights.rms_ffn_weight[curLayer], dim, config.rmsNormEps); }); @@ -325,12 +326,12 @@ public final class Llama { weights.w3[l].matmul(nTokens, state.xb, state.hb2, config.hiddenDim, dim); // SwiGLU non-linearity - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { state.hb[t].mapInPlace(value -> value / (float) (1.0 + Math.exp(-value))); }); // elementwise multiply with w3(x) - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { state.hb[t].multiplyInPlace(state.hb2[t]); }); @@ -338,13 +339,13 @@ public final class Llama { weights.w2[l].matmul(nTokens, state.hb, state.xb, dim, config.hiddenDim); // residual connection - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { state.x[t].addInPlace(state.xb[t]); }); } // final rmsnorm - Tensor.parallelFor(0, nTokens, t -> { + AbstractFloatTensor.parallelFor(0, nTokens, t -> { rmsnorm(state.x[t], state.x[t], weights.rms_final_weight, dim, config.rmsNormEps); }); diff --git a/source/net/yacy/ai/llama3/Llama3.java b/source/net/yacy/ai/llama3/Llama3.java index 0982ec067..065e5ced0 100644 --- a/source/net/yacy/ai/llama3/Llama3.java +++ b/source/net/yacy/ai/llama3/Llama3.java @@ -152,7 +152,7 @@ public class Llama3 { // JVM version 21.0.7+6-LTS: 17.6 T/s // performance on M4 Max: - // JVM version 21.0.5+11-LTS: 24.5 T/s + // JVM version 21.0.5+11-LTS: 26.3 T/s System.out.println("JVM version " + Runtime.version()); diff --git a/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java b/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java index 83f7abe1b..48564d7e9 100644 --- a/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java +++ b/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java @@ -31,7 +31,8 @@ import java.util.Objects; import net.yacy.ai.llama3.Tensor.Q4_0FloatTensor; import net.yacy.ai.llama3.Tensor.Q8_0FloatTensor; -import net.yacy.ai.llama3.Tensor.Tensor; +import net.yacy.ai.llama3.Tensor.AbstractFloatTensor; +import net.yacy.ai.llama3.Tensor.FloatTensor; public final class GGMLTensorEntry { @@ -73,12 +74,12 @@ public final class GGMLTensorEntry { } } - public Tensor loadQuantized() { - Tensor tensor = null; + public FloatTensor loadQuantized() { + FloatTensor tensor = null; switch (ggmlType) { //case F32: return new F32FloatTensor(FloatTensor.numberOfElements(entry.shape()), entry.memorySegment()); - case Q8_0: tensor = new Q8_0FloatTensor(Tensor.numberOfElements(this.shape()), this.buffer); break; - case Q4_0: tensor = new Q4_0FloatTensor(Tensor.numberOfElements(this.shape()), this.buffer); break; + case Q8_0: tensor = new Q8_0FloatTensor(AbstractFloatTensor.numberOfElements(this.shape()), this.buffer); break; + case Q4_0: tensor = new Q4_0FloatTensor(AbstractFloatTensor.numberOfElements(this.shape()), this.buffer); break; default: throw new UnsupportedOperationException("Quantization format " + ggmlType); } return tensor; diff --git a/source/net/yacy/ai/llama3/Model/GGUF.java b/source/net/yacy/ai/llama3/Model/GGUF.java index ae277a71f..aa61ea9b6 100644 --- a/source/net/yacy/ai/llama3/Model/GGUF.java +++ b/source/net/yacy/ai/llama3/Model/GGUF.java @@ -38,7 +38,7 @@ import java.util.Comparator; import java.util.Objects; import java.util.stream.Collectors; -import net.yacy.ai.llama3.Tensor.Tensor; +import net.yacy.ai.llama3.Tensor.AbstractFloatTensor; /* * GGUF File Reader. For specification see https://github.com/ggml-org/ggml/blob/master/docs/gguf.md @@ -203,7 +203,7 @@ public final class GGUF { GGUFTensorInfo ti = entry.getValue(); long offset = ti.offset(); int sizeInBytes = Math.toIntExact(ti.ggmlType().byteSizeFor( - Tensor.numberOfElements(ti.dimensions()))); + AbstractFloatTensor.numberOfElements(ti.dimensions()))); MappedByteBuffer tensorBuffer = (MappedByteBuffer) fullBuffer.duplicate(); tensorBuffer.position((int)offset); @@ -224,7 +224,7 @@ public final class GGUF { List<Long> boundaries = new ArrayList<>(); for (GGUFTensorInfo ti : tensorInfos.values()) { long start = ti.offset(); - long end = start + ti.ggmlType().byteSizeFor(Tensor.numberOfElements(ti.dimensions())); + long end = start + ti.ggmlType().byteSizeFor(AbstractFloatTensor.numberOfElements(ti.dimensions())); boundaries.add(start); boundaries.add(end); } @@ -276,7 +276,7 @@ public final class GGUF { GGUFTensorInfo ti = entry.getValue(); String name = ti.name(); long tensorOffset = ti.offset() + tensorDataOffset; - long tensorSize = ti.ggmlType().byteSizeFor(Tensor.numberOfElements(ti.dimensions())); + long tensorSize = ti.ggmlType().byteSizeFor(AbstractFloatTensor.numberOfElements(ti.dimensions())); long tensorEnd = tensorOffset + tensorSize; // Find all segments that overlap with this tensor diff --git a/source/net/yacy/ai/llama3/Model/ModelLoader.java b/source/net/yacy/ai/llama3/Model/ModelLoader.java index a66597d0f..4cd7ce40b 100644 --- a/source/net/yacy/ai/llama3/Model/ModelLoader.java +++ b/source/net/yacy/ai/llama3/Model/ModelLoader.java @@ -35,7 +35,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import net.yacy.ai.llama3.Llama; -import net.yacy.ai.llama3.Tensor.Tensor; +import net.yacy.ai.llama3.Tensor.FloatTensor; public final class ModelLoader { private static final String TOKENIZER_LLAMA_3_MODEL = "gpt2"; @@ -169,7 +169,7 @@ public final class ModelLoader { float[] ropeFreqsImag = ropeFreqs.second(); - Tensor tokenEmbeddingTable = tensorEntries.get("token_embd.weight").loadQuantized(); + FloatTensor tokenEmbeddingTable = tensorEntries.get("token_embd.weight").loadQuantized(); Llama.Weights qw = new Llama.Weights( tokenEmbeddingTable, loadArrayOfFloatBuffer(config.numberOfLayers, i -> tensorEntries.get("blk." + i + ".attn_norm.weight")), @@ -256,8 +256,8 @@ public final class ModelLoader { } - private static Tensor[] loadArrayOfQuantized(int size, IntFunction<GGMLTensorEntry> getTensorEntry) { - Tensor[] array = new Tensor[size]; + private static FloatTensor[] loadArrayOfQuantized(int size, IntFunction<GGMLTensorEntry> getTensorEntry) { + FloatTensor[] array = new FloatTensor[size]; for (int i = 0; i < size; i++) { array[i] = getTensorEntry.apply(i).loadQuantized(); } diff --git a/source/net/yacy/ai/llama3/Model/Vocabulary.java b/source/net/yacy/ai/llama3/Model/Vocabulary.java index f2ca7e4dd..9f139be3a 100644 --- a/source/net/yacy/ai/llama3/Model/Vocabulary.java +++ b/source/net/yacy/ai/llama3/Model/Vocabulary.java @@ -27,13 +27,13 @@ import java.util.stream.*; public final class Vocabulary { private final String[] tokens; - private final float[] scores; + //private final float[] scores; private final Map<String, Integer> tokenToIndex; // Primary constructor public Vocabulary(String[] tokens, float[] scores, Map<String, Integer> tokenToIndex) { this.tokens = tokens == null ? null : Arrays.copyOf(tokens, tokens.length); - this.scores = scores == null ? null : Arrays.copyOf(scores, scores.length); + //this.scores = scores == null ? null : Arrays.copyOf(scores, scores.length); this.tokenToIndex = tokenToIndex == null ? null : new HashMap<>(tokenToIndex); } diff --git a/source/net/yacy/ai/llama3/Sampler.java b/source/net/yacy/ai/llama3/Sampler.java index 5eda5cc58..c94876cab 100644 --- a/source/net/yacy/ai/llama3/Sampler.java +++ b/source/net/yacy/ai/llama3/Sampler.java @@ -26,13 +26,12 @@ import java.util.Comparator; import java.util.Random; import net.yacy.ai.llama3.Tensor.FloatTensor; -import net.yacy.ai.llama3.Tensor.Tensor; @FunctionalInterface interface Sampler { - int sampleToken(Tensor logits); + int sampleToken(FloatTensor logits); - Sampler ARGMAX = Tensor::argmax; + Sampler ARGMAX = FloatTensor::argmax; static Sampler selectSampler(int vocabularySize, float temperature, float topp, long rngSeed) { Sampler sampler; @@ -71,7 +70,7 @@ interface Sampler { } @Override - public int sampleToken(Tensor logits) { + public int sampleToken(FloatTensor logits) { // sample index from probabilities (they must sum to 1!) float random0to1 = rng.nextFloat(); float cdf = 0.0f; @@ -120,7 +119,7 @@ interface Sampler { } @Override - public int sampleToken(Tensor logits) { + public int sampleToken(FloatTensor logits) { // top-p sampling (or "nucleus sampling") samples from the smallest set of // tokens that exceed probability topp. This way we never sample tokens that // have very low probabilities and are less likely to go "off the rails". diff --git a/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java new file mode 100644 index 000000000..afe9fef73 --- /dev/null +++ b/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java @@ -0,0 +1,269 @@ +/** + * FloatTensor.java + + * This file was extracted from the llama3/qwen2 projects + * https://github.com/mukel/llama3.java + * https://github.com/mukel/qwen2.svm.java + * + * License: MIT License + * + * Copyright (c) 2024 Andrej Karpathy (for llama2.c) + * Copyright (c) 2024 Alfonso² Peterssen (for llama3/qwen2) + * Copyright (c) 2023 Georgi Gerganov et al. (for llama.cpp) + * Copyright (c) 2025 Michael Peter Christen for modifications: + * The code was modified to fit the YaCy AI project: + * - back-port to Java 11 (removal of Vector API operations and record types) + * - removal of interactive mode and system.out printing + * - separation of the classes in the single java and refactoring + * - run-time performance optimizations for dot product computation of quantized values + * - joining of llama3/qwen2 into one code base; multi-arch options + * - alignment with code from https://github.com/ggml-org/llama.cpp/ + */ + +package net.yacy.ai.llama3.Tensor; + +import java.util.Arrays; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +import net.yacy.ai.llama3.Model.GGMLType; + +/** + * Over-simplified, shapeless, float tensor. + * <p> + * Not a strict tensor, but rather just a sequence of floats, not required to be backed by memory + * e.g. can represent a sequence of quantized floats. + */ +public abstract class AbstractFloatTensor implements FloatTensor { + + /** + * Converts a 16-bit float (half-precision) to a 32-bit float (single-precision). + * + * @param h the half-precision float as a short + * @return the single-precision float + */ + public final static float float16ToFloat(short h) { + + final int hBits = h & 0xFFFF; // treat as unsigned + final int sign = (hBits >>> 15) & 0x00000001; + + int exp = (hBits >>> 10) & 0x0000001F; + int mant = hBits & 0x000003FF; + int fBits; + + if (exp == 0) { + if (mant == 0) { + // zero + fBits = sign << 31; + } else { + // subnormal + while ((mant & 0x00000400) == 0) { + mant <<= 1; + exp -= 1; + } + exp += 1; + mant &= ~0x00000400; + fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13); + } + } else if (exp == 31) { + // Inf/NaN + fBits = (sign << 31) | 0x7F800000 | (mant << 13); + } else { + // normalized number + fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13); + } + + return Float.intBitsToFloat(fBits); + } + + /** + * Converts a 32-bit float (single-precision) to a 16-bit float (half-precision). + * + * @param f the single-precision float + * @return the half-precision float as a short + */ + public final static short floatToFloat16(final float f) { + final int fBits = Float.floatToIntBits(f); + final int sign = (fBits >>> 31) & 0x00000001; + final int exp = (fBits >>> 23) & 0x000000FF; + final int mant = fBits & 0x007FFFFF; + + short hBits; + + if (exp == 0xFF) { + // Inf/NaN + hBits = (short) ((sign << 15) | 0x7C00 | (mant >>> 13)); + } else if (exp < 112) { + // subnormal or zero + hBits = (short) (sign << 15); + } else if (exp > 143) { + // overflow to Inf + hBits = (short) ((sign << 15) | 0x7C00); + } else { + // normalized number + hBits = (short) ((sign << 15) | ((exp - 112) << 10) | (mant >>> 13)); + } + + return hBits; + } + + public abstract int size(); + + public abstract float getFloat(final int index); + + public abstract void setFloat(final int index, final float value); + + abstract GGMLType type(); + + public static int numberOfElements(final int... dimensions) { + assert Arrays.stream(dimensions).allMatch(i -> i > 0); + return Arrays.stream(dimensions).reduce(Math::multiplyExact).orElseThrow(); + } + + public static void parallelFor(final int startInclusive, final int endExclusive, final IntConsumer action) { + if (startInclusive == 0 && endExclusive == 1) { + action.accept(0); + return; + } + IntStream.range(startInclusive, endExclusive).parallel().forEach(action); + } + + public static void parallelForLong(final long startInclusive, final long endExclusive, final LongConsumer action) { + if (startInclusive == 0 && endExclusive == 1) { + action.accept(0); + return; + } + LongStream.range(startInclusive, endExclusive).parallel().forEach(action); + } + + public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { + float result = 0f; + for (int j = 0; j < size; j++) { + result += this.getFloat(thisOffset + j) * that.getFloat(thatOffset + j); + } + return result; + } + + public void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1) { + parallelFor(0, dim0, i -> out.setFloat(i, dot(i * dim1, that, 0, dim1))); + } + + public void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1) { + if (that.length != out.length) { + throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length)); + } + parallelForLong(0, dim0 * context, ti -> { + int idxArr = (int) (ti / dim0); + int i = (int) (ti % dim0); + out[idxArr].setFloat(i, dot(i * dim1, that[idxArr], 0, dim1)); + }); + } + + @FunctionalInterface + public interface AggregateFunction { + float apply(float acc, float value); + } + + public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce) { + float result = seed; + for (int i = 0; i < size; ++i) { + result = reduce.apply(result, getFloat(thisOffset + i)); + } + return result; + } + + private float sum(final int thisOffset, final int size) { + return reduce(thisOffset, size, 0f, Float::sum); + } + + private float max(final int thisOffset, final int size) { + return reduce(thisOffset, size, Float.NEGATIVE_INFINITY, Float::max); + } + + public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { + int endOffset = thatOffset + size; + for (int i = thatOffset; i < endOffset; ++i) { + that.setFloat(i, this.getFloat(i - thatOffset + thisOffset)); + } + } + + public int argmax() { + int size = this.size(); + assert size > 0; + int maxIndex = 0; + float maxValue = this.getFloat(maxIndex); + int endIndex = size; + for (int i = 0; i < endIndex; ++i) { + float f = this.getFloat(i); + if (f > maxValue) { + maxValue = f; + maxIndex = i; + } + } + return maxIndex; + } + + public FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) { + int endIndex = thisOffset + size; + for (int i = thisOffset; i < endIndex; ++i) { + this.setFloat(i, mapFunction.apply(this.getFloat(i))); + } + return this; + } + + public final FloatTensor mapInPlace(final MapFunction mapFunction) { + return mapInPlace(0, size(), mapFunction); + } + + public FloatTensor mapWithIndexInPlace(final int thisOffset, final int size, final FloatTensor.MapWithIndexFunction mapWithIndexFunction) { + int endOffset = thisOffset + size; + for (int i = thisOffset; i < endOffset; ++i) { + this.setFloat(i, mapWithIndexFunction.apply(this.getFloat(i), i)); + } + return this; + } + + private final FloatTensor addInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, int size) { + return mapWithIndexInPlace(thisOffset, size, (value, index) -> value + that.getFloat(index - thisOffset + thatOffset)); + } + + public final FloatTensor addInPlace(final FloatTensor that) { + return addInPlace(0, that, 0, size()); + } + + private final FloatTensor multiplyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { + return mapWithIndexInPlace(thisOffset, size, (value, index) -> value * that.getFloat(index - thisOffset + thatOffset)); + } + + public final FloatTensor multiplyInPlace(final FloatTensor that) { + return multiplyInPlace(0, that, 0, size()); + } + + public final FloatTensor divideInPlace(final int thisOffset, final int size, final float value) { + return mapInPlace(thisOffset, size, f -> f / value); + } + + public FloatTensor fillInPlace(final int thisOffset, final int size, final float value) { + return mapInPlace(thisOffset, size, unused -> value); + } + + public final FloatTensor softmaxInPlace(final int thisOffset, final int size) { + // find max value (for numerical stability) + float maxVal = max(thisOffset, size); + // exp and sum + mapInPlace(thisOffset, size, f -> (float) Math.exp(f - maxVal)); + float sum = sum(thisOffset, size); + // normalize + return divideInPlace(thisOffset, size, sum); + } + + public FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a) { + // this[thatOffset ... thatOffset + size) = a * that[thatOffset ... thatOffset + size) + this[thisOffset ... thisOffset + size) + for (int i = 0; i < size; ++i) { + this.setFloat(thisOffset + i, a * that.getFloat(thatOffset + i) + this.getFloat(thisOffset + i)); + } + return this; + } +}
\ No newline at end of file diff --git a/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java index a66e60e91..90c747182 100644 --- a/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java @@ -28,7 +28,7 @@ import java.lang.invoke.VarHandle; import net.yacy.ai.llama3.Model.GGMLType; -public final class ArrayFloatTensor extends FloatTensor implements Tensor { +public final class ArrayFloatTensor extends AbstractFloatTensor implements FloatTensor { public final float[] values; private static final VarHandle FLOAT_ARRAY_HANDLE; @@ -45,8 +45,8 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { this.values = values; } - public static Tensor allocate(final int... dims) { - int numberOfElements = Tensor.numberOfElements(dims); + public static FloatTensor allocate(final int... dims) { + int numberOfElements = AbstractFloatTensor.numberOfElements(dims); return new ArrayFloatTensor(new float[numberOfElements]); } @@ -71,13 +71,13 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { } @Override - public final FloatTensor fillInPlace(final int thisOffset, final int size, final float value) { + public final AbstractFloatTensor fillInPlace(final int thisOffset, final int size, final float value) { Arrays.fill(this.values, thisOffset, thisOffset + size, value); return this; } @Override - public final FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) { + public final AbstractFloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) { int endIndex = thisOffset + size; for (int i = thisOffset; i < endIndex; ++i) { this.values[i] = mapFunction.apply(this.values[i]); @@ -86,7 +86,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { } @Override - public final void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public final void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { final int delta = thisOffset - thatOffset; if (that instanceof ArrayFloatTensor) { final ArrayFloatTensor aft = (ArrayFloatTensor) that; @@ -103,7 +103,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { } @Override - public final float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public final float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { float result = 0f; if (that instanceof ArrayFloatTensor) { final ArrayFloatTensor aft = (ArrayFloatTensor) that; @@ -126,20 +126,20 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { } @Override - public final void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1) { + public final void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1) { if (that instanceof ArrayFloatTensor) { - Tensor.parallelFor(0, dim0, i -> ((ArrayFloatTensor) out).values[i] = this.dot(i * dim1, that, 0, dim1)); + AbstractFloatTensor.parallelFor(0, dim0, i -> ((ArrayFloatTensor) out).values[i] = this.dot(i * dim1, that, 0, dim1)); } else { - Tensor.parallelFor(0, dim0, i -> out.setFloat(i, this.dot(i * dim1, that, 0, dim1))); + AbstractFloatTensor.parallelFor(0, dim0, i -> out.setFloat(i, this.dot(i * dim1, that, 0, dim1))); } } @Override - public final void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1) { + public final void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1) { if (that.length != out.length) { throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length)); } - Tensor.parallelForLong(0, dim0 * context, ti -> { + AbstractFloatTensor.parallelForLong(0, dim0 * context, ti -> { int idxArr = (int) (ti / dim0); int i = (int) (ti % dim0); out[idxArr].setFloat(i, this.dot(i * dim1, that[idxArr], 0, dim1)); @@ -147,7 +147,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor { } @Override - public final Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a) { + public final FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a) { if (that instanceof Q4_0FloatTensor) { Q4_0FloatTensor qft = (Q4_0FloatTensor) that; final float[] decodedBlock = Q4_0FloatTensor.scratchBuffer.get(); diff --git a/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java index b8315ef48..f711f566a 100644 --- a/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java @@ -27,7 +27,7 @@ import java.nio.ByteOrder; import net.yacy.ai.llama3.Model.GGMLType; -public final class BF16FloatTensor extends FloatTensor implements Tensor { +public final class BF16FloatTensor extends AbstractFloatTensor implements FloatTensor { final int size; final ByteBuffer buffer; diff --git a/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java index f16781986..c123b5efc 100644 --- a/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java @@ -28,7 +28,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import net.yacy.ai.llama3.Model.GGMLType; -public class DirectBufferFloatTensor extends FloatTensor implements Tensor { +public class DirectBufferFloatTensor extends AbstractFloatTensor implements FloatTensor { final ByteBuffer byteBuffer; // must be direct @@ -52,8 +52,8 @@ public class DirectBufferFloatTensor extends FloatTensor implements Tensor { } } - public static Tensor allocate(final int... dims) { - int numberOfElements = Tensor.numberOfElements(dims); + public static FloatTensor allocate(final int... dims) { + int numberOfElements = AbstractFloatTensor.numberOfElements(dims); int bytesNeeded = numberOfElements * Float.BYTES; ByteBuffer buffer = ByteBuffer.allocateDirect(bytesNeeded).order(ByteOrder.nativeOrder()); return new DirectBufferFloatTensor(buffer); diff --git a/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java index 109434789..62048a3c8 100644 --- a/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java @@ -28,7 +28,7 @@ import java.nio.ByteOrder; import net.yacy.ai.llama3.Model.GGMLType; -public final class F16FloatTensor extends FloatTensor implements Tensor { +public final class F16FloatTensor extends AbstractFloatTensor implements FloatTensor { final int size; final ByteBuffer buffer; diff --git a/source/net/yacy/ai/llama3/Tensor/FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/FloatTensor.java index eff7e3052..281765bae 100644 --- a/source/net/yacy/ai/llama3/Tensor/FloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/FloatTensor.java @@ -1,5 +1,5 @@ /** - * FloatTensor.java + * Tensor.java * This file was extracted from the llama3/qwen2 projects * https://github.com/mukel/llama3.java @@ -20,227 +20,55 @@ * - alignment with code from https://github.com/ggml-org/llama.cpp/ */ -package net.yacy.ai.llama3.Tensor; - -import net.yacy.ai.llama3.Model.GGMLType; - -/** - * Over-simplified, shapeless, float tensor. - * <p> - * Not a strict tensor, but rather just a sequence of floats, not required to be backed by memory - * e.g. can represent a sequence of quantized floats. - */ -public abstract class FloatTensor implements Tensor { - - /** - * Converts a 16-bit float (half-precision) to a 32-bit float (single-precision). - * - * @param h the half-precision float as a short - * @return the single-precision float - */ - public final static float float16ToFloat(short h) { - final int hBits = h & 0xFFFF; // treat as unsigned - final int sign = (hBits >>> 15) & 0x00000001; +package net.yacy.ai.llama3.Tensor; - int exp = (hBits >>> 10) & 0x0000001F; - int mant = hBits & 0x000003FF; - int fBits; +import net.yacy.ai.llama3.Tensor.AbstractFloatTensor.AggregateFunction; - if (exp == 0) { - if (mant == 0) { - // zero - fBits = sign << 31; - } else { - // subnormal - while ((mant & 0x00000400) == 0) { - mant <<= 1; - exp -= 1; - } - exp += 1; - mant &= ~0x00000400; - fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13); - } - } else if (exp == 31) { - // Inf/NaN - fBits = (sign << 31) | 0x7F800000 | (mant << 13); - } else { - // normalized number - fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13); - } +public interface FloatTensor { - return Float.intBitsToFloat(fBits); + @FunctionalInterface + public interface MapFunction { + float apply(float value); } - - /** - * Converts a 32-bit float (single-precision) to a 16-bit float (half-precision). - * - * @param f the single-precision float - * @return the half-precision float as a short - */ - public final static short floatToFloat16(final float f) { - final int fBits = Float.floatToIntBits(f); - final int sign = (fBits >>> 31) & 0x00000001; - final int exp = (fBits >>> 23) & 0x000000FF; - final int mant = fBits & 0x007FFFFF; - - short hBits; - if (exp == 0xFF) { - // Inf/NaN - hBits = (short) ((sign << 15) | 0x7C00 | (mant >>> 13)); - } else if (exp < 112) { - // subnormal or zero - hBits = (short) (sign << 15); - } else if (exp > 143) { - // overflow to Inf - hBits = (short) ((sign << 15) | 0x7C00); - } else { - // normalized number - hBits = (short) ((sign << 15) | ((exp - 112) << 10) | (mant >>> 13)); - } - - return hBits; + @FunctionalInterface + public interface MapWithIndexFunction { + float apply(float value, int index); } - public abstract int size(); + public int size(); - public abstract float getFloat(final int index); - - public abstract void setFloat(final int index, final float value); + public int argmax(); - abstract GGMLType type(); - - public static float scalarDot(final FloatTensor thiz, final int thisOffset, final Tensor that, final int thatOffset, final int size) { - float result = 0f; - for (int j = 0; j < size; j++) { - result += thiz.getFloat(thisOffset + j) * that.getFloat(thatOffset + j); - } - return result; - } - - public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) { - return scalarDot(this, thisOffset, that, thatOffset, size); - } - - public void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1) { - Tensor.parallelFor(0, dim0, i -> out.setFloat(i, dot(i * dim1, that, 0, dim1))); - } - - public void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1) { - if (that.length != out.length) { - throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length)); - } - Tensor.parallelForLong(0, dim0 * context, ti -> { - int idxArr = (int) (ti / dim0); - int i = (int) (ti % dim0); - out[idxArr].setFloat(i, dot(i * dim1, that[idxArr], 0, dim1)); - }); - } - - @FunctionalInterface - public interface AggregateFunction { - float apply(float acc, float value); - } - - public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce) { - float result = seed; - for (int i = 0; i < size; ++i) { - result = reduce.apply(result, getFloat(thisOffset + i)); - } - return result; - } - - private float sum(final int thisOffset, final int size) { - return reduce(thisOffset, size, 0f, Float::sum); - } - - private float max(final int thisOffset, final int size) { - return reduce(thisOffset, size, Float.NEGATIVE_INFINITY, Float::max); - } - - public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) { - int endOffset = thatOffset + size; - for (int i = thatOffset; i < endOffset; ++i) { - that.setFloat(i, this.getFloat(i - thatOffset + thisOffset)); - } - } + public float getFloat(final int index); - public int argmax() { - int size = this.size(); - assert size > 0; - int maxIndex = 0; - float maxValue = this.getFloat(maxIndex); - int endIndex = size; - for (int i = 0; i < endIndex; ++i) { - float f = this.getFloat(i); - if (f > maxValue) { - maxValue = f; - maxIndex = i; - } - } - return maxIndex; - } - - public Tensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) { - int endIndex = thisOffset + size; - for (int i = thisOffset; i < endIndex; ++i) { - this.setFloat(i, mapFunction.apply(this.getFloat(i))); - } - return this; - } - - public final Tensor mapInPlace(final MapFunction mapFunction) { - return mapInPlace(0, size(), mapFunction); - } - - public Tensor mapWithIndexInPlace(final int thisOffset, final int size, final Tensor.MapWithIndexFunction mapWithIndexFunction) { - int endOffset = thisOffset + size; - for (int i = thisOffset; i < endOffset; ++i) { - this.setFloat(i, mapWithIndexFunction.apply(this.getFloat(i), i)); - } - return this; - } - - private final Tensor addInPlace(final int thisOffset, final Tensor that, final int thatOffset, int size) { - return mapWithIndexInPlace(thisOffset, size, (value, index) -> value + that.getFloat(index - thisOffset + thatOffset)); - } - - public final Tensor addInPlace(final Tensor that) { - return addInPlace(0, that, 0, size()); - } - - private final Tensor multiplyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size) { - return mapWithIndexInPlace(thisOffset, size, (value, index) -> value * that.getFloat(index - thisOffset + thatOffset)); - } - - public final Tensor multiplyInPlace(final Tensor that) { - return multiplyInPlace(0, that, 0, size()); - } + public void setFloat(final int index, final float value); - public final Tensor divideInPlace(final int thisOffset, final int size, final float value) { - return mapInPlace(thisOffset, size, f -> f / value); - } - - public Tensor fillInPlace(final int thisOffset, final int size, final float value) { - return mapInPlace(thisOffset, size, unused -> value); - } + public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size); + + public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size); + + public void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1); + + public void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1); + + public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce); - public final Tensor softmaxInPlace(final int thisOffset, final int size) { - // find max value (for numerical stability) - float maxVal = max(thisOffset, size); - // exp and sum - mapInPlace(thisOffset, size, f -> (float) Math.exp(f - maxVal)); - float sum = sum(thisOffset, size); - // normalize - return divideInPlace(thisOffset, size, sum); - } + public FloatTensor mapInPlace(final MapFunction mapFunction); + + public FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction); + + public FloatTensor addInPlace(final FloatTensor that); + + public FloatTensor multiplyInPlace(final FloatTensor that); + + public FloatTensor divideInPlace(final int thisOffset, final int size, final float value); + + public FloatTensor fillInPlace(final int thisOffset, final int size, final float value); + + public FloatTensor softmaxInPlace(final int thisOffset, final int size); + + public FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a); - public Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a) { - // this[thatOffset ... thatOffset + size) = a * that[thatOffset ... thatOffset + size) + this[thisOffset ... thisOffset + size) - for (int i = 0; i < size; ++i) { - this.setFloat(thisOffset + i, a * that.getFloat(thatOffset + i) + this.getFloat(thisOffset + i)); - } - return this; - } }
\ No newline at end of file diff --git a/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java index e413f2436..c06f20313 100644 --- a/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java @@ -28,7 +28,7 @@ import java.lang.invoke.VarHandle; import net.yacy.ai.llama3.Model.GGMLType; -public final class Q4_0FloatTensor extends FloatTensor implements Tensor { +public final class Q4_0FloatTensor extends AbstractFloatTensor implements FloatTensor { private final int size; private final ByteBuffer buffer; @@ -74,7 +74,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor { int blockIndex = index >>> LOG2_QUANT_BLOCK_SIZE; // index / QUANT_BLOCK_SIZE; int blockOffset = blockIndex * GGMLType.Q4_0.typeSize; final long offset = blockOffset; - float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset)); + float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset)); final int modIndex = index & (GGMLType.Q4_0.blockSize - 1); //index % QUANT_BLOCK_SIZE; final boolean isLow = modIndex < QUANT_HALF_BLOCK; final int adjustedIndex = modIndex - (isLow ? 0 : QUANT_HALF_BLOCK); @@ -93,7 +93,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor { while (inPos < end) { final int blockIndex = inPos >>> LOG2_QUANT_BLOCK_SIZE; final int blockOffset = blockIndex * GGMLType.Q4_0.typeSize; - float scale = FloatTensor.float16ToFloat(buffer.getShort((int) (long) blockOffset)); + float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) (long) blockOffset)); final int blockStart = blockIndex * GGMLType.Q4_0.blockSize; final int blockEnd = Math.min(blockStart + GGMLType.Q4_0.blockSize, end); @@ -142,7 +142,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor { public static final ThreadLocal<float[]> scratchBuffer = ThreadLocal.withInitial(() -> new float[GGMLType.Q4_0.blockSize]); @Override - public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { final float[] decoded = scratchBuffer.get(); int remaining = size; int srcIndex = thisOffset; @@ -172,7 +172,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor { * dot product which has the getFloat method inlined in such a way that it processes full blocks at once. * This gains a > 2.5 times token/s performance increase compared to the generic dot-getFloat implementation. */ - public final float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public final float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { float result = 0.0f; int index = 0; final int blockLimit = size - (size % GGMLType.Q4_0.blockSize); @@ -183,7 +183,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor { // Get this block final int thisBlockIndex = (thisOffset + index) >>> LOG2_QUANT_BLOCK_SIZE; // (thisOffset + index) / QUANT_BLOCK_SIZE; final int thisBlockOffset = thisBlockIndex * GGMLType.Q4_0.typeSize; - final float thisScale = FloatTensor.float16ToFloat(buffer.getShort((int) (long) thisBlockOffset)); + final float thisScale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) (long) thisBlockOffset)); // Process block: read all quantized values from this block at once final int quantOffset = thisBlockOffset + QUANT_FLOAT16_BYTES; diff --git a/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java index 04e26ae5c..f388f03a3 100644 --- a/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java +++ b/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java @@ -26,7 +26,7 @@ import java.nio.ByteBuffer; import net.yacy.ai.llama3.Model.GGMLType; -public final class Q8_0FloatTensor extends FloatTensor { +public final class Q8_0FloatTensor extends AbstractFloatTensor { final int size; final ByteBuffer buffer; @@ -60,12 +60,12 @@ public final class Q8_0FloatTensor extends FloatTensor { int blockOffset = blockIndex * GGMLType.Q8_0.typeSize; byte quant = buffer.get((int) (long) (blockOffset + GGMLType.FLOAT16_BYTES + withinBlockIndex)); final long offset = blockOffset; - float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset)); + float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset)); return quant * scale; } @Override - public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { assert 0 <= thisOffset && thisOffset + size <= this.size; assert 0 <= thatOffset && thatOffset + size <= that.size(); @@ -86,7 +86,7 @@ public final class Q8_0FloatTensor extends FloatTensor { // Get common scale factor for this block int blockOffset = block * GGMLType.Q8_0.typeSize; final long offset = blockOffset; - float thisScale = FloatTensor.float16ToFloat(buffer.getShort((int) offset)); + float thisScale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset)); // Compute sum of products for this block float blockSum = 0f; @@ -113,7 +113,7 @@ public final class Q8_0FloatTensor extends FloatTensor { } @Override - public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) { + public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) { assert 0 <= thisOffset && thisOffset + size <= this.size; assert 0 <= thatOffset && thatOffset + size <= that.size(); @@ -127,7 +127,7 @@ public final class Q8_0FloatTensor extends FloatTensor { byte quant = buffer.get((int) (long) (blockOffset + GGMLType.FLOAT16_BYTES + withinBlockIndex)); final long offset = blockOffset; - float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset)); + float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset)); that.setFloat(i, quant * scale); } } diff --git a/source/net/yacy/ai/llama3/Tensor/Tensor.java b/source/net/yacy/ai/llama3/Tensor/Tensor.java deleted file mode 100644 index dcbe32476..000000000 --- a/source/net/yacy/ai/llama3/Tensor/Tensor.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Tensor.java - - * This file was extracted from the llama3/qwen2 projects - * https://github.com/mukel/llama3.java - * https://github.com/mukel/qwen2.svm.java - * - * License: MIT License - * - * Copyright (c) 2024 Andrej Karpathy (for llama2.c) - * Copyright (c) 2024 Alfonso² Peterssen (for llama3/qwen2) - * Copyright (c) 2023 Georgi Gerganov et al. (for llama.cpp) - * Copyright (c) 2025 Michael Peter Christen for modifications: - * The code was modified to fit the YaCy AI project: - * - back-port to Java 11 (removal of Vector API operations and record types) - * - removal of interactive mode and system.out printing - * - separation of the classes in the single java and refactoring - * - run-time performance optimizations for dot product computation of quantized values - * - joining of llama3/qwen2 into one code base; multi-arch options - * - alignment with code from https://github.com/ggml-org/llama.cpp/ - */ - - -package net.yacy.ai.llama3.Tensor; - -import java.util.Arrays; -import java.util.function.IntConsumer; -import java.util.function.LongConsumer; -import java.util.stream.IntStream; -import java.util.stream.LongStream; - -import net.yacy.ai.llama3.Tensor.FloatTensor.AggregateFunction; - -public interface Tensor { - - @FunctionalInterface - public interface MapFunction { - float apply(float value); - } - - @FunctionalInterface - public interface MapWithIndexFunction { - float apply(float value, int index); - } - - public int size(); - - public int argmax(); - - public float getFloat(final int index); - - public void setFloat(final int index, final float value); - - public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size); - - public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size); - - public void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1); - - public void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1); - - public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce); - - public Tensor mapInPlace(final MapFunction mapFunction); - - public Tensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction); - - public Tensor addInPlace(final Tensor that); - - public Tensor multiplyInPlace(final Tensor that); - - public Tensor divideInPlace(final int thisOffset, final int size, final float value); - - public Tensor fillInPlace(final int thisOffset, final int size, final float value); - - public Tensor softmaxInPlace(final int thisOffset, final int size); - - public Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a); - - public static int numberOfElements(final int... dimensions) { - assert Arrays.stream(dimensions).allMatch(i -> i > 0); - return Arrays.stream(dimensions).reduce(Math::multiplyExact).orElseThrow(); - } - - public static void parallelFor(final int startInclusive, final int endExclusive, final IntConsumer action) { - if (startInclusive == 0 && endExclusive == 1) { - action.accept(0); - return; - } - IntStream.range(startInclusive, endExclusive).parallel().forEach(action); - } - - public static void parallelForLong(final long startInclusive, final long endExclusive, final LongConsumer action) { - if (startInclusive == 0 && endExclusive == 1) { - action.accept(0); - return; - } - LongStream.range(startInclusive, endExclusive).parallel().forEach(action); - } -}
\ No newline at end of file |
