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 代码解读
首先,总结这段代码的流程:
- 导入了必要的TensorFlow Keras模块。
- 初始化了一个Sequential模型,这表示我们的模型会按顺序堆叠各层。
- 添加了一个Embedding层,用于将整数索引(对应词汇)转换为密集向量。
- 添加了一个双向LSTM层,其中包含100个神经元。
- 添加了两个Dense全连接层,分别包含10个和6个神经元。
- 使用
sparse_categorical_crossentropy
损失函数编译了模型。 - 打印了模型的摘要。
- 使用给定的训练数据和验证数据对模型进行了训练。
- 打印了训练的结果。
现在,逐行解读代码:
- 导入依赖:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,SimpleRNN,Embedding,LSTM,Bidirectional
import tensorflow as tf
导入了创建和训练RNN模型所需的TensorFlow Keras库。
- 初始化模型:
rnn = Sequential()
选择了一个顺序模型,这意味着你可以简单地按顺序添加层。
- 添加Embedding层:
rnn.add(Embedding(input_dim=dict_size,output_dim=60,input_length=max_comment_length))
此层将整数索引转换为固定大小的向量。dict_size
是词汇表的大小,max_comment_length
是输入评论的最大长度。
- 添加LSTM层:
rnn.add(Bidirectional(LSTM(units=100)))
选择了双向LSTM,这意味着它会考虑过去和未来的信息。它有100个神经元。
- 添加全连接层:
rnn.add(Dense(units=10,activation=tf.nn.relu))
rnn.add(Dense(units=6,activation=tf.nn.softmax))
这两个Dense层用于模型的输出,最后一层使用softmax激活函数进行6类的分类。
- 编译模型:
rnn.compile(loss='sparse_categorical_crossentropy',optimizer="adam",metrics=['accuracy'])
选择了一个适合分类问题的损失函数,并选择了adam优化器。
- 显示模型摘要:
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
- 训练模型:
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中记录的损失和准确率,可以用于进一步分析和可视化。
- 显示训练结果:
print(result)
<keras.callbacks.History object at 0x0000013AEAAE1A30>
print(result.history)
loss
和 accuracy
表示训练集,而 val_loss
和 val_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 过拟合
- 过拟合的迹象:
- 训练损失在减少,验证损失却在增加:这通常是过拟合的标志。模型过于拟合训练集中的特定模式,但这些模式可能并不适用于验证集中的数据,因此验证损失变高,验证准确率下降。
- 验证准确率停止提高甚至下降:这进一步验证了模型在验证集上的表现变差。
解决办法:
从你提供的训练结果来看,模型在训练集上的表现逐渐提高,但在验证集上的表现却没有同步提升,甚至有所下降。这种现象可以被解读为过拟合,即模型在训练集上学到了过多的细节和噪声,导致在验证集(或测试集)上的泛化能力下降。
具体解读:
-
训练集表现:
- 第9轮:训练集的损失为0.5810,准确率为77.41%。
- 第10轮:训练集的损失降低至0.5403,准确率提高到79.69%。
从这些结果可以看出,模型在训练集上的性能一直在改善,损失减少,准确率逐步提升,表明模型在训练数据上学得很好。
-
验证集表现:
- 第9轮:验证集的损失为1.1522,准确率为60.02%。
- 第10轮:验证集的损失增加到1.1694,准确率下降至58.16%。
验证集上的结果显示,尽管模型在训练集上表现良好,但在验证集上的损失却有所增加,准确率下降。这表明模型可能开始过拟合,无法很好地泛化到未见过的数据。
过拟合的迹象:
- 训练损失在减少,验证损失却在增加:这通常是过拟合的标志。模型过于拟合训练集中的特定模式,但这些模式可能并不适用于验证集中的数据,因此验证损失变高,验证准确率下降。
- 验证准确率停止提高甚至下降:这进一步验证了模型在验证集上的表现变差。
改进建议:
-
增加正则化:
- 可以在模型中加入Dropout层,以随机丢弃部分神经元,防止模型过度拟合。
- 还可以尝试使用L2正则化,增加权重惩罚来减少过拟合。
-
提前停止训练(Early Stopping):
- 使用
EarlyStopping
回调函数,在验证集的损失不再下降时提前停止训练,这可以防止模型进一步过拟合。
- 使用
-
数据增强:
- 通过对文本数据的增强(例如同义词替换等)来增加数据多样性,从而帮助模型更好地泛化。
-
减小模型复杂度:
- 通过减少RNN单元数量或层数来减小模型复杂度,防止模型过拟合。
可以结合这些策略来优化模型的泛化能力,减少验证集上的损失并提升验证准确率。
3 双向LSTM介绍(BiLSTM)
例子:
4 自设问题
下面这个问题可以帮助理解如何使用RNN模型进行文本分类,并思考和解决数据预处理和模型调优的问题:
问题:在现有代码基础上,尝试改进RNN模型的分类性能
具体任务如下:
-
数据预处理:
- 现有代码将所有评论文本转换为字符串,并对文本进行了Tokenization和Padding。考虑在此基础上,是否可以添加更多的数据清洗和预处理步骤,例如移除停用词、标点符号等,以提升模型的准确性。
-
模型架构:
- 现有模型使用了双向LSTM层,你可以尝试不同的RNN变体,如GRU,或者增加更多的LSTM层、增加神经元数量等,观察这些改动对模型性能的影响。
-
参数调优:
- 调整模型的超参数,如学习率、批量大小、词向量维度等。尝试使用网格搜索或随机搜索方法,找到最佳参数组合。
-
正则化方法:
- 在模型中添加正则化方法,如Dropout层,来防止模型过拟合。
-
评价指标:
- 现有代码使用准确率作为评估指标。你可以尝试其他评价指标,如F1分数、召回率、精确率等,综合评估模型性能。
-
数据增强:
- 在文本数据上应用数据增强技术,例如同义词替换、随机插入、删除等,增加训练数据的多样性,提高模型泛化能力。
提示:
- 可以参考TensorFlow或Keras的官方文档,了解更多关于文本预处理和RNN模型的高级用法。
- 尝试将训练数据分为训练集和验证集,通过验证集评估模型性能,避免过拟合。
- 记录每次改动后的模型性能,并与之前的结果进行对比,分析哪些改动对模型性能有显著提升。
通过这些改进,可以深入理解RNN模型在文本分类任务中的应用,并掌握数据预处理、模型调优和性能评估的技能。