Combate NLP: Pytorch implementa 7 clasificaciones clásicas de texto chino de aprendizaje profundo: TextCNN+TextRNN+FastText+TextRCNN+TextRNN_Attention+DPCNN+Transformer

inserte la descripción de la imagen aquí


Introducción Introducción

Este documento utiliza Pytorch como marco para implementar 7 modelos clásicos de clasificación de texto chino de aprendizaje profundo, incluidos TextCNN, TextRNN, FastText, TextRCNN, TextRNN_Attention, DPCNN y Transformer .

Primero, se proporciona una descripción detallada del conjunto de datos, incluida la fuente del conjunto de datos, el método de preprocesamiento y el método de división. De esta manera, el lector puede comprender las características del conjunto de datos y cómo preparar los datos.

En cuanto a la construcción del entorno, se proporcionan las bibliotecas dependientes necesarias y las instrucciones de configuración del entorno para ayudar a los lectores a funcionar sin problemas y realizar experimentos.

Para cada modelo, proporcionamos instrucciones detalladas, incluida la estructura del modelo, el formato de los datos de entrada y el proceso de entrenamiento e inferencia del modelo. Estas descripciones ayudan al lector a comprender cómo funciona cada modelo y los detalles de implementación.

Finalmente, proporcionamos informes detallados de los resultados de la capacitación y las pruebas. Estos resultados pueden ayudar a los lectores a evaluar, comparar y analizar el rendimiento de cada modelo en tareas de clasificación de texto chino.

A través de este artículo, los lectores pueden conocer los detalles de implementación y el rendimiento de varios modelos de clasificación de texto chino de aprendizaje profundo. Este documento no solo proporciona una referencia para los investigadores académicos, sino que también proporciona un código reutilizable y pautas experimentales para desarrolladores y profesionales para ayudarlos a lograr mejores resultados en las tareas de clasificación de texto chino.

conjunto de datos

Se extrajeron 200.000 titulares de noticias de THUCNews , con una longitud de texto entre 20 y 30. Hay 10 categorías en total, cada una con 20.000 entradas.

Ingrese el modelo en unidades de palabras, utilizando el vector de palabras previamente entrenado: Sogou News Word+Character 300d.

Categorías: Finanzas, Bienes Raíces, Acciones, Educación, Tecnología, Sociedad, Actualidad, Deportes, Juegos, Entretenimiento.

El conjunto de datos, el vocabulario y los vectores de palabras pre-entrenados correspondientes han sido empaquetados, consulte la THUCNewscarpeta en la dirección de Github a continuación para obtener más detalles.

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Entorno de Python e instalación de los paquetes dependientes correspondientes

  • pitón 3.7 o superior
  • pytorch 1.1 o superior
  • tqdm
  • aprendió
  • tensorboardX

Configuración del entorno Anaconda

  1. Inicie sesión en el sitio web oficial de Anaconda , descargue e instale Anaconda

  2. Luego abra la terminal e ingrese los siguientes comandos de la terminal en secuencia:

Nuevo ambiente:chinese_text_classification

conda create --name chinese_text_classification python==3.8.10

Activa el entorno:

conda activate chinese_text_classification

Ingrese los siguientes comandos a su vez para instalar los paquetes de python relevantes

conda install pytorch
conda install scikit-learn
conda install tqdm
conda install tensorboardX

Tenga en cuenta que el pytorch instalado anteriormente es la versión de la CPU por defecto. Si desea instalar la versión GPU de pytorch, puede consultar los siguientes pasos.

Primero, asegúrese de haber instalado correctamente el controlador de gráficos NVIDIA y de que su tarjeta gráfica sea compatible con CUDA. Puede encontrar el controlador correspondiente y la información de compatibilidad de CUDA en el sitio web oficial de NVIDIA.

Antes de instalar PyTorch en un entorno de Python, debe instalar CUDAToolkit para su versión de CUDA. Puede descargar e instalar CUDAToolkit para su versión de CUDA desde el sitio web para desarrolladores de NVIDIA.

Después de completar los pasos anteriores, puede usar el siguiente comando para ver su versión relacionada con GPU.

nvcc --version

inserte la descripción de la imagen aquí
Si no hay una versión anterior, debe verificar si CUDA y CUDAToolkit están instalados.

Luego, puede descargar la versión correspondiente de whl desde el sitio web de descarga de pytorch para la instalación, debido a que el archivo de pytorch de la versión gpu general es muy grande, no se recomienda usar pip para instalar directamente. Por ejemplo, el siguiente es el comando pytorch para instalar la versión gpu directamente usando pip, lo que demora aproximadamente 13 horas:

pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117

inserte la descripción de la imagen aquí
Puede usar directamente el navegador para abrir el sitio web de descarga que aparece en la imagen de arriba: https://download.pytorch.org/whl/cu117 y seleccionar torchpara buscar las palabras clave en la imagen de arriba cu117-cp38-cp38-win_amd64.whl. Haga clic para descargar. Generalmente, si la velocidad de la red es rápida, la descarga se puede completar en unos 10 minutos.

inserte la descripción de la imagen aquí
Después de que la descarga sea exitosa, puede usar directamente el siguiente comando para instalar:

pip install <path/to/your/whl/file.whl>

Reemplácelo <path/to/your/whl/file.whl>con la ruta del archivo .wl real (p. ej.: pip install /path/to/your/file.whl)

dirección del código fuente

Dirección de Github: https://github.com/649453932/Chinese-Text-Classification-Pytorch

TextoCNN

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Después de la capa incrustada: cargue el vector de palabras previamente entrenado o la inicialización aleatoria, la dimensión del vector de palabras es embed_size: Incrustación (4762, 300) 3.
Capa convolucional:
(0): Conv2d (1) , 256, kernel_size=(2, 300), stride=(1,1))
(1): Conv2d (1, 256, kernel1_size=(3, 300), stride=(1, 1))
(2): Conv2d (1, 256, kernel_size=(4, 300), stride=(1, 1))
4. Capa de abandono: abandono (p=0,5, en el lugar=falso)
5. Conexión completa: lineal (in_features=768, out_features =10, sesgo=Verdadero)

Análisis:
la operación de convolución es equivalente a extraer la información de 2 gramos, 3 gramos y 4 gramos en la oración. Se utilizan múltiples convoluciones para extraer varias características, y la combinación máxima extraerá la información más importante y la conservará.

El diagrama esquemático es el siguiente:
inserte la descripción de la imagen aquí

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'TextCNN'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 20                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度
        self.filter_sizes = (2, 3, 4)                                   # 卷积核尺寸
        self.num_filters = 256                                          # 卷积核数量(channels数)


'''Convolutional Neural Networks for Sentence Classification'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.convs = nn.ModuleList(
            [nn.Conv2d(1, config.num_filters, (k, config.embed)) for k in config.filter_sizes])
        self.dropout = nn.Dropout(config.dropout)
        self.fc = nn.Linear(config.num_filters * len(config.filter_sizes), config.num_classes)

    def conv_and_pool(self, x, conv):
        x = F.relu(conv(x)).squeeze(3)
        x = F.max_pool1d(x, x.size(2)).squeeze(2)
        return x

    def forward(self, x):
        out = self.embedding(x[0])
        out = out.unsqueeze(1)
        out = torch.cat([self.conv_and_pool(out, conv) for conv in self.convs], 1)
        out = self.dropout(out)
        out = self.fc(out)
        return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model TextCNN

El proceso de formación es el siguiente:
inserte la descripción de la imagen aquí

Los resultados del entrenamiento y la prueba son los siguientes:
usando la versión de CPU de pytorch, toma 15 minutos y 25 segundos, y la tasa de precisión es 90.99%

inserte la descripción de la imagen aquí

TextoRNN

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Después de incrustar la capa: cargue el vector de palabra previamente entrenado o la inicialización aleatoria, la dimensión del vector de palabra es embed_size: Incrustación (4762, 300)
3. LSTM bidireccional: (lstm): LSTM (300) , 128, num_layers=2, batch_first=True, abandono=0.5, bidireccional=True)
4. Conexión completa: Lineal (in_features=256, out_features=10, bias=True)

Análisis:
LSTM puede capturar mejor las relaciones semánticas de larga distancia, pero debido a su estructura recursiva, no se puede calcular en paralelo y la velocidad es lenta.

El diagrama esquemático es el siguiente:

inserte la descripción de la imagen aquí

# coding: UTF-8
import torch
import torch.nn as nn
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'TextRNN'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 10                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度, 若使用了预训练词向量,则维度统一
        self.hidden_size = 128                                          # lstm隐藏层
        self.num_layers = 2                                             # lstm层数


'''Recurrent Neural Network for Text Classification with Multi-Task Learning'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.lstm = nn.LSTM(config.embed, config.hidden_size, config.num_layers,
                            bidirectional=True, batch_first=True, dropout=config.dropout)
        self.fc = nn.Linear(config.hidden_size * 2, config.num_classes)

    def forward(self, x):
        x, _ = x
        out = self.embedding(x)  # [batch_size, seq_len, embeding]=[128, 32, 300]
        out, _ = self.lstm(out)
        out = self.fc(out[:, -1, :])  # 句子最后时刻的 hidden state
        return out

    '''变长RNN,效果差不多,甚至还低了点...'''
    # def forward(self, x):
    #     x, seq_len = x
    #     out = self.embedding(x)
    #     _, idx_sort = torch.sort(seq_len, dim=0, descending=True)  # 长度从长到短排序(index)
    #     _, idx_unsort = torch.sort(idx_sort)  # 排序后,原序列的 index
    #     out = torch.index_select(out, 0, idx_sort)
    #     seq_len = list(seq_len[idx_sort])
    #     out = nn.utils.rnn.pack_padded_sequence(out, seq_len, batch_first=True)
    #     # [batche_size, seq_len, num_directions * hidden_size]
    #     out, (hn, _) = self.lstm(out)
    #     out = torch.cat((hn[2], hn[3]), -1)
    #     # out, _ = nn.utils.rnn.pad_packed_sequence(out, batch_first=True)
    #     out = out.index_select(0, idx_unsort)
    #     out = self.fc(out)
    #     return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model TextRNN

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados del entrenamiento y la prueba son los siguientes:
usando la versión de CPU de pytorch, toma 18 minutos y 54 segundos, y la tasa de precisión es 90.90%

