Aprendizaje de entrada NLP 2: clasificación de texto (basado en keras para construir LSTM)

0. Introducción

Este artículo presentará en detalle el uso de keras para construir un modelo LSTM de una sola capa, multicapa, unidireccional y bidireccional de una manera práctica para completar la clasificación de datos de noticias. Es principalmente para principiantes de PNL, por lo que está escrito con el mayor detalle posible. Por supuesto, las valiosas opiniones de todos los grandes son bienvenidas.
Los datos utilizados son un subconjunto de los datos de noticias de la Universidad de Tsinghua THUCNews,
enlace de descarga: https://pan.baidu.com/s/1U_Ypqiu8Cq4IAWdqiDLR_g Código de extracción: 9766 Los conjuntos de entrenamiento, val y prueba
del sitio web adjunto de THUCNews http://thuctc.thunlp.org/
contienen datos 50,000, 5,000 y 10,0 00 noticias respectivamente, e indicar a qué noticia pertenece cada noticia Una categoría, 10 categorías en total.
Primero publique el enlace de referencia:
Enlace de referencia 1
Enlace de referencia 2
Enlace de referencia 3
Enlace de referencia 4
Enlace de referencia 5
La referencia principal de este artículo https://zhuanlan.zhihu.com/p/39884984 describe el proceso de construcción y entrenamiento del modelo con más detalle Sobre esta base, agregué mi propia comprensión y notas más detalladas, así como más estructuras del modelo.

1. Dependencia ambiental

Este artículo utiliza principalmente los siguientes módulos:

módulo Versión
duro 2.4.3
scikit-aprender 0.19.1
espía 1.0.0
nacido en el mar 0.8.1
entumecido 1.14.0

Tenga en cuenta que si la versión de numpy es demasiado alta, puede ocurrir el problema de la falla de importación de sklearn.

2. Procesamiento de segmentación de palabras

Después de obtener los datos, primero debemos hacer un preprocesamiento simple, que consiste en segmentar las noticias. Hay varias herramientas disponibles. También podría usar la herramienta de segmentación de palabras chinas de uso común jieba.

train_df['cutword'] = ''         # 给dataframe新建一个名为cutword的列
for row in train_df.iterrows():           # 对每一行进行操作
    cut_res = ' '.join(jieba.cut(row[1]['text']))           # jieba分词
    #print(cut_res)
    train_df['cutword'][row[0]] = cut_res          # 将分词的结果写入dataframe

El marco de datos después del procesamiento debería verse así.
resultado de la segmentación de palabras
El mismo valor y prueba también deben procesarse de la misma manera.
O si no quiere hacer la segmentación de palabras usted mismo si le resulta problemático, puede usar directamente el archivo csv dividido en el enlace de referencia 1, y hay un enlace de descarga en el texto original.

3. Construcción de modelos

3.1 LSTM unidireccional de una sola capa

Esta parte es en realidad una reproducción del contenido en https://zhuanlan.zhihu.com/p/39884984 . Si al blogger original le importa, recuérdeme que lo modifique o elimine.

El primero es importar los paquetes requeridos:

import pandas as pd
import numpy as np
from sklearn import metrics                                      # 模型评价指标
from sklearn.preprocessing import LabelEncoder,OneHotEncoder    # 用于对数据集的标签进行编码
from keras.models import Model                                  # 通用模型定义方法
from keras import Sequential                                    # 序列模型定义方法
from keras.layers import LSTM, Activation, Dense, Dropout, Input, Embedding    # keras中添加的各层
from keras.optimizers import RMSprop                             # 优化器
from keras.preprocessing.text import Tokenizer                  # 词典生成
from keras.preprocessing import sequence                        # 主要用于序列的padding
from keras.callbacks import EarlyStopping                         # 训练过程中的早停
import seaborn as sns

Aquí, debido a mi pereza, no configuré la fuente y la reemplacé directamente con pinyin cuando dibujé más tarde.
Datos de importacion:

train_df = pd.read_csv('/root/news/cnews_train.csv')
val_df = pd.read_csv('/root/news/cnews_val.csv')
test_df = pd.read_csv('/root/news/cnews_test.csv')
test_df.head()

Codificar la etiqueta de los datos

