Instructions to use litert-community/iris-3b-Turbo-LiteRT with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/iris-3b-Turbo-LiteRT with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 7,728 Bytes
743fd20 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | package com.iris3b
import java.io.File
import java.nio.file.Files
import java.security.MessageDigest
/**
* The files of one run in the app's files dir, as the rows file names them (no Android API, so the
* JVM check reads the very files the phone gets):
*
* - rows file: `{"kind": "generic", "config": <config file>, "inputs": [{"file": ...}, ...]
* (every file the run needs; devicebench pushes them), "rows": [{"key", "prompt", "noise"}]}`;
* - config file: the residency policy (P1 / P2 / P4, with P4's `prefetch` rule), the dumps, the
* tokenizer and embedding files, the host tensor pack and its index, and per graph its file,
* accel, precision, signature, input names in feed order and output names with element counts;
* - tokenizer file: `{"files": {name: text}}`, written out to `tok/` for [PromptTokens], with
* `tok/embed_bf16.bin` a symbolic link to the embedding table.
*/
class RunFiles(val dir: File, rowsName: String) {
class Row(val key: String, val prompt: String, val noise: String)
class Tensor(val offset: Long, val count: Int, val shape: List<Int>)
val rowsDoc: Map<String, Any?> = obj(Json.parse(File(dir, rowsName).readText()))
val configName: String = rowsDoc.str("config")
val config: Map<String, Any?> = obj(Json.parse(File(dir, configName).readText()))
val rows: List<Row> = rowsDoc.list("rows").map {
val r = obj(it)
Row(r.str("key"), r.str("prompt"), (r["noise"] as? String) ?: "noise_pix")
}
val residency: Residency = Residency.valueOf(config.str("residency"))
/**
* Graph roles whose GPU program cache is serialized, as role prefixes: `"program_cache": true`
* = every graph, a list (e.g. `["dit_"]`) = the roles starting with one of them, absent = none.
*/
val programCacheRoles: List<String> = when (val pc = config["program_cache"]) {
true -> listOf("")
is List<*> -> pc.map { it as String }
else -> emptyList()
}
val programCache: Boolean = programCacheRoles.isNotEmpty()
val dumps: List<String> = (config["dumps"] as? List<*>)?.map { it as String } ?: emptyList()
private val hostFile = File(dir, obj(config["host"]).str("file"))
private val hostIndex: Map<String, Tensor> = obj(obj(config["host"])["tensors"]).mapValues { (_, v) ->
val t = obj(v)
Tensor(t.num("offset").toLong(), t.num("count").toInt(), (t["shape"] as List<*>).map { (it as Double).toInt() })
}
private val hostCache = HashMap<String, FloatArray>()
/** Host tensors from the pack, each read once. */
val host = HostTensors { name ->
hostCache.getOrPut(name) {
val t = requireNotNull(hostIndex[name]) { "host tensor $name is not in ${hostFile.name}" }
HostOps.readFloats(hostFile, t.offset, t.count)
}
}
fun hostShape(name: String): List<Int> = requireNotNull(hostIndex[name]) { "no host tensor $name" }.shape
/** Every file the rows file declares, with a check that each is a regular file. */
fun missingInputs(): List<String> =
rowsDoc.list("inputs").map { obj(it).str("file") }.filter { !File(dir, it).isFile }
/** The [EngineConfig] the config file describes. */
fun engineConfig(): EngineConfig {
val g = obj(config["graphs"])
return EngineConfig(
residency = residency,
textEncoder = g.list("te").map { spec(it) },
adapter = spec(g["adapter"]),
dual = g.list("dual").map { spec(it) },
dualModRows = g.list("dual_mod_rows").map { range(it) },
single = g.list("single").map { spec(it) },
singleModRows = g.list("single_mod_rows").map { range(it) },
pixel = spec(g["pixel"]),
dumps = dumps.toSet(),
prefetch = prefetchRule(),
)
}
/** P4's memory rule: `"prefetch": {"factor": x, "margin_bytes": n}`, each defaulting to [PrefetchRule]'s. */
private fun prefetchRule(): PrefetchRule {
val p = config["prefetch"] as? Map<*, *> ?: return PrefetchRule()
val default = PrefetchRule()
return PrefetchRule(
factor = (p["factor"] as? Double) ?: default.factor,
marginBytes = (p["margin_bytes"] as? Double)?.toLong() ?: default.marginBytes,
)
}
/** Every graph file the config names, in pipeline order. */
fun graphFiles(): List<String> {
val c = engineConfig()
return (c.textEncoder + c.adapter + c.dual + c.single + c.pixel).map { it.file }
}
/**
* Writes the tokenizer files into `tok/` and links `tok/embed_bf16.bin` to the embedding
* table; returns the sha256 of every file written.
*/
fun materializeTokenizer(): Map<String, String> {
val tok = File(dir, "tok")
tok.mkdirs()
val files = obj(obj(Json.parse(File(dir, config.str("tokenizer")).readText()))["files"])
val sums = LinkedHashMap<String, String>()
for ((name, text) in files) {
val bytes = (text as String).toByteArray(Charsets.UTF_8)
File(tok, name).writeBytes(bytes)
sums[name] = sha256(bytes)
}
val link = File(tok, PromptTokens.EMBEDDING_FILE).toPath()
Files.deleteIfExists(link)
Files.createSymbolicLink(link, File(dir, config.str("embed")).absoluteFile.toPath())
return sums
}
/** The output tensors of one row in out-file order: the image, then the dumps. */
fun outputs(): List<Pair<String, Int>> {
val out = arrayListOf("image" to HostOps.IMAGE_FLOATS)
for (d in dumps) {
when (d) {
"te" -> out.add("te" to HostOps.TE_FLOATS)
"y0" -> out.add("y0" to HostOps.WINDOW * HostOps.DIT_HIDDEN)
"v" -> for (s in 0 until 4) out.add("v_s$s" to HostOps.PATCHES * HostOps.PIXELS * HostOps.CHANNELS)
else -> throw IllegalArgumentException("unknown dump $d")
}
}
return out
}
private fun spec(any: Any?): GraphSpec {
val m = obj(any)
val accel = (m["accel"] as? String) ?: "gpu"
require(accel == "gpu" || accel == "cpu") { "${m["role"]}: accel $accel (gpu or cpu)" }
val file = m.str("file")
return GraphSpec(
role = m.str("role"),
file = file,
precision = m.str("precision"),
signature = m.str("signature"),
inputs = m.list("inputs").map { it as String },
outputs = m.list("outputs").map {
val pair = it as List<*>
(pair[0] as String) to (pair[1] as Double).toInt()
},
accel = accel,
threads = ((m["threads"] ?: config["cpu_threads"]) as? Double)?.toInt() ?: 4,
xnnpackCache = m["xnnpack_cache"] as? Boolean ?: false,
bytes = File(dir, file).length(),
)
}
private fun range(any: Any?): IntArray = (any as List<*>).map { (it as Double).toInt() }.toIntArray()
companion object {
@Suppress("UNCHECKED_CAST")
fun obj(any: Any?): Map<String, Any?> = requireNotNull(any as? Map<String, Any?>) { "expected a JSON object" }
fun Map<String, Any?>.str(key: String): String = requireNotNull(this[key] as? String) { "missing string $key" }
fun Map<String, Any?>.num(key: String): Double = requireNotNull(this[key] as? Double) { "missing number $key" }
fun Map<String, Any?>.list(key: String): List<Any?> = requireNotNull(this[key] as? List<*>) { "missing list $key" }
fun sha256(bytes: ByteArray): String =
MessageDigest.getInstance("SHA-256").digest(bytes).joinToString("") { "%02x".format(it) }
}
}
|