Modelo Generativo de Inteligencia Artificial-Seq2Seq: plan de optimización del modelo Seq2Seq

1. Usado en seq2seq teacher forcing[usado en el decodificador en la fase de entrenamiento]

En el caso anterior de seq2seq, presentamos teacher frocinglo que es. En ese momento, nuestra entrada y salida eran muy similares, por lo que en ese momento la nuestra teacher forcingse implementó en cada paso de tiempo, por lo que ahora nuestra entrada y salida son diferentes. ¿Cómo usarlo?

Podemos atravesar la capa exterior del paso de tiempo en cada lote del decodificador.teacher forcing

el código se muestra a continuación:

use_teacher_forcing = random.random() > 0.5
if use_teacher_forcing: #使用teacher forcing
    for t in range(config.max_len):
        decoder_output_t, decoder_hidden, decoder_attn_t = self.forward_step(decoder_input, decoder_hidden,
                                                                             encoder_outputs)
        decoder_outputs[:, t, :] = decoder_output_t
        #使用正确的输出作为下一步的输入
        decoder_input = target[:, t].unsqueeze(1)  # [batch_size,1]

else:#不适用teacher forcing,使用预测的输出作为下一步的输入
    for t in range(config.max_len):
        decoder_output_t ,decoder_hidden,decoder_attn_t = self.forward_step(decoder_input,decoder_hidden,encoder_outputs)
        decoder_outputs[:,t,:] = decoder_output_t
        value, index = torch.topk(decoder_output_t, 1) # index [batch_size,1]
        decoder_input = index

El código completo del decodificador:

import torch
import torch.nn as nn
import config
import random
import torch.nn.functional as F
from word_sequence import word_sequence

class Decoder(nn.Module):
    def __init__(self):
        super(Decoder,self).__init__()
        self.max_seq_len = config.max_len
        self.vocab_size = len(word_sequence)
        self.embedding_dim = config.embedding_dim
        self.dropout = config.dropout

        self.embedding = nn.Embedding(num_embeddings=self.vocab_size,embedding_dim=self.embedding_dim,padding_idx=word_sequence.PAD)
        self.gru = nn.GRU(input_size=self.embedding_dim,
                          hidden_size=config.hidden_size,
                          num_layers=1,
                          batch_first=True,
                          dropout=self.dropout)
        self.log_softmax = nn.LogSoftmax()

        self.fc = nn.Linear(config.hidden_size,self.vocab_size)

    def forward(self, encoder_hidden,target,target_length):
        # encoder_hidden [batch_size,hidden_size*2]
        # target [batch_size,seq-len]

        decoder_input = torch.LongTensor([[word_sequence.SOS]]*config.batch_size).to(config.device)	# 初始化解码器的input
        decoder_hidden = encoder_hidden # 初始化解码器的hidden_state, 形状为:[batch_size,hidden_size*2]【*2是因为编码器使用了bidirectional,所以编码器的输出维度为hidden_size*2】
        decoder_outputs = torch.zeros(config.batch_size,config.max_len,self.vocab_size).to(config.device) # 初始化解码器的输出,形状为: [batch_size,seq_len,14]
        if random.random() > 0.5:
            for t in range(config.max_len):
                decoder_output_t , decoder_hidden = self.forward_step(decoder_input,decoder_hidden)
                decoder_outputs[:,t,:] = decoder_output_t
                value, index = torch.topk(decoder_output_t, 1) # 获取当前时间步的预测值  index [batch_size,1]
                decoder_input = index	# 使用当前时间步的预测值作为下一个时间步的输入
        else:
            for t in range(config.chatbot_target_max_len):
                decoder_output_t , decoder_hidden = self.forward_step(decoder_input,decoder_hidden)
                decoder_outputs[:,t,:] = decoder_output_t
                decoder_input = target[:,t].unsqueeze(-1)   #把真实值作为下一步的输入
        return decoder_outputs,decoder_hidden

    def forward_step(self,decoder_input,decoder_hidden):
        """
        :param decoder_input:[batch_size,1]
        :param decoder_hidden: [1,batch_size,hidden_size*2]
        :return: out:[batch_size,vocab_size],decoder_hidden:[1,batch_size,hidden_size*2]
        """
        embeded = self.embedding(decoder_input)  #embeded: [batch_size,1 , embedding_dim]
        out,decoder_hidden = self.gru(embeded,decoder_hidden) #out [1, batch_size, hidden_size*2], decoder_hidden:[1,batch_size,hidden_size*2]
        out = out.squeeze(0)
        out = F.log_softmax(self.fc(out),dim=-1)#[batch_Size, vocab_size]
        out = out.squeeze(1)
        # print("out size:",out.size(),decoder_hidden.size())
        return out,decoder_hidden

