人工智能 LLM 革命前夜:一文读懂横扫自然语言处理的 Transformer 模型

  • UPDATED:2023 年 1 月 27 日,本文登上 ATA 头条。(注:ATA 全称 Alibaba Technology Associate,是阿里集团最大的技术社区)
  • UPDATED:2023 年 2 月 2 日,本文在 ATA 获得鲁肃点赞。(注:鲁肃,本名程立,是阿里合伙人、阿里集团上一任 CTO)

大家好!我是麦克船长,目前就职于阿里巴巴集团,任总监/资深综合运营专家,先后负责过淘宝行业产品团队、天天特卖、大聚划算运营中心。网名一直用「麦克船长」,中科大计算机本科毕业后先是做的音视频流媒体技术、分布式系统等等,干过 Full Stack,后来创业在技术、产品、运营、营销、供应链等等方面多年后来到阿里,在淘系带过不同业务的产品、运营团队。文本来自我的个人博客:MikeCaptain - 麦克船长的技术、产品与商业博客,梳理了自己在春节期间对 NLP 基础模型的技术演变学习笔记记录,写就于大年初一在香港过春节时。本文包含 3 个章节:

  • 第一章,主要介绍 Transformer 出现之前的几个主流语言模型,包括 N 元文法(n-gram)、多层感知器(MLP)、卷积神经网络(CNN)、循环神经网络(RNN)。其中 CNN 主要应用领域在计算机视觉,因此没有更详细展开。其他模型也未面面俱到,主要考虑还是一个领域学习者的角度来了解和应用,而非研究。
  • 第二章,是本文的核心,先介绍了注意力机制(Attention Mechanism),然后基于第一章对此前几大语言模型了解后,我们能更好地理解 Transformer 为什么会带来革命性的影响。
  • 第三章,是一个 Transformer 的实现版本,基于 Tensorflow。

Durante el Festival de Primavera, además de este artículo, también clasifiqué un artículo sobre "Revisión de la evolución del modelo de lenguaje grande (LLM) después de Transformer" y un artículo sobre "LLM lidera la revolución de la productividad, brindando el control del pulso tecnológico en el próximo Años", pero no tengo tiempo para ordenar la composición tipográfica, y lo publicaré cuando tenga tiempo en el futuro. Estos derechos deberían ser pasatiempos técnicos para matar el tiempo durante el Festival de Primavera. Porque son notas técnicas que son sesgado hacia el aprendizaje, todos son bienvenidos a criticar, corregir y comunicar .

  • Autor: Zhong Chao (Capitán Mike)
  • Correo electrónico: zhongchao.ustc#gmail.com (#->@)
  • WeChat: sinosuperman (especifique "empresa/organización, puesto" para mi comodidad, gracias)
  • Cuándo: 22 de enero de 2023

prefacio

Este artículo intenta aclarar una pregunta desde un punto de vista técnico: ¿Cuál es el origen de la explosión de AIGC en el último año y el rápido desarrollo del campo NLP (Natural Language Processing) en los últimos cinco años?

Una vez respondida esta pregunta, habrá dos preguntas más, pero este artículo no ha respondido por el momento: 1) Si se considera que pasar el test de Turing representa AGI (Inteligencia General Artificial, inteligencia artificial general), ¿cuál es la desarrollo actual de PNL e incluso AGI? 2) ¿Cuál podría ser la ruta de desarrollo de AGI en los próximos años?

Aprovechando la época de la Fiesta de la Primavera escribí una nota tan larga con decenas de miles de palabras que espero que amigos con aficiones comunes puedan leerla y corregirla.

1. Después de que llegué a Ali, el primer pasatiempo nuevo que agregué fue "Modelos de Transformers", y el segundo pasatiempo nuevo fue "Modelos de Transformers".

Escribí un tallo tan frío, pero lo que realmente quiero decir es que el primero se refiere a los modelos de juguetes hechos a mano relacionados con los famosos "Transformers" de IP, y el segundo se refiere a Transformer, el modelo de lenguaje de inteligencia artificial que lidera el revolución. Estos dos pasatiempos no tienen una conexión superficial y directa con el trabajo de comercio electrónico que realizo actualmente, por lo que deben considerarse pasatiempos.

En 2022, la aplicación de "IA generativa" ha logrado un rápido desarrollo. Como practicante de "Internet clásico", siento profundamente los cambios disruptivos que la tecnología de IA puede traer esta vez, lo que me emociona y me pone ansioso. En la primera mitad de 2022, desde la persona a cargo del negocio de Venta Especial de Tiantian hasta la persona a cargo del Centro de Operaciones de Dajuhuasuan, he estado prestando atención a la propuesta modelo de transmisión en vivo en la plataforma de marketing durante mucho tiempo el año pasado, y he estado pensando en una pregunta: el papel de la eficiencia del comercio electrónico en vivo (método de interpretación del producto más adecuado + derechos de dominio privado + compra impulsiva, etc.) frente a la ineficiencia del comercio electrónico de transmisión en vivo (distribución de transmisión en vivo sin tripulación emparejamiento de bienes + miles de personas en la sala de transmisión en vivo + estado desconocido del producto + ancla incontrolable, etc.), ¿Puede promover un modelo que conserve la alta eficiencia de la transmisión en vivo y resuelva la baja eficiencia de la transmisión en vivo al mismo tiempo?

Hay un montón de cosas para explorar aquí, y esa no es la intención original de la serie del Capitán Mike, pero es una introducción a por qué estoy empezando a prestar mucha atención a la IA. La base de tecnología humana digital del comercio electrónico de transmisión en vivo incluye captura de movimiento, simulación de expresión facial, representación visual, generación de voz en vivo, síntesis de voz, etc. Basado en los primeros principios, encontré que si bien muchas tecnologías como la captura de movimiento y la representación visual aún tienen grandes desafíos, desde una perspectiva comercial, lo que realmente afecta la mente del usuario es la generación e interpretación del habla en vivo, a excepción del ancla de cabeza. , a la gran mayoría de los productos de transmisión en vivo les está yendo muy mal en este sentido, por lo que hay un gran contenido generado por "aprendizaje automático" que supera el espacio de mercado de la mayoría de los practicantes que no son cabezas, y esto depende completamente del procesamiento del lenguaje natural (PNL ).

Este problema pertenece a la categoría de "IA generativa". Los círculos de ciencia y tecnología extranjeros lo llaman "Gen-AI", es decir, IA generativa, y los círculos de ciencia y tecnología chinos lo llaman "AIGC", es decir, contenido generado por IA. que corresponde a UGC y PGC. El nombre de Gen-AI está más relacionado con el tema, específicamente con el "modelo de IA generativa", que es un "motor de contenido". El nombre chino está más relacionado con la "aplicación de contenido".

Hablando de AIGC, el familiar ChatGPT debutará a fines de 2022. También es debido a la ruptura de ChatGPT que la atención de AIGC en el círculo de tecnología doméstica se ha disparado. Desde mediados del año pasado, he estado prestando atención al código abierto de Stable Diffusion, la estrella en el campo de "Vensen graph, text2image", y luego he prestado atención a la explosión de las aplicaciones de text2image, incluido Disco Diffusion, MidJourney, DALL E 2, etc., todos ellos derivados de CV (computer vision) El avance técnico provocado por el desarrollo del modelo Diffusion en el campo.

Las imágenes generadas por IA son realmente sorprendentes. Me encantan los mods de Transformers y me gustan mucho los mechs, así que generé algunas imágenes al azar y las publiqué aquí para que todos las vean, la velocidad de creación es de minutos. (Nota: en la actualidad, las imágenes generadas por IA se basan principalmente en el desarrollo de aplicaciones de Diffusion, y el principal impulsor del texto generado por IA es el modelo Transformer, que solo se muestra aquí)

Pero desde la perspectiva de los primeros principios, la amplitud de la aplicación de generar imágenes es mucho menor que la de generar texto. La esencia del contenido del texto es la comprensión y la generación del lenguaje y los caracteres. La historia humana tiene 6 millones de años, pero la historia de la civilización humana tiene solo unos 6.000 años. La razón del gran desarrollo de la civilización en los últimos 2.000 años es principalmente debido a la invención de la escritura por parte de los humanos hace más de 3.500 años. Por lo tanto, la IA genera texto significa que la IA puede colaborar eficientemente con los humanos de una manera que los humanos están familiarizados (lenguaje y escritura), lo que seguramente detonará una revolución en la productividad. Y esto afectará profundamente a muchos campos, como el comercio electrónico, el contenido, los juegos, la computación en la nube y los servicios empresariales.

2. Dominar la base técnica es la habilidad básica para comprender el pulso de la IA en la actualidad, y este pulso impulsará todos los ámbitos de la vida.

Una vez que preste mucha atención a la IA y la PNL, descubrirá que todavía se encuentra en una etapa de avances tecnológicos. Si habla de IA, PNL y AIGC sin prestar atención a la tecnología, solo puede ser un "fanático" No hay manera de tener una conversación en profundidad con los que marcan tendencias en la industria, y mucho menos participar en ellos. Entonces, este Festival de Primavera, el Capitán Mike volvió a su intención original cuando estaba haciendo tecnología, entregó algunos materiales y aprendió la tecnología clave del modelo de lenguaje de PNL. Aquí, lo compartirá con usted como una nota de estudio técnico. Aunque me preocupa que la clase se elimine, pero de acuerdo con el método de aprendizaje de salida defendido por el Sr. Feynman, tiro el contenido que he aprendido y ordenado. Además de ayudarme más a mí mismo, también puedo hacer que algunos estudiantes que también están preocupados por esto. Las partes interesadas son bienvenidas. Mis compañeros de clase agregaron mi WeChat (señal de WeChat sinosuperman) para comunicarse conmigo en su tiempo libre.

Después de leer este artículo, primero haga algunas suposiciones sobre su conocimiento básico anterior. Si aún no lo entiende, puede encontrar el siguiente contenido mientras lee y hacer algunas consultas simples:

  • Presentación de palabras: Representación de palabras en el procesamiento del lenguaje natural, que involucra principalmente incrustaciones.
  • Tensor: se requiere un poco de base, como comprender la forma de los tensores, subir y bajar dimensiones, etc. Pero no implicará cuestiones complejas, y es suficiente tener una base matemática para operaciones simples sobre tensores de primer orden (vectores) y tensores de segundo orden (matrices). Para el tensor de tercer orden, probablemente puedas imaginar su significado espacial. Comprender la distancia entre palabras en el modelo de lenguaje tiene su significado espacial y geométrico.
  • Marco técnico: marco PyTorch o TensorFlow. Debido a la relación entre el tiempo y la extensión, al clasificarlos durante el Festival de Primavera, para la base del marco, busqué principalmente en Google, pregunté a ChatGPT y busqué directamente el texto completo en la lectura de WeChat.

Como nota técnica, es inevitable que haya errores o malentendidos, y las correcciones son bienvenidas. En este artículo, Graphviz se usa para dibujar imágenes y KaTeX se usa para generar fórmulas. Después de publicar en ATA, inevitablemente habrá algunas partes que no son compatibles (las que se encontraron se han arreglado), por favor, perdónenme.

Capítulo 1 Varios modelos de lenguaje clave de la PNL antes de 2017

En cuanto a la base técnica de la PNL, creo que se compone principalmente de dos partes: representación de palabras (Word Presentation), modelo de lenguaje (Language Model). Para el método de representación de palabras, no lo introduciré en detalle aquí, la idea básica es representar las palabras como vectores (tensores unidimensionales), los más básicos One-Hot, Word2Vec, GloVe, fastText, etc. La evolución técnica de esta parte también avanza constantemente. Por ejemplo, en el modelo Transformer en el que se centrará este artículo, la representación de palabras utilizada es "introducir vectores de palabras conscientes del contexto".

El modelo de lenguaje es desde el primer N-gram (N-Gram, que se presentará en este artículo), hasta el primer perceptrón (Perceptron) después de que se propuso la red neuronal, y luego a la red neuronal convolucional (CNN), y luego, una red neuronal recurrente (RNN, incluido el modelo Encoder-Decoder) que considera características de secuencia, hasta que Transformer, que nació en 2017, se divide aproximadamente en estas cinco etapas principales. Debido a que el enfoque de este artículo es Transformer, daré una descripción general rápida de los primeros cuatro modelos y luego presentaré el mecanismo de atención más simple. Basado en esto, presentaré Transformer en detalle y daré un ejemplo de código completo y refinado. conferencias

Sección 1 · Modelo de lenguaje N-grama

1.1 Asunción de Markov y modelo de lenguaje N-grama

La probabilidad de la siguiente palabra solo depende de las n-1 palabras anteriores. Esta suposición se llama "suposición de Markov". Los N-gramas también se denominan cadenas de Markov de orden N-1.

  • Un gramo (1 gramo), unigrama, cadena de Markov de orden cero, no depende de ninguna palabra anterior;
  • Gramática binaria (2 gramos), bigrama, cadena de Markov de primer orden, solo depende de la primera palabra;
  • Trigram (3-gram), trigram, cadena de Markov de segundo orden, solo depende de las 2 primeras palabras;
  • ……

Prediga la probabilidad de que una palabra aparezca en el tiempo t hasta las primeras t-1 palabras y utilice la estimación de máxima verosimilitud:

PAGS ( peso ∣ w 1 , w 2 . . . peso − 1 ) = C ( w 1 , w 2 , . . . peso ) C ( w 1 , w 2 , . . . peso − 1 ) PAGS(w_t | w_1 ,w_2...w_{t-1}) = \frac{C(w_1,w_2,...w_t)}{C(w_1,w_2,...w_{t-1})}PAG ( wtw1,w2... qt 1)=C ( w1,w2,... qt 1)C ( w1,w2,... qt)

Además, la probabilidad de que aparezca un grupo de palabras (es decir, una oración) es:

PAGS ( w 1 , w 2 , . . . peso ) = PAGS ( peso ∣ w 1 , w 2 , . . . peso − 1 ) ⋅ PAGS ( peso − 1 ∣ w 1 , w 2 , . . . peso − 2 ) ⋅ . . . ⋅ PAGS ( w 1 ) = ∏ yo = 1 t − 1 PAGS ( wi ∣ w 1 : yo − 1 ) \begin{alineado} PAGS(w_1,w_2,...w_t) &= P(w_t | w_1,w_2 ,...w_{t-1}) \cdot P(w_{t-1} | w_1,w_2,...w_{t-2}) \cdot ... \cdot P(w_1) \\ & = \displaystyle\prod_{i=1}^{t-1}P(w_i | w_{1:i-1}) \end{alineado}PAG ( w1,w2,... qt)=PAG ( wtw1,w2,... qt 1)PAG ( wt 1w1,w2,... qt 2)...PAG ( w1)=yo = 1t 1PAG ( wyow1 : yo 1)

Para resolver el problema de calcular la probabilidad del comienzo y el final de una oración, introducimos dos etiquetas <BOS> y <EOS> para representar el comienzo y el final de la oración respectivamente, así que w 0 = w_0 =w0= <BOS>、wlongitud + 1 = w_{longitud + 1} =wlongitud + 1 _ _ _ _ _= <EOS>, donde longitud es el número de palabras.

En concreto, por ejemplo, para bigram, el modelo se expresa de la siguiente manera:

PAGS ( w 1 , w 2 , . . . peso ) = ∏ yo = 1 t - 1 PAGS ( wi ∣ wi - 1 ) PAGS ( peso ∣ peso - 1 ) = C ( peso - 1 , peso ) C ( peso - 1 ) \begin{alineado} P(w_1,w_2,...w_t) &= \displaystyle\prod_{i=1}^{t-1}P(w_i | w_{i-1}) \\ P( w_t | w_{t-1}) &= \frac{C(w_{t-1}, w_t)}{C(w_{t-1})} \end{alineado}PAG ( w1,w2,... qt)PAG ( wtwt 1)=yo = 1t 1PAG ( wyowyo 1)=C ( wt 1)C ( wt 1,wt)

  • Si el número de ocurrencias de una palabra es 0, la multiplicación de esta cadena es 0, ¿qué debo hacer?
  • Debido a que se basa en la suposición de Markov, el valor de la ventana fija N tendrá un desempeño deficiente en palabras de larga distancia.
  • Si el valor de N se establece en un valor grande para resolver dependencias de palabras de larga distancia, dará lugar a una gran escasez de datos (demasiadas frecuencias cero) y la escala del parámetro explotará rápidamente (cálculo de tensor de alta dimensión).

Para el primer problema anterior, presentamos métodos como suavizado/regresión/diferencia para resolverlo, mientras que los dos últimos problemas se resuelven mejor después de la aparición del modelo de red neuronal.

1.2 Suavizado/Descuento

Aunque limitar el tamaño de la ventana n reduce la posibilidad de que la probabilidad de la palabra sea 0, pero cuando la n del n-grama es relativamente grande, habrá un problema de palabras fuera de registro (Out Of Vocabulary, OOV). Por otro lado, los datos de entrenamiento pueden no cubrir el 100% de las palabras que se pueden encontrar en la práctica. Por lo tanto, para evitar que ocurra una probabilidad 0, existe una tecnología de parche que hace la transición suave de cero a distinto de cero.

La técnica de suavizado más simple es el descuento. Esta es una manera muy fácil de pensar, que consiste en ahorrar una pequeña parte de la probabilidad general del 100 % y dar estas palabras de frecuencia cero (también se suelen considerar juntas las palabras de baja frecuencia). Los métodos de suavizado comunes incluyen: suavizado plus 1, suavizado plus K, suavizado Good-Turing, suavizado Katz, etc.

1.2.1, Descuento por adición / Suavizado de Laplace (Descuento por adición / Suavizado de Laplace)

Agregue 1 para suavizar, es decir, aumente directamente el número de ocurrencias de todas las palabras en 1, no solo para palabras de frecuencia cero y palabras de baja frecuencia. Si continúa tomando bigram como ejemplo, el modelo se convertirá en:

PAGS ( wi ∣ wi - 1 ) = C ( wi - 1 , wi ) + 1 ∑ j = 1 norte ( C ( wi - 1 , wj ) + 1 ) = C ( wi - 1 , wi ) + 1 C ( wi − 1 ) + ∣ V ∣ PAGS(w_i | w_{i-1}) = \frac{C_(w_{i-1},w_i) + 1}{\displaystyle\sum_{j=1}^n(C_ (w_{i-1},w_j) + 1)} = \frac{C(w_{i-1}, w_i) + 1}{C(w_{i-1}) + |\mathbb{V}| }PAG ( wyowyo 1)=j = 1n( C(wyo 1,wj)+1 )C(wyo 1,wyo)+1=C ( wyo 1)+V C ( wyo 1,wyo)+1