inserte la descripción de la imagen aquí

TextoRNN_Att

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Después de la capa de incrustación: cargue el vector de palabra previamente entrenado o inicialice aleatoriamente, la dimensión del vector de palabra es embed_size: [batch_size, seq_len, embed_size] 3. LSTM bidireccional:
el el tamaño de la capa oculta es hidden_size, obtenga el estado de la capa oculta en todo momento (capa oculta hacia adelante y costura de capa oculta hacia atrás) [batch_size, seq_len, hidden_size * 2] 4. Inicialice una matriz de peso aprendible w w=[hidden_size * 2
,
1 ]
5. Para LSTM La salida de la activación no lineal se multiplica por matriz con w, y se normaliza por softmax para obtener la puntuación en cada momento:
[batch_size, seq_len, 1]
6. Multiplica el estado de la capa oculta de LSTM en cada momento correspondiente a Las puntuaciones se suman para obtener el valor final de la capa oculta [
tamaño_lote, tamaño_oculto * 2] después del promedio ponderado [tamaño_lote, 1]



Análisis:
Los pasos 4 a 6 son el proceso de cálculo del mecanismo de atención, que en realidad es un promedio ponderado de las capas ocultas de cada momento de lstm. Por ejemplo, si la longitud de la oración es 4, primero calcule el puntaje normalizado en 4 momentos: [0.1, 0.3, 0.4, 0.2] y luego

h último= 0,1 h 1 + 0,3 h 2 + 0,4 h 3 + 0,2 h 4 h_{\text {último}}=0,1 h_1+0,3 h_2+0,4 h_3+0,2 h_4húltimo=0,1 horas1+0.3h _2+0,4 horas3+0.2h _4

El diagrama esquemático es el siguiente:

inserte la descripción de la imagen aquí

# coding: UTF-8
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'TextRNN_Att'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 10                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度, 若使用了预训练词向量,则维度统一
        self.hidden_size = 128                                          # lstm隐藏层
        self.num_layers = 2                                             # lstm层数
        self.hidden_size2 = 64