2. Utilice el recorte de degradado

Anteriormente, les presentamos 梯度消失(梯度过小,在多层计算后导致其值太小而无法计算)y 梯度爆炸(梯度过大,导致其值在多层的计算后太大而无法计算).

En las redes neuronales profundas comunes, especialmente las RNN, a menudo usamos 梯度裁剪métodos para suprimir gradientes excesivos, que pueden prevenir eficazmente las explosiones de gradientes.

La implementación del recorte de degradado es muy simple, solo necesita establecer un umbral y establecer el umbral cuando el degradado es mayor que el umbral.

Inserte la descripción de la imagen aquí

Código de implementación:

loss.backward()
#进行梯度裁剪
nn.utils.clip_grad_norm_(model.parameters(),[5,10,15])
optimizer.step()

3. Utilice el mecanismo de atención [utilizado en el decodificador]

4. Predicción del algoritmo BeamSearch [predicción alternativa del algoritmo codicioso]

4.1 Introducción a Beam Search

En el proceso de evaluación del modelo, cada vez que elegimos la identificación del token con la probabilidad más alta como salida, ¿es la probabilidad de la oración de salida completa la más alta?

Inserte la descripción de la imagen aquí
Beam searchTambién conocido como 束集搜索, es un algoritmo que se usa para optimizar los resultados de salida en seq2seq ( no se usa en el proceso de entrenamiento, pero se usa en la evaluación o predicción ).

Por ejemplo: en el proceso tradicional de obtener la salida del decodificador, solo el resultado con la probabilidad más alta se selecciona cada vez como la salida del paso de tiempo actual.Cuando la salida termina, encontraremos que la oración completa puede no ser ser suave. Aunque la salida en cada paso de tiempo es de hecho la más probable, la probabilidad general no es necesariamente la más grande. A menudo la llamamosgreedy search[贪心算法]

Para resolver los problemas anteriores, puede considerar calcular el producto de probabilidad de todas las salidas y elegir la ruta con la mayor probabilidad, que se puede lograr全局最优解 . Pero en este caso, significa que si la oración es muy larga y hay muchas palabras candidatas, entonces los datos que deben guardarse serán muy grandes y la cantidad de datos que deben calcularse será muy grande.

Entonces Beam Search es un método entre los dos métodos anteriores. Suponiendo que Beam width = 2, significa la máxima probabilidad de ahorrar cada vez. Aquí, guarda dos a la vez. El siguiente paso de tiempo es el mismo, y también es reservado Dos, de modo que se pueda lograr el propósito de restringir el tamaño del espacio de búsqueda, mejorando así la eficiencia del algoritmo . Beam Search也不是全局最优. [El algoritmo de Viterbi es globalmente óptimo]

  • el ancho del haz es un hiperparámetro.
    • Cuando el ancho del haz = 1, es un algoritmo codicioso.
    • Cuando el ancho del haz = palabras candidatas, es para calcular todas las probabilidades.

Por ejemplo, en la siguiente figura:

Use un diagrama de árbol para representar la salida posible de cada paso de tiempo, donde el número representa la probabilidad condicional

La flecha amarilla indica una búsqueda codiciosa, la probabilidad no es la más grande

Si el ancho del haz se establece en 2, el resultado de la ruta verde se puede encontrar más tarde, y este resultado es el más grande.

Inserte la descripción de la imagen aquí