donde NNN representa la suma de las frecuencias de palabras de todas las palabras,∣ V ∣ |\mathbb{V}|V representa el tamaño del vocabulario.

Si muchas de las palabras del vocabulario tienen una frecuencia muy pequeña, la frecuencia de palabra de cada palabra es +1 y la desviación del resultado es bastante grande. En otras palabras, +1 es demasiado para escenas con muchas palabras de baja frecuencia, y se debe agregar un número menor (1 < δ < 1). Así que existe la siguiente técnica de "suavizado delta".

1.2.2, Suavizado K / Suavizado δ (Descuento Add-K / Suavizado Delta)

Reemplace +1 con δ, veamos que el modelo de bigrama anterior debería verse así:

PAGS ( wi ∣ wi - 1 ) = C ( wi - 1 , wi ) + δ ∑ j = 1 norte ( C ( wi - 1 , wj ) + δ ) = C ( wi - 1 , wi ) + δ C ( wi − 1 ) + δ ∣ V ∣ PAGS(w_i | w{i-1}) = \frac{C_(w_{i-1},w_i) + \delta}{\displaystyle\sum_{j=1}^n (C_(w_{i-1},w_j) + \delta)} = \frac{C(w_{i-1}, w_i) + \delta}{C(w_{i-1}) + \delta| \mathbb{V}|}PAG ( wyocon yo1 )=j = 1n( C(wyo 1,wj)+re )C(wyo 1,wyo)+d=C ( wyo 1)+δ V C ( wyo 1,wyo)+d

δ es un hiperparámetro, y determinar su valor requiere el uso de la perplejidad (Perplejidad, generalmente abreviada como PPL). Además, algunos artículos también llaman a este método "Add-K Smoothing, Add-K Smoothing".

1.2.3 Perplejidad

Para un conjunto de prueba específico, la perplejidad se define como el recíproco de la media geométrica de la probabilidad de cada palabra en el conjunto de prueba, de la siguiente manera:

PPL ⁡ ( D prueba ) = 1 PAGS ( w 1 , w 2 . . . wn ) n \operatorname{PPL}(\mathbb{D}_{prueba}) = \frac{1}{\sqrt[n]{ P(w_1,w_2...w_n)}}PPL ( Dprueba _ _)=nortePAG ( w1,w2... qn) 1

PAGS ( w 1 , w 2 , . . . wt ) = ∏ yo = 1 t − 1 PAGS ( wi ∣ wi − 1 ) PAGS(w_1,w_2,...w_t) = \displaystyle\prod_{i=1 }^{t-1}P(w_i|w_{i-1})PAG ( w1,w2,... qt)=yo = 1t 1PAG ( wyowyo 1) en la fórmula anterior, se obtiene la fórmula de cálculo de PPL:

PPL ⁡ ( re prueba ) = ( ∏ yo = 1 norte PAGS ( wi ∣ w 1 : yo − 1 ) ) − 1 norte \operatorname{PPL}(\mathbb{D}_{prueba}) = (\displaystyle\prod_ {i=1}^nP(w_i|w_{1:i-1}))^{-\frac{1}{n}}PPL ( Dprueba _ _)=(yo = 1nPAG ( wyow1 : yo 1) )norte1

1.3 Retroceso

En el modelo de gramática multivariable, por ejemplo, tome 3 gramos como ejemplo, si la probabilidad de algunas gramáticas ternarias es cero, cero no se usa para representar la probabilidad y la probabilidad se devuelve a 2 gramos, como sigue.