'''Attention-Based Bidirectional Long Short-Term Memory Networks for Relation Classification'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.lstm = nn.LSTM(config.embed, config.hidden_size, config.num_layers,
                            bidirectional=True, batch_first=True, dropout=config.dropout)
        self.tanh1 = nn.Tanh()
        # self.u = nn.Parameter(torch.Tensor(config.hidden_size * 2, config.hidden_size * 2))
        self.w = nn.Parameter(torch.zeros(config.hidden_size * 2))
        self.tanh2 = nn.Tanh()
        self.fc1 = nn.Linear(config.hidden_size * 2, config.hidden_size2)
        self.fc = nn.Linear(config.hidden_size2, config.num_classes)

    def forward(self, x):
        x, _ = x
        emb = self.embedding(x)  # [batch_size, seq_len, embeding]=[128, 32, 300]
        H, _ = self.lstm(emb)  # [batch_size, seq_len, hidden_size * num_direction]=[128, 32, 256]

        M = self.tanh1(H)  # [128, 32, 256]
        # M = torch.tanh(torch.matmul(H, self.u))
        alpha = F.softmax(torch.matmul(M, self.w), dim=1).unsqueeze(-1)  # [128, 32, 1]
        out = H * alpha  # [128, 32, 256]
        out = torch.sum(out, 1)  # [128, 256]
        out = F.relu(out)
        out = self.fc1(out)
        out = self.fc(out)  # [128, 64]
        return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model TextRNN_Att

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados del entrenamiento y la prueba son los siguientes:
usando la versión de CPU de pytorch, toma 10 minutos y 48 segundos, y la tasa de precisión es 89.89%

inserte la descripción de la imagen aquí

Texto rápido

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Capa de incrustación: inicialización aleatoria, la dimensión del vector de palabra es embed_size, 2 gramos y 3 gramos son iguales:
palabra: [batch_size, seq_len, embed_size]
2 gramos: [batch_size , seq_len, incrustar_tamaño]
3 gramos: [batch_size, seq_len, incrustar_tamaño]
3. Empalme capa de incrustación:
[batch_size, seq_len, embed_size * 3]
4. Encuentre el promedio de todas las palabras seq_len
[batch_size, incrustar_tamaño * 3]
5. Completo conexión + activación no lineal: tamaño de capa oculta hidden_size
[batch_size, hidden_size]
6. Conexión completa + normalización softmax:
[batch_size, num_class]==>[batch_size, 1]

El diagrama esquemático es el siguiente:
inserte la descripción de la imagen aquí

# coding: UTF-8
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'FastText'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 20                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度
        self.hidden_size = 256                                          # 隐藏层大小
        self.n_gram_vocab = 250499                                      # ngram 词表大小


'''Bag of Tricks for Efficient Text Classification'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.embedding_ngram2 = nn.Embedding(config.n_gram_vocab, config.embed)
        self.embedding_ngram3 = nn.Embedding(config.n_gram_vocab, config.embed)
        self.dropout = nn.Dropout(config.dropout)
        self.fc1 = nn.Linear(config.embed * 3, config.hidden_size)
        # self.dropout2 = nn.Dropout(config.dropout)
        self.fc2 = nn.Linear(config.hidden_size, config.num_classes)

    def forward(self, x):

        out_word = self.embedding(x[0])
        out_bigram = self.embedding_ngram2(x[2])
        out_trigram = self.embedding_ngram3(x[3])
        out = torch.cat((out_word, out_bigram, out_trigram), -1)

        out = out.mean(dim=1)
        out = self.dropout(out)
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model FastText

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados del entrenamiento y la prueba son los siguientes:
usando la versión de CPU de pytorch, se tardó 1 hora, 47 minutos y 40 segundos, y la tasa de precisión fue del 92,07 %.

inserte la descripción de la imagen aquí

TextoRCNN

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Después de la capa de incrustación: cargue el vector de palabra previamente entrenado o inicialice aleatoriamente, la dimensión del vector de palabra es embed_size: [batch_size, seq_len, embed_size] 3. LSTM bidireccional:
el el tamaño de la capa oculta es hidden_size, obtenga el estado de la capa oculta en todo momento (capa oculta hacia adelante y empalme de capa oculta hacia atrás) [batch_size, seq_len, hidden_size * 2] 4. Concatene la
capa incrustada con la salida LSTM y realice una activación no lineal:
[batch_size, seq_len, hidden_size * 2 + incrustar_tamaño]
5. Capa de agrupación: tome las características más grandes de seq_len
[batch_size, hidden_size * 2 + embed_size]
6. Softmax después de la conexión completa
[batch_size, num_class] ==> [batch_size, 1]

Análisis:
el valor de la capa oculta (hacia adelante + hacia atrás) del LSTM bidireccional en cada momento puede representar la información semántica hacia adelante y hacia atrás de la palabra actual, y el valor oculto y el valor de incrustación se empalman para representar una palabra; luego el máximo La capa de agrupación se utiliza para filtrar información útil sobre entidades.

El diagrama esquemático es el siguiente:

inserte la descripción de la imagen aquí

# coding: UTF-8
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'TextRCNN'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 1.0                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 10                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度, 若使用了预训练词向量,则维度统一
        self.hidden_size = 256                                          # lstm隐藏层
        self.num_layers = 1                                             # lstm层数