La siguiente figura es un ejemplo de dar ancho de haz = 3

  1. Primero ingrese start token <s>, y luego obtenga cuatro salidas (aquí suponga que una tiene cuatro salidas :) X,Y,Z,</s>, elija tres con la probabilidad más alta, X, Y, W
  2. Luego coloque X, Y, W en el siguiente paso de tiempo como entrada y obtenga tres salidas diferentes (un total de 12 secuencias de salida: XX, XY, XZ, X </s>, YX, YY, YZ, Y </ s >, WX, WY, WZ, W </s>), entre estas 12 salidas, encuentre las tres con la mayor probabilidad de salida (como se muestra en la siguiente figura: XX, XY, WY) y guárdelas en Beam.
  3. Luego ponga XX, XY, WY en el siguiente paso de tiempo como entrada, respectivamente, y obtenga tres conjuntos de salidas diferentes (un total de 12 secuencias de salida: XXX, XXY, XXZ, XX </s>, XYX, XYY, XYZ, XY </s>, WYX, WYY, WYZ, WY </s>), entre estas 12 salidas, encuentre las tres con la mayor probabilidad de salida (como se muestra en la siguiente figura: XXX, XYX, WYX) y guárdelas en Haz.
  4. Luego coloque XXX, XYX, WYX en el siguiente paso de tiempo como entrada, respectivamente, y obtenga tres salidas diferentes (un total de 12 secuencias de salida: XXXX, XXXY, XXXZ, XXX </s>, XYXX, XYXY, XYXZ, XYX < / s>, WYXX, WYXY, WYXZ, WYX </s>), entre estas 12 salidas, encuentre las tres con la mayor probabilidad de salida (como se muestra en la siguiente figura: XYXW, XYXX, WYX </s>) y guarde para transmitirlos.
  5. Luego ponga XYXW, XYXX, WYX </s> en el siguiente paso de tiempo como entrada y obtenga tres conjuntos diferentes de salidas (un total de 12 secuencias de salida: XYXWX, XYXWY, XYXWZ, XYXW </s>, XYXXX, XYXXY, XYXXZ, XYXX </s>, WYX </s> X, WYX </s> Y, WYX </s> Z, WYX </s> </s>), tres de estas 12 salidas se encuentran Las tres con la probabilidad de salida más alta (como se muestra en la figura siguiente: XYXW </s>, XYXWY, XYXX </s>) se guardan en Beam.
  6. Continúe repitiendo los pasos anteriores hasta que la probabilidad de secuencia cuando se obtenga el carácter final sea el máximo o se alcance la longitud máxima de la oración max_len , y el ciclo finalice . En este momento, elija la ruta con el producto de mayor probabilidad.
  7. Concatenar todos los resultados con la mayor probabilidad en toda la ruta, por ejemplo, aquí puede haber<s>,X,Y,X,W,</s>

Inserte la descripción de la imagen aquí

4.2 Explicación de Beam Search

Para el modelo entrenado por el algoritmo MLE, la búsqueda de haz solo es necesaria para la predicción. Debido a que conoce la respuesta correcta durante el entrenamiento, no es necesario que vuelva a realizar esta búsqueda.

Al predecir, suponga que el tamaño del vocabulario es 3 y el contenido es a, b, c. El tamaño del haz es 2, cuando el decodificador decodifica:

  1. Al generar la primera palabra, seleccione las 2 palabras con la mayor probabilidad, asumiendo ayc, entonces las 2 secuencias actuales son ay c.
  2. Al generar la segunda palabra, combinamos las secuencias actuales ayc con todas las palabras del vocabulario respectivamente para obtener las nuevas 6 secuencias aa ab ac ca cb cc, calculamos la puntuación de cada secuencia y seleccionamos la puntuación más alta 2 secuencias, como la nueva secuencia actual, si es aa, cb.
  3. Este proceso se repetirá más tarde hasta que se encuentre el carácter final o se alcance la longitud máxima. Finalmente se emiten las 2 secuencias con las puntuaciones más altas.

4.3 Implementación de Beam serach

En las ideas descritas anteriormente, debemos prestar atención a lo siguiente:

  1. Cómo guardar los datos, los resultados de ancho de haz máximo de cada salida y cómo guardar los resultados anteriores después
  2. Cómo comparar las probabilidades después de guardar, mantenga las tres más probables
  3. No solo es posible guardar solo la información con la probabilidad actual más alta, sino también el resultado de salida de la ruta anterior entre los tres con la probabilidad actual más alta

4.3.1 Estructura de datos: comprensión del montón

Para lo anterior, se reserva un número limitado de datos, y es necesario reservarlo según el tamaño. Para ello se puede utilizar una estructura de datos con prioridad. Aquí podemos utilizar esta estructura de datos

