第二章 文本预处理
1 认识文本预处理
- 文本预处理及作用
properties
前提:之前用pandas等进行数据分析,也属于文本预处理的范围
文本预处理作用:将文本转换成模型能够识别的张量形式,进而实现模型的训练
规范张量的尺寸,指导模型超参数的选择,提升模型的评估指标
- 文本预处理的主要环节
properties
1.文本处理的基本方法:分词、NER、POS
2.文本张量的表示方法:one-hot、word2vec、wordEmbedding
3.文本语料的数据分析:标签数量分析(类别不均衡问题)、句子长度分析、词频统计和关键词词云
4.文本特征处理:添加n-gram特征、文本长度规范
5.数据增强方法:回译数据增强
2 文本处理的基本方法
2.1 分词
- 分词的意义
分词就是将连续的字序列,按照一定的规范,重新组合成词序列的过程
一般实现模型训练的时候,模型接受的文本基本最小单位是词语,因此我们需要对文本进行分词
词语是语意理解的基本单元
英文具有天然的空格分隔符,而中文分词的目的:寻找一个合适的分词边界,进行准确分词
-
常用分词工具
-
jieba分词工具
- 精确模式:就是按照人类擅长的表达词汇的习惯来分词
python import jieba content = "我喜欢学习" jieba.cut(content, cut_all=False) # 返回生成器,cut_all=False默认 jieba.lcut(content, cut_all=False) # 返回列表- 全模式分词:将尽可能成词的词汇分割出来
python import jieba content = "我喜欢学习" jieba.cut(content, cut_all=True) # 返回生成器 jieba.lcut(content, cut_all=True) # 返回列表- 搜索引擎模式:在精确模式分词的基础上,将长粒度的词再次切分
python import jieba content = "我喜欢学习" jieba.cut_for_search(content) # 返回生成器 jieba.lcut_for_search(content) # 返回列表结果- 支持中文繁体分词
python import jieba content = "煩惱即是菩提,我暫且不提" jieba.lcut(content) ['煩惱', '即', '是', '菩提', ',', '我', '暫且', '不', '提']-
支持用户自定义词典
-
词典的意义
properties 可以根据自定义词典,修改jieba分词方式,优先考虑词典里面的词来切分 格式:词语 词频(可省略) 词性(可省略)- 代码实现
python import jieba content = "我喜欢学习" jieba.load_userdict("字典文件路径") jieba.lcut(content)
2.2 命名实体识别(NER)
- 定义
命名实体:通常指:人名、地名、机构名、时间、日期、货币、百分比等专有名词
NER:从一段文本中识别出上述描述的命名实体
- 作用
同词汇一样,命名实体也是人类理解文本的基础单元,也是AI解决NLP领域高阶任务的重要环节
- 实现方式
模型训练(后续项目)
2.3 词性标注
- 定义
对每个词语进行词性的标注:动词、名词、形容词等
- 实现方式
import jieba.posseg as pseg
content = "我喜欢学习"
pseg.lcut(content)
3 文本张量的表示方法
3.1 文本张量表示
意义:将文本转换为向量(数字)的形式,使得模型能够识别进而实现训练,一般是进行词向量的表示
实现的方式:
one-hot
word2Vec
wordEmbedding
3.2 One-Hot 词向量表示
- 定义
针对每一个词汇,都会用一个向量表示,向量的长度是n,n代表去重之后的词汇总量,而且向量中只有0和1两种数字
俗称:独热编码、01编码
- 代码实现
```python import jieba from tensorflow.keras.preprocessing.text import Tokenizer import joblib
def onehot_gen(sent: str): # 1 准备语料vocabs vocabs = list(set(jieba.lcut(sent)))
# 2 实例化词汇映射器Tokenizer,使用映射器拟合现有文本数据,内部生成 index_word、word_index
my_tokenizer = Tokenizer()
my_tokenizer.fit_on_texts(vocabs)
# 3 查询单词idx 赋值zero_list 生成onehot
for vocab in vocabs:
zero_list = [0] * len(vocabs)
# word_index 要对应 -1,my_tokenizer 从 1 开始
idx = my_tokenizer.word_index[vocab] - 1
zero_list[idx] = 1
print(vocab, '/t', zero_list)
# 4 使用joblib工具保存映射器 joblib.dump()
my_path = 'onehot_vec.pkl'
joblib.dump(my_tokenizer, my_path)
print('my_tokenizer saved')
print(my_tokenizer.word_index) # char: idx
print(my_tokenizer.index_word) # idx: char
def onehot_use(char): my_path = ‘onehot_vec.pkl’ my_tokenizer = joblib.load(my_path) zero_list = [0] * len(my_tokenizer.word_index) idx = my_tokenizer.word_index[char] - 1 zero_list[idx] = 1 print(f’{char}的onehot编码为{zero_list}’)
if name == ‘main’: sent = ‘两只黄鹂鸣翠柳,一行白鹭上青天。’ onehot_gen(sent) onehot_use(‘黄鹂’) ```
-
One-Hot编码的缺点
-
割裂了词与词之间的联系
- 如果n过大,会导致占用大量的内存(维度爆炸)
3.3 Word2Vec模型
word2vec是一种无监督的训练方法,本质是训练一个模型,将模型的参数矩阵当作所有词汇的词向量表示
两种训练方式:cbow、skipgram
CBOW
properties
核心思想:给一段文本,选择一定的窗口,然后利用上下文预测中间目标词
- 实现过程
源文档图片缺失:CBOW讲解1--01
源文档图片缺失:CBOW3
skip-gram
核心思想: 给出一段文本,选择一定窗口获取数据集,利用中间词来预测上下文
- 实现过程
源文档图片缺失:skipgram
- 获取词向量
源文档图片缺失:image-20240615145319921
Fasttext训练word2vec
- 基本过程
1.获取数据
2.训练模型(训练词向量)
3.向量的检验
4.超参数设定
5.模型的保存和加载
- 代码实现
# coding:utf-8
import fasttext
# 1 获取训练数据,已完成
# 2 词向量的训练保存加载
def dm_fasttext_train_save_load():
# 01 训练模型
my_model = fasttext.train_unsupervised('./data/fil9')
# 02 保存模型
my_model.save_model('./data/fil9.bin')
# 03 加载模型
my_model = fasttext.load_model('./data/fil9.bin')
# 3 查看单词对应的词向量
# 通过get_word_vector方法来获得指定词汇的词向量, 默认词向量训练出来是1个单词100特征
def dm_fasttext_get_word_vector():
# 01 加载模型
my_model = fasttext.load_model("./data/fil9.bin")
# 02 查询某个词汇的词向量
result = my_model.get_word_vector("the")
print(f'the的词向量是{type(result)}')
print(f'the的词向量是{result.shape}')
print(f'the的词向量是{result}')
# 4 检验模型效果
# 查找"运动"的邻近单词, 可以发现"体育网", "运动汽车", "运动服"等
def dm_fasttext_get_nearest_neighbors():
# 01 加载模型
my_model = fasttext.load_model("data/fil9.bin")
# 02 查询sports的临近单词
results1 = my_model.get_nearest_neighbors("sports")
print(f'sports的邻居--》{results1}')
# 查询music的临近单词
results2 = my_model.get_nearest_neighbors("music")
print(f'music的邻居--》{results2}')
# 查询music的临近单词
results3 = my_model.get_nearest_neighbors("dog")
print(f'dog的邻居--》{results3}')
# 5 模型超参数设定
def dm_fasttext_train_args():
# 训练模型 设置超参数
my_model = fasttext.train_unsupervised('./data/fil9',
"cbow",
epoch=1,
lr=0.1,
dim=300,
thread=8)
# # 保存模型
# my_model.save_model('./data/fil9.bin')
if __name__ == '__main__':
# dm_fasttext_train_save_load()
# dm_fasttext_get_word_vector()
dm_fasttext_get_nearest_neighbors()
# dm_fasttext_train_args()
3.4 词嵌入Word Embedding
- word2vec缺点
-
静态方法,不能解决一词多义问题
-
实现过程
借助nn.Embedding(vocab_size, embed_dim): vocab_size代表词汇(去重之后)的总量,embed_dim是我们设定的词向量维度;本质创建一个权重矩阵(充当向量矩阵),矩阵参数会随着模型的训练迭代更新
- 代码实现
# coding:utf-8
import torch
import torch.nn as nn
from torch.utils.tensorboard import SummaryWriter
import tensorflow as tf
import tensorboard as tb
from tensorflow.keras.preprocessing.text import Tokenizer
import jieba
from rich import print
# 实验:nn.Embedding层词向量可视化分析
# 1 对句子分词 word_list
# 2 对句子word2id求my_token_list,对句子文本数值化sentence2id
# 3 创建nn.Embedding层,查看每个token的词向量数据
def dm_embedding_show():
# 1 获取语料
sentence1 = '传智教育是一家上市公司,旗下有黑马程序员品牌。我是在黑马这里学习人工智能'
sentence2 = "我爱自然语言处理"
sentences = [sentence1, sentence2]
# 2 对句子分词 word_list
word_list = []
for s in sentences:
word_list.append(jieba.lcut(s))
print(f'word_list--》{word_list}')
# 3 实例化Tokenizer
my_tokenizer = Tokenizer()
my_tokenizer.fit_on_texts(word_list)
print(f'my_tokenizer.word_index--》{my_tokenizer.word_index}')
print(f'my_tokenizer.index_word--》{my_tokenizer.index_word}')
print('*' * 80)
# 4 拿到所有的单词
# my_token_list = list(my_tokenizer.index_word.values())
my_token_list = my_tokenizer.index_word.values()
print(my_token_list)
print('*' * 80)
# 5 打印句子2id
sequence2id = my_tokenizer.texts_to_sequences(word_list)
print(f'sequence2id--》{sequence2id}')
# 6 获得embedding
my_embed = nn.Embedding(num_embeddings=len(my_token_list), embedding_dim=8)
print(f'my_embed--》{my_embed.weight.shape}')
print(my_embed.weight.data)
# 7 查询词汇的向量
for idx in range(len(my_tokenizer.index_word)):
temp_v = my_embed(torch.tensor(idx))
word = my_tokenizer.index_word[idx + 1]
print(f'{word}--->:词汇对用的向量是:{temp_v}')
# 8 可视化
my_summary = SummaryWriter()
my_summary.add_embedding(my_embed.weight.data, my_token_list)
my_summary.close()
# 终端操作可视化:tensorboard --logdir=runs --host 0.0.0.0
if __name__ == '__main__':
dm_embedding_show()
-
可视化工具:
-
工具:tensorboard
- 命令: tensorboard –logdir=runs –host 0.0.0.0
4 文本数据分析
4.1 文本数据分析的作用
1: 帮助我们理解语料
2: 可以分析语料中可能存在的问题,指导我们设定模型的超参数等功能
4.2 标签数量分布
目的: 查询样本是否均衡,如果样本过大,需要减少数据;如果样本过少,需要增加数据
- 代码实现
import jieba
import jieba.posseg as pseg
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from itertools import chain
from wordcloud import WordCloud
def dm_label_sns_countplot():
'''
获取标签数量分布
0 什么标签数量分布:求标签0有多少个 标签1有多少个 标签2有多少个
1 设置显示风格plt.style.use('fivethirtyeight')
2 pd.read_csv(path, sep='/t') 读取训练集 验证集
csv: 使用逗号, 作为列与列之间的分隔符; tsv: 使用制表符(Tab,\t)作为分隔符。
3 sns.countplot() 统计label标签的0、1分组数量
4 画图展示 plt.title() plt.show()
'''
# 1 设置显示风格
plt.style.use('fivethirtyeight')
# plt.style.use('Solarize_Light2')
# 2 读取数据
train_data = pd.read_csv('./cn_data/train.tsv', sep="/t")
print(f'train_data--》{train_data.head()}')
print(f'train_data--》{type(train_data)}')
dev_data = pd.read_csv('./cn_data/dev.tsv', sep="/t")
# 3 统计训练集label标签的0、1分组数量
# hue="label"表示根据"label"显示不同的颜色
sns.countplot(x="label", data=train_data, hue="label")
# sns.countplot(y="label", data=train_data)
# sns.countplot(x=train_data["label"])
plt.title("train_data")
plt.show()
# 4 统计验证集label标签的0、1分组数量
sns.countplot(x="label", data=dev_data, hue="label")
plt.title("dev_data")
plt.show()
4.3 句子长度分布
目的: 模型一般规定需要输入固定的尺寸,也就是长度统一,通过分析句子长度,可以明确大部分样本属于什么长度范围,然后进行句子的长短补齐或截断
- 代码实现:(柱状图、曲线图)
def dm_len_sns_countplot_distplot():
'''
获取句子长度分布
'''
# 1 设置显示风格
plt.style.use('fivethirtyeight')
# 2 读取数据
train_data = pd.read_csv('./cn_data/train.tsv', sep="/t")
dev_data = pd.read_csv('./cn_data/dev.tsv', sep="/t")
# 3 添加一列 句子长度
train_data["sentence_length"] = list(map(lambda x: len(x), train_data["sentence"]))
print(f'修改后的train_data--》{train_data.head()}')
dev_data["sentence_length"] = list(map(lambda x: len(x), dev_data["sentence"]))
print(f'修改后的dev_data--》{dev_data.head()}')
# 4 画图训练集
# countplot 柱状图、displot 直方图、kde=True 生成核密度估计图
# plt.xticks([]) 不显示任何 x 轴的刻度标签,设置与否都是为了观看
sns.countplot(x="sentence_length", data=train_data)
plt.xticks([])
# plt.yticks([i for i in range(0, 100, 10)])
plt.show()
sns.displot(x="sentence_length", data=train_data, kde=True)
plt.xticks([i for i in range(0, 3500, 200)])
# plt.yticks([i for i in range(0, 100, 10)])
plt.show()
# 5 画图验证集
sns.countplot(x="sentence_length", data=dev_data)
plt.xticks([])
plt.show()
sns.displot(x="sentence_length", data=dev_data, kde=True)
plt.yticks([])
plt.show()
- 代码实现(散点图)
def dm_sns_stripplot():
'''
获取正负样本长度散点分布
'''
# 1 设置显示风格
plt.style.use('fivethirtyeight')
# 2 读取数据
train_data = pd.read_csv('./cn_data/train.tsv', sep="/t")
dev_data = pd.read_csv('./cn_data/dev.tsv', sep="/t")
# 3 添加一列 句子长度
train_data["sentence_length"] = list(map(lambda x: len(x), train_data["sentence"]))
dev_data["sentence_length"] = list(map(lambda x: len(x), dev_data["sentence"]))
# 4 画图训练集
sns.stripplot(y="sentence_length", x="label", data=train_data, hue="label")
plt.show()
# 5 画图验证集
sns.stripplot(y="sentence_length", x="label", data=dev_data)
plt.show()
4.4 词频和高频词云
词频: 这里指的是统计样本中词汇的总数量(需要去重)
高频词云目的: 可以方便我们查看数据集中是否存在脏数据,进而实现人工的审核和清洗
- 词频:词汇总数统计
def dm_tj_words_counts():
'''
获取不同词汇总数统计
'''
# 1 读取数据
train_data = pd.read_csv('./cn_data/train.tsv', sep="/t")
# print(f'train_data--》{train_data.head()}')
dev_data = pd.read_csv('./cn_data/dev.tsv', sep="/t")
# 2 获取训练数据语料的词语数量
# chain(*) 扁平化处理
train_vocab = set(chain(*map(lambda x: jieba.lcut(x), train_data["sentence"])))
print(f'训练集词语数量---》{len(train_vocab)}')
# 3 获取验证数据语料的词语数量
dev_vocab = set(chain(*map(lambda x: jieba.lcut(x), dev_data["sentence"])))
print(f'验证集词语数量---》{len(dev_vocab)}')
- 高频词云
# 7 获取训练集高频形容词词云
# 7.1 获取每个句子的形容词列表
def get_a_list(text):
r = []
for g in pseg.lcut(text):
# g.flag 词性;g.word 具体str
if g.flag == "a":
r.append(g.word)
return r
# 7.2 根据词云列表产生词云
def get_word_cloud(keywords_list):
# 实例化词云对象
my_wordcloud = WordCloud(font_path='./cn_data/SimHei.ttf', max_words=100, background_color="white")
# 准备数据
a_str = " ".join(keywords_list)
# 产生词云
my_wordcloud.generate(a_str)
# 画图展示
plt.figure()
# interpolation="bilinear" 指定图像在显示时的插值方法,用于根据已知数据点估计未知数据点的值
# "bilinear" 插值是一种常用插值方法,它使用四个最近邻像素的加权平均来估计新像素的值
plt.imshow(my_wordcloud, interpolation="bilinear")
# off 关闭或隐藏坐标轴
plt.axis("off")
plt.show()
# 7.3 生成词云
def dm_word_cloud():
# 1 获取数据
train_data = pd.read_csv('./cn_data/train.tsv', sep="/t")
# 2 获取积极的评论样本
p_train_data = train_data[train_data["label"] == 1]["sentence"]
# print(f'p_train_data--》{p_train_data.head()}')
# 3 获取每个句子中形容的列表
p_a_words = list(chain(*map(lambda x: get_a_list(x), p_train_data)))
# print(f'p_a_words--》{p_a_words[:3]}')
# print(f'p_a_words--》{len(p_a_words)}')
get_word_cloud(p_a_words)
# 4 获取消极的评论样本
n_train_data = train_data[train_data["label"] == 0]["sentence"]
# print(f'n_train_data--》{n_train_data.head()}')
# 5 获取每个句子中形容的列表
n_a_words = list(chain(*map(lambda x: get_a_list(x), n_train_data)))
get_word_cloud(n_a_words)
5 文本特征处理
对语料添加普适性的特征:n-gram
对语料进行规范长度:适配模型的输入
5.1 添加N-Gram特征
定义: 将连续的相邻的词或者字组合到一块,就称为n-gram特征
eg: 我| 爱| 黑马
bi-gram: 我爱|爱黑马
tri—gram: 我爱黑马
- 代码实现
# coding:utf-8
def add_n_gram(a: list):
n_gram = 2
print(f'列表推导式结果--》{[a[i:] for i in range(n_gram)]}')
# * 把list中每个元素作为独立的部分 传给zip
return set(zip(*[a[i:] for i in range(n_gram)]))
result = add_n_gram(a=[1, 3, 2, 1, 5, 3])
print(result)
5.2 文本长度规范
意义: 模型一般需要固定尺寸的输入,因此需要对文本句子进行补齐(一般用0补齐)或者截断
- 代码实现
# coding:utf-8
from keras.preprocessing import sequence
# 1 使用 api 填充
def padding(x_train):
max_len = 10
return sequence.pad_sequences(x_train, max_len, padding="post", truncating="post")
# 假定x_train里面有两条文本, 一条长度大于10, 一天小于10
x_train = [[1, 23, 5, 32, 55, 63, 2, 21, 78, 32, 23, 1],
[2, 32, 1, 23, 1]]
# 2 自定义填充
def my_padding(x: list):
max_len = 5
# paddiing
x = x + (max_len - len(x)) * [0]
# truncating
x = x[:max_len]
return x
a = my_padding(x=[1, 2, 3, 45, 5, 6, 7, 8])
print(a)
6 文本数据增强
目的: 增加数据集(扩展数据集)
6.1 回译数据增强
定义: 通过将一种语言翻译成不同的语言,再转换回来的一种方式
eg: 中文---韩文----英语---中文
使用工具: ChatGPT