'''Recurrent Convolutional Neural Networks for Text Classification'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.lstm = nn.LSTM(config.embed, config.hidden_size, config.num_layers,
                            bidirectional=True, batch_first=True, dropout=config.dropout)
        self.maxpool = nn.MaxPool1d(config.pad_size)
        self.fc = nn.Linear(config.hidden_size * 2 + config.embed, config.num_classes)

    def forward(self, x):
        x, _ = x
        embed = self.embedding(x)  # [batch_size, seq_len, embeding]=[64, 32, 64]
        out, _ = self.lstm(embed)
        out = torch.cat((embed, out), 2)
        out = F.relu(out)
        out = out.permute(0, 2, 1)
        out = self.maxpool(out).squeeze()
        out = self.fc(out)
        return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model TextRCNN

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados del entrenamiento y la prueba son los siguientes:
usando la versión de CPU de pytorch, toma 10 minutos y 23 segundos, y la tasa de precisión es 90.83%

inserte la descripción de la imagen aquí

DPCNN

Descripcion del modelo

1. Entrada del modelo: [batch_size, seq_len]
2. Después de la capa de incrustación: cargue el vector de palabra previamente entrenado o inicialice aleatoriamente, la dimensión del vector de palabra es embed_size: [batch_size, seq_len, embed_size] 3. Realice la convolución,
250 palabras con un tamaño de núcleo de convolución 3, esta capa se llama región incrustada en el papel.
[batch_size, 250, seq_len - 3 + 1]
4. Conecte dos capas de convolución (+relu), cada capa tiene 250 núcleos de convolución con un tamaño de 3, (convolución de igual longitud, primero relleno y luego convolución, para asegurar la convolución La longitud de la secuencia antes y después del producto permanece sin cambios)
[batch_size, 250, seq_len - 3 + 1]
5. A continuación, realice la operación en el cuadro pequeño de la figura anterior.
I. Realice la agrupación máxima con un tamaño de 3 y un tamaño de paso de 2, y comprima la longitud de la secuencia a la mitad del original. (Muestreo)
II Conecte dos capas de convolución de igual longitud (+relu), cada capa tiene 250 núcleos de convolución con un tamaño de 3.
III El resultado de I más el resultado de II. (Conexión residual)
Repita la operación anterior hasta que la longitud de la secuencia sea igual a 1.
[batch_size, 250, 1]
6. Conexión completa + normalización softmax:
[batch_size, num_class]==>[batch_size, 1]

Análisis:
el proceso de TextCNN es similar a la extracción de información de N-Gram, y solo hay una capa, lo que dificulta la captura de características de larga distancia.
En contraste con DPCNN, se puede ver que su región incrustada es un TextCNN que elimina la capa de agrupación y luego superpone la capa convolucional.
inserte la descripción de la imagen aquí

La longitud de la secuencia de cada capa se reduce a la mitad (como se muestra en la figura anterior), lo que se puede entender de esta manera: es equivalente a hacer N-Gram en N-Gram. Cuanto más tarde es la capa, más información se fusiona en cada posición, y la última capa extrae la información semántica de toda la secuencia.

El diagrama esquemático es el siguiente:
inserte la descripción de la imagen aquí

# coding: UTF-8
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'DPCNN'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 1000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 20                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 1e-3                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度
        self.num_filters = 250                                          # 卷积核数量(channels数)


'''Deep Pyramid Convolutional Neural Networks for Text Categorization'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
        self.conv_region = nn.Conv2d(1, config.num_filters, (3, config.embed), stride=1)
        self.conv = nn.Conv2d(config.num_filters, config.num_filters, (3, 1), stride=1)
        self.max_pool = nn.MaxPool2d(kernel_size=(3, 1), stride=2)
        self.padding1 = nn.ZeroPad2d((0, 0, 1, 1))  # top bottom
        self.padding2 = nn.ZeroPad2d((0, 0, 0, 1))  # bottom
        self.relu = nn.ReLU()
        self.fc = nn.Linear(config.num_filters, config.num_classes)

    def forward(self, x):
        x = x[0]
        x = self.embedding(x)
        x = x.unsqueeze(1)  # [batch_size, 250, seq_len, 1]
        x = self.conv_region(x)  # [batch_size, 250, seq_len-3+1, 1]

        x = self.padding1(x)  # [batch_size, 250, seq_len, 1]
        x = self.relu(x)
        x = self.conv(x)  # [batch_size, 250, seq_len-3+1, 1]
        x = self.padding1(x)  # [batch_size, 250, seq_len, 1]
        x = self.relu(x)
        x = self.conv(x)  # [batch_size, 250, seq_len-3+1, 1]
        while x.size()[2] > 2:
            x = self._block(x)
        x = x.squeeze()  # [batch_size, num_filters(250)]
        x = self.fc(x)
        return x

    def _block(self, x):
        x = self.padding2(x)
        px = self.max_pool(x)

        x = self.padding1(px)
        x = F.relu(x)
        x = self.conv(x)

        x = self.padding1(x)
        x = F.relu(x)
        x = self.conv(x)

        # Short Cut
        x = x + px
        return x

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model DPCNN

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados de la capacitación y la prueba son los siguientes:
con la versión de CPU de pytorch, tomó 19 minutos y 26 segundos, y la tasa de precisión fue del 91,21 %.
inserte la descripción de la imagen aquí