PAGS ( wi ∣ wi − 2 wi − 1 ) = { PAGS ( wi ∣ wi − 2 wi − 1 ) C ( wi − 2 wi − 1 wi ) > 0 PAGS ( wi ∣ wi − 1 ) C ( wi − 2 wi − 1 wi ) = 0 y C ( wi − 1 wi ) > 0 P(w_i|w_{i-2}w_{i-1}) = \begin{casos} P(w_i|w_{i-2}w_ {i-1}) & C(w_{i-2}w_{i-1}w_i) > 0 \\ P(w_i|w_{i-1}) & C(w_{i-2}w_{i -1}w_i) = 0 \enspace y \enspace C(w_{i-1}w_i) > 0 \end{casos}PAG ( wyowyo 2wyo 1)={ PAG ( wyowyo 2wyo 1)PAG ( wyowyo 1)C ( wyo 2wyo 1wyo)>0C ( wyo 2wyo 1wyo)=0y _C ( wyo 1wyo)>0

1.4 Interpolación

Si el modelo de N-gramas usa el método alternativo, solo considera que cuando la probabilidad de n-gramas es 0, la alternativa es n-1 gramos, por lo que es natural preguntar: cuando el n-grama no es cero, ¿puede también se ponderará de acuerdo a un peso determinado?Considerar n-1 gramo? Así que hay un método de interpolación. Tome 3 gramos como ejemplo, teniendo en cuenta tanto 2 gramos como 1 gramo:

PAGS ( wi ∣ wi − 2 wi − 1 ) = λ 1 PAGS ( wi ∣ wi − 2 wi − 1 ) + λ 2 PAGS ( wi ∣ wi − 1 ) + λ 3 PAGS ( wi ) PAGS(w_i|w_{i -2}w_{i-1}) = \lambda_1 P(w_i|w_{i-2}w_{i-1}) + \lambda_2 P(w_i|w_{i-1}) + \lambda_3 P(w_i )PAG ( wyowyo 2wyo 1)=yo1PAG ( wyowyo 2wyo 1)+yo2PAG ( wyowyo 1)+yo3PAG ( wyo)

Sección 2 Perceptrón

Los problemas significativos del modelo N-grama se han mencionado en la sección "Hipótesis de Markov y el modelo de lenguaje N-grama". Estos problemas se resuelven básicamente en el modelo de red neuronal, y para entender el modelo de red neuronal, debemos comenzar con el perceptrón (Perceptron). El modelo de perceptrón se propuso en 1957 y el modelo de perceptrón multicapa (MLP) se propuso en 1959. MLP a veces se llama ANN, es decir, Red Neuronal Artificial A continuación, entendámoslo de una manera simple y hagamos algunos ejercicios prácticos.

2.1, Perceptron (Perceptron): una red neuronal de avance para tareas de clasificación binaria

x es un vector de entrada, w es un vector de peso (un vector de valores de peso asignados a cada valor en el vector de entrada). Para dar un ejemplo de una tarea específica, por ejemplo, si el producto interno de estos dos vectores excede un cierto valor, se juzga como 1, de lo contrario, es 0. Esta es en realidad una tarea de clasificación. Entonces el valor de salida final se puede expresar de la siguiente manera:

y = { 1 ( ω ⋅ x ≥ 0 ) 0 ( ω ⋅ x < 0 ) y = \begin {casos} 1 & (\omega \cdot x \geq 0) \\ 0 & (\omega \cdot x \lt 0) \end{casos}y={ 10( ayX0 )( ayX<0 )

Este es un perceptrón típico (Perceptron), que generalmente se usa para resolver problemas de clasificación. También puede agregar otro término de sesgo (sesgo), de la siguiente manera:

y = { 1 ( ω ⋅ X + segundo ≥ 0 ) 0 ( ω ⋅ X + segundo < 0 ) y = \begin{cases} 1 & (\omega \cdot x + segundo \geq 0) \\ 0 & (\ omega \cdot x+b\lt0)\end{casos}y={ 10( ayX+b0 )( ayX+b<0 )

El perceptrón es en realidad una red neuronal de avance que consta de una capa de entrada, una capa de salida y ninguna capa oculta. Y la salida es una función binaria, que se utiliza para resolver problemas de clasificación binaria.

2.2, Regresión lineal (Regresión lineal): de perceptrones de valores discretos (resolución de problemas de clase) a regresión lineal de valores continuos (resolución de problemas de regresión)

En general, pensamos en la salida de un perceptrón como un valor discreto. En términos generales, pensamos que el problema que resuelve el valor discreto como salida es un problema de clasificación; en consecuencia, el problema que resuelve el valor continuo es la regresión (Regresión). Por ejemplo, para el perceptrón anterior, si directamente ω ⋅ x + b \omega \cdot x + bVayaX+b como valor de salida, se convierte en un modelo de un problema de regresión lineal.

A continuación, usamos PyTorch para implementar un ejemplo de código de regresión lineal. En primer lugar, debemos comprender que hay una función muy utilizada en la biblioteca de PyTorch:

nn.Linear(in_features, out_features)

Esta función inicializa automáticamente los pesos y sesgos cuando se crea, y puede calcular una transformación lineal de los datos de entrada llamando a su forwardfunción . Específicamente, cuando la entrada xes , forwardla función calcula y = ω ⋅ x + by = \omega \cdot x + by=VayaX+b , de los cualesWWW ybbb son el peso y el sesgo de nn.Linearla capa

Tomemos un ejemplo de código completo:

import torch
import torch.nn as nn

# 定义模型
class LinearRegression(nn.Module):
    def __init__(self, input_size, output_size):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(input_size, output_size)

    def forward(self, x):
        return self.linear(x)

# 初始化模型
model = LinearRegression(input_size=1, output_size=1)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 创建输入特征 X 和标签 y
X = torch.Tensor([[1], [2], [3], [4]])
y = torch.Tensor([[2], [4], [6], [8]])

# 训练模型
for epoch in range(100):
    # 前向传播,在本文 2.7 节有详细介绍
    predictions = model(X)
    loss = criterion(predictions, y)

    # 反向传播,在本文 2.7 节有详细介绍
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# 创建测试数据 X_test 和标签 y_test
X_test = torch.Tensor([[5], [6], [7], [8]])
y_test = torch.Tensor([[10], [12], [14], [16]])

# 测试模型
with torch.no_grad():
    predictions = model(X_test)
    loss = criterion(predictions, y_test)
    print(f'Test loss: {
      
      loss:.4f}')

El código anterior primero crea una clase de modelo de regresión LinearRegressionlineal , que tiene una forwardfunción de propagación hacia adelante, que en realidad calcula el valor de salida cuando se le llama y.

El programa principal primero crea una instancia de modelo de regresión lineal, luego define un evaluador de función de pérdida para evaluar el efecto del modelo y utiliza Stochastic Gradient Descent como optimizador.

Luego cree un tensor de características de entrada y un tensor de etiquetas. Utilice este conjunto de funciones y etiquetas para el entrenamiento. El proceso de entrenamiento consiste en Xcalcular predictions, y luego calcular la pérdida para el evaluador yjunto con loss, y luego realizar la propagación hacia atrás (presentada en la Sección 2.7 de este artículo). Tenga en cuenta las tres líneas de código para la retropropagación:

optimizer.zero_grad()
loss.backward()
optimizer.step()

Dicho entrenamiento se realiza 100 veces (cada vez que se actualizan los parámetros del modelo en una caja negra, uno epoches un proceso de entrenamiento, a veces llamado iterationo step, continuamente optimiza los parámetros del modelo de acuerdo con lossel entrenamiento .

Luego, creamos un conjunto de tensores de valor de característica de prueba X_testy tensores de etiqueta de prueba y_test, y luego los usamos para probar el rendimiento del modelo, y pasamos las características de prueba predictionsy y_testal evaluador para obtener loss. En este ejemplo obtenemos el siguiente resultado:

Test loss: 0.0034

2.3, Regresión logística (Regresión logística): regresión lineal sin restricciones de rango, a regresión logística limitada a un rango (a menudo se usa en problemas de clasificación)

Puede ver el problema de regresión lineal, el valor de salida no está limitado. Si limitado (límite) en un específico ( 0 , L ) (0, L)( 0 ,L ) , se llama regresión logística. Entonces, ¿cómo convertir una regresión lineal en una regresión logística? Generalmente, se transforma mediante la siguiente fórmula:

y = L 1 + mi − k ( z − z 0 ) y = \frac{L}{1 + e^{-k(z-z_0)}}y=1+mi- k ( z - z0)L

Así que el original z ∈ ( − ∞ , + ∞ ) z \in (-\infty, +\infty)z( -∞ , _+ ) se transforma eny ∈ ( 0 , L ) y \in (0, L)y( 0 ,L ) .

  • Función de activación : esta función que limita el valor de salida a un rango objetivo se denomina función de activación .
  • La inclinación de la función viene dada por kkcontrol k , cuanto mayor sea la pendiente.
  • z = z 0 z = z_0z=z0时,y = L 2 y = \frac{L}{2}y=2L

Aquí hay un código de muestra para la biblioteca scikit-learn basada en Python:

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# 这是 scikit-learn 库里的一个简单的数据集
iris = load_iris()

# 把 iris 数据集拆分成训练集和测试集两部分
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.25, random_state=42)

# 用 scikit-learn 库创建一个逻辑回归模型的实例
lr = LogisticRegression()

# 用上边 split 出来的训练集数据,训练 lr 模型实例
lr.fit(X_train, y_train)

# 用训练过的模型,拿测试集的输入数据做测试
predictions = lr.predict(X_test)

# 用测试集的数据验证精确性
accuracy = lr.score(X_test, predictions)
print(accuracy)

2.4, Regresión sigmoidea (Sigmoid Regression): regresión logística normalizada, generalmente utilizada para tareas de clasificación binaria

Cuando L = 1, k = 1, z 0 = 0 L = 1, k = 1, z_0 = 0L=1 ,k=1 ,z0=0 , la función de activación en este momento esSigmoid SigmoidFunción S i g m o i d , también a menudo expresada comoσ \sigmafunción σ , como sigue:

y = 1 1 + e − zy = \frac{1}{1 + e^{-z}}y=1+miz1

El rango de valores de la regresión sigmoidea está exactamente entre (0, 1), por lo que siempre se usa como una función de activación para la normalización. Y un modelo de regresión lineal, y luego normalizado con la función sigmoidea, esto también se denomina a menudo "regresión sigmoidea". La palabra sigmoide significa forma de S. Podemos ver su función en la imagen de la siguiente manera:

Debido a la normalización, el valor de salida también puede entenderse como una probabilidad. Por ejemplo, si nos enfrentamos a un problema de clasificación binaria, entonces la salida corresponde a la probabilidad de pertenecer a esta categoría.

Tal modelo sigmoide se puede expresar como:

y = S igmoide ( W ⋅ x + b ) y = Sigmoid(W \cdot x + b)y=S i g m o i d ( WX+segundo )

另外sigmoideo sigmoideoLa derivada (es decir, el gradiente) de la función s i g m o i d es fácil de calcular:y ′ = y ⋅ ( 1 − y ) y' = y \cdot (1-y)y=y( 1y ) . Esto es muy conveniente para que el "algoritmo de descenso de gradiente" optimice los parámetros del modelo de acuerdo con la pérdida. Regresión sigmoidea, generalmente utilizada para tareas de clasificación binaria. Entonces, ¿qué pasa con el caso de más que binario? Esto lleva a la regresión Softmax a continuación.

2.5 Regresión Softmax (Softmax Regression): de sigmoide para tareas binarias a Softmax para tareas de clasificación multivariante

En relación con la regresión logística, Softmax también se conoce como regresión logística multinomial. Se dice que Sigmoid generalmente se usa para resolver problemas de clasificación binaria, luego la regresión Softmax se usa para problemas multivariados. Tomemos una pregunta específica para explicar. Por ejemplo, el problema es juzgar a qué categoría de producto pertenece el producto representado por la imagen para cualquier imagen de entrada de un producto de comercio electrónico. Supongamos que tenemos un total de 100 categorías. Luego, una imagen, por ejemplo, tiene todos sus valores de píxeles como valores de características de entrada, y la salida es un vector zz de 100 dimensionesz , cada valor zi z_ien el vector de salidazyoIndica la probabilidad de pertenecer a la categoría correspondiente yi y_iyyo

yi = Softmax ( z ) yo = eziez 1 + ez 2 + . . . + ez 1 00 y_i = Softmax(z)_i = \frac{e^{z_i}}{e^{z_1} + e^{z_2} + ... + e^{z_100}}yyo=S o f t máx ( z ) _yo=miz1+miz2+...+miz100mizyo

Luego el final yyCada elemento en el vector y corresponde a esta entrada zzz es la probabilidad respectiva de pertenecer a estas 100 categorías. Entonces, si regresa al problema general, el modelo de esta regresión Softmax es el siguiente:

y = S oftmax ( W ⋅ x + b ) y = Softmax(W \cdot x + b)y=S o f t máx ( W _X+segundo )

Para el ejemplo anterior de imágenes de productos de comercio electrónico, suponiendo que el tamaño de cada imagen es de 512x512, la expansión de este modelo es la siguiente:

[ y 1 y 2 . . . y 100 ] = S oftmax ( [ w 1 , 1 , w 1 , 2 , . . . w 1 , 512 w 2 , 1 , w 2 , 2 , . . . w 2 , 512 . . . . . . . . . . . . w 100 , 1 , w 100 , 2 , . . . w 100 , 512 ] ⋅ [ x 1 x 2 . . . x 512 ] + [ segundo 1 segundo 2 . . . segundo 512 ] ) \begin{ bmatriz} y_1 \\ y_2 \\ ... \\ y_{100} \end{bmatriz} = Softmax(\begin{bmatriz} w_{1,1}, & w_{1,2}, & ... & w_{1, 512} \\ w_{2,1}, & w_{2,2}, & ... & w_{2, 512} \\ ... & ... & ... & .. \\ w_{100,1}, & w_{100,2}, & ... & w_{100, 512} \end{bmatrix} \cdot \begin{bmatrix} x_1 \\ x_2 \\ ... \\ x_{512} \end{bmatriz} + \begin{bmatriz} b_1 \\ b_2 \\ ... \\ b_{512} \end{bmatriz}) y1y2...y100 =S o f t máx ( _ w1 , 1,w2 , 1,...w100 , 1,w1 , 2,w2 , 2,...w100 , 2,............w1 , 512w2 , 512...w100 , 512 X1X2...X512 + b1b2...b512 )

Esto para el vector de entrada xxx realizaw ⋅ x + bw \cdot x + bwX+La operación b también se conoce comúnmente como "mapeo lineal/cambio lineal".

2.6, Perceptrón multicapa (Perceptrón multicapa)

Todas las tareas que encontramos arriba se resuelven con modelos lineales. A veces, cuando el problema se complica, tenemos que introducir un modelo no lineal.

Aquí vamos a introducir una nueva función de activación - R e LU ReLUR e LU (Unidad lineal rectificada): una función de activación no lineal, que se define de la siguiente manera:

R e LU ( z ) = max ( 0 , z ) ReLU(z) = max(0, z)RELU ( z ) _ _=máx ( 0 , _z )

Por ejemplo, el problema de clasificación de dígitos escritos a mano del conjunto de datos MNIST es una tarea típica de clasificación no lineal. Aquí hay un código de ejemplo:

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms

# 定义多层感知器模型
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)

    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

# 超参数
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001

# 加载 MNIST 数据集
train_dataset = datasets.MNIST(root='../../data',
                               train=True,
                               transform=transforms.ToTensor(),
                               download=True)

test_dataset = datasets.MNIST(root='../../data',
                              train=False,
                              transform=transforms.ToTensor())

# 数据加载器
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                          batch_size=batch_size,
                                          shuffle=False)

model = MLP(input_size, hidden_size, num_classes)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):
    for images, labels in train_loader:
        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # 输出训练损失
    print(f'Epoch {
      
      epoch + 1}, Training Loss: {
      
      loss.item():.4f}')

En este código, podemos ver que la definición del modelo de MLP es:

nn.Linear(input_size, hidden_size)
nn.ReLU()
nn.Linear(hidden_size, num_classes)

De manera similar al código de muestra del modelo anterior, también se utilizan la retropropagación, el evaluador de función de pérdida y el optimizador que presentaré en la Sección 2.7 de este artículo. Si se expresa como una fórmula, es la siguiente definición del modelo:

z = W 1 ⋅ x + segundo 1 h = R mi LU ( z ) y = W 2 ⋅ h + segundo 2 \begin{alineado} &z = W_1 \cdot x + b_1 \\ &h = ReLU(z) \\ &y = W_2 \cdot h + b_2 \end{alineado}z=W1X+b1h=RELU ( z ) _ _y=W2h+b2

Sabemos que MLP suele ser un modelo con las mismas longitudes de entrada y salida, pero en algunos casos, también es posible construir un modelo MLP con diferentes longitudes de entrada y salida. Por ejemplo, después de ingresar un conjunto de secuencias, la salida es un resultado de clasificación discreto.

2.7 Describa brevemente cómo entrenar un modelo: propagación directa y propagación inversa

Este es un tema muy importante. Sin embargo, el tiempo para el Festival de Primavera es limitado, por lo que esta parte solo puede abreviarse. Nos centramos más en el modelo de lenguaje en sí. Aquí hay una breve introducción, y puede complementarse más adelante.

  • El entrenamiento de una red neuronal incluye principalmente dos pasos: propagación hacia adelante y propagación hacia atrás.
  • La propagación directa consiste en ingresar datos en el modelo y obtener resultados de salida basados ​​en un conjunto de parámetros determinados (como el peso W en MLP, el sesgo b, etc.). Calcule la función de pérdida en función de los resultados de salida para medir el rendimiento del modelo con los parámetros actuales.
  • El método de retropropagación más utilizado es el método de descenso del gradiente (aquí no se analizan otros métodos), que se basa en la función de pérdida, usa los parámetros como variables para encontrar la derivada parcial (calcular el gradiente) y resolver la función de pérdida a lo largo de la dirección. de descenso de gradiente valor, el parámetro en este momento puede reemplazar el parámetro anterior. Este es un proceso típico de entrenamiento de optimización de modelos.

Problema extendido: desaparición de gradiente, problema de explosión de gradiente: porque la derivada parcial de la función de pérdida se calcula desde la capa de salida a la capa de entrada en sentido inverso según la "regla de la cadena matemática". Matemáticamente, este es un cálculo de multiplicación. Cuanto mayor sea número, más propenso a este problema. En este proceso de derivación, el gradiente puede ser cero, es decir, el gradiente desaparece. También es posible que el valor del gradiente sea particularmente grande.

Resolver el problema de la desaparición del gradiente y la explosión del gradiente es otro tema importante y es difícil ampliar las notas técnicas aquí debido a las limitaciones de espacio. Los métodos aproximados como el recorte de gradiente, el entrenamiento previo capa por capa propuesto por Hinton y luego el ajuste general en teoría también funcionan. El LSTM y ResNet mencionados más adelante en este artículo también pueden resolver el problema, y ​​también podemos aprender sobre diversas soluciones en la industria. , tenga la oportunidad de intercambiar y aprender con amigos.

2.8, un problema importante de MLP, ayúdanos a conducir al modelo CNN

Podemos ver que en MLP, no importa cuántas capas haya, el vector de salida hn h_n de una determinada capahnPara cada valor en , el vector de salida hn + 1 h_{n+1} se calculará en la siguiente capahn + 1utilizado para cada valor de . Específicamente, si el valor de salida para una determinada capa es el siguiente:

hn + 1 = S oftmax ( W n + 1 ⋅ hn + bn + 1 ) h_{n+1} = Softmax(W_{n+1} \cdot h_n + b_{n+1})hn + 1=S o f t máx ( W _n + 1hn+bn + 1)

El llamado "uso" en el párrafo anterior en realidad está dirigido a hn h_nhnGenerar valores propios correspondientes W n + 1 W_{n+1}Wn + 1El valor en cada fila y columna en la matriz de peso y bn + 1 b_{n+1}bn + 1Cada valor en el vector de sesgo. Si lo dibujas con una imagen, es:

Se puede ver que todos los elementos de la entrada están conectados, es decir, se les asigna un peso w y un término de polarización b, por lo que esto se denomina "Capa completamente conectada" o " Capa densa" . Pero para algunas tareas sería una tontería hacerlo y costaría muchos cálculos inútiles.

Por lo tanto, necesitamos un modelo con un menor costo computacional de enfoque, por lo que tenemos una red neuronal convolucional (CNN).

Sección 3 Redes Neuronales Convolucionales (CNN)

Cada elemento de cada capa en el MLP debe multiplicarse por un peso W de un parámetro independiente, más un sesgo B. Esta capa de red neuronal a menudo se denomina "capa totalmente conectada" o capa densa (capa de dencia). tiene un problema significativo: si la información local importante del contenido de entrada se mueve solo un poco y no se pierde, el resultado de salida completo cambiará mucho después de que se procese la capa completamente conectada; esto no es razonable.

Entonces pensaríamos, ¿qué pasa si usamos una capa más pequeña completamente conectada y solo procesamos entradas locales importantes? De hecho, esta idea es similar a n-gram, que utiliza una ventana para escanear la pieza. Convolutional Neural Network (CNN) nació en base a esto.

  • Núcleo de convolución: el núcleo de convolución es una capa pequeña y densa que se utiliza para extraer características locales, también conocido como núcleo de convolución (kernel) / filtro (filtro) / campo receptivo (campo receptivo / campo de visión).
  • Capa de agrupación (Pooling, o capa de agrupación): El proceso de agregación adicional de los resultados procesados ​​por el kernel de convolución. Para muestras con diferentes tamaños de entrada, habrá la misma cantidad de salidas de características después de la agrupación.
  • Extraiga múltiples características locales: un núcleo de convolución solo puede extraer un único tipo de características locales, y se requieren múltiples núcleos de convolución para extraer múltiples características locales. En algunos artículos, puede ver que "múltiples modos" y "múltiples canales" en realidad se refieren a múltiples núcleos que identifican múltiples funciones.
  • Capa de clasificación completamente conectada: las características múltiples obtenidas por múltiples núcleos de convolución deben pasar por una capa de clasificación completamente conectada para la toma de decisiones final.

Hacerlo tiene varias propiedades:

  • Localidad: la salida solo está determinada por los datos dentro de un área de tamaño de ventana específica.
  • Traducción invariable: para la misma característica, solo se usa un kernel para calcular al escanear diferentes áreas.
  • El tamaño del parámetro de la capa convolucional no tiene nada que ver con el tamaño de los datos de entrada y salida.

El principal campo de aplicación de CNN es la visión artificial. En NLP, los datos de texto son altamente dimensionales y el lenguaje tiene una estructura más compleja que las imágenes. Por lo tanto, las CNN generalmente no son adecuadas para problemas de PNL.

Sección 4. Redes Neuronales Recurrentes (RNN)

RNN (Recurrent Neural Network), un poderoso modelo de red neuronal capaz de predecir secuencias de datos como texto, voz y series temporales. Demostraremos cómo usar RNN a través de ejemplos de códigos vívidos y casos prácticos, y experimentaremos sus capacidades en la vida real. Aprenderá a usar RNN para resolver una variedad de problemas de aprendizaje automático y se pondrá manos a la obra con RNN para resolver problemas del mundo real. Este artículo le proporcionará una guía completa de introducción a RNN y le brindará una comprensión más profunda de RNN.

La R de RNN (Recurrent Neural Network) significa Recurrente, por lo que esta es una red neuronal recurrente. En primer lugar, debe comprender que no necesita comprender CNN para aprender el modelo RNN. Solo necesitas entender MLP para aprender RNN.

4.1 RNN con estructura clásica

La figura anterior es un diagrama esquemático de una estructura RNN clásica, y el lado derecho de la flecha Desplegar es una ilustración ampliada. Secuencia de entrada (utilice xx aquíx representa) pasado a la capa oculta (capa oculta, aquí usehhh indica), la secuencia de salida se genera después del procesamiento (aquíooo medios). Cuando se ingresa la siguiente palabra de la secuencia, la capa oculta del paso anterior afectará la salida de este paso en conjunto. EE. UU.U ,VVV ,WWW ambos representan el peso. En esta teoría de la estructura clásica, puedes ver un punto muy importante, es decir, la longitud de la secuencia de entrada es la misma que la longitud de la secuencia de salida.

Los escenarios de aplicación de esta estructura clásica, como ingresar su versión del dialecto de Sichuan para una pieza de mandarín, procesar y generar cada cuadro de un video, etc.

Sabemos que RNN se procesa uno por uno y los elementos de datos en cada secuencia están ordenados, por lo que es imposible calcular todos los elementos de datos en una secuencia en paralelo. Sin embargo, cuando se calculan secuencias diferentes, los cálculos respectivos de secuencias diferentes se pueden paralelizar. Si tomamos el resultado ht − 1 h_{t-1} emitido por la capa oculta en el tiempo anterior tht 1Páselo a una función de activación (por ejemplo, use la tanhfunción ) y luego compárelo con la entrada xt x_{t} en el momento actual tXtJuntos, se genera un tiempo tt después del procesamientosalidaht h_t de tht. Luego pase la salida de la capa oculta a través de la regresión logística multinomial (Softmax) para generar el valor de salida final yyy , podemos representar este modelo de la siguiente manera:

ht = tanh ( W xh ⋅ xt + bxh + W hh ⋅ ht − 1 + bhh ) yt = S oftmax ( W hy ⋅ ht + bhy ) \begin{alineado} &h_t = tanh(W^{xh} \cdot x_t + b^{xh} + W^{hh} \cdot h_{t-1} + b^{hh}) \\ &y_t = Softmax(W^{hy} \cdot h_t + b^{hy}) \end{ alineado}ht=Inglés ( W _xh _Xt+bxh _+WS.Sht 1+bhh )yt=S o f t máx ( W _hola _ht+bh y )

El diagrama correspondiente es el siguiente:

Este tipo de RNN con el mismo número de elementos de datos de entrada y salida generalmente se denomina N vs. N RNN. Si usamos PyTorch para implementar un RNN clásico muy simple de la siguiente manera:

import torch
import torch.nn as nn

# 创建一个 RNN 实例
# 第一个参数
rnn = nn.RNN(10, 20, 1, batch_first=True)  # 实例化一个单向单层RNN

# 输入是一个形状为 (5, 3, 10) 的张量
# 5 个输入数据项(也可以说是样本)
# 3 个数据项是一个序列,有 3 个 steps
# 每个 step 有 10 个特征
input = torch.randn(5, 3, 10)

# 隐藏层是一个 (1, 5, 20) 的张量
h0 = torch.randn(1, 5, 20)

# 调用 rnn 函数后,返回输出、最终的隐藏状态
output, hn = rnn(input, h0)

print(output)
print(hn)

Interpretemos este código:

  • Este código instancia una red RNN con 1 capa oculta.
  • Su entrada es un tensor de forma (5, 3, 10), lo que indica que hay 5 muestras, cada muestra tiene 3 pasos de tiempo y la dimensión característica de cada paso de tiempo es 10.
  • El estado oculto inicial es un tensor de forma (1, 5, 20).
  • Después de llamar a la función rnn, se devuelven la salida y el estado oculto final.
  • La forma de la salida es (5, 3, 20), lo que significa que hay 5 muestras, cada muestra tiene 3 pasos de tiempo y la dimensión de salida de cada paso de tiempo es 20.
  • La forma del estado oculto final es (1, 5, 20), lo que indica que el estado oculto final es 5

Sin embargo, el ejemplo de código anterior no escribe un RNN específico por sí mismo, sino que usa el RNN predeterminado de PyTorch, así que escribamos uno nosotros mismos:

class MikeCaptainRNN(nn.Module):
    def __init__(self, input_size, hidden_size):
        super().__init__()

        # 对于 RNN,输入维度就是序列数
        self.input_size = input_size

        # 隐藏层有多少个节点/神经元,经常将 hidden_size 设置为与序列长度相同
        self.hidden_size = hidden_size

        # 输入层到隐藏层的 W^{xh} 权重、bias^{xh} 偏置项
        self.weight_xh = torch.randn(self.hidden_size, self.input_size) * 0.01
        self.bias_xh = torch.randn(self.hidden_size)

        # 隐藏层到隐藏层的 W^{hh} 权重、bias^{hh} 偏置项
        self.weight_hh = torch.randn(self.hidden_size, self.hidden_size) * 0.01
        self.bias_hh = torch.randn(self.hidden_size)

    # 前向传播
    def forward(self, input, h0):

    	# 取出这个张量的形状
        N, L, input_size = input.shape

        # 初始化一个全零张量
        output = torch.zeros(N, L, self.hidden_size)

        # 处理每个时刻的输入特征
        for t in range(L):

        	# 获得当前时刻的输入特征,[N, input_size, 1]。unsqueeze(n),在第 n 维上增加一维
            x = input[:, t, :].unsqueeze(2)  
            w_xh_batch = self.weight_xh.unsqueeze(0).tile(N, 1, 1)  # [N, hidden_size, input_size]
            w_hh_batch = self.weight_hh.unsqueeze(0).tile(N, 1, 1)  # [N, hidden_size, hidden_size]

            # bmm 是矩阵乘法函数
            w_times_x = torch.bmm(w_xh_batch, x).squeeze(-1)  # [N, hidden_size]。squeeze(n),在第n维上减小一维
            w_times_h = torch.bmm(w_hh_batch, h0.unsqueeze(2)).squeeze(-1)  # [N, hidden_size]
            h0 = torch.tanh(w_times_x + self.bias_ih + w_times_h + self.bias_hh)
            output[:, t, :] = h0
        return output, h0.unsqueeze(0)

La interpretación del código fuente está en los comentarios.

4.2, N frente a 1 RNN

En la figura anterior, si solo se mantiene la última salida, es una RNN de N frente a 1. Dichos escenarios de aplicación, como juzgar si una secuencia de texto es en inglés o alemán, como juzgar si es un contenido emocional positivo, negativo o neutral en función de una secuencia de entrada, o juzgar qué canción es en función de una secuencia de entrada de voz Canción ( Escuche la canción y reconozca la canción).

ht = tanh ( W xh ⋅ xt + bxh + W hh ⋅ ht − 1 + bhh ) y = S oftmax ( W hy ⋅ hn + bhy ) \begin{alineado} &h_t = tanh({W^{xh}} \cdot x_t + {b^{xh}} + {W^{hh}} \cdot h_{t-1} + {b^{hh}}) \\ &y = Softmax({W^{hy}} \cdot h_n + {b^{hy}}) \end{alineado}ht=Inglés ( W _xh _Xt+bxh _+WS.Sht 1+bhh )y=S o f t máx ( W _hola _hn+bh y )

Es decir, en este modelo, cada secuencia solo produce una salida hn h_n cuando la capa oculta procesa el último elemento de datos.hnSi se representa mediante un diagrama esquemático, es la siguiente estructura:

4.3, 1 contra N RNN

Por el contrario, en la figura anterior, si solo se mantiene una x, entonces es un RNN de 1 contra N. La aplicación de este tipo de escena, como AI para crear música y extraer o reconocer algún contenido de texto a través de una imagen.

ht = { tanh ( W xh ⋅ x + bxh + 0 + bhh ) ( t = 1 ) tanh ( 0 + bxh + W hh ⋅ ht − 1 + bhh ) ( t > 1 ) yt = S oftmax ( W hy ⋅ ht + bhy ) \begin{alineado} &h_t = \begin{casos} tanh(W^{xh} \cdot x + b^{xh} + 0 + b^{hh}) & (t=1) \\ tanh( 0 + b^{xh} + W^{hh} \cdot h_{t-1} + b^{hh}) & (t>1) \end{casos} \\ &y_t = Softmax(W^{hy} \cdot h_t + b^{hy}) \end{alineado}ht={ Inglés ( W _xh _X+bxh _+0+bhh )t ingles ( 0+bxh _+WS.Sht 1+bhh )( t=1 )( t>1 )yt=S o f t máx ( W _hola _ht+bh y )

El diagrama esquemático es el siguiente:

Hasta ahora podemos ver que la capa oculta de RNN puede almacenar contenido relevante sobre los datos de entrada, por lo que la capa oculta de RNN a menudo se denomina unidad de memoria.

4.4, red de memoria a corto y largo plazo LSTM (Memoria a largo plazo)

4.4.1 ¿Cómo entender este Corto Plazo?

El modelo LSTM se propuso en el documento de 1997 "Memoria a largo plazo a corto plazo". Comencemos con la definición del modelo y entendámoslo con precisión:

ht = ht − 1 + tanh ( W xh ⋅ xt + bxh + W hh ⋅ ht − 1 + bhh ) yt = S oftmax ( W hy ⋅ ht + bhy ) \begin{alineado} &h_t = h_{t-1} + tanh(W^{xh} \cdot x_t + b^{xh} + W^{hh} \cdot h_{t-1} + b^{hh}) \\ &y_t = Softmax(W^{hy} \cdot h_t + b^{hy}) \end{alineado}ht=ht 1+Inglés ( W _xh _Xt+bxh _+WS.Sht 1+bhh )yt=S o f t máx ( W _hola _ht+bh y )

上式中与经典结构的 RNN(输入与输出是 N vs. N)相比,唯一的区别是第一个式子中多了一个「 h t − 1 h_{t-1} ht1」。如果我们把第一个式子的 t a n h tanh tanh 部分记作 u t u_t ut

u t = t a n h ( W x h ⋅ x t + b x h + W h h ⋅ h t − 1 + b h h ) u_t = tanh(W^{xh} \cdot x_t + b^{xh} + W^{hh} \cdot h_{t-1} + b^{hh}) ut=tanh(Wxhxt+bxh+Whhht1+bhh)

所以:

h t = h t − 1 + u t h_t = h_{t-1} + u_t ht=ht1+ut

那么可以展开出如下一组式子:

h k + 1 = h k + u k + 1 h k + 2 = h k + 1 + u k + 2 . . . . . . h t − 1 = h t − 2 + u t − 1 h t = h t − 1 + u t \begin{aligned} h_{k+1} &= h_k + u_{k+1} \\ h_{k+2} &= h_{k+1} + u_{k+2} \\ &...... \\ h_{t-1} &= h_{t-2} + u_{t-1} \\ h_t &= h_{t-1} + u_t \end{aligned} hk+1hk+2ht1ht=hk+uk+1=hk+1+uk+2......=ht2+ut1=ht1+ut

如果我们从 h k + 1 h_{k+1} hk+1 h n h_n hn 的所有式子左侧相加、右侧相加,我们就得到如下式子:

h k + 1 + . . . + h t − 1 + h t = h k + h k + 1 + . . . + h t − 2 + h t − 1 + u k + 1 + u k + 2 + . . . + u t − 1 + u t \begin{aligned} &h_{k+1} + ... + h_{t-1} + h_t \\ = &h_k + h_{k+1} + ... + h_{t-2} + h_{t-1} \\+ &u_{k+1} + u_{k+2} + ... + u_{t-1} + u_t \end{aligned} =+hk+1+...+ht1+hthk+hk+1+...+ht2+ht1uk+1+uk+2+...+ut1+ut

进而推导出:

h t = h k + u k + 1 + u k + 2 + . . . + u t − 1 + u t h_t = h_k + u_{k+1} + u_{k+2} + ... + u_{t-1} + u_t ht=hk+uk+1+uk+2+...+ut1+ut

从这里我们就可以看到,第 t 时刻的隐藏层输出,直接关联到第 k 时刻的输出,t 到 k 时刻的相关性则用 u k + 1 u_{k+1} uk+1 u t u_t ut 相加表示。也就是有 t-k 的短期(Short Term)记忆。

4.4.2 Introduzca la puerta de olvido f, la puerta de entrada i, la puerta de salida o, la celda de memoria c

Si tenemos la fórmula ht = ht − 1 + ut h_t = h_{t-1} + u_tht=ht 1+tut¿Qué tal asignar un peso a los dos términos de la derecha? Es el resultado de que la capa oculta calcule el elemento de datos anterior por el elemento de datos anterior a través de la capa oculta. Los dos se consideran como un par de proporciones de peso, de la siguiente manera:

pie = sigmoide ( W f , xh ⋅ xt + bf , xh + W f , hh ⋅ xt − 1 + bf , hh ) ht = ft ⊙ ht − 1 + ( 1 − ft ) ⊙ ut \begin{aligned} &f_t = sigmoide(W^{f,xh} \cdot x_t + b^{f,xh} + W^{f,hh} \cdot x_{t-1} + b^{f,hh}) \\ &h_t = f_t \odot h_{t-1} + (1 - f_t) \odot u_t \end{alineado}Ft=s i g m o i d ( Wf , x hXt+bf , x h+Wf , ehXt 1+bf , hh )ht=Ftht 1+( 1Ft)tut

en:

  • ⊙ \ odot es el producto de Hardamard, es decir, la multiplicación de elementos correspondientes de tensores.
  • pies piesFtEs "Olvidar puerta (Olvidar puerta)". Cuando el valor es pequeño, el peso del tiempo t-1 es muy pequeño, es decir, "olvidar el momento anterior en este momento". Este valor debe calcularse en función de los datos de entrada en el momento t y la salida de los datos en la capa oculta en el momento t-1, y cada elemento debe tener un valor entre (0, 1), para que pueda usar la función sigmoide para obtener este valor:

Pero así, para el pasado ht − 1 h_{t-1}ht 1y presente ut u_ttutFormados mutuamente excluyentes, solo pueden fluir y refluir. Pero, de hecho, tanto el pasado como el presente pueden ser muy importantes y pueden no serlo, por lo que seguimos usando ft f_t para el pasado.FtOlvida la puerta, úsala por el momentoitPuerta de entrada:

f t = s i g m o i d ( W f , x h ⋅ x t + b f , x h + W f , h h ⋅ x t − 1 + b f , h h ) i t = s i g m o i d ( W i , x h ⋅ x t + b i , x h + W i , h h ⋅ h t − 1 + b i , h h ) h t = f t ⊙ h t − 1 + i t ⊙ u t \begin{aligned} &f_t = sigmoid(W^{f,xh} \cdot x_t + b^{f,xh} + W^{f,hh} \cdot x_{t-1} + b^{f,hh}) \\ &i_t = sigmoid(W^{i,xh} \cdot x_t + b^{i,xh} + W^{i,hh} \cdot h_{t-1} + b^{i,hh}) \\ &h_t = f_t \odot h_{t-1} + i_t \odot u_t \end{aligned} ft=sigmoid(Wf,xhxt+bf,xh+Wf,hhxt1+bf,hh)it=sigmoid(Wi,xhxt+bi,xh+Wi,hhht1+bi,hh)ht=ftht1+itut

其中:

  • f t f_t ft 类似地,定义输入门 i t i_t it ,但是注意 f t f_t ft h t − 1 h_{t-1} ht1 而非 x t − 1 x_{t-1} xt1 有关。

再引入一个输出门:

o t = s i g m o i d ( W o , x h ⋅ x t + b o , x h + W o , h h ⋅ x t − 1 + b o , h h ) o_t = sigmoid(W^{o,xh} \cdot x_t + b^{o,xh} + W^{o,hh} \cdot x_{t-1} + b^{o,hh}) ot=sigmoid(Wo,xhxt+bo,xh+Wo,hhxt1+bo,hh)

再引入记忆细胞 c t c_t ct,它是原来 h t h_t ht 的变体,与 t-1 时刻的记忆细胞有遗忘关系(通过遗忘门),与当下时刻有输入门的关系:

c t = f t ⊙ c t − 1 + i t ⊙ u t c_t = f_t \odot c_{t-1} + i_t \odot u_t ct=ftct1+itut

那么此时 h t h_t ht,我们可以把 h t h_t ht 变成:

h t = o t ⊙ t a n h ( c t ) h_t = o_t \odot tanh(c_t) ht=ottanh(ct)

记忆细胞这个概念还有有一点点形象的,它存储了过去的一些信息。OK,到此我们整体的 LSTM 模型就变成了这个样子:

f t = s i g m o i d ( W f , x h ⋅ x t + b f , x h + W f , h h ⋅ x t − 1 + b f , h h ) i t = s i g m o i d ( W i , x h ⋅ x t + b i , x h + W i , h h ⋅ h t − 1 + b i , h h ) o t = s i g m o i d ( W o , x h ⋅ x t + b o , x h + W o , h h ⋅ x t − 1 + b o , h h ) u t = t a n h ( W x h ⋅ x t + b x h + W h h ⋅ h t − 1 + b h h ) c t = f t ⊙ c t − 1 + i t ⊙ u t h t = o t ⊙ t a n h ( c t ) y t = S o f t m a x ( W h y ⋅ h t + b h y ) \begin{aligned} &f_t = sigmoid(W^{f,xh} \cdot x_t + b^{f,xh} + W^{f,hh} \cdot x_{t-1} + b^{f,hh}) \\ &i_t = sigmoid(W^{i,xh} \cdot x_t + b^{i,xh} + W^{i,hh} \cdot h_{t-1} + b^{i,hh}) \\ &o_t = sigmoid(W^{o,xh} \cdot x_t + b^{o,xh} + W^{o,hh} \cdot x_{t-1} + b^{o,hh}) \\ &u_t = tanh(W^{xh} \cdot x_t + b^{xh} + W^{hh} \cdot h_{t-1} + b^{hh}) \\ &c_t = f_t \odot c_{t-1} + i_t \odot u_t \\ &h_t = o_t \odot tanh(c_t) \\ &y_t = Softmax(W^{hy} \cdot h_t + b^{hy}) \end{aligned} ft=sigmoid(Wf,xhxt+bf,xh+Wf,hhxt1+bf,hh)it=sigmoid(Wi,xhxt+bi,xh+Wi,hhht1+bi,hh)ot=sigmoid(Wo,xhxt+bo,xh+Wo,hhxt1+bo,hh)ut=tanh(Wxhxt+bxh+Whhht1+bhh)ct=ftct1+itutht=ottanh(ct)yt=Softmax(Whyht+bhy)

4.5、双向循环神经网络、双向 LSTM

双向循环神经网络很好理解,就是两个方向都有,例如下图:

在 PyTorch 中使用 nn.RNN 就有参数表示双向:

bidirectional – If True, becomes a bidirectional RNN. Default: False

bidirectional:默认设置为 False。若为 True,即为双向 RNN。

4.6、堆叠循环神经网络(Stacked RNN)、堆叠长短时记忆网络(Stacked LSTM)

在 PyTorch 中使用 nn.RNN 就有参数表示双向:

num_layers – Number of recurrent layers. E.g., setting num_layers=2 would mean stacking two RNNs together to form a stacked RNN, with the second RNN taking in outputs of the first RNN and computing the final results. Default: 1

num_layers:隐藏层层数,默认设置为 1 层。当 num_layers >= 2 时,就是一个 stacked RNN 了。

4.7、N vs. M 的 RNN

对于输入序列长度(长度 N)和输出序列长度(长度 M)不一样的 RNN 模型结构,也可以叫做 Encoder-Decoder 模型,也可以叫 Seq2Seq 模型。首先接收输入序列的 Encoder 先将输入序列转成一个隐藏态的上下文表示 C。C 可以只与最后一个隐藏层有关,甚至可以是最后一个隐藏层生成的隐藏态直接设置为 C,C 还可以与所有隐藏层有关。

有了这个 C 之后,再用 Decoder 进行解码,也就是从把 C 作为输入状态开始,生成输出序列。

具体地,可以如下表示:

C = E n c o d e r ( X ) Y = D e c o d e r ( C ) \begin{aligned} &C = Encoder(X) \\ &Y = Decoder(C) \\ \end{aligned} C=Encoder(X)Y=Decoder(C)

进一步展开:

e t = E n c o d e r L S T M / G R U ( x t , e t − 1 ) C = f 1 ( e n ) d t = f 2 ( d t − 1 , C ) y t = D e c o d e r L S T M / G R U ( y t − 1 , d t − 1 , C ) \begin{aligned} e_t &= Encoder_{LSTM/GRU}(x_t, e_{t-1}) \\ C &= f_1(e_n) \\ d_t &= f_2(d_{t-1}, C) \\ y_t &= Decoder_{LSTM/GRU}(y_{t-1}, d_{t-1}, C) \end{aligned} etCdtyt=EncoderL STM / GR U( Xt,mit 1)=F1( min)=F2( ret 1,c )=Decodificador _ _ _L STM / GR U( yt 1,dt 1,c )

Este tipo de aplicación es muy amplia, porque la mayoría de las veces la longitud de la secuencia de entrada y la secuencia de salida son diferentes, como la aplicación más común "traducción", que traduce de un idioma a otro; otro ejemplo es un campo de AI " "Reconocimiento de voz", que genera contenido de texto reconocido después de ingresar una secuencia de voz; también hay aplicaciones de preguntas y respuestas como ChatGPT, etc.

El modelo Seq2Seq es tan bueno que se volvió popular en el campo de la PNL hasta 2018. Pero tiene problemas importantes:

  • Cuando la secuencia de entrada es muy larga, es posible que el contexto generado por el codificador no capture suficiente información, lo que da como resultado una salida final insatisfactoria del decodificador. Específicamente, después de todo, sigue siendo un modelo RNN. Cuando el espaciado de palabras es demasiado largo, todavía habrá un problema de desaparición de gradiente. La causa raíz es el uso de "recursión". Cuando la recursión actúa sobre la misma matriz de peso, si la matriz cumple la condición, si su mayor valor propio es menor que 1, debe ocurrir el problema de desaparición del gradiente. El LSTM y GRU posteriores solo pueden aliviar el problema, pero no pueden resolverlo fundamentalmente.
  • El efecto del paralelismo es pobre: ​​el resultado de cada momento depende del momento anterior.

第 5 节 · 为什么说 RNN 模型没有体现「注意力」?

Encoder-Decoder 的一个非常严重的问题,是依赖中间那个 context 向量,则无法处理特别长的输入序列 —— 记忆力不足,会忘事儿。而忘事儿的根本原因,是没有「注意力」。

对于一般的 RNN 模型,Encoder-Decoder 结构并没有体现「注意力」—— 这句话怎么理解?当输入序列经过 Encoder 生成的中间结果(上下文 C),被喂给 Decoder 时,这些中间结果对所生成序列里的哪个词,都没有区别(没有特别关照谁)。这相当于在说:输入序列里的每个词,对于生成任何一个输出的词的影响,是一样的,而不是输出某个词时是聚焦特定的一些输入词。这就是模型没有注意力机制。

人脑的注意力模型,其实是资源分配模型。NLP 领域的注意力模型,是在 2014 年被提出的,后来逐渐成为 NLP 领域的一个广泛应用的机制。可以应用的场景,比如对于一个电商平台中很常见的白底图,其边缘的白色区域都是无用的,那么就不应该被关注(关注权重为 0)。比如机器翻译中,翻译词都是对局部输入重点关注的。

所以 Attention 机制,就是在 Decoder 时,不是所有输出都依赖相同的「上下文 C t C_t Ct」,而是时刻 t 的输出,使用 C t C_t Ct,而这个 C t C_t Ct 来自对每个输入数据项根据「注意力」进行的加权。

第 6 节 · 基于 Attention 机制的 Encoder-Decoder 模型

En 2015, Dzmitry Bahdanau y otros propusieron el mecanismo de "Atención" en el documento "Traducción automática neuronal mediante el aprendizaje conjunto para alinear y traducir" . Siga al capitán a continuación y el Capitán Mike se lo explicará claramente en términos simples.

Debajo de la imagen en ei e_imiyoRepresenta la salida de la capa oculta del codificador, di d_idyorepresenta la salida de la capa oculta del decodificador

Refinamiento adicional sobre C t C_tCtEn parte, el capitán aquí cita el gráfico del libro "Predicción de la secuencia espacio-temporal del estado del tráfico a corto plazo basada en el aprendizaje profundo":

h ~ i \widetilde{h}_i en este gráficoh yoy di d_i en la figura anteriordyoCorrespondiente, hola h_ihyoIgual que ei e_i en la foto anteriormiyocorresponder.

por tiempo ttLa salida que producirá t , cada celda oculta de la capa oculta está relacionada conC t C_tCtExiste una relación de pesos α t , i \alpha_{t,i}at , yoSea 1 ≤ yo ≤ norte 1\le i\le n1in , este valor de ponderación es el mismo que "la salida de la capa oculta después de que el elemento de entrada pasa por el codificadorei ( 1 ≤ i ≤ n ) e_i (1\le i\le n)miyo1in ) , la salida de la capa oculta del decodificador en el momento anteriordt − 1 d_{t-1}dt 1" Están relacionados a:

si , t = puntaje ( ei , dt − 1 ) α yo , t = exp ( si , t ) ∑ j = 1 nexp ( sj , t ) \begin{alineado} &s_{i,t} = puntaje(e_i,d_ {t-1}) \\ &\alpha_{i,t} = \frac{exp(s_{i,t})}{\textstyle\sum_{j=1}^n exp(s_{j,t} )} \end{alineado}syo , t=puntuación ( eyo,dt 1)ayo , t=j = 1ne x p ( sj , t)e x p ( syo , t)

Puntuación de puntuación de uso comúnLa función de puntuación es:

  • 点积(Producto escalar)模型:si , t = dt − 1 T ⋅ ei s_{i,t} = {d_{t-1}}^T \cdot e_isyo , t=dt 1Tmiyo
  • 缩放点积(Producto punto escalado)模型:si , t = dt − 1 T ⋅ edimensiones de dt − 1 o ei s_{i,t} = \frac{ { d_{t-1}}^T \cdot e_i }{\sqrt{\smash[b]{dimensiones\:de\:d_{t-1}\:o\:e_i}}}syo , t=dimensiones _ _ _ _ _ _ _ _de _dt 1omiyo dt 1Tmiyo, para evitar un resultado de producto escalar demasiado grande debido a una dimensión vectorial demasiado grande

Entonces el vector de contexto se expresa como:

C t = ∑ yo = 1 norte α yo , tei \begin{alineado} &C_t = \displaystyle\sum_{i=1}^n \alpha_{i,t} e_i \end{alineado}Ct=yo = 1nayo , tmiyo

¿Recuerdas la fórmula de representación del modelo Codificador-Decodificador mencionada por el capitán en la parte RNN?

et = Codificador LSTM / GRU ( xt , et − 1 ) C = f 1 ( en ) dt = f 2 ( dt − 1 , C ) yt = Codificador LSTM / GRU ( yt − 1 , dt − 1 , C ) \begin{alineado} e_t &= Codificador_{LSTM/GRU}(x_t, e_{t-1}) \\ C &= f_1(e_n) \\ d_t &= f_2(d_{t-1}, C) \ \ y_t &= Decodificador_{LSTM/GRU}(y_{t-1}, d_{t-1}, C) \end{alineado}mitCdtyt=codificador _ _ _ _ _L STM / GR U( Xt,mit 1)=F1( min)=F2( ret 1,c )=Decodificador _ _ _L STM / GR U( yt 1,dt 1,c )

El modelo de Codificador-Decodificador con el mecanismo de Atención es el siguiente.

et = codificador LSTM / GRU ( xt , et − 1 ) C t = f 1 ( mi 1 , mi 2 . . . en , dt − 1 ) dt = f 2 ( dt − 1 , C t ) yt = D codificador LSTM / GRU ( yt − 1 , dt − 1 , C t ) \begin{aligned} e_t &= Encoder_{LSTM/GRU}(x_t, e_{t-1}) \\ C_t &= f_1(e_1,e_2. ..e_n,d_{t-1}) \\ d_t &= f_2(d_{t-1}, C_t) \\ y_t &= Decodificador_{LSTM/GRU}(y_{t-1}, d_{t- 1}, C_t) \end{alineado}mitCtdtyt=codificador _ _ _ _ _L STM / GR U( Xt,mit 1)=F1( mi1,mi2... min,dt 1)=F2( ret 1,Ct)=Decodificador _ _ _L STM / GR U( yt 1,dt 1,Ct)

Este tipo de atención que considera codificador y decodificador al mismo tiempo se denomina "Atención codificador-descodificador" y, a menudo, se denomina "Atención vainilla". Se puede ver que la diferencia central anterior es la segunda fórmula C t C_tCt. Después de agregar Atención, se dan diferentes distribuciones de atención a todos los datos. En concreto, por ejemplo, utilizamos la siguiente función para definir este modelo:

mi = tanh ( W xe ⋅ x + bxe ) si , t = puntuación ( ei , dt − 1 ) α yo , t = esi , t ∑ j = 1 nesj , t C t = ∑ yo = 1 norte α yo , teidt = tanh ( W dd ⋅ dt − 1 + bdd + W yd ⋅ yt − 1 + byd + W cd ⋅ C t + bcd ) y = S oftmax ( W dy ⋅ d + bdy ) \begin{alineado} e &= tanh (W^{xe} \cdot x + b^{xe}) \\ s_{i,t} &= puntuación(e_i,d_{t-1}) \\ \alpha_{i,t} &= \frac {e^{s_{i,t}}}{\textstyle\sum_{j=1}^ne^{s_{j,t}}} \\ C_t &= \displaystyle\sum_{i=1}^n \alpha_{i,t} e_i \\ d_t &= tanh(W^{dd} \cdot d_{t-1} + b^{dd} + W^{yd} \cdot y_{t-1} + b ^{yd} + W^{cd} \cdot C_t + b^{cd}) \\ y &= Softmax(W^{dy} \cdot d + b^{dy}) \end{alineado}misyo , tayo , tCtdttu=Inglés ( W _x miX+bxe ) _=puntuación ( eyo,dt 1)=j = 1nmisj , tmisyo , t=yo = 1nayo , tmiyo=Inglés ( W _dddt 1+bdd+Wyd _yt 1+byd _+Wcd _Ct+bcd ) _=S o f t máx ( W _dy _d+bd y )

¿Puedes encontrar algún problema con el mecanismo de atención aquí? Este mecanismo de atención ignora la información de ubicación . Por ejemplo, los tigres aman a los conejos y los conejos aman a los tigres producirán la misma puntuación de atención.

Capítulo 2 Transformador nació en 2017

El Capitán Mike miró por primera vez a Transformer a través de una animación como ejemplo. Esta imagen proviene de la publicación del blog de Google "Transformer: una nueva arquitectura de red neuronal para la comprensión del lenguaje" :

Casi todos los blogs bien explicados y las respuestas que se encuentran en Internet en chino apuntan al mismo blog: " The Illustrated Transformer" de Jay Alammar , por lo que se recomienda a los lectores que lean este artículo juntos.

El modelo Transformer utiliza Self-Atention, Multiple-Head Attention, ResNet y Short-Cut. Primero, expliquemos claramente algunos conceptos básicos a través de las Secciones 1 a 4, y luego expliquemos el modelo general de Transformador en la Sección 5, que será mucho más fácil de entender. En la última sección 6 llegamos a una práctica práctica.

Sección 7 Mecanismo de Auto-Atención (Auto-Atención)

La autoatención es la clave para comprender Transformer El autor original limitó la extensión del artículo y no dio demasiadas explicaciones. Lo siguiente es mi propio entendimiento, y puedo entender algunos de los conceptos mágicos en Transformer de manera más transparente y en línea con el sentido común.

7.1 Una pieza de contenido de lenguaje natural en sí misma "implica" mucha información interna relacionada

En el modelo Codificador-Decodificador con atención, la atención a una palabra en la secuencia de salida Y proviene de la secuencia de entrada X, entonces, ¿qué sucede si X e Y son iguales? ¿Qué escenarios tendrá esta demanda? Debido a que pensamos que algunas palabras en un fragmento de texto están determinadas por otras palabras, puede entenderse aproximadamente como el principio de "cloze". Entonces tal fragmento de texto en realidad tiene la autoatención de cada palabra en él, por ejemplo:

Lao Wang es mi supervisor y me gusta mucho su accesibilidad.

Para el "él" en esta oración, si la autoatención se calcula en base a esta oración, es obvio que "Old Wang" debe recibir la mayor atención. Inspirándonos en esto, creemos que:

Una parte del lenguaje natural en realidad implica que para obtener información Q sobre cierto aspecto, puede obtener alguna información (V) como resultado al prestar atención a cierta información K.

qqQ es recuperación de consulta/consulta,KKK ,VVV es clave y valor respectivamente. Por lo tanto, es similar a nuestra búsqueda de "libros de PNL" en el sistema de recuperación de libros (esto esQQP ), obtuve un libro electrónico llamado "Procesamiento práctico del lenguaje natural", el título del libro es la clave y este libro electrónico es el valor. Solo para la comprensión del lenguaje natural, creemos que cualquier contenido implica muchoQQQ -KKK -VVasociación de V. En general, está inspirado en la consulta-clave-valor en el campo de la recuperación de información.

Basándonos en esta inspiración, expresamos la fórmula de la autoatención como:

Z = Atención propia ( X ) = Atención ( Q , K , V ) \begin{aligned} Z = Atención propia(X) = Atención(Q,K,V) \end{aligned}Z=A t e n c i ó n ( X ) _ _ _ _=A t e n c i ó n ( Q ,K ,V )

XXDespués de calcular X por autoatención, se obtiene ZZZ._ _ Luego tomamos este ZZcon información de autoatención.Z para operaciones posteriores. Debe enfatizarse aquí queZZCada elemento zi z_ien el vector Zzyoestán relacionados de alguna manera con todos los elementos de X, no solo con xi x_iXyorelacionado.

7.2 Cómo calcular Q, K, V

Q, K, V todos provienen de una transformación lineal de la entrada X:

Q = WQ ⋅ XK = WK ⋅ XV = WV ⋅ X \begin{alineado} Q &= W^Q \cdot X \\ K &= W^K \cdot X \\ V &= W^V \cdot X \ fin {alineado}qkV=WqX=WkX=WVX

WQ 、 WK 、 WVW^Q、W^K、W^VWQ ,WKWV comienza con una inicialización aleatoria y obtiene muy buen rendimiento después del entrenamiento. paraXXCada palabra vectorxi x_i en XXyo, después de esta transformación obtenemos:

qi = WQ ⋅ xiki = WK ⋅ xivi = WV ⋅ xi \begin{alineado} q_i &= W^Q \cdot x_i \\ k_i &= W^K \cdot x_i \\ v_i &= W^V \cdot x_i \ fin {alineado}qyokyovyo=WqXyo=WkXyo=WVXyo

7.3 Función de atención: cómo pasar de Z a Q y V

Basándonos en la inspiración anterior, creemos que XXDespués de la minería de autoatención, X obtiene:

  • Información implícita 1: la asociación entre un conjunto de consultas y un conjunto de claves, denotado como qk (piense en el sistema de recuperación de información que utiliza consultas para reclutar claves primero)
  • Información implícita 2: un conjunto de valores
  • Información implícita 3: alguna asociación entre qk y valor

¿Cómo expresar estos tres conjuntos de información respectivamente? Se necesita algo de inspiración aquí, porque la ciencia de la computación en realidad está "simulando y restaurando" el mundo real. La dirección de investigación actual en el campo de la IA es simular y restaurar el pensamiento del cerebro humano. Por lo tanto, este tipo de "restauración de la simulación" es para encontrar un cierto método de aproximación, por lo que no puede entenderse de acuerdo con el razonamiento lógico de las matemáticas y la física, sino que debe entenderse de acuerdo con la "ingeniería" o la "ciencia de la computación". Métodos computacionales" , por lo que a menudo se necesitan algunas heurísticas para encontrar algún tipo de "representación".

El autor de Transformer aquí piensa que QQQ yKKLa correlación entre los dos vectores de K es que estamos usando QQQ encontrarlo enKKproyección sobre K , si QQQ ,KKK es un vector de unidad de longitud, por lo que esta proyección en realidad puede entenderse como encontrar "QQQ yKKSimilitud entre K vectores":

  • Si QQQ yKKK es vertical, entonces los dos vectores son ortogonales y su producto escalar (Producto escalar) es 0;
  • Si QQQ yKKK es paralelo, entonces el producto escalar de dos vectores es el producto de módulo de ambos∥ Q ∥ ∥ K ∥ \|Q\|\|K\|Q ∥∥ K
  • Si QQQ yKKK está en cierto ángulo, entonces el producto escalar esQQQ enKKEl módulo de la proyección en K.

Por lo tanto, "información implícita 1" puede usar " Q ⋅ KQ\cdot KqK ” entonces se representa mediante la normalización Softmax. Esta representación es una matriz en la que todos los elementos son 0~1, lo que puede entenderse como la "puntuación de atención" en el mecanismo de atención correspondiente, es decir, una "matriz de puntuación de atención (Attention Score Matrix)".

Y "información implícita 2" es ingresar XXLas características después de la transformación lineal de X se consideran XXOtra representación de X. Luego usamos esta "matriz de puntaje de atención" para bendecira VVV , este proceso de producto escalar representa "información implícita 3". Así que tenemos la siguiente fórmula:

Z = Atención ( Q , K , V ) = S oftmax ( Q ⋅ KT ) ⋅ V \begin{aligned} Z = Atención(Q,K,V) = Softmax(Q \cdot K^T) \cdot V \ fin {alineado}Z=A t e n c i ó n ( Q ,K ,V )=S o f t máx ( Q _kt )V

De hecho, aquí, esta función de atención ya está disponible. A veces, para evitar Q ⋅ KTQ \cdot K^T porque la dimensión del vector es demasiado grandeqkEl resultado del producto punto T es demasiado grande, demos otro paso:

Z = Atención ( Q , K , V ) = S oftmax ( Q ⋅ KT dk ) ⋅ V \begin{aligned} Z = Atención(Q,K,V) = Softmax(\frac{Q \cdot K^T} {\sqrt{\smash[b]{d_k}}}) \cdot V \end{alineado}Z=A t e n c i ó n ( Q ,K ,V )=S o f t máx ( _dk qkT)V

aquí dk d_kdkes el vector ki k_i en la matriz Kkyodimensión. Hay una explicación adicional para este paso de corrección, es decir, si hay un problema con la estabilidad del modelo después de la normalización de Softmax. ¿Cómo entiendes? Si se supone que QQQ yKKCada dato dimensional de cada vector en K tiene media cero y varianza unitaria, por lo que los datos de entrada son estables, entonces, ¿cómo hacer que la "información implícita 1" siga siendo estable después del cálculo? Es decir, el resultado de la operación aún mantiene la media cero y la varianza unitaria, es decir, dividiendo por "dk \sqrt{\smash[b]{d_k}}dk 」。

7.4 Otras funciones de atención

Para recordarles a todos que la representación de este tipo de información implícita es solo una elección en el método de cálculo, si es buena o mala depende de la evaluación del resultado, por lo que, incluidas las anteriores, las funciones de atención comunes son (incluso usted puede definirlas). tú mismo):

Z = Atención ( Q , K , V ) = { = S oftmax ( QTK ) V = S oftmax ( QKT dk ) V = S oftmax ( ω T tanh ( W [ q ; k ] ) ) V = S oftmax ( QTWK ) V = coseno [ QTK ] VZ = Atención(Q,K,V) = \begin{casos} \begin{aligned} &= Softmax(Q^TK) V \\ &= Softmax(\frac{QK^T} {\sqrt{\smash[b]{d_k}}}) V \\ &= Softmax(\omega^T tanh(W[q;k])) V \\ &= Softmax(Q^TWK) V \\ &= coseno[Q^TK] V \end{alineado} \end{casos}Z=A t e n c i ó n ( Q ,K ,V )= =S o f t máx ( Q _T K)V=S o f t máx ( _dk Q KT) V=S o f t max ( o _T tanh(W[q;k ])) V=S o f t máx ( Q _T WK)V=porque en e [ QT K]V

En este punto, partimos de la entrada original XXX obtiene unZZZ , puedes usarZZZ 's.

Sección 8. Atención multicabezal

Aquí entendemos la "autoatención", y el artículo de Transformer mejora aún más la capa de atención al agregar un mecanismo de atención de "múltiples cabezas". Primero veamos qué es y luego veamos sus ventajas. A partir de esta sección, una gran cantidad de ilustraciones en este artículo son citadas de "The Illustrated Transformer" . El autor Jay Alammar escribió un artículo gráfico muy profundo, que ha sido citado mucho y es excelente. Te recomiendo que lo leas. de nuevo

El transformador utiliza 8 cabezales, es decir, 8 grupos de QQ diferentesQ -KKK -VVV :

Q 0 = W 0 Q ⋅ X ; K 0 = W 0 K ⋅ X ; V 0 = W 0 V ⋅ XQ 1 = W 1 Q ⋅ X ; K 1 = W 0 K ⋅ X ; V 1 = W 1 V ⋅ X . . . . Q 7 = W 7 Q ⋅ X ; K 7 = W 0 K ⋅ X ; V 7 = W 7 V ⋅ X \begin{alineado} Q_0 = W_0^Q \cdot X ;\enspace K_0 = &W_0^K \cdot X ;\enspace V_0 = W_0^V \cdot X \\ Q_1 = W_1^Q \cdot X ;\enspace K_1 = &W_0^K \cdot X ;\enspace V_1 = W_1^V \cdot X \\ &.... \\ Q_7 = W_7^Q \cdot X ;\enspace K_7 = &W_0^K \cdot X ;\enspace V_7 = W_7^V \cdot X \end{alineado}q0=W0qx ;k0=q1=W1qx ;k1=q7=W7qx ;k7=W0kx ;V0=W0VXW0kx ;V1=W1VX....W0kx ;V7=W7VX

De esta manera obtenemos 8 ZZZ :

Z 0 = Atención ( Q 0 , K 0 , V 0 ) = S oftmax ( Q 0 ⋅ K 0 T dk ) ⋅ V 0 Z 1 = Atención ( Q 1 , K 1 , V 1 ) = S oftmax ( Q 1 ⋅ K 1 T rek ) ⋅ V 1 . . . Z 7 = Atención ( Q 7 , K 7 , V 7 ) = S oftmax ( Q 7 ⋅ K 7 T dk ) ⋅ V 7 \begin{aligned} &Z_0 = Atención(Q_0,K_0,V_0) = Softmax(\frac {Q_0 \cdot K_0^T}{\sqrt{\smash[b]{d_k}}}) \cdot V_0 \\ &Z_1 = Atención(Q_1,K_1,V_1) = Softmax(\frac{Q_1 \cdot K_1^T }{\sqrt{\smash[b]{d_k}}}) \cdot V_1 \\ &... \\ &Z_7 = Atención(Q_7,K_7,V_7) = Softmax(\frac{Q_7 \cdot K_7^T} {\sqrt{\smash[b]{d_k}}}) \cdot V_7 \\ \end{alineado}Z0=A t e n c i ó n ( Q0,k0,V0)=S o f t máx ( _dk q0k0T)V0Z1=A t e n c i ó n ( Q1,k1,V1)=S o f t máx ( _dk q1k1T)V1...Z7=A t e n c i ó n ( Q7,k7,V7)=S o f t máx ( _dk q7k7T)V7

Luego ponemos Z 0 Z_0Z0 Z 7 Z_7 Z7 沿着行数不变的方向全部连接起来,如下图所示:

我们再训练一个权重矩阵 W O W^O WO,然后用上面拼接的 Z 0 − 7 Z_{0-7} Z07 乘以这个权重矩阵:

于是我们会得到一个 Z 矩阵:

到这里就是多头注意力机制的全部内容,与单头注意力相比,都是为了得到一个 Z Z Z 矩阵,但是多头用了多组 Q Q Q- K K K- V V V,然后经过拼接、乘以权重矩阵得到最后的 Z Z Z。我们总览一下整个过程:

通过多头注意力,每个头都会关注到不同的信息,可以如下类似表示:

这通过两种方式提高了注意力层的性能:

  • 多头注意力机制,扩展了模型关注不同位置的能力。 Z Z Z 矩阵中的每个向量 z i z_i zi 包含了与 X X X 中所有向量 x i x_i xi 有关的一点编码信息。反过来说,不要认为 z i z_i zisolo dale xi x_iXyorelacionado.
  • El mecanismo de atención de múltiples cabezas proporciona múltiples "subespacios de representación QQ" para la capa de atención.Q -KKK -VVV yZZZ._ _ Tal matriz de entradaXXX , se expresará como 8 matrices diferentesZZZ , todos contienen algún tipo de interpretación de la información de datos sin procesar implícita en él.

Sección 9 Fenómenos de Degeneración, Red Residual y Atajo

9.1 Fenómeno de degeneración

Para una red neuronal de 56 capas, naturalmente sentimos que debería ser mejor que una red neuronal de 20 capas, por ejemplo, desde la perspectiva de la cuantificación de la tasa de error (error). Sin embargo, el artículo "Deep Residual Learning for Image Recognition" del erudito chino He Kaiming y otros nos mostró el resultado opuesto, y la razón de este problema no se debe a la explosión de gradiente/desaparición de gradiente causada por la gran cantidad de capas (después de todos, se ha utilizado la regresión).La uniformidad resuelve este problema), pero debido a un fenómeno anómalo, que llamamos "degeneración". He Kaiming y otros creen que esto se debe a la existencia de "una capa de red que es difícil de optimizar".

9.2 Mapeo de identidad

Si los 36 pisos son un flaco favor, es mejor no tener ninguno, ¿no? Entonces, si las 36 capas de red adicionales no tienen ningún efecto en la mejora del rendimiento (como la tasa de error), e incluso más, los datos de entrada antes de estas 36 capas son exactamente los mismos que los datos de salida después de estas 36 capas, entonces si se resumen estas 36 capas en una función f 36 f_{36}F36, que es una función del mapeo de identidad:

f 36 ( x ) = x f_{36}(x) = xF36( X )=X

Volvamos a la aplicación práctica. Si no sabemos si N capas consecutivas en una red neuronal mejorarán o reducirán el rendimiento, entonces podemos establecer una conexión que omita estas capas para lograr:

Si estas N capas pueden mejorar el rendimiento, utilice estas N capas; de lo contrario, omítalas.

Esto es como darle a la red neuronal de capa N un espacio para prueba y error, y decidiremos si los usamos después de confirmar su rendimiento. Al mismo tiempo, también se puede entender que estas capas se pueden optimizar por separado y, si se mejora el rendimiento, no se omitirán.

9.3 Red Residual y Atajo

Si las primeras 20 capas ya pueden alcanzar una tasa de precisión del 99 %, ¿puede la introducción de estas 36 capas mejorar aún más la tasa de precisión del "1 % restante del residual" para alcanzar el 100 %? Entonces, esta red de 36 capas se llama "Red residual (Red residual, a menudo denominada ResNet)", que es muy vívida.

Y el atajo que puede omitir la red residual de N-capa a menudo se llama Short-Cut, y también se llama Skip Conntection, que resuelve el "fenómeno de degeneración" mencionado anteriormente en el aprendizaje profundo.

Sección 10 Codificación posicional del transformador (Positional Embedding)

Recuerda lo que mencioné al final de la segunda parte:

Este mecanismo de atención ignora la información de ubicación. Por ejemplo, los tigres aman a los conejos y los conejos aman a los tigres producirán la misma puntuación de atención.

10.1 Codificación posicional sinusoidal en el papel del transformador

Ahora resolvamos este problema, para cada vector de entrada xi x_iXyoGenerar un vector de codificación de posición ti t_ityo, la dimensión de este vector de codificación de posición es la misma que la del vector de entrada (la representación vectorial incrustada de la palabra):

El documento de Transformer proporciona la siguiente fórmula para calcular el valor de cada bit del vector de codificación de posición:

P pos , 2 i = sin ( pos 1000 0 2 idmodel ) P pos , 2 i + 1 = cos ( pos 1000 0 2 idmodel ) \begin{aligned} P_{pos,2i} &= sin(\frac{pos} {10000^{\frac{2i}{d_{modelo}}}}) \\ P_{pos,2i+1} &= cos(\frac{pos}{10000^{\frac{2i}{d_{modelo }}}}) \end{alineado}PAGpos , 2 yo _PAGpos , 2 i + 1 _=está en (1000 0dm o d e l2 yopos _)=porque (1000 0dm o d e l2 yopos _)

De esta forma, para una incrustación, si su posición en el contenido de entrada es pos, entonces su vector de codificación se expresa como:

[ P pos , 0 , P pos , 1 , . . . , P pos , dx − 1 ] \begin{alineado} [P_{pos,0}, P_{pos,1}, ... , P_{pos,d_x-1}] \end{alineado}[ PAGpos , 0 _,PAGpos , 1 _,... ,PAGpos , d _x 1]

Si se amplía, la codificación posicional se divide en realidad en codificación posicional absoluta (codificación posicional absoluta) y codificación posicional relativa (codificación posicional relativa). El primero es generar específicamente códigos de posición y encontrar una manera de integrarlos en la entrada Lo que vimos arriba es uno. El último es para afinar la estructura de Atención para que pueda distinguir datos en diferentes ubicaciones. Además, en realidad existen algunos métodos de codificación de posición que no se pueden clasificar en estos dos tipos.

10.2 Codificación de posición absoluta

La codificación de posición absoluta, como se mencionó anteriormente, es definir un vector de codificación de posición ti t_ityo, passxi + ti x_i + t_iXyo+tyoSe obtiene un vector que contiene información de posición.

  • Codificación posicional aprendida: la codificación posicional se utiliza como parámetro de entrenamiento para generar una matriz de codificación posicional de "longitud máxima x dimensión de codificación", que se actualiza con el entrenamiento. En la actualidad, los modelos Google BERT y OpenAI GPT utilizan este tipo de codificación de posición. La desventaja es que la "extrapolación" es deficiente y si la longitud del texto supera la "longitud máxima" utilizada en el entrenamiento anterior, no se puede procesar. Actualmente hay algunos artículos que dan soluciones de optimización, como " Codificación de posición de descomposición jerárquica ".
  • Codificación posicional triangular (Codificación posicional sinusoidal): mencionado anteriormente.
  • Codificación posicional recurrente (Codificación posicional recurrente): A través de un RNN y luego un Transformador, el "orden" implícito en el RNN ya no requerirá codificación adicional. Pero esto sacrifica el paralelismo, que es una de las dos principales deficiencias de las RNN.
  • Codificación posicional del producto: use " xi ⊙ ti x_i \odot t_iXyotyo」代替「xi + ti x_i + t_iXyo+tyo」。

10.3 Codificación posicional relativa y otras codificaciones posicionales

La codificación de posición relativa provino por primera vez del documento de Google "Autoatención con representaciones de posición relativa" , que considera la posición relativa entre la posición actual y la posición de la atención.

  • Codificación de posición relativa común: clásica, XLNET, T5, DeBERTa, etc.
  • Otras codificaciones posicionales: estilo CNN, estilo plural, estilo fusión, etc.

Hasta ahora todos estamos hablando de Encoder, actualmente sabemos que un Encoder se puede representar mediante el siguiente diagrama esquemático:

Sección 11 Codificador y decodificador del transformador

11.1, la estructura gráfica de codificador y decodificador

  • La primera capa es la Capa de Atención de Cabezas Múltiples.
  • La segunda capa es a través de una red neuronal de avance (Feed Forward Neural Network, denominada FFNN).
  • Estas dos capas, cada capa tiene "Agregar y Normalización" y ResNet.

  • El decodificador tiene dos capas de atención de varios cabezales. La primera capa de atención de múltiples cabezas es la capa de atención de múltiples cabezas enmascarada, es decir, solo el contenido de la posición anterior se incluye en el proceso de cálculo de autoatención. La segunda capa de atención de múltiples cabezas está enmascarada y es una capa de atención de múltiples cabezas normal.
  • Se puede observar que la primera capa de atención es una capa de auto atención (Self Attention Layer), y la segunda es una capa de Atención Codificador-Decodificador (su KKK ,VVV viene de Codificador,QQQ proviene de la capa de autoatención), y algunos artículos usarán este ángulo para referirse a ella.
  • FNN, Add & Norm y ResNet son similares a Encoder.

11.2 El primer resultado de salida de Decoder

El proceso de producción de la primera salida final:

  • No es necesario pasar por la capa de atención de varios cabezales enmascarados (capa de autoatención).
  • Solo a través de la Capa de Atención del Codificador-Decodificador.

De esta manera, obtenemos la primera salida al igual que el modelo anterior de atención de codificador-descodificador. Pero la salida final pasará por una capa de "Linear + Softmax".

11.3 Todas las salidas posteriores del decodificador

Comience produciendo la segunda salida:

  • La capa de autoatención del decodificador utilizará los resultados de salida anteriores.
  • Como puede ver, este es un proceso en serie.

11.4, lineal y Softmax después del decodificador

Después de todos los Decodificadores, obtuvimos muchos resultados de punto flotante. El último Linear & Softmax es para resolver el problema de "cómo convertirlo en texto".

  • Linear es una red neuronal completamente conectada que proyecta la salida de los decodificadores en un vector muy grande, que llamamos vector logits.
  • Si nuestro vocabulario de salida tiene 10 000 palabras, entonces cada dimensión del vector logits tiene 10 000 unidades, y cada unidad corresponde a la probabilidad de una palabra en el vocabulario de salida.
  • Softmax normaliza cada dimensión en el vector logits, de modo que cada dimensión puede seleccionar la probabilidad de palabra más grande de 10,000 unidades, y la palabra correspondiente en el vocabulario es la palabra de salida. Terminas con una cadena de salida.

Sección 12 Modelo de transformador en su conjunto

Finalmente, echemos un vistazo a Transformer como un todo:

  • Primero, ingrese los datos para generar la representación vectorial incrustada (Incrustación) de la palabra y genere la codificación posicional (Codificación posicional, denominada PE).
  • Vaya a la sección Codificadores. Primero ingrese a la capa de atención de múltiples cabezas (Atención de múltiples cabezas), que es el procesamiento de autoatención, y luego ingrese a la capa totalmente conectada (también conocida como la capa de red neuronal de avance), cada capa tiene ResNet, Add & Norm.
  • La entrada de cada codificador proviene de la salida del codificador anterior, pero la entrada del primer codificador es incrustación + PE.
  • Vaya a la sección Decodificadores. Primero ingrese a la primera capa de atención de múltiples cabezas (capa de autoatención enmascarada) y luego ingrese a la segunda capa de atención de múltiples cabezas (capa de atención de codificador-decodificador), cada capa tiene ResNet, Add & Norm.
  • Cada decodificador tiene dos partes de entrada.
  • La entrada de la primera capa del Decodificador (capa de autoatención de cabezales múltiples de Maksed) proviene de la salida del Decodificador anterior, pero el primer Decodificador no pasa a través de la primera capa (porque también es 0 después del cálculo).
  • La entrada de la segunda capa del Decodificador (capa de atención Codificador-Decodificador), Q proviene de la primera capa del Decodificador, y la K y V de esta capa de cada Decodificador son las mismas, todas del último Codificador.
  • Finalmente, se normaliza por Linear y Softmax.

Sección 13. Rendimiento del transformador

Google publicó los siguientes datos de prueba en su blog el 31 de agosto de 2017:

Capítulo 3 Una implementación de transformador basada en la arquitectura TensorFlow

Echemos un vistazo a un modelo de Transformador simple, que es el modelo de Transformador implementado por Kyubyong que apareció anteriormente: GitHub - Transformador de Kyubyong - legado tf1.2

Sección 14. Primer tren y prueba del Transformador Kyubyong

Descargue un conjunto de datos de traducciones alemán-inglés: https://drive.google.com/uc?id=1l5y6Giag9aRPwGtuZHswh3w5v3qEz8D8

de-enDescomprima lo siguiente tgzy colóquelo en corpora/el directorio . Si necesita modificar los hiperparámetros primero, debe modificarlos hyperparams.py. Luego ejecute el siguiente comando para generar archivos de vocabulario (vocabulary files), que se almacenan en preprocessedel directorio :

mikecaptain@local $ python prepro.py

Entonces empieza a entrenar:

mikecaptain@local $ python train.py

También puede omitir la capacitación y descargar directamente el archivo preentrenado , que es un logdir/directorio , y colocarlo en el directorio raíz del proyecto. Luego puede ejecutar el programa de evaluación sobre los resultados del entrenamiento:

mikecaptain@local $ python eval.py

El archivo de resultados de la prueba "alemán-inglés" se generará en results/el directorio , el contenido es el siguiente:

- source: Sie war eine jährige Frau namens Alex
- expected: She was a yearold woman named Alex
- got: She was a <UNK> of vote called <UNK>

- source: Und als ich das hörte war ich erleichtert
- expected: Now when I heard this I was so relieved
- got: And when I was I <UNK> 's

- source: Meine Kommilitonin bekam nämlich einen Brandstifter als ersten Patienten
- expected: My classmate got an arsonist for her first client
- got: Because my first eye was a first show

- source: Das kriege ich hin dachte ich mir
- expected: This I thought I could handle
- got: I would give it to me a day

- source: Aber ich habe es nicht hingekriegt
- expected: But I didn't handle it
- got: But I didn't <UNK> <UNK>

- source: Ich hielt dagegen
- expected: I pushed back
- got: I <UNK>

...

Bleu Score = 6.598452846670836

La última línea del archivo de resultados de la evaluación es Bleu Score:

  • Esta es una métrica utilizada para evaluar la calidad de las traducciones automáticas. Se compone de varios puntajes BLEU diferentes, cada uno de los cuales indica el grado de superposición entre el resultado de la traducción y la traducción de referencia.
  • Una puntuación BLEU de uso común es BLEU-4, que calcula el grado de superposición en el resultado de la traducción con el modelo de lenguaje N-gram de la traducción de referencia n-gram (n es 4). Una puntuación más alta indica que la traducción se acerca más a la traducción de referencia.

Sección 15 Análisis del código fuente del Transformador Kyubyong

  • hparams.py: Los hiperparámetros están todos aquí, solo 30 líneas. Se explicará en la siguiente 2.1sección .
  • data_load.py: funciones relacionadas para cargar y procesar datos por lotes, el código tiene solo 92 líneas. Explicado principalmente en la siguiente 2.2parte .
  • prepro.py: Cree un archivo de vocabulario (archivo de vocabulario) para el origen y el destino, el código tiene solo 39 líneas. Las siguientes 2.3secciones te lo explicarán.
  • train.py: El código tiene solo 184 líneas. Lea en 2.4la sección .
  • modules.py: El componente básico de la red de codificación/descodificación, el código tiene solo 329 líneas. modules.pyJunto con se 2.4explicará en la sección.
  • eval.py: Evalúe el efecto, el código tiene solo 82 líneas. se leerá en 2.5la sección

Un total de más de 700 líneas de código.

15.1 Hiperparámetros

hyperparams.pyLa clase Hyperparamsde hiperparámetro y los parámetros que contiene se explican uno por uno:

  • source_train: el archivo de entrada de origen del conjunto de datos de entrenamiento, el valor predeterminado es'corpora/train.tags.de-en.de'
  • target_train: El archivo de salida de destino del conjunto de datos de entrenamiento, el valor predeterminado es'corpora/train.tags.de-en.en'
  • source_test: El archivo de entrada de origen del conjunto de datos de prueba, el valor predeterminado es'corpora/IWSLT16.TED.tst2014.de-en.de.xml'
  • target_test: El archivo de salida de destino del conjunto de datos de prueba, el valor predeterminado es'corpora/IWSLT16.TED.tst2014.de-en.en.xml'
  • batch_size: establezca el tamaño de cada lote de datos.
  • lr: establezca la tasa de aprendizaje de la tasa de aprendizaje.
  • logdir: establece el directorio donde se guardan los archivos de registro.
  • maxlen
  • min_cnt
  • hidden_units: establece el número de unidades de capa oculta en el codificador y decodificador.
  • num_blocks: el número de codificadores (bloque codificador), decodificador (bloque decodificador)
  • num_epochs: El número de iteraciones durante el entrenamiento.
  • num_heads: Recuerde que el Transformador que mencionamos en el artículo anterior usaba atención multicabezal, aquí está el número de cabezas de atención multicabezal.
  • droupout_rate: establezca la tasa de abandono de la capa de abandono; consulte la sección 2.4.1 para conocer el abandono específico.
  • sinusoid: TrueCuando significa usar la función seno para calcular el código de posición; de lo contrario, Falsesignifica usar directamente positioncomo código de posición.

15.2 Preprocesamiento

El archivo prepro.pyimplementa el proceso de preprocesamiento y crea y dos vocabularios según hp.source_trainy hp.target_trainrespectivamente ."de.vocab.tsv""en.vocab.tsv"

def make_vocab(fpath, fname):

    # 使用 codecs.open 函数读取指定文件路径(fpath)的文本内容,并将其存储在 text 变量中
    text = codecs.open(fpath, 'r', 'utf-8').read()

    # 将 text 中的非字母和空格的字符去掉
    text = regex.sub("[^\s\p{Latin}']", "", text)

    # 将 text 中的文本按照空格分割,并将每个单词存储在 words 变量中
    words = text.split()

    # words 中每个单词的词频
    word2cnt = Counter(words)

    # 检查是否存在 preprocessed 文件夹,如果不存在就创建
    if not os.path.exists('preprocessed'): os.mkdir('preprocessed')
    with codecs.open('preprocessed/{}'.format(fname), 'w', 'utf-8') as fout:

    	# 按出现次数从多到少的顺序写入每个单词和它的出现次数
    	# 在文件最前面写入四个特殊字符 <PAD>, <UNK>, <S>, </S> 分别用于填充,未知单词,句子开始和句子结束
        fout.write("{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n{}\t1000000000\n".format("<PAD>", "<UNK>", "<S>", "</S>"))
        for word, cnt in word2cnt.most_common(len(word2cnt)):
            fout.write(u"{}\t{}\n".format(word, cnt))

if __name__ == '__main__':
    make_vocab(hp.source_train, "de.vocab.tsv")
    make_vocab(hp.target_train, "en.vocab.tsv")
    print("Done")
  • Llame a la función make_vocab en la función principal preprocessedpara generar dosde.vocab.tsv archivos de vocabulario en el directorio.en.vocab.tsv
  • make_vocabEn la función , primero use codecs.openla función para leer el contenido de texto fpathde y almacenarlo en textla variable, luego use la expresión regular regexpara texteliminar los caracteres que no sean letras y espacios en , y textluego divida el texto en espacios, y Almacenar cada palabra en wordsuna variable .
  • Luego, use Counterla función para contar wordslas ocurrencias de cada palabra y almacene el conteo en word2cntla variable .
  • Finalmente, todas las palabras y frecuencias de palabras se almacenan en de.vocab.tsvy en.vocab.tsven dos archivos.

15.3 Carga de conjuntos de datos de entrenamiento/prueba

Echemos un vistazo a los siguientes archivos train.py, data_load.pyy eval.pytres:

  • train.py: este archivo contiene la definición de Graphla clase y llama a la funciónload_data.py en el archivo en su constructor para cargar los datos de entrenamiento.get_batch_data
  • data_load.py: Define las funciones para cargar datos de entrenamiento y cargar datos de prueba.
  • eval.py: La función de evaluación del resultado de la prueba se define en este archivo.

El flujo de llamadas a funciones es el siguiente:

def load_de_vocab():
    vocab = [line.split()[0] for line in codecs.open('preprocessed/de.vocab.tsv', 'r', 'utf-8').read().splitlines() if int(line.split()[1])>=hp.min_cnt]
    word2idx = {
    
    word: idx for idx, word in enumerate(vocab)}
    idx2word = {
    
    idx: word for idx, word in enumerate(vocab)}
    return word2idx, idx2word

def load_en_vocab():
    vocab = [line.split()[0] for line in codecs.open('preprocessed/en.vocab.tsv', 'r', 'utf-8').read().splitlines() if int(line.split()[1])>=hp.min_cnt]
    word2idx = {
    
    word: idx for idx, word in enumerate(vocab)}
    idx2word = {
    
    idx: word for idx, word in enumerate(vocab)}
    return word2idx, idx2word

Cargue el vocabulario alemán e inglés y la frecuencia de las palabras almacenadas en preprocessed/de.vocab.tsvy en y . El primero es consultar vectores de palabras a través de palabras, y el segundo es consultar palabras a través de vectores de palabras.preprocessed/en.vocab.tsvword2idxidx2word

load_de_vocabLas load_en_vocabfunciones create_dataestán referenciadas por la función, que convierte las oraciones del idioma de origen y de destino de entrada en representaciones indexadas, y trunca o rellena las oraciones que son demasiado largas. Consulte los comentarios en el código a continuación para obtener una explicación detallada.

# 输入参数是翻译模型的源语言语句、目标语言语句
def create_data(source_sents, target_sents):

    de2idx, idx2de = load_de_vocab()
    en2idx, idx2en = load_en_vocab()
    
    # 用 zip 函数将源语言和目标语言句子对应起来,并对句子进行截断或填充
    x_list, y_list, Sources, Targets = [], [], [], []
    for source_sent, target_sent in zip(source_sents, target_sents):
        x = [de2idx.get(word, 1) for word in (source_sent + u" </S>").split()] # 1: OOV, </S>: End of Text
        y = [en2idx.get(word, 1) for word in (target_sent + u" </S>").split()] 

        # 将句子的词的编号,原句以及编号后的句子存储下来,以供之后使用
        if max(len(x), len(y)) <=hp.maxlen:

        	# 将 x 和 y 转换成 numpy 数组并加入 x_list 和 y_list 中
            x_list.append(np.array(x))
            y_list.append(np.array(y))

            # 将原始的 source_sent 和 target_sent 加入 Sources 和 Targets 列表中
            Sources.append(source_sent)
            Targets.append(target_sent)
    
    # 对于每个 (x, y) 对,使用 np.lib.pad 函数将 x 和 y 分别用 0 进行填充,直到长度为 hp.maxlen
    # 这样做的目的是使得每个句子长度都相等,方便后续的训练
    X = np.zeros([len(x_list), hp.maxlen], np.int32)
    Y = np.zeros([len(y_list), hp.maxlen], np.int32)
    for i, (x, y) in enumerate(zip(x_list, y_list)):
        X[i] = np.lib.pad(x, [0, hp.maxlen-len(x)], 'constant', constant_values=(0, 0))
        Y[i] = np.lib.pad(y, [0, hp.maxlen-len(y)], 'constant', constant_values=(0, 0))

    # 返回转换后的索引表示,以及未经处理的源语言和目标语言句子
    # X 是原始句子中德语的索引
    # Y 是原始句子中英语的索引
    # Sources 是源原始句子列表,并与 X 一一对应
    # Targets 是目标原始句子列表,并与 Y 一一对应
    return X, Y, Sources, Targets

# 返回原始句子中德语、英语的索引
def load_train_data():
    de_sents = [regex.sub("[^\s\p{Latin}']", "", line) for line in codecs.open(hp.source_train, 'r', 'utf-8').read().split("\n") if line and line[0] != "<"]
    en_sents = [regex.sub("[^\s\p{Latin}']", "", line) for line in codecs.open(hp.target_train, 'r', 'utf-8').read().split("\n") if line and line[0] != "<"]
    
    X, Y, Sources, Targets = create_data(de_sents, en_sents)
    return X, Y

Lo get_batch_datasiguiente lee y genera lotes a partir de datos de texto:

def get_batch_data():
    
    # 加载数据
    X, Y = load_train_data()
    
    # calc total batch count
    num_batch = len(X) // hp.batch_size
    
    # 将 X 和 Y 转换成张量
    X = tf.convert_to_tensor(X, tf.int32)
    Y = tf.convert_to_tensor(Y, tf.int32)
    
    # 创建输入队列
    input_queues = tf.train.slice_input_producer([X, Y])
            
    # 创建 batch 队列,利用 shuffle_batch 将一组 tensor 随机打乱,并将它们分为多个 batch
    # 使用 shuffle_batch 是为了防止模型过拟合
    x, y = tf.train.shuffle_batch(input_queues,
                                num_threads=8,
                                batch_size=hp.batch_size, 
                                capacity=hp.batch_size*64,   
                                min_after_dequeue=hp.batch_size*32, 
                                allow_smaller_final_batch=False)
    
    return x, y, num_batch # (N, T), (N, T), ()

15.4 Construir modelo y entrenar

El proceso constructor de Graph es el proceso de construcción del modelo, el capitán analizará esta parte del código a continuación.

El proceso general involucra principalmente train.pydocumentos y modules.pydocumentos. Las definiciones de funciones principales requeridas por todos los modelos se implementan modules.pyen . Primero veamos el proceso del codificador:

El siguiente es el proceso de Transformador implementado train.pyen , el capitán te explicará cada pieza de código en detalle, así que no te preocupes. En este proceso, el codificador se define primero, la capa de incrustación se usa para convertir los datos de entrada en vectores de palabras, la capa de codificación posicional se usa para codificar la posición de los vectores de palabras, la capa de eliminación se usa para la operación de eliminación y luego Se ejecutan la atención multicabezal multicapa y la alimentación directa.

Antes de construir el modelo, primero train.pyejecute el segmento principal del programa. Primero if __name__ == '__main__', este código es una forma común de escribir en Python. Significa que cuando un archivo se ejecuta directamente, ifse ejecutará el código debajo de la declaración. Vea los comentarios para el código a continuación.

if __name__ == '__main__':                
    
    # 加载词汇表   
    de2idx, idx2de = load_de_vocab()
    en2idx, idx2en = load_en_vocab()
    
    # 构建模型并训练
    g = Graph("train"); print("Graph loaded")
    
    # 创建了一个 Supervisor 对象来管理训练过程
    sv = tf.train.Supervisor(graph=g.graph, 
                             logdir=hp.logdir,
                             save_model_secs=0)

    # 使用 with 语句打开一个会话
    with sv.managed_session() as sess:

    	# 训练迭代 hp.num_epochs 次
        for epoch in range(1, hp.num_epochs+1): 
            if sv.should_stop(): break

            # tqdm 是一个 Python 库,用来在循环执行训练操作时在命令行中显示进度条
            for step in tqdm(range(g.num_batch), total=g.num_batch, ncols=70, leave=False, unit='b'):

            	# 每次迭代都会运行训练操作 g.train_op
                sess.run(g.train_op)

            # 获取训练的步数,通过 sess.run() 函数获取 global_step 的当前值并赋值给 gs。这样可在后面使用 gs 保存模型时用这个值命名模型
            gs = sess.run(g.global_step)

            # 每个 epoch 结束时,它使用 saver.save() 函数保存当前模型的状态
            sv.saver.save(sess, hp.logdir + '/model_epoch_%02d_gs_%d' % (epoch, gs))
    
    print("Done")
  • num_epochses el número de iteraciones en el proceso de entrenamiento, que indica cuántas veces se debe ejecutar el modelo de entrenamiento en los datos de entrenamiento. Cada iteración se entrenará en el conjunto de datos de entrenamiento. En términos generales, el conjunto de datos de entrenamiento se repetirá durante muchas iteraciones hasta que alcance num_epochslos tiempos . Esto asegura que el modelo sea capaz de aprender adecuadamente las características de los datos. num_epochsEstablecer un valor que sea demasiado grande o demasiado pequeño conducirá a una disminución en el rendimiento del modelo.
15.4.1 Proceso de codificación
incrustación

embeddingSe utiliza para generar vectores de incrustación de palabras a partir de la entrada:

# 词语转换为对应的词向量表示
self.enc = embedding(self.x, 
                      vocab_size=len(de2idx), 
                      num_units=hp.hidden_units, 
                      scale=True,
                      scope="enc_embed")
  • vocab_sizees el tamaño del vocabulario.
  • num_unitses la dimensión de la palabra vector.
  • scalees un valor booleano que determina si se normalizan los vectores de palabras.
  • scopees el nombre del ámbito de la variable.
Máscaras clave

Luego genere key_masksun para enmascarar ciertas posiciones en cálculos posteriores, de modo que el modelo solo se centre en información válida.

key_masks = tf.expand_dims(tf.sign(tf.reduce_sum(tf.abs(self.enc), axis=-1)), -1)
  • self.encPrimero realice la operación de calcular el valor absoluto de cada elemento en el tensor
  • A lo largo del último orden como eje, se realiza reduce_sumla operación para obtener un (batch, sequence_length)tensor de forma.
  • Luego realice tf.signla operación para realizar la transformación de función simbólica en cada elemento recién obtenido.
  • Finalmente, el orden se expande para convertirse en un tensor (batch, sequence_length, 1)de .
Codificación posicional

Lo siguiente genera el código de posición del Transformador:

# 位置编码
if hp.sinusoid:
    self.enc += positional_encoding(self.x,
                      num_units=hp.hidden_units, 
                      zero_pad=False, 
                      scale=False,
                      scope="enc_pe")
else:
    self.enc += embedding(tf.tile(tf.expand_dims(tf.range(tf.shape(self.x)[1]), 0),
    							 [tf.shape(self.x)[0], 1]),
                      vocab_size=hp.maxlen, 
                      num_units=hp.hidden_units, 
                      zero_pad=False, 
                      scale=False,
                      scope="enc_pe")

Si son hiperparámetros hp.sinusoid=True, use positional_encodingla función para agregar información posicional a la secuencia de entrada usando las funciones seno y coseno para generar codificaciones posicionales. Si hp.sinusoid=False, usando embeddingla función , las codificaciones posicionales se generan a partir de incrustaciones de palabras aprendidas.

Después de generar el código de posición, utilícelo key_maskspara procesarlo . Tenga en cuenta key_masksque la generación de debe usar la original self.enc, así que ejecútela delante en lugar de aquí:

self.enc *= key_masks

Esto no es una multiplicación de matrices, sino una multiplicación de elementos correspondientes. key_masksEl propósito de multiplicar aquí es establecer los elementos correspondienteskey_masks a las posiciones con un valor medio de 0 a 0, de modo que se pueda excluir la influencia de estas posiciones en el cálculo.self.enc

Abandonar

La operación de abandono de TensorFlow se llama a continuación:

self.enc = tf.layers.dropout(self.enc, 
                            rate=hp.dropout_rate, 
                            training=tf.convert_to_tensor(is_training))

El abandono es una técnica de regularización comúnmente utilizada en el aprendizaje profundo. Reduce el sobreajuste al "apagar" aleatoriamente algunas neuronas durante el entrenamiento . Esto se hace para evitar que el modelo dependa demasiado de algunas funciones específicas y tenga un rendimiento deficiente con los datos nuevos.

En esta función, dropoutla capa reduce el sobreajuste del modelo al establecer aleatoriamente el valor de salida de algunas neuronas en 0 durante el entrenamiento. Esta función utiliza un parámetro rateque representa la probabilidad de que cada neurona se "apague". Esto se hace para evitar que el modelo dependa demasiado de algunas funciones específicas y tenga un rendimiento deficiente con los datos nuevos.

Bloques de codificador: atención de varios cabezales y avance

Luego mira el código de los bloques del codificador:

## Blocks
for i in range(hp.num_blocks):
    with tf.variable_scope("num_blocks_{}".format(i)):
        # 多头注意力
        self.enc = multihead_attention(queries=self.enc, 
                                        keys=self.enc, 
                                        num_units=hp.hidden_units, 
                                        num_heads=hp.num_heads, 
                                        dropout_rate=hp.dropout_rate,
                                        is_training=is_training,
                                        causality=False)
        
        # 前馈神经网络
        self.enc = feedforward(self.enc, num_units=[4*hp.hidden_units, hp.hidden_units])

El código anterior es el proceso de implementación de la llamada de función del codificador (Codificador), que también es consistente con la introducción del principio del modelo anterior por parte del Capitán Mike.La capa de incrustación, la capa de codificación posicional, la capa de abandono, la atención multicabezal y la alimentación hacia adelante Las operaciones también se utilizan en la definición. Entre ellos, Multihead Attention es diferente en codificación y decodificación, lo mencionaremos más adelante en la sección Decodificador, hay capas de autoatención y capas de Codificador-Decodificador.

  • El hiperparámetro hp.num_blocks indica el número de capas de Encoder Blocks, cada capa tiene una Atención Multi-Head y un Feed Forward.
  • La atención de múltiples cabezales en este codificador se basa en la autoatención (tenga en cuenta que es diferente de la parte del decodificador más adelante)
  • causalityEl parámetro indica si se debe utilizar Atención causal, que es un tipo de Atención propia, pero solo utiliza información pasada para evitar que el modelo interfiera con información futura. Generalmente, para un cierto paso de tiempo en la secuencia de predicción, solo se trata la información anterior, no la información de toda la secuencia. Este código causalityse establece en False, es decir, prestará atención a la información de toda la secuencia.
15.4.2 Proceso de decodificación

Mira el proceso de decodificación de nuevo:

incrustación

Veamos cada fragmento de código uno por uno, enfocándonos en la diferencia desde la etapa de codificación:

self.dec = embedding(self.decoder_inputs, 
                      vocab_size=len(en2idx), 
                      num_units=hp.hidden_units,
                      scale=True, 
                      scope="dec_embed")
  • embeddingla entrada esself.decoder_inputs
  • Tamaño del glosario con idioma de salida traducido Longitud del glosario en ingléslen(en2idx)
Máscaras clave
key_masks = tf.expand_dims(tf.sign(tf.reduce_sum(tf.abs(self.dec), axis=-1)), -1)
  • key_masksPara variables de entrada self.dec.
Codificación posicional y abandono
# 位置编码
if hp.sinusoid:
    self.dec += positional_encoding(self.decoder_inputs,
                      vocab_size=hp.maxlen, 
                      num_units=hp.hidden_units, 
                      zero_pad=False, 
                      scale=False,
                      scope="dec_pe")
else:
    self.dec += embedding(tf.tile(tf.expand_dims(tf.range(tf.shape(self.decoder_inputs)[1]), 0),
    							 [tf.shape(self.decoder_inputs)[0], 1]),
                      vocab_size=hp.maxlen, 
                      num_units=hp.hidden_units, 
                      zero_pad=False, 
                      scale=False,
                      scope="dec_pe")

self.dec *= key_masks

self.dec = tf.layers.dropout(self.dec, 
                            rate=hp.dropout_rate, 
                            training=tf.convert_to_tensor(is_training))
  • ingresarself.decoder_inputs
  • Especificar vocab_sizeparámetroshp.maxlen
Bloques decodificadores: atención de varios cabezales y avance
## 解码器模块
for i in range(hp.num_blocks):
    with tf.variable_scope("num_blocks_{}".format(i)):
        # 多头注意力(自注意力)
        self.dec = multihead_attention(queries=self.dec, 
                                        keys=self.dec, 
                                        num_units=hp.hidden_units, 
                                        num_heads=hp.num_heads, 
                                        dropout_rate=hp.dropout_rate,
                                        is_training=is_training,
                                        causality=True, 
                                        scope="self_attention")
        
        # 多头注意力(Encoder-Decoder 注意力)
        self.dec = multihead_attention(queries=self.dec, 
                                        keys=self.enc, 
                                        num_units=hp.hidden_units, 
                                        num_heads=hp.num_heads,
                                        dropout_rate=hp.dropout_rate,
                                        is_training=is_training, 
                                        causality=False,
                                        scope="vanilla_attention")

        # 前馈神经网络
        self.dec = feedforward(self.dec, num_units=[4*hp.hidden_units, hp.hidden_units])
  • Cuando utilice multihead_attentionel módulo decodificador de funciones , preste atención a scopela diferencia en los parámetros pasados. En primer lugar, la capa de autoatención utiliza parámetros self_attention, que corresponden a queriesyes self.decy keysyes self.dec. Luego, "Atención de codificador-decodificador" usa parámetros vanilla_attention, y el correspondiente es queriesdel decodificador self.dec, pero es keysdel codificador self.enc.
15.4.3、Incrustación、Codificación posicional、Atención de varios cabezales、Reenvío
Implementación de la función de incrustación
def embedding(inputs, 
              vocab_size, 
              num_units, 
              zero_pad=True, 
              scale=True,
              scope="embedding", 
              reuse=None):
    with tf.variable_scope(scope, reuse=reuse):

    	# 创建一个名为 `lookup_table`、形状为 (vocab_size, num_units) 的矩阵
        lookup_table = tf.get_variable('lookup_table',
                                       dtype=tf.float32,
                                       shape=[vocab_size, num_units],
                                       initializer=tf.contrib.layers.xavier_initializer())

        # lookup_table 的第一行插入一个全零行,作为 PAD 的词向量
        if zero_pad:
            lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
                                      lookup_table[1:, :]), 0)

        # 在词向量矩阵 lookup_table 中查找 inputs
        outputs = tf.nn.embedding_lookup(lookup_table, inputs)
        
        # 对输出的词向量进行除以根号 num_units 的操作,可以控制词向量的统计稳定性。
        if scale:
            outputs = outputs * (num_units ** 0.5) 
            
    return outputs
Implementación de la función de codificación posicional
def positional_encoding(inputs,
                        num_units,
                        zero_pad=True,
                        scale=True,
                        scope="positional_encoding",
                        reuse=None):

    N, T = inputs.get_shape().as_list()
    with tf.variable_scope(scope, reuse=reuse):

    	# tf.range(T) 生成一个 0~T-1 的数组
    	# tf.tile() 将其扩展成 N*T 的矩阵,表示每个词的位置
        position_ind = tf.tile(tf.expand_dims(tf.range(T), 0), [N, 1])

        # First part of the PE function: sin and cos argument
        position_enc = np.array([
            [pos / np.power(10000, 2.*i/num_units) for i in range(num_units)]
            for pos in range(T)])

        # 用 numpy 的 sin 和 cos 函数对每个位置进行编码
        position_enc[:, 0::2] = np.sin(position_enc[:, 0::2])  # dim 2i
        position_enc[:, 1::2] = np.cos(position_enc[:, 1::2])  # dim 2i+1

        # 将编码结果转为张量
        lookup_table = tf.convert_to_tensor(position_enc)

        # 将编码的结果与位置索引相关联,得到最终的位置编码
        if zero_pad:
        	# 如果 zero_pad 参数为 True,则在编码结果的开头添加一个全 0 的向量
            lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
                                      lookup_table[1:, :]), 0)
        outputs = tf.nn.embedding_lookup(lookup_table, position_ind)

        # scale 参数为 True,则将编码结果乘上 num_units 的平方根
        if scale:
            outputs = outputs * num_units**0.5

        return outputs