# 对标签进行编码
# labelencoder的效果是将标签转变为数字编号,本例中就是0-9的数字
train_y = train_df.label
val_y = val_df.label
test_y = test_df.label
LabelE = LabelEncoder()
train_y = LabelE.fit_transform(train_y).reshape(-1,1)
val_y = LabelE.transform(val_y).reshape(-1,1)
test_y = LabelE.transform(test_y).reshape(-1,1)

# 对标签进行one-hot编码
# 再将刚才的编号转为one-hot
OneHotE = OneHotEncoder()
train_y = OneHotE.fit_transform(train_y).toarray()
val_y = OneHotE.transform(val_y).toarray()
test_y = OneHotE.transform(test_y).toarray()

De acuerdo con la frecuencia de palabras, las palabras en el texto están numeradas y las palabras con mayor frecuencia de palabras tienen números más pequeños.

max_words = 5000                    # 词表中的最大词语数量
max_len = 600                       # 新闻向量的最大长度
tok = Tokenizer(num_words=max_words)    
tok.fit_on_texts(train_df.cutword)

Use tok.word_index.items() para ver los números correspondientes a las 10 palabras con la mayor frecuencia de palabras:

for ii,iterm in enumerate(tok.word_index.items()):
    if ii < 10:
        print(iterm)
    else:
        break

输出:
('我们', 1)
('一个', 2)
('中国', 3)
('可以', 4)
('基金', 5)
('没有', 6)
('自己', 7)
('他们', 8)
('市场', 9)
('这个', 10)

Use tok.word_counts.items() para ver la frecuencia de las primeras 10 palabras en el diccionario de sinónimos en el diccionario de sinónimos:

for ii,iterm in enumerate(tok.word_counts.items()):
    if ii < 10:
        print(iterm)
    else:
        break

输出:
('马晓旭', 2)
('意外', 1641)
('受伤', 1948)
('国奥', 148)
('警惕', 385)
('无奈', 1161)
('大雨', 77)
('格外', 529)
('青睐', 1092)
('殷家', 1)

Hasta ahora cada palabra ha sido representada por un número, luego cada noticia se puede convertir en un vector. Y para garantizar que en la entrada del siguiente modelo todos los vectores mantengan la misma dimensión, se requiere la operación de relleno, es decir, todas las noticias se llenan con la misma longitud, max_len=600

train_seq = tok.texts_to_sequences(train_df.cutword)
val_seq = tok.texts_to_sequences(val_df.cutword)
test_seq = tok.texts_to_sequences(test_df.cutword)

# 将每个序列调整为相同的长度
train_seq_mat = sequence.pad_sequences(train_seq,maxlen=max_len)
val_seq_mat = sequence.pad_sequences(val_seq,maxlen=max_len)
test_seq_mat = sequence.pad_sequences(test_seq,maxlen=max_len)

Una vez realizados los preparativos, puede utilizar keras para construir el modelo LSTM. La construcción de modelos en keras incluye principalmente dos métodos, modelo general y modelo de secuencia. El artículo original al que se hace referencia utiliza el modelo general Model para construir. Dado que este artículo está dirigido a principiantes, también proporciona otro método para construir el modelo de secuencia Sequential. En cuanto a la diferencia entre los dos modelos, no daré una introducción detallada aquí, puedes Baidu por ti mismo.
Primero el modelo general:

inputs = Input(name='inputs',shape=[max_len])
## Embedding(词汇表大小,batch大小,每个新闻的词长)
layer = Embedding(max_words+1,128,input_length=max_len)(inputs)     # 定义Embedding层,128是embedding之后的维度
layer = LSTM(128)(layer)                                     # 定义LSTM层,上一层的输出维度128
layer = Dense(128,activation="relu",name="FC1")(layer)       # 定义全连接层
layer = Dropout(0.5)(layer)
layer = Dense(10,activation="softmax",name="FC2")(layer)
model = Model(inputs=inputs,outputs=layer)                # 建立模型
model.summary()
model.compile(loss="categorical_crossentropy",optimizer=RMSprop(),metrics=["accuracy"])    # 损失函数、优化器、评价标准

Resumen del modelo:
Resumen Modelo
en este punto, se han completado la preparación de datos y la construcción de la estructura del modelo, y luego puede comenzar la capacitación. Para ahorrar tiempo, aquí se adopta un mecanismo de parada anticipada.

