NLP之LSTM与BiLSTM

代码展示了如何使用TensorFlow和Keras构建一个双向LSTM模型,用于分析服装评论的情感。模型包括词嵌入、双向LSTM层和分类层,用于预测评论的评级。
摘要由CSDN通过智能技术生成

1 代码展示

import pandas as pd
import tensorflow as tf
tf.random.set_seed(1)
df = pd.read_csv("../data/Clothing Reviews.csv")
print(df.info())

df['Review Text'] = df['Review Text'].astype(str)
x_train = df['Review Text']
y_train = df['Rating']
print(y_train.unique())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 23486 entries, 0 to 23485
Data columns (total 11 columns):
 #   Column                   Non-Null Count  Dtype 
---  ------                   --------------  ----- 
 0   Unnamed: 0               23486 non-null  int64 
 1   Clothing ID              23486 non-null  int64 
 2   Age                      23486 non-null  int64 
 3   Title                    19676 non-null  object
 4   Review Text              22641 non-null  object
 5   Rating                   23486 non-null  int64 
 6   Recommended IND          23486 non-null  int64 
 7   Positive Feedback Count  23486 non-null  int64 
 8   Division Name            23472 non-null  object
 9   Department Name          23472 non-null  object
 10  Class Name               23472 non-null  object
[4 5 3 2 1]
from tensorflow.keras.preprocessing.text import Tokenizer

dict_size = 14848
tokenizer = Tokenizer(num_words=dict_size)

tokenizer.fit_on_texts(x_train)
print(len(tokenizer.word_index),tokenizer.index_word)

x_train_tokenized = tokenizer.texts_to_sequences(x_train)
from tensorflow.keras.preprocessing.sequence import pad_sequences
max_comment_length = 120
x_train = pad_sequences(x_train_tokenized,maxlen=max_comment_length)

for v in x_train[:10]:
    print(v,len(v))
# 构建RNN神经网络
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,SimpleRNN,Embedding,LSTM,Bidirectional
import tensorflow as tf

rnn = Sequential()
# 对于rnn来说首先进行词向量的操作
rnn.add(Embedding(input_dim=dict_size,output_dim=60,input_length=max_comment_length))
rnn.add(Bidirectional(LSTM(units=100)))  # 第二层构建了100个RNN神经元
rnn.add(Dense(units=10,activation=tf.nn.relu))
rnn.add(Dense(units=6,activation=tf.nn.softmax))  # 输出分类的结果
rnn.compile(loss='sparse_categorical_crossentropy',optimizer="adam",metrics=['accuracy'])
print(rnn.summary())
result = rnn.fit(x_train,y_train,batch_size=64,validation_split=0.3,epochs=10)
print(result)
print(result.history)

2 代码解读

2.1 代码解读

首先,总结这段代码的流程:

  1. 导入了必要的TensorFlow Keras模块。
  2. 初始化了一个Sequential模型,这表示我们的模型会按顺序堆叠各层。
  3. 添加了一个Embedding层,用于将整数索引(对应词汇)转换为密集向量。
  4. 添加了一个双向LSTM层,其中包含100个神经元。
  5. 添加了两个Dense全连接层,分别包含10个和6个神经元。
  6. 使用sparse_categorical_crossentropy损失函数编译了模型。
  7. 打印了模型的摘要。
  8. 使用给定的训练数据和验证数据对模型进行了训练。
  9. 打印了训练的结果。

现在,逐行解读代码:

  1. 导入依赖:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,SimpleRNN,Embedding,LSTM,Bidirectional
import tensorflow as tf

导入了创建和训练RNN模型所需的TensorFlow Keras库。

  1. 初始化模型:
rnn = Sequential()

选择了一个顺序模型,这意味着你可以简单地按顺序添加层。

  1. 添加Embedding层:
rnn.add(Embedding(input_dim=dict_size,output_dim=60,input_length=max_comment_length))

此层将整数索引转换为固定大小的向量。dict_size是词汇表的大小,max_comment_length是输入评论的最大长度。

  1. 添加LSTM层:
rnn.add(Bidirectional(LSTM(units=100)))

选择了双向LSTM,这意味着它会考虑过去和未来的信息。它有100个神经元。

  1. 添加全连接层:
rnn.add(Dense(units=10,activation=tf.nn.relu))
rnn.add(Dense(units=6,activation=tf.nn.softmax))

这两个Dense层用于模型的输出,最后一层使用softmax激活函数进行6类的分类。

  1. 编译模型:
rnn.compile(loss='sparse_categorical_crossentropy',optimizer="adam",metrics=['accuracy'])

选择了一个适合分类问题的损失函数,并选择了adam优化器。

  1. 显示模型摘要:
print(rnn.summary())

这将展示模型的结构和参数数量。

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding (Embedding)       (None, 120, 60)           890880    
                                                                 
 bidirectional (Bidirectiona  (None, 200)              128800    
 l)                                                              
                                                                 
 dense (Dense)               (None, 10)                2010      
                                                                 
 dense_1 (Dense)             (None, 6)                 66        
                                                                 
