【NLP保姆级实战】拒绝“盲人摸象”!训练深度学习模型前必做的5大文本数据分析(纯干货)
摘要
在自然语言处理(NLP)项目中,构建一个优秀的模型是终极目标,但成功的基石往往在于数据。在“垃圾进,垃圾出”的原则下,高质量的数据才是制胜的关键。而探索性数据分析(Exploratory Data Analysis,EDA),正是我们深入挖掘数据潜能、提升模型性能的秘密武器。EDA不仅能帮助我们理解数据、发现问题,还能指导我们优化模型架构,使其更贴合数据的真实分布,从而达到事半功倍的效果。本文将深入探讨NLP任务中,五个至关重要的EDA步骤,助你从数据层面掌控项目主动权。
本文将详细阐述在NLP项目中进行有效EDA的五大关键步骤,并辅以实践案例,旨在帮助读者全面了解数据集,提升模型性能,并避免常见的错误。
- 标签分布分析:确保样本均衡,为模型评估设定基线。
- 句子长度分析:为文本序列处理(如填充和截断)提供指导。
- 异常点检测:识别并处理可能影响模型性能的异常数据。
- 词汇量统计:评估语料的丰富度与复杂度,指导模型设计。
- 词云可视化:快速洞察文本特征,进行数据质量评估。
1. 标签分布分析:构建平衡的数据基石
1.1 重要性
在NLP的分类任务中,标签的分布直接影响了模型的性能评估和训练策略。如果正负样本比例严重失衡,模型很可能倾向于预测多数类,而忽略少数类,导致整体准确率虚高,但对少数类的识别能力很差。因此,了解标签分布是评估模型性能和制定训练策略的基础。
- 评估基线:如果数据集是平衡的(例如二分类任务中正负样本比例接近1:1),那么一个随机猜测模型的准确率基线是50%。如果标签严重失衡,基线会偏离50%,这对评估模型性能提出更高的要求。
- 指导数据增强和调整:如果发现标签分布不均衡,需要考虑采用数据增强(如过采样少数类)或调整损失函数(如加权交叉熵损失)等方法,以提高模型在少数类上的性能。
1.2 分析方法
我们可以使用Seaborn库的countplot或pie函数来可视化标签分布。
countplot:以柱状图的形式展示每个标签的样本数量。简单直观,便于比较不同标签之间的数量差异。pie:以饼图的形式展示各个标签的占比。更适合直观地了解每个标签在总体中的比例。
1.3 实践步骤
- 读取数据:使用
Pandas的read_csv等函数读取数据。 - 绘制分布图:使用
sns.countplot(x='label', data=df)或df['label'].value_counts().plot.pie(autopct='%1.1f%%')等函数绘制标签分布图。 - 解读结果:观察柱状图或饼图,评估标签分布是否均衡。如果分布不均衡,需要考虑后续的数据处理策略。
def dm_label_sns_countplot():
# 1 设置显示风格plt.style.use('fivethirtyeight')
plt.style.use('fivethirtyeight')
# 2 pd.read_csv 读训练集 验证集数据
train_data = pd.read_csv(filepath_or_buffer = './cn_data/train.tsv', sep='\t')
dev_data = pd.read_csv(filepath_or_buffer = './cn_data/dev.tsv', sep='\t')
# 3 sns.countplot() 统计label标签的0、1分组数量
sns.countplot(x='label', data = train_data)
# 4 画图展示 plt.title() plt.show()
plt.title('train_label')
plt.show()
# 验证集上标签的数量分布
# 3-2 sns.countplot() 统计label标签的0、1分组数量
sns.countplot(x='label', data = dev_data)
# 4-2 画图展示 plt.title() plt.show()
plt.title('dev_label')
plt.show()
2. 句子长度分析:为文本序列处理奠定基础
2.1 重要性
在深度学习模型中,通常需要将文本序列转换为固定长度的张量。例如,在循环神经网络(RNN)和Transformer模型中,每个输入样本的维度需要一致。这就涉及到截断(Truncation)和填充(Padding)操作。
句子长度分析可以帮助我们:
- 确定
max_length:通过分析句子长度的分布,选择一个合适的max_length,以覆盖大部分句子,同时避免过长的序列带来的计算负担。 - 选择合适的填充方式:根据句子长度的分布,选择在句首或句尾进行填充。
2.2 分析方法
- 计算句子长度:对数据集中的每个句子,计算其包含的token数量。
- 可视化长度分布:使用
Seaborn的countplot、displot或kdeplot等函数可视化句子长度的分布情况。
2.3 实践步骤
- 计算长度:使用
apply(len)或者map(lambda x: len(x.split()))等方法计算每个句子的长度,并创建一个新的列存储这些长度。 - 绘制柱状图:
sns.countplot(x='sentence_length', data=df)。 - 绘制核密度估计图(KDE):
sns.displot(x='sentence_length', data=df, kind='kde')。 - 解读结果:
- 峰值:找到句子长度分布的峰值,它表示大多数句子的长度。
- 90/95分位数:找到90%或95%的句子长度的覆盖范围。这可以帮助你确定
max_length的合理值。如果大部分句子长度都集中在某个范围内,选择该范围内的值作为max_length即可。 - 异常值:注意是否存在长度非常长或非常短的句子,这些可能代表数据质量问题,需要进一步审查。
def dm_len_sns_countplot_distplot():
# 1 设置显示风格plt.style.use('fivethirtyeight')
plt.style.use('fivethirtyeight')
# 2 pd.read_csv 读训练集 验证集数据
train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
dev_data = pd.read_csv(filepath_or_buffer='./cn_data/dev.tsv', sep='\t')
# 3 求数据长度列 然后求数据长度的分布
train_data['sentence_length'] = list( map(lambda x: len(x), train_data['sentence']))
# 4 绘制数据长度分布图-柱状图
sns.countplot(x='sentence_length', data=train_data)
# sns.countplot(x=train_data['sentence_length'])
plt.xticks([]) # x轴上不要提示信息
# plt.title('sentence_length countplot')
plt.show()
# 5 绘制数据长度分布图-曲线图
sns.displot(x='sentence_length', data=train_data)
# sns.displot(x=train_data['sentence_length'])
plt.yticks([]) # y轴上不要提示信息
plt.show()
# 验证集
# 3 求数据长度列 然后求数据长度的分布
dev_data['sentence_length'] = list(map(lambda x: len(x), dev_data['sentence']))
# 4 绘制数据长度分布图-柱状图
sns.countplot(x='sentence_length', data=dev_data)
# sns.countplot(x=dev_data['sentence_length'])
plt.xticks([]) # x轴上不要提示信息
# plt.title('sentence_length countplot')
plt.show()
# 5 绘制数据长度分布图-曲线图
sns.displot(x='sentence_length', data=dev_data)
# sns.displot(x=dev_data['sentence_length'])
plt.yticks([]) # y轴上不要提示信息
plt.show()
3. 异常点检测:净化数据,提升模型鲁棒性
3.1 重要性
数据集中可能存在一些异常数据,例如:
- 错误的标注:标签与文本内容不符。
- 格式错误:文本格式不规范,例如包含了大量的噪声字符或HTML标签。
- 极端长度:句子过长或过短,可能与数据集的整体风格不符。
这些异常数据会干扰模型的训练,降低模型的泛化能力。通过识别和处理异常数据,可以提高模型的鲁棒性。
3.2 分析方法
- 散点图:将句子长度与标签结合,绘制散点图,可以直观地观察不同标签下句子长度的分布情况,并发现异常点。
- 人工审查:对散点图中的异常点进行人工审查,确认其合理性。
3.3 实践步骤
- 计算句子长度:同上。
- 绘制散点图:
sns.stripplot(y='sentence_length', x='label', data=df)或sns.scatterplot(x='label', y='sentence_length', data=df)。 - 解读结果:
- 观察不同标签下句子长度的分布情况。
- 重点关注那些远离主体分布的异常点,例如,长度极长或极短的句子。
- 对于这些异常点,进行人工审查,判断其是否为数据错误,并进行相应的处理(如删除或修正)。
def dm03_sns_stripplot():
# 1 设置显示风格plt.style.use('fivethirtyeight')
plt.style.use('fivethirtyeight')
# 2 pd.read_csv 读训练集 验证集数据
train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
dev_data = pd.read_csv(filepath_or_buffer='./cn_data/dev.tsv', sep='\t')
# 3 求数据长度列 然后求数据长度的分布
train_data['sentence_length'] = list(map(lambda x: len(x), train_data['sentence']))
# 4 统计正负样本长度散点图 (对train_data数据,按照label进行分组,统计正样本散点图)
sns.stripplot(y='sentence_length', x='label', data=train_data)
plt.show()
sns.stripplot(y='sentence_length', x='label', data=dev_data)
plt.show()
4. 词汇量统计:衡量语料复杂度,指导模型设计
4.1 重要性
词汇量(Vocabulary Size)是衡量语料复杂度和规模的重要指标。
- 评估语料复杂度:词汇量越大,说明语料的语言多样性越高,内容越丰富。
- 影响模型设计:词汇量的大小直接影响
Embedding层的维度。 - 评估数据质量:如果词汇量过小,说明语料可能存在问题,例如数据清洗不充分。
4.2 分析方法
- 分词:使用
jieba或其他分词工具将文本数据分词。 - 构建词汇表:使用
set集合来存储所有不重复的词汇。 - 统计词汇量:计算词汇表的长度。
4.3 实践步骤
- 分词:使用
jieba.lcut(sentence)对每个句子进行分词。 - 构建词汇表: 使用
set对分词后的所有词进行去重,构建词汇表。 - 统计词汇量:
len(vocabulary)
# 导入jieba用于分词
# 导入chain方法用于扁平化列表
import jieba
from itertools import chain
# 进行训练集的句子进行分词, 并统计出不同词汇的总数
train_vocab = set(chain(*map(lambda x: jieba.lcut(x), train_data["sentence"])))
print("训练集共包含不同词汇总数为:", len(train_vocab))
# 进行验证集的句子进行分词, 并统计出不同词汇的总数
valid_vocab = set(chain(*map(lambda x: jieba.lcut(x), valid_data["sentence"])))
print("训练集共包含不同词汇总数为:", len(valid_vocab))
5. 词云可视化:快速洞察文本特征,评估数据质量
5.1 重要性
词云是一种强大的可视化工具,可以直观地展示文本中高频词汇。词云的出现频率,可以用字体大小直观地呈现。
- 快速洞察文本特征:通过观察词云,可以快速了解文本的核心内容和主题。
- 评估数据质量:通过比较不同类别文本的词云,可以检查数据是否符合预期,发现潜在的标注错误或数据问题。
- 辅助人工审查:可以用于辅助人工审查,快速定位有问题的样本。
5.2 分析方法
- 词频统计:统计文本中每个词的出现频率。
- 生成词云:使用
wordcloud库生成词云图。
5.3 实践步骤
- 分词与词频统计:对文本数据进行分词,统计每个词的出现频率。
- 生成词云:使用
WordCloud类生成词云,设置字体、背景颜色等参数, - 展示词云:使用
matplotlib库展示词云图。
# 使用jieba中的词性标注功能
import jieba.posseg as pseg
from wordcloud import WordCloud
# 每句话产生形容词列表
def get_a_list(text):
r = []
# 使用jieba的词性标注方法切分文本 找到形容词存入到列表中返回
for g in pseg.lcut(text):
if g.flag == "a":
r.append(g.word)
return r
# 根据词云列表产生词云
def get_word_cloud(keywords_list):
# 实例化词云生成器对象
wordcloud = WordCloud(font_path="./SimHei.ttf", max_words=100, background_color='white')
# 准备数据
keywords_string = " ".join (keywords_list)
# 产生词云
wordcloud.generate(keywords_string)
# 画图
plt.figure()
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis('off')
plt.show()
# 思路分析 训练集正样本词云 训练集负样本词云
# 1 获得训练集上正样本 p_train_data
# eg: 先使用逻辑==操作检索符合正样本 train_data[train_data['label'] == 1]
# 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
# 3 调用绘制词云函数
def dm_word_cloud():
# 1 获得训练集上正样本p_train_data
# eg: 先使用逻辑==操作检索符合正样本 train_data[train_data['label'] == 1]
train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
p_train_data = train_data[train_data['label'] == 1 ]['sentence']
# 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
p_a_train_vocab = list(chain(*map(lambda x: get_a_list(x) , p_train_data)))
# print(p_a_train_vocab)
# print(list(p_a_train_vocab))
# 3 调用绘制词云函数
get_word_cloud(p_a_train_vocab)
print('*' * 60 )
# 训练集负样本词云
n_train_data = train_data[train_data['label'] == 0 ]['sentence']
# 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
n_a_train_vocab = chain(*map(lambda x: get_a_list(x) , n_train_data) )
# print(n_a_dev_vocab)
# print(list(n_a_dev_vocab))
# 3 调用绘制词云函数
get_word_cloud(n_a_train_vocab)
6. 总结
古人云:“工欲善其事,必先利其器”。在自然语言处理的广阔天地里,探索性数据分析(EDA)不仅是一门技术,更是一门艺术。
通过标签分布看格局、通过长度分布定尺度、通过散点分布找异常、通过词汇统计估规模、通过词云展示探灵魂。完成这五步,你不仅能彻底摸清手中数据的脾气秉性,更能为后续的模型构建扫清无数的暗雷。
不要再把未知的数据直接扔进黑盒模型中了,从今天开始,养成做文本数据分析的好习惯,你的NLP进阶之路将走得更加扎实、稳健!
7. 参考资料
黑马程序员AI大模型NLP自然语言处理全套视频教程,从传统序列模型到基于Transformer的预训练模型,构建完整NLP知识体系与项目实战
感谢阅读!如果这篇文章对你有帮助,欢迎点赞、收藏并关注我,我们下期再见!
更多推荐


所有评论(0)