model_fit = model.fit(train_seq_mat,train_y,batch_size=128,epochs=10,
                      validation_data=(val_seq_mat,val_y),
                      callbacks=[EarlyStopping(monitor='val_loss',min_delta=0.0001)]     # 当val-loss不再提升时停止训练
                     )

Después de solo dos épocas en mi proceso de entrenamiento, el efecto en el conjunto de prueba ya no mejora, por lo que se activa la detención temprana. La pérdida y la precisión son las siguientes:
entrenamiento modelo
luego prediga el conjunto de prueba y observe la precisión y la recuperación del modelo.

# 对测试集进行预测
test_pre = model.predict(test_seq_mat)
# 计算混淆矩阵
confm = metrics.confusion_matrix(np.argmax(test_pre,axis=1),np.argmax(test_y,axis=1))
print(metrics.classification_report(np.argmax(test_pre,axis=1),np.argmax(test_y,axis=1)))

evaluación del modelo
Visualiza la matriz de confusión con un mapa de calor:

Labname = ["tiyu","yule","jiaju","fangchan","jiaoyu","shishang","shizheng","youxi","keji","caijing"]  # 没有设置字体,就直接用拼音代替了
plt.figure(figsize=(8,8))
sns.heatmap(confm.T, square=True, annot=True,
            fmt='d', cbar=False,linewidths=.8,
            cmap="YlGnBu")
plt.xlabel('True label',size = 14)
plt.ylabel('Predicted label',size = 14)
plt.xticks(np.arange(10)+0.5,Labname,size = 12)
plt.yticks(np.arange(10)+0.3,Labname,size = 12)
plt.show()

Mapa de calor de la matriz de confusión
En términos generales, el efecto no es malo y los resultados del blogger original son básicamente consistentes.
Luego, use el método del modelo de secuencia para construir LSTM. Pegue el código directamente:

from keras import Sequential
model = Sequential()          # 首先将模型定义为序列模型
model.add(Embedding(max_words+1, 128, input_length=max_len))    # 添加一个embedding层
model.add(LSTM(128))                                             # 添加一个LSTM层
model.add(Dense(128,activation='relu',name='FC1'))               # 添加一个全连接层
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax',name='FC2'))
model.compile(loss='categorical_crossentropy',optimizer=RMSprop(),metrics=['accuracy'])

model.summary()

Comparando los resultados del resumen, se encuentra que es completamente consistente con el modelo anterior.
resumen
O entrenar de la misma manera. Aquí simplemente deje que la época se fije en 2.

model_fit = model.fit(train_seq_mat,train_y,batch_size=128,epochs=2,
                      validation_data=(val_seq_mat,val_y),
                      callbacks=[EarlyStopping(monitor='val_loss',min_delta=0.0001)]
                     )

Resulta que el efecto después de entrenar durante dos épocas no es tan bueno como el efecto de entrenar al modelo durante dos épocas justo ahora. Tal vez el modelo no debería dejar de entrenar en este momento. Pero a medida que avanza la época, los efectos finales de los dos modelos deberían ser consistentes.

3.2 LSTM multicapa unidireccional

La definición de LSTM multicapa es similar a la de LSTM de una sola capa, solo preste atención a la salida de la capa anterior.
Aquí, tomando el modelo de secuencia como ejemplo, se construye un modelo LSTM de dos capas.

from keras import Sequential
model = Sequential()
model.add(Embedding(max_words+1, 128, input_length=max_len))
model.add(LSTM(128,return_sequences=True,name='LSTM1'))         # 添加第一个LSTM层
model.add(LSTM(128,name='LSTM2'))                              # 添加第二个LSTM层
model.add(Dense(128,activation='relu',name='FC1'))
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax',name='FC2'))
model.compile(loss='categorical_crossentropy',optimizer=RMSprop(),metrics=['accuracy'])

model.summary()