=================================================================
Total params: 1,021,756
Trainable params: 1,021,756
Non-trainable params: 0
_________________________________________________________________
None
  1. 训练模型:
result = rnn.fit(x_train, y_train, batch_size=64, validation_split=0.3, epochs=10)
  • batch_size=64:指定了每次迭代时使用64个样本进行训练。这意味着神经网络每次更新权重时会使用64个样本进行计算,而不是整个训练数据集。
  • validation_split=0.3: 表示从x_train和y_train中随机抽取30% 作为验证集,剩余的70% 用来训练模型。在训练过程中,验证集将用于评估模型的表现,以防止过拟合。
  • epochs=10: 指定模型将整个训练数据集重复训练10次。这意味着模型将遍历整个训练数据集10次,进行权重更新。
  • result: 训练完成后,fit()方法会返回一个历史对象,通常包含训练过程中每个epoch的损失值和其他评估指标。

用训练数据集训练了模型,其中30%的数据用作验证,训练了10个周期。

Epoch 1/10
257/257 [==============================] - 74s 258ms/step - loss: 1.2142 - accuracy: 0.5470 - val_loss: 1.0998 - val_accuracy: 0.5521
Epoch 2/10
257/257 [==============================] - 57s 221ms/step - loss: 0.9335 - accuracy: 0.6293 - val_loss: 0.9554 - val_accuracy: 0.6094
Epoch 3/10
257/257 [==============================] - 59s 229ms/step - loss: 0.8363 - accuracy: 0.6616 - val_loss: 0.9321 - val_accuracy: 0.6168
Epoch 4/10
257/257 [==============================] - 61s 236ms/step - loss: 0.7795 - accuracy: 0.6833 - val_loss: 0.9812 - val_accuracy: 0.6089
Epoch 5/10
257/257 [==============================] - 56s 217ms/step - loss: 0.7281 - accuracy: 0.7010 - val_loss: 0.9559 - val_accuracy: 0.6043
Epoch 6/10
257/257 [==============================] - 56s 219ms/step - loss: 0.6934 - accuracy: 0.7156 - val_loss: 1.0197 - val_accuracy: 0.5999
Epoch 7/10
257/257 [==============================] - 57s 220ms/step - loss: 0.6514 - accuracy: 0.7364 - val_loss: 1.1192 - val_accuracy: 0.6080
Epoch 8/10
257/257 [==============================] - 57s 222ms/step - loss: 0.6258 - accuracy: 0.7486 - val_loss: 1.1350 - val_accuracy: 0.6100
Epoch 9/10
257/257 [==============================] - 57s 220ms/step - loss: 0.5839 - accuracy: 0.7749 - val_loss: 1.1537 - val_accuracy: 0.6019
Epoch 10/10
257/257 [==============================] - 57s 222ms/step - loss: 0.5424 - accuracy: 0.7945 - val_loss: 1.1715 - val_accuracy: 0.5744
<keras.callbacks.History object at 0x00000244DCE06D90>
  • **时间信息:**例如 74s 258ms/step,表示该epoch的训练时间总共是74秒,平均每步耗时258毫秒。
  • 训练损失和准确率:
    • loss: 在每个epoch的训练结束时,显示训练损失(loss),这是模型预测值与真实标签之间的误差度量。随着训练的进行,损失值逐渐下降,例如从1.2142下降到0.5424,说明模型在训练集上的表现逐渐改善。
    • accuracy: 表示训练集的准确率,随着训练的进行,准确率从0.5470上升到0.7945,说明模型在训练集上的预测能力在提升。
  • 验证损失和准确率:
    • val_loss:这是在验证集上的损失,通常用于评估模型的泛化能力。在这里可以看到,验证损失在最初几次epoch中有所下降(例如从1.0998下降到0.9321),但从第4个epoch开始,验证损失又逐渐增大(最终达到1.1715),表明模型可能开始出现过拟合问题。
    • val_accuracy:表示在验证集上的准确率。验证准确率最初从0.5521上升到第3个epoch的0.6168,但随后逐渐下降到0.5744,这也反映出过拟合的现象,即模型在训练集上表现很好,但在验证集上表现较差。
  • <keras.callbacks.History object>:最后,fit()函数返回了一个History对象,其中包含了所有epoch中记录的损失和准确率,可以用于进一步分析和可视化。
  1. 显示训练结果:
print(result)
<keras.callbacks.History object at 0x0000013AEAAE1A30>
print(result.history)

lossaccuracy 表示训练集,而 val_lossval_accuracy 表示验证集