Es una cola de prioridad, pero en realidad no es una cola

  • 队列Todo es primero en entrar, primero en salir o primero en entrar , último en salir
  • Obtenga datos solo de acuerdo con el nivel de prioridad .
  • Es una estructura de datos de primero en entrar, último en salir , con operaciones de apilamiento y estallido

Entre los módulos que vienen con python, hay heapqun módulo llamado módulo que proporciona todos los métodos. A través del siguiente código entenderemos cómo usar heapq

my_heap = [] #使用列表保存数据

 #往列表中插入数据,优先级使用插入的内容来表示,就是一个比较大小的操作,越大优先级越高
heapq.heappush(my_heap,[29,True,"xiaohong"]) 
heapq.heappush(my_heap,[28,False,"xiaowang"])
heapq.heappush(my_heap,[29,False,"xiaogang"])

for i in range(3):
    ret= heapq.heappop(my_heap)  #pop操作,优先级最小的数据
    print(ret)
    
#输出如下:
[28, False, 'xiaowang']
[29, False, 'xiaogang']
[29, True, 'xiaohong']

Se puede encontrar que el orden de salida no es el orden de inserción de datos, pero según su prioridad, salte de pequeño a grande (Falso <Verdadero).

4.3.2 Usar montón para implementar la búsqueda de vigas

Para guardar los datos, podemos guardar los datos en la búsqueda del haz en el montón y, al mismo tiempo, agregar datos al montón mientras juzgamos el número de datos, solo guardar los datos del ancho del haz.

class Beam:
    def __init__(self):
        self.heap = list() #保存数据的位置
        self.beam_width = config.beam_width #保存数据的总数

    def add(self,probility,complete,seq,decoder_input,decoder_hidden):
        """
        添加数据,同时判断总的数据个数,多则删除
        :param probility: 概率乘积
        :param complete: 最后一个是否为EOS
        :param seq: list,所有token的列表
        :param decoder_input: 下一次进行解码的输入,通过前一次获得
        :param decoder_hidden: 下一次进行解码的hidden,通过前一次获得
        :return:
        """
        heapq.heappush(self.heap,[probility,complete,seq,decoder_input,decoder_hidden])
        #判断数据的个数,如果大,则弹出。保证数据总个数小于等于3
        if len(self.heap)>self.beam_width:
            heapq.heappop(self.heap)

    def __iter__(self):#让该beam能够被迭代
        return iter(self.heap)

Método de implementación, complete la búsqueda de la viga en el proceso de evaluación del modelo

Ideas:

  1. Construya la <SOS>primera información de entrada, como el símbolo de inicio, y guárdela en el montón
  2. Saque los datos del montón y realice la operación forward_step para obtener la salida del paso de tiempo actual, oculto
  3. Seleccione las salidas topk (k = ancho del haz) de la salida como la siguiente entrada
  4. Guarde la entrada y otros datos necesarios para el próximo paso en un nuevo montón
  5. Obtenga los datos con la prioridad más alta (la probabilidad más alta) en el nuevo montón, determine si los datos son el final de EOS o si alcanzan la longitud máxima, si es así, detenga la iteración
  6. De lo contrario, vuelva a recorrer los datos en el nuevo montón

el código se muestra a continuación

