一、自然语言处理
1 什么是自然语言处理
【什么是人工智能,分别对应哪几个领域】
AI是模仿甚至超越人的某项机能,NLP、CV、ASR
NLP是机器理解并生成人类语言

2 自然语言处理的发展简史
properties
1950 -- 图灵提出"机器能思考吗",划时代性的话题
1956 -- 达特茅斯会议,提出人工智能(Artificial Intelligence),AI的元年,之前都是人类智能(Human Intelligence)
1957-1970 -- NLP开始出现两大阵营:规则 + 统计
1994-1999 -- 统计学开始占据主导地位
2000-2012 -- 机器学习开始盛行
2012-2022 -- 传统的深度学习占据主导地位
2023 -- 大模型时代
- 基于规则举例:设计语言学规则,解析句子结构,例如一个句子由主谓宾构成。
- 基于统计举例:统计机器翻译模型,收集大量平行语料库,例如中英互译,统计不同短语的分配概率,计算给定源语言翻译为目标语言的可能性。
3 自然语言处理的应用场景
properties
语音助手:小爱同学
机器翻译:谷歌翻译
搜素引擎:百度翻译
智能对话:文心一言
推荐系统:短视频app推荐
......
二、文本预处理
1 认识文本预处理
-
【文本预处理及作用】
properties所处阶段:数据输入到模型之前 作用:数据清洗、指导超参数的确定,,,
-
文本预处理的主要环节
properties1.文本处理的基本方法:分词、NER、POS 2.文本张量的表示方法:one-hot、word2vec、wordEmbedding 3.文本语料的数据分析:标签数量分析(类别不均衡问题)、句子长度分析、词频统计和关键词词云 4.文本特征处理:添加n-gram特征、文本长度规范 5.数据增强方法:回译数据增强
2 文本处理的基本方法
【文本处理的基本方法有几种】
分词、pos、ner(2步,第一步命名实体的边界识别,序列标注任务,就是token级别的分类;第二步,对span进行分类,可以看做是句子级别的分类。)
2.1 分词
- 分词的意义
properties
分词就是将连续的字序列,按照一定的规范,重新组合成词序列的过程
一般实现模型训练的时候,模型接受的文本基本最小单位是词语,因此我们需要对文本进行分词
词语是语意理解的基本单元
英文具有天然的空格分隔符,而中文分词的目的:寻找一个合适的分词边界,进行准确分词
-
常用分词工具
-
jieba分词工具
- 精确模式:就是按照人类擅长的表达词汇的习惯来分词
pythonimport jieba content = "我喜欢学习" jieba.cut(content, cut_all=False) # 返回生成器,cut_all=False默认 jieba.lcut(content, cut_all=False) # 返回列表
- 全模式分词:将尽可能成词的词汇分割出来
pythonimport jieba content = "我喜欢学习" jieba.cut(content, cut_all=True) # 返回生成器 jieba.lcut(content, cut_all=True) # 返回列表
- 搜索引擎模式:在精确模式分词的基础上,将长粒度的词再次切分
pythonimport jieba content = "我喜欢学习" jieba.cut_for_search(content) # 返回生成器 jieba.lcut_for_search(content) # 返回列表结果
- 支持中文繁体分词
pythonimport jieba content = "煩惱即是菩提,我暫且不提" jieba.lcut(content) ['煩惱', '即', '是', '菩提', ',', '我', '暫且', '不', '提']
-
支持用户自定义词典
- 词典的意义
properties可以根据自定义词典,修改jieba分词方式,优先考虑词典里面的词来切分 格式:词语 词频(可省略) 词性(可省略)
- 代码实现
pythonimport jieba content = "我喜欢学习" jieba.load_userdict("字典文件路径") jieba.lcut(content)
-
2.2 命名实体识别(实体抽取、ner)
- 定义
properties
命名实体:通常指人名、地名、机构名等专有名词
NER:从一段文本中识别出上述描述的命名实体
常见的7类命名实体:人名、地名、机构名、时间、日期、货币、百分比
- 作用
properties
同词汇一样,命名实体也是人类理解文本的基础单元,也是AI解决NLP领域高阶任务的重要环节
2.3 词性标注(pos)
- 定义
properties
对每个词语进行词性的标注:动词、名词、形容词等
- 实现方式
python
import jieba.posseg as pseg # one.flag 词性 one.word token
content = "我喜欢学习"
pseg.lcut(content)
3 文本张量的表示方法
【文本张量的表示方法有几种】
onehot、word2vec、word embedding
【word2vec有几种方法,分别解释一下】
cbow、skipgram
3.1 文本张量表示
properties
意义:将文本转换为向量(数字)的形式,使得模型能够识别进而实现训练,一般是进行词向量的表示
实现的方式:
one-hot
word2Vec
wordEmbedding
3.2 One-Hot 词向量表示
- 定义
properties
针对每一个词汇,都会用一个向量表示,向量的长度是n(n就是词表大小),n代表去重之后的词汇总量,而且向量中只有0和1两种数字
俗称:独热编码、01编码
-
代码实现
pythonimport 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的优缺点】
优点:简单,容易理解
缺点:1、相似度为0;2、大语料情况下,占用大量资源
3.3 Word2Vec模型
properties
word2vec是一种无监督(自监督)的训练方法,本质是训练一个模型,
将模型的参数矩阵当作所有词汇的词向量表示
两种训练方式:CBOW Skipgram
CBOW