{
'loss': [1.2142471075057983, 0.9334620833396912, 0.8363043069839478, 0.7795010805130005, 0.7280740141868591, 0.693393349647522, 0.6514003872871399, 0.6257606744766235, 0.5839114189147949, 0.5423741340637207], 
'accuracy': [0.5469586253166199, 0.6292579174041748, 0.6616179943084717, 0.6833333373069763, 0.7010340690612793, 0.7156326174736023, 0.7363746762275696, 0.748600959777832, 0.7748783230781555, 0.7944647073745728], 
'val_loss': [1.0997602939605713, 0.9553984999656677, 0.932131290435791, 0.9812102317810059, 0.9558586478233337, 1.019730806350708, 1.11918044090271, 1.1349923610687256, 1.1536787748336792, 1.1715185642242432], 
'val_accuracy': [0.5520862936973572, 0.609423816204071, 0.6168038845062256, 0.6088560819625854, 0.6043145060539246, 0.5999148488044739, 0.6080045700073242, 0.6099914908409119, 0.6019017696380615, 0.574368417263031]
}

这将展示训练过程中的损失和准确性等信息。

2.2 问题解决(自我提升)

从上面的结果显示,

2.2.1 过拟合

  • 过拟合的迹象
    • 训练损失在减少,验证损失却在增加:这通常是过拟合的标志。模型过于拟合训练集中的特定模式,但这些模式可能并不适用于验证集中的数据,因此验证损失变高,验证准确率下降。
    • 验证准确率停止提高甚至下降:这进一步验证了模型在验证集上的表现变差。

解决办法:
从你提供的训练结果来看,模型在训练集上的表现逐渐提高,但在验证集上的表现却没有同步提升,甚至有所下降。这种现象可以被解读为过拟合,即模型在训练集上学到了过多的细节和噪声,导致在验证集(或测试集)上的泛化能力下降。

具体解读:

  1. 训练集表现

    • 第9轮:训练集的损失为0.5810,准确率为77.41%。
    • 第10轮:训练集的损失降低至0.5403,准确率提高到79.69%。

    从这些结果可以看出,模型在训练集上的性能一直在改善,损失减少,准确率逐步提升,表明模型在训练数据上学得很好。

  2. 验证集表现

    • 第9轮:验证集的损失为1.1522,准确率为60.02%。
    • 第10轮:验证集的损失增加到1.1694,准确率下降至58.16%。

    验证集上的结果显示,尽管模型在训练集上表现良好,但在验证集上的损失却有所增加,准确率下降。这表明模型可能开始过拟合,无法很好地泛化到未见过的数据。

过拟合的迹象:

  • 训练损失在减少,验证损失却在增加:这通常是过拟合的标志。模型过于拟合训练集中的特定模式,但这些模式可能并不适用于验证集中的数据,因此验证损失变高,验证准确率下降。
  • 验证准确率停止提高甚至下降:这进一步验证了模型在验证集上的表现变差。

改进建议:

  1. 增加正则化

    • 可以在模型中加入Dropout层,以随机丢弃部分神经元,防止模型过度拟合。
    • 还可以尝试使用L2正则化,增加权重惩罚来减少过拟合。
  2. 提前停止训练(Early Stopping)

    • 使用EarlyStopping回调函数,在验证集的损失不再下降时提前停止训练,这可以防止模型进一步过拟合。
  3. 数据增强

    • 通过对文本数据的增强(例如同义词替换等)来增加数据多样性,从而帮助模型更好地泛化。
  4. 减小模型复杂度

    • 通过减少RNN单元数量或层数来减小模型复杂度,防止模型过拟合。

可以结合这些策略来优化模型的泛化能力,减少验证集上的损失并提升验证准确率。

3 双向LSTM介绍(BiLSTM)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 自设问题

下面这个问题可以帮助理解如何使用RNN模型进行文本分类,并思考和解决数据预处理和模型调优的问题:

问题:在现有代码基础上,尝试改进RNN模型的分类性能

具体任务如下:

  1. 数据预处理

    • 现有代码将所有评论文本转换为字符串,并对文本进行了Tokenization和Padding。考虑在此基础上,是否可以添加更多的数据清洗和预处理步骤,例如移除停用词、标点符号等,以提升模型的准确性。
  2. 模型架构

    • 现有模型使用了双向LSTM层,你可以尝试不同的RNN变体,如GRU,或者增加更多的LSTM层、增加神经元数量等,观察这些改动对模型性能的影响。
  3. 参数调优

    • 调整模型的超参数,如学习率、批量大小、词向量维度等。尝试使用网格搜索或随机搜索方法,找到最佳参数组合。
  4. 正则化方法

    • 在模型中添加正则化方法,如Dropout层,来防止模型过拟合。
  5. 评价指标

    • 现有代码使用准确率作为评估指标。你可以尝试其他评价指标,如F1分数、召回率、精确率等,综合评估模型性能。
  6. 数据增强

    • 在文本数据上应用数据增强技术,例如同义词替换、随机插入、删除等,增加训练数据的多样性,提高模型泛化能力。

提示:

  • 可以参考TensorFlow或Keras的官方文档,了解更多关于文本预处理和RNN模型的高级用法。
  • 尝试将训练数据分为训练集和验证集,通过验证集评估模型性能,避免过拟合。
  • 记录每次改动后的模型性能,并与之前的结果进行对比,分析哪些改动对模型性能有显著提升。

通过这些改进,可以深入理解RNN模型在文本分类任务中的应用,并掌握数据预处理、模型调优和性能评估的技能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值