Implementación de la función de atención de múltiples cabezas
def multihead_attention(queries, 
                        keys, 
                        num_units=None, 
                        num_heads=8, 
                        dropout_rate=0,
                        is_training=True,
                        causality=False,
                        scope="multihead_attention", 
                        reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        # Set the fall back option for num_units
        if num_units is None:
            num_units = queries.get_shape().as_list()[-1]
        
        # Linear Projections
        # 使用三个全连接层对输入的 queries、keys 分别进行线性变换,将其转换为三个维度相同的张量 Q/K/V
        Q = tf.layers.dense(queries, num_units, activation=tf.nn.relu) # (N, T_q, C)
        K = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)
        V = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)
        
        # Split and concat
        # 按头数 split Q/K/V,再各自连接起来
        Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*N, T_q, C/h) 
        K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*N, T_k, C/h) 
        V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*N, T_k, C/h) 

        # Multiplication
        # 计算 Q_, K_, V_ 的点积来获得注意力权重
        # 其中 Q_ 的维度为 (hN, T_q, C/h)
        # K_ 的维度为 (hN, T_k, C/h)
        # 计算出来的结果 outputs 的维度为 (h*N, T_q, T_k)
        outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # (h*N, T_q, T_k)

        # Scale
        # 对权重进行 scale,这里除以了 K_ 的第三维的平方根,用于缩放权重
        outputs = outputs / (K_.get_shape().as_list()[-1] ** 0.5)
        
        # Key Masking
        # 这里需要将 keys 的有效部分标记出来,将无效部分设置为极小值,以便在之后的 softmax 中被忽略
        key_masks = tf.sign(tf.reduce_sum(tf.abs(keys), axis=-1)) # (N, T_k)
        key_masks = tf.tile(key_masks, [num_heads, 1]) # (h*N, T_k)
        key_masks = tf.tile(tf.expand_dims(key_masks, 1), [1, tf.shape(queries)[1], 1]) # (h*N, T_q, T_k)
        
        paddings = tf.ones_like(outputs)*(-2**32+1)
        outputs = tf.where(tf.equal(key_masks, 0), paddings, outputs) # (h*N, T_q, T_k)
  
        # Causality = Future blinding
        if causality:

        	# 创建一个与 outputs[0, :, :] 相同形状的全 1 矩阵
            diag_vals = tf.ones_like(outputs[0, :, :]) # (T_q, T_k)

            # 对 diag_vals 进行处理,返回一个下三角线矩阵
            tril = tf.linalg.LinearOperatorLowerTriangular(diag_vals).to_dense() # (T_q, T_k)
            masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(outputs)[0], 1, 1]) # (h*N, T_q, T_k)
   
   			# 将 masks 为 0 的位置的 outputs 值设置为一个非常小的数
   			# 这样会导致这些位置在之后的计算中对结果产生非常小的影响,从而实现了遮盖未来信息的功能
            paddings = tf.ones_like(masks)*(-2**32+1)
            outputs = tf.where(tf.equal(masks, 0), paddings, outputs) # (h*N, T_q, T_k)
  
        # 对于每个头的输出,应用 softmax 激活函数,这样可以得到一个概率分布
        outputs = tf.nn.softmax(outputs) # (h*N, T_q, T_k)
         
        # Query Masking
        # 对于查询(queries)进行 masking,这样可以避免输入序列后面的词对之前词的影响
        query_masks = tf.sign(tf.reduce_sum(tf.abs(queries), axis=-1)) # (N, T_q)
        query_masks = tf.tile(query_masks, [num_heads, 1]) # (h*N, T_q)
        query_masks = tf.tile(tf.expand_dims(query_masks, -1), [1, 1, tf.shape(keys)[1]]) # (h*N, T_q, T_k)
        outputs *= query_masks # broadcasting. (N, T_q, C)
          
        # Dropouts & Weighted Sum
        # 对于每个头的输出,应用 dropout 以及进行残差连接
        outputs = tf.layers.dropout(outputs, rate=dropout_rate, training=tf.convert_to_tensor(is_training))
        outputs = tf.matmul(outputs, V_) # ( h*N, T_q, C/h)
        
        # Restore shape
        # 将每个头的输出拼接起来,使用 tf.concat 函数,将不同头的结果按照第二维拼接起来
        # 得到最终的输出结果,即经过多头注意力计算后的结果
        outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) # (N, T_q, C)
              
        # Residual connection
        outputs += queries
              
        # Normalize
        outputs = normalize(outputs) # (N, T_q, C)
 
    return outputs