Es necesario prestar atención a la diferencia entre la adición de las dos capas de LSTM. No hay diferencia entre la adición de la segunda capa de LSTM y la capa anterior de LSTM, pero si la capa superior de LSTM no escribe return_sequences=True, será dimensionalmente incorrecto al ejecutar la segunda capa de la declaración de suma de LSTM. Se trata del parámetro return_sequences de LSTM.
Este parámetro se usa para controlar si la capa LSTM devuelve el estado oculto de cada nodo.En keras, el return_sequences predeterminado es False, es decir, solo se devuelve el estado oculto de la última vez.
De esta forma, si no registras el estado oculto en todo momento, entonces en el proceso de entrada de la segunda capa LSTM, la dimensión de entrada ya no será 128, por lo que se reportará un error.
Luego mire el resumen del modelo:
resumen
luego intente entrenar dos épocas, la relación de tiempo no se ha entrenado mucho.
tren
El entrenamiento del modelo se ha ralentizado, pero a juzgar por las dos primeras épocas, agregar una capa de LSTM no mejoró la precisión del modelo. No sé cuál será el efecto si continuamos entrenando.

3.3 LSTM bidireccional

Usando keras para construir un modelo bidireccional, solo necesita importar Bidireccional y luego agregar Bidireccional delante de la capa LSTM para que sea bidireccional.

from keras import Sequential
from keras.layers import Bidirectional
model = Sequential()
model.add(Embedding(max_words+1, 128, input_length=max_len))
model.add(Bidirectional(LSTM(128)))
model.add(Dense(128,activation='relu',name='FC1'))
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax',name='FC2'))
model.compile(loss='categorical_crossentropy',optimizer=RMSprop(),metrics=['accuracy'])

model.summary()

Luego, entrena durante 2 épocas de la misma manera.
tren
El efecto no parece haber cambiado mucho. Es más largo que el tiempo requerido para el entrenamiento en el caso de una sola capa unidireccional.

4. Guarda y aplica el modelo.

4.1 Guardar el modelo

Además de guardar el modelo, también debe guardar el tokenizador entrenado. El siguiente código se está guardando y cargando respectivamente.

import pickle
# saving
with open('tok.pickle', 'wb') as handle:
    pickle.dump(tok, handle, protocol=pickle.HIGHEST_PROTOCOL)

# loading
with open('tok.pickle', 'rb') as handle:
    tok = pickle.load(handle)

Luego guarde el modelo como un archivo h5 y cargue el modelo:

from keras.models import load_model
# 保存模型
model.save('LSTM.h5')  

del model  
# 加载模型
model = load_model(LSTM.h5')
# 如果工作空间中已有模型model,需要先把它删了再加载。
# del model

4.2 Aplicación del modelo

Una vez que se entrena el modelo, se puede utilizar para clasificar noticias. Si desea ver directamente el efecto de clasificación en el conjunto de validación:

val_seq = tok.texts_to_sequences(val_df.cutword)
# 将每个序列调整为相同的长度
val_seq_mat = sequence.pad_sequences(val_seq,maxlen=max_len)
# 对验证集进行预测
pre = model.predict(val_seq_mat)

pre es una matriz, cada elemento contiene diez elementos y la posición del elemento más grande es su categoría correspondiente.
Pero con tal uso, parece. . . No es muy conveniente Si desea ver la categoría correspondiente a las noticias de una determinada identificación en el conjunto de verificación, puede usar una función tan simple para lograrlo:

def pre_res(id):
    '''
    查看指定id的新闻的分类结果
    '''
    loc = np.argmax(val_pre[id])
    if loc == 0:
        res = '体育'
    elif loc == 1:
        res = '娱乐'
    elif loc == 2:
        res = '家居'
    elif loc == 3:
        res = '房产'
    elif loc == 4:
        res = '教育'
    elif loc == 5:
        res = '时尚'
    elif loc == 6:
        res = '时政'
    elif loc == 7:
        res = '游戏'
    elif loc == 8:
        res = '科技'
    elif loc == 9:
        res = '财经'
    
    return res

tener una prueba:

pre_res(4998)

El resultado es la financiación. La clasificación es precisa.

5. fin

En este punto, la introducción del uso de keras para compilar LSTM está completa. A continuación, depende del momento y el estado de ánimo para cambiar la versión de TensorFlow o torch, así que nos vemos la próxima vez.

Supongo que te gusta

Origin blog.csdn.net/weixin_44826203/article/details/107536669
Recomendado
Clasificación