大模型核心技术(三):CNN、RNN 与 NLP返回课程目录

第二章 文本预处理

1 认识文本预处理

properties 前提:之前用pandas等进行数据分析,也属于文本预处理的范围 文本预处理作用:将文本转换成模型能够识别的张量形式,进而实现模型的训练 规范张量的尺寸,指导模型超参数的选择,提升模型的评估指标

properties 1.文本处理的基本方法:分词、NER、POS 2.文本张量的表示方法:one-hot、word2vec、wordEmbedding 3.文本语料的数据分析:标签数量分析(类别不均衡问题)、句子长度分析、词频统计和关键词词云 4.文本特征处理:添加n-gram特征、文本长度规范 5.数据增强方法:回译数据增强

2 文本处理的基本方法

2.1 分词

分词就是将连续的字序列,按照一定的规范,重新组合成词序列的过程
一般实现模型训练的时候,模型接受的文本基本最小单位是词语,因此我们需要对文本进行分词
词语是语意理解的基本单元
英文具有天然的空格分隔符,而中文分词的目的:寻找一个合适的分词边界,进行准确分词

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(‘黄鹂’) ```

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

借助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()

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