Transformador

Descripcion del modelo

El diagrama esquemático es el siguiente:
inserte la descripción de la imagen aquí
Como la mayoría de los modelos seq2seq, la estructura del transformador también se compone de un codificador y un decodificador.

codificador:

El codificador consta de 6 bloques idénticos (la parte izquierda de la estructura del modelo), y la capa se refiere a la unidad en el lado izquierdo del diagrama esquemático. Cada bloque consta de un bloque de autoatención de varios cabezales y un bloque de propagación directa completamente conectado. Ambas partes también agregaron conexión residual y normalización.

autoatención de múltiples cabezas

inserte la descripción de la imagen aquí

La atención de múltiples cabezas es combinar múltiples mecanismos de atención y unir las salidas después del procesamiento de la atención. Se puede expresar como:

 MultiHead ( Q , K , V ) = Concat ( cabeza 1 , ⋅ , cabeza n ) WO hheadi = Atención ⁡ ( QW i Q , KW i K , VW i V ) \begin{alineado} & \text { MultiHead }(Q , K, V)=\text { Concat }\left(\text { head }_1, \cdot, \text { head }_n\right) W^O \\ & h^{head_i}=\operatorname{Atención }\left(Q W_i^Q, K W_i^K, V W_i^V\right) \\ & \end{alineado} cabezales múltiples  ( Q ,K ,V )= concat (  cabeza 1,, cabeza n)WOhcabeza _ _ _yo=Atención( Q Wiq,KW _ik,VW _iV)

Y la atención propia es que Q, K y V toman el mismo valor.

Decodificador :

Las estructuras de Decoder y Encoder son muy similares Vale la pena prestar atención a la entrada y salida de Decoder.

  • Entrada: incluye la salida del Encoder y la salida del Decoder correspondiente a la posición i-1. Entonces, la atención en el medio no es autoatención, su K, V proviene del codificador y Q proviene de la salida del decodificador anterior;
  • Salida: La salida es la distribución de probabilidad de la palabra de salida correspondiente a la posición.

El mecanismo del decodificador durante el entrenamiento y la predicción también es diferente.Durante el entrenamiento, la decodificación se decodifica toda a la vez, y la verdad básica en el paso anterior se usa para predecir (la matriz de máscara también se cambiará, de modo que los tokens futuros no puedan ser visto durante la decodificación); Al predecir, debido a que no hay una verdad fundamental, debe predecirse uno por uno.

Codificación posicional :

Para reflejar las características de la secuencia en el modelo, es necesario codificar la información de posición de la secuencia en la entrada. El vector de entrada final se obtiene sumando la codificación posicional y la codificación de incrustación. En general, hay dos formas de codificar la información de ubicación: una es la codificación basada en fórmulas y la otra es la codificación aprendida dinámicamente a través del entrenamiento. El autor del texto original ha probado que los efectos de los dos métodos son básicamente los mismos, y que la codificación basada en fórmulas no requiere entrenamiento adicional y puede manejar secuencias de longitudes que no han aparecido en el conjunto de entrenamiento, por lo que el Transformador utiliza una codificación de posición basada en fórmulas:

PE ( pos , 2 i ) = sin ⁡ ( pos / 1000 0 2 i / d modelo ) PE ( pos , 2 i + 1 = cos ⁡ ( pos / 1000 0 2 i / d modelo ) \begin{reunidos} P E_ {(pos, 2 i)}=\sin \left(\text { pos } / 10000^{2 i / d_{\text {modelo}}}\right) \\ P E_{(pos, 2 i+1 }=\cos \left(pos / 10000^{2 i / d_{\text {modelo}}}\right) \end{reunidos}educación física( pos , 2 i ) _=pecado(  posición  /1000 02 i / dmodelo )educación física( pos , 2 i + 1 _=porque( p o /1000 02 i / dmodelo )

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import copy


class Config(object):

    """配置参数"""
    def __init__(self, dataset, embedding):
        self.model_name = 'Transformer'
        self.train_path = dataset + '/data/train.txt'                                # 训练集
        self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
        self.test_path = dataset + '/data/test.txt'                                  # 测试集
        self.class_list = [x.strip() for x in open(
            dataset + '/data/class.txt', encoding='utf-8').readlines()]              # 类别名单
        self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
        self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
        self.log_path = dataset + '/log/' + self.model_name
        self.embedding_pretrained = torch.tensor(
            np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
            if embedding != 'random' else None                                       # 预训练词向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备

        self.dropout = 0.5                                              # 随机失活
        self.require_improvement = 2000                                 # 若超过1000batch效果还没提升,则提前结束训练
        self.num_classes = len(self.class_list)                         # 类别数
        self.n_vocab = 0                                                # 词表大小,在运行时赋值
        self.num_epochs = 20                                            # epoch数
        self.batch_size = 128                                           # mini-batch大小
        self.pad_size = 32                                              # 每句话处理成的长度(短填长切)
        self.learning_rate = 5e-4                                       # 学习率
        self.embed = self.embedding_pretrained.size(1)\
            if self.embedding_pretrained is not None else 300           # 字向量维度
        self.dim_model = 300
        self.hidden = 1024
        self.last_hidden = 512
        self.num_head = 5
        self.num_encoder = 2


'''Attention Is All You Need'''


class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)

        self.postion_embedding = Positional_Encoding(config.embed, config.pad_size, config.dropout, config.device)
        self.encoder = Encoder(config.dim_model, config.num_head, config.hidden, config.dropout)
        self.encoders = nn.ModuleList([
            copy.deepcopy(self.encoder)
            # Encoder(config.dim_model, config.num_head, config.hidden, config.dropout)
            for _ in range(config.num_encoder)])

        self.fc1 = nn.Linear(config.pad_size * config.dim_model, config.num_classes)
        # self.fc2 = nn.Linear(config.last_hidden, config.num_classes)
        # self.fc1 = nn.Linear(config.dim_model, config.num_classes)

    def forward(self, x):
        out = self.embedding(x[0])
        out = self.postion_embedding(out)
        for encoder in self.encoders:
            out = encoder(out)
        out = out.view(out.size(0), -1)
        # out = torch.mean(out, 1)
        out = self.fc1(out)
        return out


class Encoder(nn.Module):
    def __init__(self, dim_model, num_head, hidden, dropout):
        super(Encoder, self).__init__()
        self.attention = Multi_Head_Attention(dim_model, num_head, dropout)
        self.feed_forward = Position_wise_Feed_Forward(dim_model, hidden, dropout)

    def forward(self, x):
        out = self.attention(x)
        out = self.feed_forward(out)
        return out


class Positional_Encoding(nn.Module):
    def __init__(self, embed, pad_size, dropout, device):
        super(Positional_Encoding, self).__init__()
        self.device = device
        self.pe = torch.tensor([[pos / (10000.0 ** (i // 2 * 2.0 / embed)) for i in range(embed)] for pos in range(pad_size)])
        self.pe[:, 0::2] = np.sin(self.pe[:, 0::2])
        self.pe[:, 1::2] = np.cos(self.pe[:, 1::2])
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        out = x + nn.Parameter(self.pe, requires_grad=False).to(self.device)
        out = self.dropout(out)
        return out


class Scaled_Dot_Product_Attention(nn.Module):
    '''Scaled Dot-Product Attention '''
    def __init__(self):
        super(Scaled_Dot_Product_Attention, self).__init__()

    def forward(self, Q, K, V, scale=None):
        '''
        Args:
            Q: [batch_size, len_Q, dim_Q]
            K: [batch_size, len_K, dim_K]
            V: [batch_size, len_V, dim_V]
            scale: 缩放因子 论文为根号dim_K
        Return:
            self-attention后的张量,以及attention张量
        '''
        attention = torch.matmul(Q, K.permute(0, 2, 1))
        if scale:
            attention = attention * scale
        # if mask:  # TODO change this
        #     attention = attention.masked_fill_(mask == 0, -1e9)
        attention = F.softmax(attention, dim=-1)
        context = torch.matmul(attention, V)
        return context


class Multi_Head_Attention(nn.Module):
    def __init__(self, dim_model, num_head, dropout=0.0):
        super(Multi_Head_Attention, self).__init__()
        self.num_head = num_head
        assert dim_model % num_head == 0
        self.dim_head = dim_model // self.num_head
        self.fc_Q = nn.Linear(dim_model, num_head * self.dim_head)
        self.fc_K = nn.Linear(dim_model, num_head * self.dim_head)
        self.fc_V = nn.Linear(dim_model, num_head * self.dim_head)
        self.attention = Scaled_Dot_Product_Attention()
        self.fc = nn.Linear(num_head * self.dim_head, dim_model)
        self.dropout = nn.Dropout(dropout)
        self.layer_norm = nn.LayerNorm(dim_model)

    def forward(self, x):
        batch_size = x.size(0)
        Q = self.fc_Q(x)
        K = self.fc_K(x)
        V = self.fc_V(x)
        Q = Q.view(batch_size * self.num_head, -1, self.dim_head)
        K = K.view(batch_size * self.num_head, -1, self.dim_head)
        V = V.view(batch_size * self.num_head, -1, self.dim_head)
        # if mask:  # TODO
        #     mask = mask.repeat(self.num_head, 1, 1)  # TODO change this
        scale = K.size(-1) ** -0.5  # 缩放因子
        context = self.attention(Q, K, V, scale)

        context = context.view(batch_size, -1, self.dim_head * self.num_head)
        out = self.fc(context)
        out = self.dropout(out)
        out = out + x  # 残差连接
        out = self.layer_norm(out)
        return out


class Position_wise_Feed_Forward(nn.Module):
    def __init__(self, dim_model, hidden, dropout=0.0):
        super(Position_wise_Feed_Forward, self).__init__()
        self.fc1 = nn.Linear(dim_model, hidden)
        self.fc2 = nn.Linear(hidden, dim_model)
        self.dropout = nn.Dropout(dropout)
        self.layer_norm = nn.LayerNorm(dim_model)

    def forward(self, x):
        out = self.fc1(x)
        out = F.relu(out)
        out = self.fc2(out)
        out = self.dropout(out)
        out = out + x  # 残差连接
        out = self.layer_norm(out)
        return out

Ejecute los siguientes comandos en la terminal para entrenamiento y prueba:

python run.py --model Transformer

El proceso de formación es el siguiente:

inserte la descripción de la imagen aquí

Los resultados del entrenamiento y las pruebas son los siguientes:

El bloguero usó la versión CPU de pytorch, que tardó casi 4 horas y no completó una ronda.

inserte la descripción de la imagen aquí

Desinstale rápidamente la versión de CPU de pytorch, utilice la versión de GPU de pytorch y tardará 4 minutos y 25 segundos en ejecutarse. Precisión: 90,01%.

inserte la descripción de la imagen aquí

Comparación de los efectos de cada modelo

Modelo cuenta Observación
TextoCNN 90,99% Kim 2014 Clasificación de texto clásico de CNN
TextoRNN 90,90% BiLS™
TextoRNN_Att 89,89% BiLSTM+Atención
TextoRCNN 90,83% BiLSTM+agrupación
Texto rápido 92,07% arco+bigrama+trigrama, el efecto es sorprendentemente bueno
DPCNN 91,21% Pirámide profunda CNN
Transformador 90,01% menos efectivo

Referencias

Implementación de pytorch de clasificación de texto chino

Descarga de otra información

Si desea continuar aprendiendo sobre rutas de aprendizaje y sistemas de conocimiento relacionados con la inteligencia artificial, bienvenido a leer mi otro blog " Pesado | Ruta de aprendizaje de conocimiento básico de aprendizaje de inteligencia artificial completa, todos los materiales se pueden descargar directamente desde el disco de la red sin pagar atención a las rutinas "
Este blog se refiere a la conocida plataforma de código abierto de Github, la plataforma de tecnología de IA y expertos en campos relacionados: Datawhale, ApacheCN, AI Youdao y Dr. Huang Haiguang, etc. Hay alrededor de 100G de materiales relacionados, y espero ayuda a todos tus amigos.

Supongo que te gusta

Origin blog.csdn.net/qq_31136513/article/details/131589556
Recomendado
Clasificación