Implementación de la función Feed Forward

La siguiente es la definición de la capa de red neuronal feedforward. Esta es una transformación no lineal. Aquí se utilizan algunos conocimientos de redes neuronales convolucionales (CNN). Veamos el código y lo explicamos:

def feedforward(inputs, 
                num_units=[2048, 512],
                scope="multihead_attention", 
                reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        # Inner layer
        params = {
    
    "inputs": inputs, "filters": num_units[0], "kernel_size": 1,
                  "activation": tf.nn.relu, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        
        # Readout layer
        params = {
    
    "inputs": outputs, "filters": num_units[1], "kernel_size": 1,
                  "activation": None, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        
        # 连接一个残差网络 ResNet
        outputs += inputs
        
        # 归一化后输出
        outputs = normalize(outputs)
    
    return outputs
  • En primer lugar, se utiliza una capa convolucional (conv1d) como capa interna, una capa convolucional como capa de lectura y el tamaño del kernel de convolución es 1.
  • filtersEl parámetro se utiliza para controlar el número de canales de salida en la capa convolucional. El número de canales de salida de la capa interna se establece en num_units[0]y el de la capa de lectura se establece en num_units[1]. Esto también se interpreta a veces como el número de neuronas. Los valores predeterminados de estos dos son 2048 y 512 respectivamente, y los hiperparámetros se pasan al llamar a [4 * hidden_units, hidden_units].
  • La capa interna se utiliza ReLUcomo función de activación, luego se conecta una red residual RedNet y la salida de la capa de lectura se agrega a la entrada original.
  • Finalmente, normalizeuse normalizar para procesar la salida y luego regrese. Veamos normalizela función .
def normalize(inputs, 
              epsilon = 1e-8,
              scope="ln",
              reuse=None):
    with tf.variable_scope(scope, reuse=reuse):

    	# 输入数据的形状
        inputs_shape = inputs.get_shape()
        params_shape = inputs_shape[-1:]
    
    	# 平均数、方差
        mean, variance = tf.nn.moments(inputs, [-1], keep_dims=True)

        # 拉伸因子 beta
        beta= tf.Variable(tf.zeros(params_shape))

        # 缩放因子 gamma
        gamma = tf.Variable(tf.ones(params_shape))

        # 归一化:加上一个非常小的 epsilon,是为了防止除以 0
        normalized = (inputs - mean) / ( (variance + epsilon) ** (.5) )

        outputs = gamma * normalized + beta
        
    return outputs
  • Esta función implementa la normalización de capas, que se utiliza para resolver el problema de la inestabilidad de datos en redes neuronales profundas.
15.4.4 Operación después de la codificación y decodificación

Después del decodificador Linear & Softmax:

# 全连接层得到的未经过归一化的概率值
self.logits = tf.layers.dense(self.dec, len(en2idx))

# 预测的英文单词 idx
self.preds = tf.to_int32(tf.arg_max(self.logits, dimension=-1))
self.istarget = tf.to_float(tf.not_equal(self.y, 0))

# 正确预测数量,除以所有样本数,得到准确率
self.acc = tf.reduce_sum(tf.to_float(tf.equal(self.preds, self.y))*self.istarget)/ (tf.reduce_sum(self.istarget))

#  记录了模型的准确率的值,用于 tensorboard 可视化
tf.summary.scalar('acc', self.acc)

Al procesar los datos del conjunto de entrenamiento, el procesamiento final Linear & Softmaxposterior es el siguiente. tf.nn.softmax_cross_entropy_with_logitsLa pérdida de entropía cruzada se usa aquí para calcular la tasa de error del modelo mean_loss, y el optimizador de Adam se usa AdamOptimizerpara optimizar los parámetros del modelo.

# 使用 label_smoothing 函数对真实标签进行标签平滑,得到 self.y_smoothed
self.y_smoothed = label_smoothing(tf.one_hot(self.y, depth=len(en2idx)))

El siguiente código implementa una técnica llamada "Suavizado de etiquetas".

def label_smoothing(inputs, epsilon=0.1):

	# 获取输入的类别数,并将其赋值给变量 K
    K = inputs.get_shape().as_list()[-1] # number of channels
    return ((1-epsilon) * inputs) + (epsilon / K)

Durante el proceso de entrenamiento, la etiqueta de la muestra se representa como una matriz bidimensional, donde la primera dimensión representa el número de la muestra y la segunda dimensión representa la etiqueta de la muestra. La forma de esta matriz es (número de muestras, número de categorías), por lo que el número de categorías corresponde a la última dimensión. Específicamente en este caso de uso del modelo, la primera dimensión es el número de oraciones de muestra en alemán y la última dimensión es el tamaño del vocabulario en inglés.

Se utiliza para resolver el problema de sobreajuste que se produce al entrenar el modelo. En el suavizado de etiquetas, agregamos algo de ruido a la etiqueta de cada muestra, de modo que el modelo no se puede entrenar completamente dependiendo de la etiqueta de la muestra, lo que reduce la posibilidad de sobreajuste. Específicamente, este código inputsmultiplica por 1-epsilon, más epsilon / K, donde epsilones el factor de suavizado y Kes el número de categorías de etiquetas (tamaño del vocabulario en inglés). Esto puede hacer que la predicción de la etiqueta del modelo sea más estable durante el proceso de entrenamiento y reducir el riesgo de sobreajuste.

Luego nos fijamos en las operaciones de seguimiento.

# 对于分类问题来说,常用的损失函数是交叉熵损失
self.loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=self.y_smoothed)
self.mean_loss = tf.reduce_sum(self.loss * self.istarget) / (tf.reduce_sum(self.istarget))

# Training Scheme
self.global_step = tf.Variable(0, name='global_step', trainable=False)

# Adam 优化器 self.optimizer,用于优化损失函数
self.optimizer = tf.train.AdamOptimizer(learning_rate=hp.lr, beta1=0.9, beta2=0.98, epsilon=1e-8)

# 使用优化器的 minimize() 函数创建一个训练操作 self.train_op,用于更新模型参数。这个函数会自动计算梯度并应用更新
self.train_op = self.optimizer.minimize(self.mean_loss, global_step=self.global_step)
   
# 将平均损失写入 TensorFlow 的 Summary 中,用于 tensorboard 可视化
tf.summary.scalar('mean_loss', self.mean_loss)

# 将所有的 summary 合并到一起,方便在训练过程中写入事件文件
self.merged = tf.summary.merge_all()

15.5 Evaluación del efecto

def eval(): 
    # 创建一个处理测试数据集的 Graph 实例
    g = Graph(is_training=False)
    print("Graph loaded")
    
    # 加载测试数据
    X, Sources, Targets = load_test_data()
    de2idx, idx2de = load_de_vocab()
    en2idx, idx2en = load_en_vocab()
     
    # Start session         
    with g.graph.as_default():

    	# TensorFlow 中用于管理训练的一个类
    	# 它可以帮助你轻松地管理训练过程中的各种资源,如模型参数、检查点和日志
        sv = tf.train.Supervisor()

        # 创建一个会话
        with sv.managed_session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:

            # 恢复模型参数
            sv.saver.restore(sess, tf.train.latest_checkpoint(hp.logdir))
            print("Restored!")
              
            # 获取模型名称
            mname = open(hp.logdir + '/checkpoint', 'r').read().split('"')[1] # model name
             
            ## Inference
            if not os.path.exists('results'): os.mkdir('results')

            # 初始化结果文件
            with codecs.open("results/" + mname, "w", "utf-8") as fout:
                list_of_refs, hypotheses = [], []

                # 循环处理数据
                for i in range(len(X) // hp.batch_size):
                     
                    # 获取小批量数据
                    x = X[i*hp.batch_size: (i+1)*hp.batch_size]
                    sources = Sources[i*hp.batch_size: (i+1)*hp.batch_size]
                    targets = Targets[i*hp.batch_size: (i+1)*hp.batch_size]
                     
                    # 使用自回归推理(Autoregressive inference)得到预测结果
                    preds = np.zeros((hp.batch_size, hp.maxlen), np.int32)
                    for j in range(hp.maxlen):
                        _preds = sess.run(g.preds, {
    
    g.x: x, g.y: preds})
                        preds[:, j] = _preds[:, j]
                     
                    # 将预测结果写入文件
                    for source, target, pred in zip(sources, targets, preds): # sentence-wise
                        got = " ".join(idx2en[idx] for idx in pred).split("</S>")[0].strip()
                        fout.write("- source: " + source +"\n")
                        fout.write("- expected: " + target + "\n")
                        fout.write("- got: " + got + "\n\n")
                        fout.flush()
                          
                        # bleu score
                        ref = target.split()
                        hypothesis = got.split()
                        if len(ref) > 3 and len(hypothesis) > 3:
                            list_of_refs.append([ref])
                            hypotheses.append(hypothesis)
              
                # 计算 BLEU 分数,并将其写入文件
                score = corpus_bleu(list_of_refs, hypotheses)
                fout.write("Bleu Score = " + str(100*score))
                                          
if __name__ == '__main__':
    eval()
    print("Done")

Sección 16 Rendimiento del transformador Kyubyong y algunos problemas

La última línea del archivo de resultados de la evaluación Bleu Score = 6.598452846670836indica que el resultado de la traducción de este modelo de traducción tiene un grado relativamente alto de superposición con la traducción de referencia y que la calidad de la traducción es buena. Sin embargo, cabe señalar que la puntuación BLEU no puede reflejar completamente la calidad de la traducción, ya que no puede evaluar los problemas de gramática, semántica, entonación, etc.

Además, hemos guardado los datos del proceso en el logdir en el código anterior, solo para la comodidad de la visualización posterior, podemos usar TensorBoard para visualizar, el método de uso específico es el siguiente:

mikecaptain@local $ tensorboard --logdir logdir

Luego verifíquelo en el navegador http://localhost:6006, el ejemplo es el siguiente:

Mirando hacia atrás en la implementación del código de todo el modelo, podemos ver de manera más intuitiva que este Transformador puede capturar mejor las dependencias a larga distancia y mejorar la calidad de la traducción. Sin embargo, existen algunos problemas con la implementación de Kyubyong Transformer. El modelo de Transformer también necesita ajustar muchos hiperparámetros durante el proceso de entrenamiento, como la tasa de aprendizaje (learning rate), el tamaño del lote, etc. Diferentes tareas pueden requerir diferentes ajustes de hiperparámetros.

Fin · Años después de Transformer

Las ventajas de Transformer son obvias:

  • Más rápido: buen paralelismo: antes de que naciera Transformer, RNN era el modelo principal en el campo de NLP, pero RNN tiene un paralelismo deficiente (procesamiento en serie de secuencias).
  • No se olvida: la distancia de las palabras se reduce a 1: el modelo RNN maneja el contenido de texto largo que se ha perdido (lo que significa una gran distancia espacial de las palabras en el modelo RNN).
  • Manejo de secuencias de diferentes longitudes: Secuencias que no requieren que los datos de entrada sean de longitud fija.
  • Facilidad de transferencia de aprendizaje.

Por lo tanto, el modelo basado en el principio Transformer ha logrado un excelente rendimiento en muchas tareas de PNL.

Después de todo, el campo del aprendizaje automático (Machine Learning) sigue siendo una ciencia experimental, y es una ciencia experimental muy cercana a la industria. La perspectiva del aprendizaje automático para ver los resultados experimentales no es resumir y abstraer los resultados experimentales para promover el desarrollo de la ciencia teórica. Los resultados experimentales del aprendizaje automático deben evaluarse y existen estándares de evaluación cuantitativos objetivos para sus efectos. Entonces, el aprendizaje automático, todo habla con resultados. El modelo grande GPT de OpenAI nació basado en la parte Decoder de la arquitectura Transformer, y el modelo grande BERT de Google nació basado en la parte Encoder de su arquitectura. Ambos nacieron en 2018. En los últimos años han ido surgiendo varias ideas de optimización basadas en Transformer, y la maestra es ChatGPT basado en GPT-3.5 o InstructGPT a finales de 2022.

Gracias por su paciencia al leer este artículo de casi 80 000 palabras. Debido a que son las notas técnicas del capitán, los puntos clave se han resuelto en detalle. En el seguimiento, les hablaré sobre la situación actual de AIGC. Si el contenido de este artículo es más como un tutorial (estudio en profundidad de la tecnología de origen), entonces nuestra discusión de seguimiento puede ser más como un informe (para el desarrollo académico y de la industria actual. descripción general del status quo), prestaremos más atención a los dos temas en la parte "Prefacio" del artículo: 1) Si pasar la prueba de Turing representa AGI (Inteligencia General Artificial, inteligencia artificial general), ¿cuál es el desarrollo actual de NLP e incluso AGI? 2) ¿Cuál podría ser la ruta de desarrollo de AGI en los próximos años?

AI eventualmente subvertirá todos los ámbitos de la vida. La gente de Ali tiene la responsabilidad de dedicar algún tiempo a prestar atención al pulso del desarrollo de vanguardia. Le invitamos a comunicarse conmigo en DingTalk o WeChat (id: sinosuperman).

Finalmente, el Capitán Mike les deseó a todos un feliz Año Nuevo y les deseó a todos salud y felicidad en el Año del Conejo.

referencia

  • https://web.stanford.edu/~jurafsky/slp3/3.pdf
  • https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html
  • "Procesamiento del lenguaje natural: un método basado en modelos de preentrenamiento" Che Wanxiang et al.
  • https://cs.stanford.edu/people/karpathy/convnetjs/
  • https://arxiv.org/abs/1706.03762
  • https://arxiv.org/abs/1512.03385
  • https://github.com/Kyubyong/transformer/
  • http://jalammar.github.io/ilustrado-transformador/
  • https://towardsdatascience.com/this-is-how-to-train-better-transformer-models-d54191299978
  • "Procesamiento del lenguaje natural en la práctica: aplicación y producción del modelo previo al entrenamiento" por Anku A. Patel et al.
  • https://lilianweng.github.io/posts/2018-06-24-attention/
  • https://github.com/lilianweng/transformer-tensorflow/
  • "Predicción de la secuencia espacio-temporal del estado del tráfico vial a corto plazo basada en el aprendizaje profundo" Cui Jianxun
  • https://www.zhihu.com/question/325839123
  • https://luweikxy.gitbook.io/machine-learning-notes/self-attention-and-transformer
  • Aprendizaje profundo con Python (2.ª edición) de François Cholet
  • https://en.wikipedia.org/wiki/Attention_(aprendizaje_máquina)
  • https://zhuanlan.zhihu.com/p/410776234
  • https://www.tensorflow.org/tensorboard/get_started
  • https://paperswithcode.com/method/multi-head-attention
  • https://zhuanlan.zhihu.com/p/48508221
  • https://www.joshbelanich.com/self-attention-layer/
  • https://learning.rasa.com/transformers/kvq/
  • http://deeplearning.stanford.edu/tutorial/supervised/ConvolutionalNeuralNetwork/
  • https://zhuanlan.zhihu.com/p/352898810
  • https://towardsdatascience.com/beautifully-illustrated-nlp-models-from-rnn-to-transformer-80d69faf2109
  • https://medium.com/analytics-vidhya/understanding-qkv-in-transformer-self-attention-9a5eddaa5960

Supongo que te gusta

Origin blog.csdn.net/Poechant/article/details/128878358
Recomendado
Clasificación