# decoder中的新方法
def evaluatoin_beamsearch_heapq(self,encoder_outputs,encoder_hidden):
    """使用 堆 来完成beam search,对是一种优先级的队列,按照优先级顺序存取数据"""

    batch_size = encoder_hidden.size(1)
    #1. 构造第一次需要的输入数据,保存在堆中
    decoder_input = torch.LongTensor([[word_sequence.SOS] * batch_size]).to(config.device)
    decoder_hidden = encoder_hidden #需要输入的hidden

    prev_beam = Beam()
    prev_beam.add(1,False,[decoder_input],decoder_input,decoder_hidden)
    while True:
        cur_beam = Beam()
        #2. 取出堆中的数据,进行forward_step的操作,获得当前时间步的output,hidden
        #这里使用下划线进行区分
        for _probility,_complete,_seq,_decoder_input,_decoder_hidden in prev_beam:
            #判断前一次的_complete是否为True,如果是,则不需要forward
            #有可能为True,但是概率并不是最大
            if _complete == True:
                cur_beam.add(_probility,_complete,_seq,_decoder_input,_decoder_hidden)
            else:
                decoder_output_t, decoder_hidden,_ = self.forward_step(_decoder_input, _decoder_hidden,encoder_outputs)
                value, index = torch.topk(decoder_output_t, config.beam_width)  # [batch_size=1,beam_widht=3]
             	#3. 从output中选择topk(k=beam width)个输出,作为下一次的input
            	for m, n in zip(value[0], index[0]):
                    decoder_input = torch.LongTensor([[n]]).to(config.device)
                    seq = _seq + [n]
                    probility = _probility * m
                    if n.item() == word_sequence.EOS:
                    	complete = True
                    else:
                        complete = False

                 	#4. 把下一个实践步骤需要的输入等数据保存在一个新的堆中
                	 cur_beam.add(probility,complete,seq,decoder_input,decoder_hidden)
          #5. 获取新的堆中的优先级最高(概率最大)的数据,判断数据是否是EOS结尾或者是否达到最大长度,如果是,停止迭代
          best_prob,best_complete,best_seq,_,_ = max(cur_beam)
         if best_complete == True or len(best_seq)-1 == config.max_len: #减去sos
            return self._prepar_seq(best_seq)
         else:
            #6. 则重新遍历新的堆中的数据
            prev_beam = cur_beam
                                    
      def _prepar_seq(self,seq):#对结果进行基础的处理,共后续转化为文字使用
        if seq[0].item() == word_sequence.SOS:
            seq=  seq[1:]
        if  seq[-1].item() == word_sequence.EOS:
            seq = seq[:-1]
        seq = [i.item() for i in seq]
        return seq

4.3.3 Modificar el modelo seq2seq

Use evaluattoin_beamsearch_heapq para ver el efecto en seq2seq, y encontrará que el efecto de usar la búsqueda de haz es mejor que el efecto de usar la atención sola

Usando el pequeño corpus de pollo amarillo (500,000 preguntas y respuestas), se usa una sola palabra como símbolo, y el resultado del entrenamiento después de 5 épocas, la izquierda es la pregunta, la derecha es la respuesta

你在干什么 >>>>> 你想干啥?
你妹 >>>>> 不是我
你叫什么名字 >>>>> 你猜
你个垃圾 >>>>> 你才是,你
你是傻逼 >>>>> 是你是傻
笨蛋啊 >>>>> 我不是,你

5. Otros métodos de modelos de optimización

  1. Inicialización de parámetros
  2. Optimice los datos y el corpus existentes
    • Limpieza de datos
      • Manejo de puntuación, expresiones y lengua extranjera
      • Reemplace sustantivos como la hora, el nombre de la persona, la ubicación, etc.con los símbolos correspondientes
    • Nunca entienda el ángulo, prepare el corpus con diferentes niveles de complejidad
      • Ángulo: clima, alimentación, género ...
      • Complejidad: simple, general, compleja
  3. Optimización de la perspectiva de ingeniería
    • Use plantillas para hacer coincidir preguntas comunes y devolver respuestas preestablecidas
    • Utilice el modelo de clasificación para clasificar la pregunta y devolver la respuesta preestablecida
    • Utilice el modelo de búsqueda para devolver respuestas a preguntas similares del corpus existente.
  4. De acuerdo con el problema específico, use el modelo de clasificación para el entrenamiento y luego entrene al individuo de regreso al tema como modelo.
    • Por ejemplo, para solicitar el nombre, puede usar fasttext para realizar el reconocimiento de intención primero y 询问名字luego devolver directamente el nombre después de ingresar a la clasificación.
    • O construya manualmente muchas preguntas relacionadas con el nombre para la capacitación, de modo que pueda responder los resultados de manera más personalizada
  5. Modificar y limpiar el corpus existente directamente, y reemplazar más respuestas en el corpus, como las que piden nombres, preguntan sobre el clima, etc., para que se puedan responder en mayor medida respuestas más estandarizadas.
  6. Use el modelo de búsqueda, ya no use este tipo de modelo generativo

Supongo que te gusta

Origin blog.csdn.net/u013250861/article/details/115034195
Recomendado
Clasificación