FHE está cambiando el juego y todos estamos invitados a jugar.
Si no tienes idea de lo que estoy hablando, significa que últimamente has estado viviendo bajo una roca. Vaya a explorar algunos de los recursos completos de FHE.org y regrese.
Para que FHE cumpla su promesa al mundo de la tecnología, debe venir junto con una nueva generación de herramientas industriales para desarrollo, compilación y ejecución en tiempo de ejecución, que cualquiera pueda usar para crear aplicaciones homomórficas fácilmente.
Sin embargo, en la actualidad, muchos expertos y empresas en el espacio FHE todavía invierten la mayor parte de su tiempo y esfuerzos en mejorar la criptografía detrás de FHE, en lugar de centrarse en crear excelentes herramientas de desarrollo para no expertos. Escúchame bien: mejorar las funcionalidades y el rendimiento básicos de FHE siempre será una buena noticia. Pero en el gran esquema de las cosas, estas mejoras incrementales promueven la adopción global de manera marginal, en el mejor de los casos. Tendrán un impacto en la adopción en algún momento, pero no ahora .
Desde mi punto de vista, es obvio que el mundo tecnológico necesita hoy en día cadenas de herramientas FHE potentes y fáciles de usar para los desarrolladores, para comenzar a desbloquear historias de éxito impulsadas por FHE y hacer que FHE pase de ser una tendencia tecnológica a un cambio de paradigma real en el negocio de la seguridad digital. Creo que el estado actual de conocimiento sobre FHE, tanto científica como tecnológicamente, ya es suficiente para construir tales herramientas en el presente y ponerlas a disposición de las masas conocedoras de la tecnología sin más demora. La integración continua de nuevas funciones se desarrollará de forma natural con el tiempo, como siempre ocurre.
Pero aquí está la cuestión: FHE viene en muchos sabores. Dependiendo del esquema criptográfico que esté utilizando, o del uso particular que le dé, surge una forma diferente de representar la computación y ejecutar programas homomórficos. Es como si los esquemas FHE fueran animales completamente diferentes, uno que te proporciona máquinas de Turing y otro cálculo lambda. La biodiversidad siempre es buena en términos tecnológicos o no, pero también significa que es necesario definir una estrategia viable al instrumentar la FHE en la práctica.
Mi empresa Zama se centra en un esquema FHE en particular que es TFHE . TFHE logra el procesamiento de datos homomórficos con activos muy específicos: arranques súper rápidos y cálculos expresados como redes de búsquedas de tablas. Hemos llegado a adquirir una comprensión profunda de cómo estas peculiaridades, que solían hacer de TFHE una especie de desvalido en el espacio FHE, pueden traducirse en bibliotecas homomórficas, compilación, máquinas virtuales o aceleración de hardware.
Otros contendientes destacados de FHE como CKKS , BGV o BFV implican conceptos muy diferentes en su instrumentación práctica: los bootstraps son demasiado lentos para ser una opción, por lo que el procesamiento está limitado en profundidad; sin embargo, los datos se pueden vectorizar con procesamiento por lotes masivo, los cálculos se expresan como circuitos polinomiales y - en el caso de CKKS - los resultados son aproximados. Por lo tanto, la traducción de BGV/BFV y CKKS a compiladores y tiempos de ejecución requiere una mentalidad totalmente diferente por parte de los creadores de herramientas.
Sin embargo, es poco probable que a los desarrolladores les importe mucho qué esquema en particular impulsa su cadena de herramientas FHE y sus entornos de ejecución, siempre y cuando sean fáciles de operar y se cumplan los requisitos de rendimiento en las aplicaciones homomórficas implementadas. Ellos mismos son constructores creativos y su atención debe centrarse en las nuevas experiencias que pueden ofrecer a sus usuarios.
Por lo tanto, el objetivo final de los habilitadores de tecnología FHE es concebir y ofrecer herramientas que no sólo estén listas para su momento de máxima audiencia en el multiverso de desarrollo, sino que sean lo suficientemente potentes como para establecer un nuevo estándar en la industria. Para lograrlo, tienen que arriesgarse sobre qué esquema FHE tiene más probabilidades de llevarlos allí.
Vamos a jugar un juego.
Tomemos como ejemplo un desarrollador que no sabe nada de las complejidades de FHE pero que quiere crear una aplicación homomórfica. Aquí usted es el creador de herramientas, se enfrenta a ese desarrollador, de quien puede esperar cierta familiaridad con las prácticas comunes de desarrollo y los conceptos básicos de la informática, pero todo lo demás (matemáticas avanzadas y similares) está prohibido. ¿Cómo se puede lograr que ellos mismos produzcan con éxito la aplicación FHE?
Este artículo explora varias estrategias para ganar ese juego apostando en TFHE.
Dependiendo de la naturaleza de la aplicación (procesamiento de datos personalizados, redes neuronales, contratos inteligentes o programación de propósito general), se explorarán caminos ganadores habilitados por TFHE. Esta exploración nos llevará por un camino fácil, uno difícil y algunos otros intermedios, con diferentes niveles de preparación tecnológica en su realización práctica.
TFHE significa Toro FHE. También conocido como CGGI por los nombres de sus descubridores, TFHE ocupa una posición única dentro del panorama FHE: es el mecanismo más conocido para permitir el arranque programable (PBS).
En pocas palabras, una PBS es una búsqueda de tablas homomórficas. Devuelve un cifrado de T[x]
donde T
es una función tabulada de su elección, dado un cifrado de un índice x
. Su velocidad de carrera no depende de las entradas de T
sino sólo del número de entradas, y está en el rango de milisegundos. Además, un PBS restablece el ruido de cifrado incrustado en su texto cifrado de salida, por lo que puede componer PBS en secuencia indefinidamente, sabiendo que su aplicación homomórfica siempre manejará textos cifrados limpios.
El modelo computacional que defiende TFHE es por excelencia.
La unidad de procesamiento elemental en los programas TFHE se parece exactamente a una neurona y compone 2 operaciones homomórficas básicas:
Una combinación lineal de entradas que devuelve E(x)
donde x = w_1 x_1 + … + w_n x_n modulo m
, dadas las entradas cifradas E(x_1), …, E(x_n)
y un conjunto de pesos de texto sin formato w_1, …, w_n
.
Un PBS que calcula E(T[x])
a partir de E(x)
donde T
es una tabla de texto sin formato de tamaño m
.
En la "neurona TFHE", m
, x_i
, w_i
, así como las entradas T[0], …, T[m-1]
son todos números enteros, y se pueden elegir libremente los "parámetros" m
, w_1, …, w_n
y T
Dado que la combinación lineal tiene un costo casi nulo, el cuello de botella de eficiencia es el PBS cuyo tiempo de funcionamiento depende únicamente del módulo m
: la velocidad disminuye a medida que m
aumenta. Esto invita a utilizar valores pequeños (pares o impares, pero al menos 2) para los módulos de las neuronas TFHE, aunque se debe encontrar un equilibrio para evitar una reducción demasiado drástica de su expresividad computacional.
Ahora, de la misma manera que las neuronas se ensamblan en capas en las redes neuronales para beneficiarse del paralelismo y los trucos de HPC, las redes TFHE acumulan capas de neuronas TFHE. Cada capa presenta un módulo de matriz de peso, un módulo común m
y un vector de tablas de búsqueda de tamaño m
. Sin embargo, el módulo puede diferir libremente en la capa anterior o siguiente, al igual que la forma de la capa.
¡Y eso concluye TFHE! Ahí lo tienes, sólo necesitamos expresar funciones sistemáticamente como redes de búsqueda. Ahora tenemos nuestro modelo computacional homomórfico.
En la realidad real, TFHE admite múltiples extensiones de ese modelo (gráficos arbitrarios de operadores de nivel inferior, textos cifrados de varios tipos, búsquedas de tablas de múltiples salidas, múltiples formas de empaquetar variables, etc.). Pero podemos ignorar estas mejoras por ahora porque la visión de la red de búsqueda ya es lo suficientemente poderosa como para permitirnos convertir un programa simple en un equivalente homomórfico y ejecutarlo. Así que podemos centrarnos en cómo hacer precisamente eso, al menos durante una primera versión de la tecnología.
Como tal, una red TFHE es sólo un modelo y aún no está lista para su ejecución adecuada dentro de una aplicación homomórfica. Aunque los módulos, las matrices de peso y las tablas de búsqueda están completamente especificados para todas sus capas, todavía falta un ingrediente crucial que es la parametrización criptográfica.
Los parámetros criptográficos dictan todas las métricas posibles sobre lo que sucederá dentro de la red en tiempo de ejecución: tamaños concretos de texto cifrado, las dimensiones reales del tensor de las claves de arranque y conmutación de claves internas de los PBS, varias opciones algorítmicas dentro de operadores homomórficos de bajo nivel, cómo se utilizan las precisiones y el ruido del texto plano. Los niveles evolucionan a lo largo de la red y, en última instancia, los detalles de cómo cifrar y descifrar. También predicen el uso y el rendimiento de la memoria.
Descubrir qué conjunto de parámetros optimiza la ejecución de una red TFHE puede ser engorroso y, en cualquier caso, terriblemente difícil, incluso para los expertos, hacerlo con lápiz y papel como en los primeros días de FHE, ya que todo depende de todo. . Además, pueden coexistir varios conjuntos óptimos simultáneamente, lo que requiere un arbitraje entre el tamaño de la clave pública, la latencia del camino crítico o el rendimiento máximo. Afortunadamente, el problema de la automatización de esta tarea se ha resuelto en los últimos años y ahora existen poderosas herramientas de optimización para determinar rápidamente la mejor instanciación criptográfica de una red TFHE determinada.
Una vez creada una instancia con parámetros criptográficos, una red TFHE se vuelve verdaderamente ejecutable o, más exactamente, es susceptible de convertirse en un verdadero ejecutable mediante un paso de compilación apropiado.
Un programa TFHE es una colección de redes TFHE parametrizadas unidas mediante "lógica simple".
La lógica simple está hecha de
instrucciones que operan sobre variables de texto sin formato (es decir, variables normales, no cifradas),
ramificación, ya sea incondicional o condicionada a predicados de texto plano,
lectura y escritura de memoria en direcciones de texto plano, aritmética de punteros,
llamadas a subrutinas/funciones.
Básicamente, la lógica simple contiene cualquier lógica de programación compatible con el lenguaje, con exclusión de un solo caso: la modificación de variables cifradas del programa, que es prerrogativa de las partes TFHE del programa. Lo único que la lógica simple puede hacer con los textos cifrados (y con las claves públicas TFHE por igual) es moverlos sin cambios y alimentarlos a las partes TFHE, como si se estuvieran ejecutando dentro de su propio coprocesador o contenedor separado.
Para todos los efectos, un programa que cumple con esta definición está completo y listo para convertirse en una aplicación homomórfica de pleno derecho, cualquiera que sea el lenguaje de programación. La compilación personalizada proporcionará este mapeo final y el objeto resultante se puede ejecutar sobre un tiempo de ejecución habilitado para TFHE o como un ejecutable independiente y autónomo.
Se puede utilizar un lenguaje dedicado para unificar la representación de los programas TFHE, como algunos DSL, o mejor, un dialecto MLIR , de modo que la compilación se pueda realizar con el mismo compilador de fondo.
La naturaleza precisa del tiempo de ejecución (biblioteca compartida/dinámica, VM o de otro tipo) es solo una modalidad aquí. Cualquiera de las opciones conducirá a una aplicación homomórfica operativa impulsada por TFHE que se puede implementar y exponer a los usuarios.
Ahora volvamos al juego por un minuto.
Estamos ante un desarrollador que no sabe nada de TFHE ni de lo anterior pero quiere construir una aplicación homomórfica. Supongamos que hemos lanzado el compilador discutido anteriormente y el tiempo de ejecución habilitado para TFHE, si corresponde.
Nuestro objetivo está resuelto, sólo necesitamos un programa TFHE para llegar a un ejecutable. Pero… ¿cómo diablos vamos a hacer que el desarrollador autoproduzca algo tan específico como un programa TFHE en primer lugar?
Aquí viene el camino fácil y ganador: encapsula toda la complejidad en una API FHE de caja negra.
En cualquier lenguaje de programación, un programa (simple) puede verse esencialmente como una combinación de 3 ingredientes:
la lógica programática, hecha de instrucciones nativas del lenguaje y construcciones de alcance,
variables y una selección de tipos de datos que el programa les asigna, elegidos entre todos los tipos de datos posibles,
Llama a una selección de funciones externas, seleccionadas entre todas las funciones externas disponibles, que el programa utiliza para operar sobre sus variables.
Los tipos de datos tienen su propio sublenguaje, una mezcla de tipos nativos y construcciones de tipificación para extender estos tipos nativos y combinarlos en tipos estructurados de nivel superior. El sistema de tipos está destinado a proporcionar suficiente expresividad para cubrir prácticamente cualquier estructura de datos que el programa pueda requerir. Las funciones externas son las que proporcionan las bibliotecas, estándar o no, pero también pueden ser invocadas implícitamente mediante instrucciones nativas del lenguaje (piense en operadores para aritmética modular o división).
Así que los programas simples son en realidad "simples":
Escúchame bien aquí. No estoy diciendo que todos los conceptos de programación de alto nivel como polimorfismo, objetos con sus miembros y atributos, clases y herencia jerárquica, plantillas, macros, rasgos, hilos, recursividad, azúcar sintáctico, anotaciones y todo lo demás imaginable de costo cero. Las abstracciones proporcionadas por el lenguaje son necesarias y fáciles de manejar para el desarrollador, aunque se han inventado para simplificar su trabajo.
Solo digo que para nuestro propósito, son simplemente decoraciones inocuas que desaparecen en el momento de la compilación, porque de la fuente se infiere una versión reducida pero equivalente en forma normal del programa. Esa versión del programa, expresada en cualquier representación intermedia (IR), está hecha de bloques básicos en línea recta (secuencias de instrucciones que son elementales en esa IR) conectados por algún gráfico de flujo de control.
Ahora bien, es este programa en su forma normal el que es "simple". Quiero decir, lo suficientemente simple como para aumentarlo con capacidades homomórficas.
Usted gana el juego lanzando una biblioteca FHE en el idioma nativo y dejando que el desarrollador se ocupe de cómo usarla mejor.
La biblioteca expondrá nuevos tipos de datos (los cifrados, para complementar los simples) y una colección de funciones homomórficas que imitan (más o menos) las funciones simples con las que el desarrollador está familiarizado, solo que funcionan con tipos de datos cifrados. En resumen, está ampliando el sistema de tipos y el ecosistema de la biblioteca y dejando que la inteligencia del desarrollador descubra cómo utilizar estas extensiones para crear su aplicación homomórfica.
Esto no está particularmente ligado al TFHE; cualquier esquema FHE servirá. En eso es en lo que se centran los proveedores de las diversas bibliotecas FHE: mejorar la usabilidad y la facilidad de uso de FHE exponiendo funciones homomórficas de alto nivel que se ven y se sienten lo más cerca posible de la experiencia de codificación simple. Toda la complejidad criptográfica se abstrae en cajas negras a las que el programa realizará llamadas de Oracle.
Por supuesto, eso puede funcionar bien para el desarrollador. Bueno... si cumples tu parte del trato como proveedor de bibliotecas.
Descubrirán un programa homomórfico que ahora se parece a esto.
Ahora coexisten variables simples y cifradas y el programa debe mantener una división estricta entre estos 2 tipos de objetos. Esto se debe a que existe esta regla de oro de FHE que indica que cuando se aplica una función a una combinación de argumentos simples y cifrados, el resultado está necesariamente cifrado, por ejemplo, fhe_add(E(x), y)
devuelve E(x+y)
y pronto. Por lo tanto, las variables simples pueden ingresar a algunas funciones FHE pero nunca pueden salir de ellas. El cifrado homomórfico "contamina" todo lo que toca mediante la computación.
Entonces, mira... ¿cómo se ramifica condicionalmente a una variable cifrada entonces?
Bueno, no puedes. Pero no es un gran problema en absoluto.
En una aplicación FHE, la bifurcación condicional solo puede actuar sobre valores booleanos simples, no sobre valores cifrados. ¿Cómo sabrías dónde saltar en función de un bit cifrado? No tienes la clave privada del usuario para descifrar ese bit.
Afortunadamente, FHE también te ofrece trucos sencillos para remediar esto.
Supongamos que el desarrollador inicialmente quería hacer algo como
if (x == 0) then y = 3 else z = 7
pero se da cuenta de que, en ese momento, la variable x
estará realmente cifrada. ¿Cómo adaptar ese fragmento de código?
Lo primero que debe hacer es reelaborar la declaración if
simple para obtener un fragmento equivalente de código lineal que utilice multiplexación:
bit = (x == 0) // bit = 1 if x == 0 otherwise 0 y = 3 * bit + y * (1 - bit) // y = 3 if bit == 1 otherwise no change z = z * bit + 7 * (1 - bit) // z = 7 if bit == 0 otherwise no change
En una segunda pasada, el desarrollador tiene que propagar el hecho de que x
es de tipo cifrado en las líneas siguientes:
bit = fhe_is_equal(x, 0) // bit, y_new and z_new are encrypted y_new = fhe_add(fhe_mul(3, bit), fhe_mul(y, fhe_sub(1, bit))) z_new = fhe_add(fhe_mul(z, bit), fhe_mul(7, fhe_sub(1, bit)))
Se puede comprobar que esto es funcionalmente equivalente a la intención inicial del desarrollador, y eso es todo.
Si el lenguaje de programación permite sobrecargar los operadores nativos, la API FHE puede incluso hacer que las funciones FHE explícitas del segundo fragmento sean superfluas, de modo que la primera reescritura sea lo único que el desarrollador debe hacer.
Para darle una dosis extra de azúcar sintáctico al desarrollador, puedes incluso exponer un operador ternario sobrecargado a? b : c
donde cualquier argumento puede cifrarse o no. El fragmento de código se vuelve aún más simple:
bit = (x == 0) // bit = 1 if x == 0 otherwise 0 y_new = bit? 3: y // y = 3 if bit == 1 otherwise no change z_new = bit? z: 7 // z = 7 if bit == 0 otherwise no change
Esto se generaliza fácilmente a declaraciones arbitrarias if/switch
: cada vez que hay una condición cifrada para verificar, el desarrollador solo tiene que usar multiplexación para fusionar los múltiples cuerpos de la declaración en un solo bloque de código de línea recta equivalente.
Ahora, las construcciones de bucles que involucran condiciones cifradas se pueden regularizar con el mismo espíritu. Tomemos por ejemplo
for (i = 0; i < x; i++) do <body> // i is plain, x is encrypted
donde x
debe ser de tipo cifrado. Primero, descompóngalo en una declaración for
simple y una declaración if
irregular:
for (i = 0; i < known_max_value_of_x; i++) do if (i < x) then <body> // i is plain, x is encrypted
y luego regularizar la declaración if
como antes:
for (i = 0; i < known_max_value_of_x; i++) do bit = (i < x) // i is plain, x and bit are encrypted <new_body> // new body regularized using bit? _ : _
Observe que esto requiere un valor inmediato known_max_value_of_x
. El valor máximo admitido por el tipo cifrado de x
se puede usar de forma predeterminada, pero en muchos casos el desarrollador conoce un límite superior mucho mejor en x
que permite reducir el recuento total de bucles a un mínimo estricto.
Al final del día, las transformaciones anteriores se generalizan fácilmente en un método sistemático para regularizar el flujo de control irregular, que es fácil de asimilar para los codificadores y agregarlo a sus hábitos de codificación.
fhEVM de Zama es un marco completo de código abierto para el desarrollo y la implementación de contratos inteligentes confidenciales en la máquina virtual Ethereum (EVM). Los contratos fhEVM son contratos simples de Solidity que se crean utilizando cadenas de herramientas tradicionales de Solidity. La experiencia de desarrollo familiar se ve reforzada por FHE mediante la biblioteca TFHE.sol
que proporciona tipos de datos cifrados y reemplazos FHE para funciones estándar.
Los tipos de datos cifrados admitidos actualmente son
ebool, euint4, euint8, euint16, euint32, euint64, eaddress
y pronto también se incluirán enteros cifrados con signo. Las variables cifradas se crean a partir de textos cifrados de entrada sin formato utilizando constructores dedicados:
function mint(bytes calldata encryptedAmount) public onlyContractOwner { euint64 amount = TFHE.asEuint64(encryptedAmount); balances[contractOwner] = balances[contractOwner] + amount; totalSupply = totalSupply + amount; }
Los operadores nativos de Solidity +, -, *, &, |, ^, etc
están sobrecargados para comodidad del desarrollador, y los operadores de comparación proporcionados eq, ne, gt, lt, ge, le
devuelven un ebool
booleano cifrado. El operador ternario homomórfico _? _ : _
se llama select
:
function bid(bytes calldata encryptedBid) internal { euint32 bid = TFHE.asEuint32(encryptedBid); ebool isAbove = TFHE.le(bid, highestBid); // Replace highest bid highestBid = TFHE.select(isAbove, bid, highestBid); }
En el lado del tiempo de ejecución, fhEVM proporciona un EVM habilitado para TFHE donde las funciones homomórficas se exponen como contratos precompilados, lo que resulta de la integración de la biblioteca TFHE-rs
Rust de código abierto.
Consulte el documento técnico de fhEVM para obtener más información sobre esto.
¿Recuerda cómo se ven los programas TFHE ejecutables, redes TFHE parametrizadas ensambladas mediante lógica simple? Bueno, sólo necesitas una forma de asignar la lógica del software del desarrollador a eso.
El primer requisito es asegurarse de que la lógica del programa sea "sencilla". Eso es exactamente lo que le enseñamos al desarrollador a producir por sí mismo al regularizar sus declaraciones de flujo de control. Así que ahora estamos bien en eso.
El segundo requisito es que todas las funciones homomórficas llamadas por el programa deben estar mapeadas a redes TFHE parametrizadas preestablecidas. Esto es más complejo de lo que parece por varias razones.
Preestablecer una red TFHE parametrizada que implemente una función determinada no es necesariamente trivial.
Simplemente construir una suma homomórfica de 2 enteros cifrados sin signo de 64 bits le lleva a muchas opciones técnicas: ¿cómo se representan las entradas de 64 bits como vectores de enteros modulares? ¿Con qué módulo (o múltiples módulos) exactamente? Y luego, ¿cómo se puede realizar un circuito de suma de 64 bits con capas de búsquedas en tablas?
Hay muchas opciones allí. Pero eventualmente tomarás una decisión a través de una buena ingeniería y realizando muchos experimentos.
Suponiendo que implementó redes TFHE para todas las funciones de la API, desea asegurarse de que se puedan componer a voluntad como bloques de Lego.
Esto no está necesariamente garantizado porque la mejor manera de representar el mismo tipo de datos cifrados puede diferir de una función a otra. Por lo tanto, debe adoptar un formato aritmético común para representar cada tipo de datos cifrados, sin incurrir en una degradación excesiva de la eficiencia de sus redes TFHE.
Nuevamente, hay muchas opciones y tendrás que arbitrar entre ellas.
Suponiendo que todas las redes TFHE sean ahora totalmente compatibles en sus formatos de entrada/salida, es posible que aún no se garantice la componibilidad.
Esto se debe a que los parámetros criptográficos que crean una instancia de una red TFHE pueden no ser compatibles con los utilizados para crear una instancia de otra. En particular, el nivel de ruido de cifrado dentro de los textos cifrados de entrada y salida debe ajustarse para determinar la componibilidad real.
Esto es similar a la impedancia en los circuitos electrónicos; no hay forma de conectar un circuito a otro si hay una discrepancia en la impedancia. Primero debes alinear sus niveles de impedancia, y aquí ocurre lo mismo. La forma más sencilla de hacerlo es utilizar conjuntos fijos de parámetros (incluso tal vez solo un conjunto único) que estén ajustados para garantizar la alineación en todas las funciones de la API. Posteriormente, se fijará el formato de las claves públicas de los usuarios, así como los parámetros utilizados en el cifrado y descifrado de los usuarios, cualesquiera que sean los códigos de desarrollador.
Si encuentra una manera de satisfacer estos 3 requisitos al diseñar sus redes TFHE y parametrizarlas, y aun así lograr un buen rendimiento general, ¡felicidades! Lo lograste.
Son lo suficientemente buenos para la programación de propósito general, suponiendo nuevamente que la API FHE sea lo suficientemente completa como para proporcionar reemplazos homomórficos para todas las funciones estándar que espera el desarrollador, con total componibilidad.
Pero es posible que no sean lo suficientemente buenos para programas que se especializan en
funciones grandes y de uso intensivo de computación como en el aprendizaje automático,
funciones personalizadas y no estándar.
Ahí es cuando entra en juego la compilación homomórfica.
El camino difícil comienza aquí: para ganar el juego fuera de la programación de propósito general, ahora entregará un compilador TFHE.
El compilador se encargará de lo que el desarrollador no tiene idea de cómo hacer por sí mismo. Se alimentará de las aportaciones del desarrollador (cualquiera que sea, su llamada) y tendrá que completar las partes que faltan para llegar a un programa TFHE.
Veamos ejemplos típicos de aplicaciones no estándar.
Al convertir una red neuronal simple en un equivalente homomórfico, el desarrollador construirá e implementará un servicio de inferencia homomórfico en el que las entradas y salidas del usuario están cifradas de extremo a extremo.
Se supone que el desarrollador debe conocer el aprendizaje automático lo suficientemente bien como para producir un modelo cuantificado entrenado, o ya posee uno.
Los detalles de cómo se realiza la cuantificación realmente importan aquí, ya que su compilador requerirá que el modelo sea básicamente una red TFHE, o que sea fácilmente adaptable a una mediante una simple reescritura. Se sabe que las técnicas de código abierto disponibles admiten esa forma de cuantificación, ya sea mediante la cuantificación posterior de un modelo previamente entrenado o, preferiblemente, mediante la realización de un entrenamiento consciente de la cuantificación (QAT), que logra niveles de precisión de última generación en comparación. a modelos no cuantificados entrenados en el mismo conjunto de datos.
Básicamente, los módulos utilizados en las capas de la red TFHE son potencias variables de 2, de modo que la precisión de las señales de activación se mide en bits. Los pesos son enteros con signo y las funciones de activación se cuantifican y se crean instancias como búsquedas en tablas. Cuando las activaciones tabulan una función de signo duro desplazada con un desplazamiento aprendido, esta definición abarca tipos de modelos como BNN , TNN y sus generalizaciones de varios bits. Pero, en principio, uno es libre de utilizar tablas de búsqueda arbitrarias en las funciones de activación y, por lo tanto, podrían incluso aprenderse durante el entrenamiento para alcanzar mejores precisiones.
Sin embargo, lo que el desarrollador no sabe cómo hacer es convertir su red TFHE en un ejecutable homomórfico. Entonces, el único ingrediente que falta aquí es simplemente una parametrización criptográfica de esa red, y esto es todo lo que su compilador tendrá que hacer antes de pasar a la etapa de compilación de back-end.
Recuerde que la parametrización de una red TFHE proporciona una instanciación criptográfica que se puede ejecutar. También controla todas las métricas relacionadas con esa ejecución, como el tamaño total de las claves públicas del usuario y el tiempo total de ejecución. Por lo tanto, la parametrización es de importancia crítica aquí, ya que el desarrollador busca reducir la latencia de la inferencia al mínimo absoluto.
Hay demasiados grados de libertad en los parámetros criptográficos de una red TFHE como para aplicarles fuerza bruta a todos. Además, las métricas a optimizar dependen de 2 componentes que son totalmente externos a la red y dependen de los algoritmos que utiliza el runtime para realizar operaciones TFHE de bajo nivel:
Una colección de fórmulas de ruido . Una fórmula de ruido relaciona las distribuciones de entrada y salida del ruido de cifrado en los puntos finales de un operador, utilizando los parámetros del operador como variables desconocidas. Establecerlos requiere análisis científico humano y validación experimental.
Una colección de métricas de costos . Las métricas de costos predicen la eficiencia multidimensional (uso de memoria, tiempo de ejecución, etc.) de un operador en función de sus parámetros. Por lo general, se infieren a partir de mediciones de referencia mediante un análisis de mejor ajuste.
Cualquier cambio en la implementación del tiempo de ejecución debe reflejarse en estos 2 módulos, ya que ambos dependen fuertemente del algoritmo y del hardware.
Las fórmulas de ruido y el modelo de costos del tiempo de ejecución, junto con la red TFHE dada, formulan una instancia específica de toda una clase de problemas de optimización y esta instancia se entrega a un optimizador dedicado. Estamos hablando de programación no lineal entera mixta con múltiples objetivos, por lo que encontrar un frente de Pareto de conjuntos óptimos de parámetros requiere resolver esa clase no trivial de problemas de optimización.
La buena ciencia y la ingeniería han llevado a la resolución de este tipo de problemas de optimización en cuestión de segundos, y los compiladores TFHE como Concrete ya cuentan con un eficiente optimizador de parámetros TFHE como módulo interno.
Varias mejoras pueden hacer que los optimizadores TFHE sean aún más rápidos en el futuro, pero incluso sin ellas, se puede considerar la parametrización de las redes TFHE como, prácticamente, un asunto cerrado.
Estas aplicaciones son de un tipo completamente diferente. El desarrollador tiene la especificación matemática de una función personalizada para implementar, junto con una restricción de precisión. Tomemos por ejemplo
donde x
es un número real entre 0 y 1, y usar una aproximación G
de F
es aceptable siempre que
El desarrollador sabe que implementar G
en el reverso de un sobre componiendo funciones API estándar probablemente será demasiado subóptimo.
Lo que hará su compilador es fabricar, sobre la marcha, una nueva red TFHE diseñada específicamente para satisfacer la especificación de G
Luego lo parametrizará y procederá con la compilación back-end para producir la aplicación homomórfica.
Bueno, ahí es donde el camino de repente se vuelve mucho más accidentado.
En la actualidad, existe una falta de conocimiento científico sobre cómo las redes TFHE, con la definición exacta que he indicado anteriormente, pueden sintetizarse automáticamente. Incluso la investigación sobre la síntesis de tipos adyacentes de circuitos que también dependen de la aritmética modular de valores enteros es, en el mejor de los casos, escasa. Por no hablar de cualquier sintetizador preexistente capaz de realizar esta tarea de la A a la Z.
Una forma de resolver el problema es explotar una fracción del poder computacional de las redes TFHE reduciéndolas a circuitos booleanos. Por ejemplo, se puede obligar a una neurona TFHE a actuar como puerta booleana ternaria.
por
x_1, x_2, x_3
como valores 0/1,m = 4
y sus pesos en (w_1, w_2, w_3) = (2, -1, 1)
,[0, 1, 1, 0]
.
Al forzar todos los pesos y tablas posibles con el mismo módulo, se puede constituir un diccionario de neuronas TFHE que puede servir para calcular puertas ternarias. El siguiente paso consiste en
Esta es sólo una estrategia ilustrativa entre muchas porque abundan los métodos que se basan en circuitos booleanos. También puede considerar las puertas binarias habituales e implementarlas con neuronas TFHE restringidas. Cingulata de CEA y más tarde el transpilador FHE de Google han sido pioneros en este camino con TFHE.
La síntesis booleana utiliza técnicas agresivas de optimización de circuitos y, en general, es un problema técnico resuelto, o más o menos, lo que hace que ese enfoque sea sólido y práctico para quien construye el compilador.
Sin embargo, una vez que se genera una red TFHE de esa manera, su ancho y profundidad pueden terminar siendo anormalmente altos, lo que lleva a un rendimiento general deficiente. Por lo tanto, existe una sospecha generalizada de que al relajar el condicionamiento booleano (totalmente artificial) de las neuronas TFHE, se puede aprovechar toda su expresividad y obtener redes mucho más pequeñas y superficiales.
Pero nuevamente, se necesita más investigación para identificar claramente cómo hacerlo. Es posible que enfoques completamente diferentes, como aprender la red TFHE con algún método de capacitación apropiado tomado del aprendizaje automático, acaben proporcionando resultados superiores. El tiempo dirá.
Suponiendo que nuestro problema de síntesis se resuelva y produzca redes TFHE personalizadas eficientes, estará listo para juntar todas las piezas móviles y diseñar un compilador que haga todo el asunto:
Tomaría como entrada un programa simple, donde las variables sensibles simplemente se anotan como cifradas.
Aceptaría redes neuronales previamente entrenadas u otros modelos de aprendizaje automático y los reinterpretaría como redes TFHE.
Aceptaría maquetas de funciones recién hechas a partir de una especificación matemática y realizaría síntesis sobre la marcha para generar redes TFHE personalizadas.
Convertiría todas las redes TFHE no parametrizadas que quedan colgadas en la unidad de compilación en instancias ejecutables utilizando un módulo optimizador cuando sea necesario.
Realizaría la etapa de back-end de la compilación para una variedad de arquitecturas de hardware o tiempos de ejecución TFHE de destino.
Aprovecharía aceleradores de hardware específicos (a través de su modelo de costos) para permitir la síntesis y parametrización de redes TFHE más rápidas.
Demonios, también podrías incluir algo de soporte para una regularización automatizada del flujo de control, de modo que el desarrollador ya no tenga que preocuparse por eso.
Eso ofrecería a los creadores de aplicaciones FHE la mejor experiencia de desarrollo.
En el contexto de la programación FHE de propósito general, una biblioteca TFHE puede proporcionar modularidad y una experiencia de desarrollo totalmente autónoma con cadenas de herramientas preexistentes.
TFHE es pionero en técnicas de compilación específicas que pueden satisfacer las necesidades del desarrollador más allá de ese punto, más particularmente para la inferencia de aprendizaje automático cifrado y, en los próximos años, para el procesamiento de datos cifrados de alta velocidad y otras aplicaciones FHE personalizadas.
En general, TFHE proporciona un camino tecnológico claro para crear cadenas de herramientas FHE más integradas y adaptables que pueden triunfar en el mundo del desarrollo de software y dar origen a una nueva ola de soluciones que priorizan la privacidad y que cualquiera puede construir y ejecutar con una facilidad sin precedentes.
Al centrarme únicamente en las redes de búsqueda de TFHE, solo he utilizado un modelo computacional entre varios que TFHE puede admitir. A medida que la investigación descubra progresivamente más de sus capacidades, no hay duda de que surgirán a la superficie nuevas formas de instrumentar TFHE.
Cuáles y cuándo es otra historia. Pero detrás de esa historia se esconde una gran cantidad de otras preguntas interesantes y potencialmente esclarecedoras sobre el futuro de la informática confidencial.