properties
核心思想:给一段文本,选择一定的窗口,然后利用上下文预测中间目标词
- 实现过程
properties
假设我们给定的训练语料只有一句话: Hope can set you free (愿你自由成长),
窗口大小为3,因此模型的第一个训练样本来自Hope can set,
因为是CBOW模式,所以将使用Hope和set作为输入,can作为输出,在模型训练时, Hope,can,set等词汇都使用它们的one-hot编码.
如图所示: 每个one-hot编码的单词与各自的变换矩阵(即参数矩阵3x5, 这里的3是指最后得到的词向量维度)相乘之后再相加, 得到上下文表示矩阵(3x1)

properties
接着, 将上下文表示矩阵与变换矩阵(参数矩阵5x3, 所有的变换矩阵共享参数)相乘,
得到5x1的结果矩阵, 它将与我们真正的目标矩阵即can的one-hot编码矩阵(5x1)进行损失的计算,
然后更新网络参数完成一次模型迭代.

properties
最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵(3x5),这个变换矩阵与每个词汇的one-hot编码(5x1)相乘,得到的3x1的矩阵就是该词汇的word2vec张量表示.
skip-gram
properties
核心思想: 给出一段文本,选择一定窗口获取数据集,利用中间词来预测上下文,
每个窗口要更新n-1次,训练更充分
Fasttext训练word2vec
- 基本过程
properties
1.获取数据
2.训练模型(训练词向量) pip install fasttext-wheel
3.向量的检验
4.超参数设定
5.模型的保存和加载
- 代码实现
python
# coding: utf-8
import fasttext
# 1 获取训练数据,已完成
# 2 词向量的训练保存加载
def dm_fasttext_train_save_load():
# 01 训练模型
my_model = fasttext.train_unsupervised('./data/file')
# 02 保存模型
my_model.save_model('./data/file.bin')
# 03 加载模型
my_model = fasttext.load_model('./data/file.bin')
# 3 查看单词对应的词向量
# 通过get_word_vector方法来获得指定词汇的词向量, 默认词向量训练出来是1个单词100特征
def dm_fasttext_get_word_vector():
# 01 加载模型
my_model = fasttext.load_model("./data/file.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/file.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/file',
"cbow",
epoch=1,
lr=0.1,
dim=300,
thread=8)
# # 保存模型
# my_model.save_model('./data/file.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缺点】
静态的固定的vec表示方法,多义词效果不佳
- 实现过程
properties
借助nn.Embedding(vocab_size, embed_dim): vocab_size代表词汇(去重之后)的总量,embed_dim是我们设定的词向量维度;本质创建一个权重矩阵(充当向量矩阵),矩阵参数会随着模型的训练迭代更新
- 代码实现
python
# 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 文本数据分析
【文本数据分析有哪些方法】
1、标签数量分布;2、句子长度分布;3、词频统计和词云
4.1 文本数据分析的作用
properties
1: 帮助我们理解语料
2: 可以分析语料中可能存在的问题,指导我们设定模型的超参数等功能
4.2 标签数量分布
properties
目的: 查询样本类别是否均衡,如果样本过大,需要减少数据;如果样本过少,需要增加数据
- 代码实现
python
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 句子长度分布
properties
目的: 模型一般规定需要输入固定的尺寸,也就是长度统一,通过分析句子长度,可以明确大部分样本属于什么长度范围,然后进行句子的长短补齐或截断
- 代码实现:(柱状图、曲线图)
python
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()
- 代码实现(散点图)
python
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 词频和高频词云
properties
词频: 这里指的是统计样本中词汇的总数量(需要去重)
高频词云目的: 可以方便我们查看数据集中是否存在脏数据,进而实现人工的审核和清洗
- 词频:词汇总数统计
python
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)}')
- 高频词云
python
# 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")
# 准备数据,要有分隔符,一个str
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】
将n个连续相邻的token组合到一起,n-gram特征
【为什么要规范文本长度】
模型的输入需要【每个batch】或者【整个数据集】,统一句子长度
properties
对语料添加普适性的特征:n-gram
对语料进行规范长度:适配模型的输入
5.1 添加N-Gram特征
properties
定义: 将连续的相邻的词或者字组合到一块,就称为n-gram特征
- 代码实现
python
# 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 文本长度规范
properties
意义: 模型一般需要固定尺寸的输入,因此需要对文本句子进行补齐(一般用0补齐)或者截断
- 代码实现
python
# 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="pre")
# 假定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 文本数据增强
properties
目的: 增加数据集(扩展数据集)
6.1 回译数据增强
properties
定义: 通过将一种语言翻译成不同的语言,再转换回来的一种方式
eg: 中文---韩文----英语---中文