2.自然语言处理NLP - 文本预处理

1. 文本处理基本方法(★)

文本表示的第一步通常是分词词表构建

1.1 分词(★)

分词的任务是将原始文本切分为若干具有独立语义的最小单元(token),分词 是所有NLP任务的起点。
词表 则是由语料库构建出来的、包含着模型可以识别的token的集合。词表中的每个token都分配有唯一的ID,并且支持token与ID之间的双向映射

在后续的训练预测的过程中,模型会首先进行分词,再通过词表将每个token映射为其对应的ID(索引)

。这些ID会被嵌入层转换为低维稠密向量(词向量 )。即词向量是根据索引生成的

在文本生成任务中,模型的输出层会针对词表中的每个token生成一个概率分布 ,用来表示下一个词的可能性

1.1.1 英文分词

按照分词粒度的大小,可分为词级分词、字符级分词、子词级分

1.1.1.1 词级分词

指的是将文本按照词语进行切分,是最传统、最直观的分词方式。在英文中,空格和标点往往是天然的分隔符

词级分词虽然便于理解和实现,但在实际中容易出现OOV (Out-Of-Vocabulary)问题,即"未登录问题"。这个问题是指在模型使用阶段,输入文本中出现了不再预先构建词表中的词语,由于模型无法识别这些词,通常会将其统一替换为特殊标记,从而导致语义信息的丢失,最终影响模型的理解与预测能力

1.1.1.2 字符级分词

指的是以单个字符为最小单位进行分词的方法,文本中的每一个字母、数字、标点、空格都会被视作一个独立的token
这种分词形式,词表的颗粒度非常小,因此几乎不存在OOV问题。但是相反地它的语义表示能力就会非常弱,此时就必须依赖更长的上下文来推断词义和语句结构,这无疑会增加模型构建难度和训练成本。此外,输入序列也会变得很长,影响模型效率。

1.1.1.3 子词级分词

指的是介于词级分词和字符级分词之间的分词方法,它主张将词语切分为更小的单元--字词。它的基本思想是:即使一个完整的词没有出现在词表中,只要它可以被拆分为词表中存在的字词单元,就可以被模型识别和表示,从而避免整体被替换为< UNK >

1.1.2 中文分词

尽管英文分词具有天然的优势,但是我们仍然可以借助"分词粒度"的视角,对中文的分词方式进行归类和分析

1.1.2.1 字符级分词

字符级分词是中文处理中最简单的一种方式,即将文本按照单个汉字进行切分,文本中的每一个汉字都被视为一个独立的 token。

由于汉字本身通常具有独立语义,因此字符级分词在中文中具备天然的可行性。相比英文中的字符分词,中文的字符分词更加"语义友好"。

1.1.2.2 词级分词

词级分词是将中文文本按照完整词语进行切分的传统方法,切分结果更贴近人类阅读习惯。

由于中文没有空格等天然词边界,词级分词通常依赖词典、规则或模型来识别词语边界。

1.1.2.3 子词级分词

虽然中文没有英文中的子词结构(如前缀、后缀、词根等),但子词分词算法(如 BPE)仍可直接应用于中文。它们以汉字为基本单位,通过学习语料中高频的字组合(如"自然"、"语言"、"处理"),自动构建子词词表。这种方式无需人工词典,具有较强的适应能力。

在当前主流的中文大模型(如通义千问、DeepSeek)中,子词分词已成为广泛采用的文本切分策略。

1.1.3 jieba分词库

"结巴"分词是中文分词领域中应用广泛的开源工具之一,具有接口简洁、模式灵活、词典可扩展等特点,在各类传统 NLP 任务中依然具备良好的实用价值。

1.1.3.1 精确模式(默认模式)

精确模式分词可使用jieba.cut或者jieba.lcut方法,前者返回一个生成器对象,后者返回一个list。具体代码如下:

python 复制代码
 import jieba

text = "小明毕业于北京大学计算机系"

words_generator = jieba.cut(text)  # 返回一个生成器
for word in words_generator:
    print(word)

words_list = jieba.lcut(text)  # 返回一个列表
print(words_list)

输出

1.1.3.2 全模式

全模式分词可使用jieba.cut或者jieba.lcut,并将cut_all参数设置为True,具体代码如下:

python 复制代码
import jieba

text = "小明毕业于北京大学计算机系"

words_generator = jieba.cut(text, cut_all=True)  # 返回一个生成器
for word in words_generator:
    print(word)

words_list = jieba.lcut(text, cut_all=True)  # 返回一个列表
print(words_list)

输出

1.1.3.3 搜索引擎模式

可使用jieba.cut_for_search或者jieba.lcut_for_search,具体代码如下:

python 复制代码
import jieba

text = "小明毕业于北京大学计算机系"

words_generator = jieba.cut_for_search(text)  # 返回一个生成器
for word in words_generator:
    print(word)

words_list = jieba.lcut_for_search(text)  # 返回一个列表
print(words_list)

输出

1.1.3.4 自定义词典模式
  • 使用用户自定义词典
  • 添加自定义词典后,jieba能够准确识别词典中出现的词汇,从而提升整体的识别准确率
  • 词典格式:每一行分三部分:词语、词频、词性,用空格隔开,顺序不可颠倒
    在项目根目录创建一个文件:userdict.txt ,内容如下:
text 复制代码
火锅底料 5 n
我讲礼貌 6 n
拿去比较 7 nz

代码

python 复制代码
import jieba

# 原始句子
sentence = """老子吃火锅 你吃火锅底料 火锅底料
对你笑呵呵 因为我讲礼貌 我讲礼貌
狠货有好多 个人拿去比较 拿去比较"""

# === 情况1:不使用自定义词典 ===
print("【未加载自定义词典】")
mydata = jieba.lcut(sentence, cut_all=False)
print('mydata -->', mydata)

# === 情况2:加载自定义词典后分词 ===
# 加载用户词典(确保路径正确)
jieba.load_userdict("./userdict.txt")

print("\n【加载自定义词典后】")
mydata2 = jieba.lcut(sentence, cut_all=False)
print('mydata2 -->', mydata2)

输出

1.2 词性标注(★)

词性标注是指:给句子中的每个词赋予一个对应的词性标签,比如名词(Noun)、动词(Verb)、形容词(Adjective)、副词(Adverb)等。

jieba词性标注案例:

python 复制代码
import jieba.posseg as pseg

# 输入句子
sentence = "小明在清华大学学习人工智能,他非常喜欢自然语言处理。"

# 使用 jieba 的 posseg 模块进行分词 + 词性标注
words = pseg.cut(sentence)

# 打印结果
for word, flag in words:
    print(f"{word}\t{flag}")

jieba 词性标注对照表(基于 ICTCLAS 标准)

标签 中文名称 英文解释 示例
Ag 形语素 Adjective morpheme (如"美"在"美丽"中)
a 形容词 Adjective 美丽、高效、绿色
ad 副形词 Adjective as adverbial 突然("他突然出现")
an 名形词 Adjective as noun 创新("推动创新")
b 区别词 Distinguisher 男、女、大型、初级
c 连词 Conjunction 和、但是、如果
d 副词 Adverb 非常、已经、马上
df 趋向副词 Directional adverb 上、下、进来、出去
dg 副语素 Adverbial morpheme (构词语素,较少单独出现)
e 叹词 Interjection 哎呀、哦、嗯
f 方位词 Locative noun 上、下、左边、中间
g 语素 Morpheme (general) (多用于未登录词语素)
h 前接成分 Prefix 阿(阿明)、老(老虎)
i 成语 Idiom 画龙点睛、一举两得
j 简称略语 Abbreviation 北大、GDP、WTO
k 后接成分 Suffix 子(桌子)、头(石头)
l 习用语 Fixed expression 换句话说、总而言之
m 数词 Numeral 三、一百、2025
n 普通名词 Common noun 苹果、技术、想法
ng 名语素除 Noun morpheme (如"民"在"人民"中)
nr 人名 Proper noun -- person 小明、李白、张伟
ns 地名 Place name 北京、杭州湾、亚洲
nt 机构团体名 Organization 清华大学、联合国、腾讯
nz 其他专有名词 Other proper noun 人工智能、区块链、iPhone
o 拟声词 Onomatopoeia 哗啦、咚咚、喵喵
p 介词 Preposition 在、从、关于、对于
q 量词 Quantifier / Measure word 个、次、公斤、年
r 代词 Pronoun 他、我们、自己、什么
s 处所词 Space/Location word 附近、周围、这里
t 时间词 Time word 今天、昨天、2025年
tg 时间语素 Time morpheme (如"春"在"春天"中)
u 助词 Auxiliary particle 的、了、着、过、吗
v 动词 Verb 学习、喜欢、运行
vd 副动词 Verb as adverbial 互相、努力("努力学习")
vg 动语素 Verb morpheme (如"学"在"学习"中)
vi 不及物动词 Intransitive verb 睡觉、咳嗽
vn 名动词 Verb as noun 研究、发展、投资
vq 趋向动词 Directional verb 上来、下去、进去
w 标点符号 Punctuation ,。!?;:""''()【】
x 非语素字 / 未知词 Non-morpheme / Unknown 字母、数字、乱码、表情符号
y 语气词 Modal particle 吗、呢、吧、啊
z 状态词 Status word 雪白、通红、冰凉

1.3 命名实体识别(★)

放洋屁叫做"Named Entity Recognition,简称NER"

  • 命名实体: 通常我们将人名, 地名, 机构名等专有名词统称命名实体. 如: 周杰伦, 黑山县, 孔子学院, 24辊方钢矫直机.
  • 顾名思义, 命名实体识别(Named Entity Recognition,简称NER)就是识别出一段文本中可能存在的命名实体.## 1.2 文本张量表示

案例:

鲁迅, 浙江绍兴人, 五四新文化运动的重要参与者, 代表作朝花夕拾.

==>

鲁迅(人名) / 浙江绍兴(地名)人 / 五四新文化运动(专有名词) / 重要参与者 / 代表作 / 朝花夕拾(专有名词)

同词汇一样, 命名实体也是人类理解文本的基础单元, 因此也是AI解决NLP领域高阶任务的重要基础环节.

1.4 窗口

什么是

"窗口"(Window)在自然语言处理(NLP)中,尤其是在词向量模型(如 Word2Vec 的 CBOW 和 Skip-gram)中,是一个非常核心的概念。

窗口(Context Window) 是指:

在一个句子中,围绕目标词(中心词)的一段局部上下文范围,通常用一个固定长度的"滑动窗口"来定义。
这个窗口决定了:哪些词会被当作当前词的"上下文"(context words)。

·
✅ 举个例子 🌰

假设有一句话:

"愿你自由成长"

我们把每个字看作一个词(为简化),词汇序列是:

愿, 你, 自, 由, 成, 长


情况1:以"自"为中心词(target word)

  • 向左看 2 个词:愿, 你
  • 向右看 2 个词:由, 成
  • 所以上下文词是:[愿, 你, 由, 成]

💡 注意:如果靠近句首或句尾,窗口会自动截断(比如"愿"左边没词,就只取右边)。


情况2:以"你"为中心词

  • 左边1个:愿
  • 右边2个:自, 由
  • 上下文:[愿, 自, 由](因为左边不够2个)

✅ 窗口大小怎么选?

  • 小窗口(如 1~2):
    捕捉语法/局部结构(比如"strong tea"、"make decision")
  • 大窗口(如 5~10):
    捕捉语义/主题相似性(比如"car"和"drive"、"engine"可能出现在较远位置)

Word2Vec 论文中常用窗口大小为 5~10。


为什么

  • 不是所有词都和当前词相关(比如相隔很远的词可能无关)
  • 降低计算复杂度:不用考虑整句
  • 符合语言局部性假设:一个词的含义主要由其邻近词决定("You shall know a word by the company it keep s" --- J.R. Firth)

3. 文本张量表示

3.1 one-hot

✅ 定义

将每个词表示为一个长度等于词汇表大小的二进制向量,只有一个位置是 1,其余都是 0。

🧪 示例

假设词汇表:["我", "爱", "猫", "狗"],共 4 个词。

  • "我" → [1, 0, 0, 0]
  • "爱" → [0, 1, 0, 0]
  • "猫" → [0, 0, 1, 0]
  • "狗" → [0, 0, 0, 1]

🌟 生动比喻

想象你有 4 个抽屉,每个抽屉放一个词。

当你说"我",就打开第一个抽屉,其他都关着。

这就是 one-hot ------只亮一个灯!

⚠️ 缺点

  • 维度爆炸(词汇表大时向量很长)
  • 无法表达语义相似性:比如"猫"和"狗"在向量中距离很远,但它们都是动物!
  • ❌ "猫"和"狗"在 one-hot 中完全不相关!

代码案例

python 复制代码
# 导包
# 1.准备数据
vocabs = ["周杰伦", "陈奕迅", "王力宏", "李宗盛", "鹿晗", '邓超']
# 2.构建词表
word2index = {name: i for i, name in enumerate(vocabs)}
print(word2index)
# 3.开始one-hot编码
for name in vocabs:
    # 初始化一行全0的列表
    zero_list = [0] * len(vocabs)
    # 同时设置name对应位置为1
    name_idx = word2index[name]
    zero_list[name_idx] = 1
    print(f"{name}的one-hot编码是:{zero_list}")

3.1.1 Tokenizer

✅ 作用

把原始文本切分成基本单位(token),供模型处理。

🧩 不同语言不同策略

语言 分词方式
英文 空格分词 ("I love NLP" → ["I", "love", "NLP"])
中文 需要分词工具 (jieba, HanLP)
其他 有些用字符级(如 BERT)

⚠️ 重要性

  • 分词错误 → 向量化失败 → 模型学不到东西
  • 所以:Tokenizer 是 NLP 的第一道关口

代码案例

python 复制代码
import jieba
# 导入keras中的词汇映射器Tokenizer : pip install tensorflow
from tensorflow.keras.preprocessing.text import Tokenizer

# 导入用于对象保存与加载的joblib
import joblib


# 思路分析 生成onehot
# 1 准备语料 vocabs
# 2 实例化词汇映射器Tokenizer, 使用映射器拟合现有文本数据 (内部生成 index_word word_index)
# 2-1 注意idx序号-1
# 3 查询单词idx 赋值 zero_list,生成onehot
# 4 使用joblib工具保存映射器 joblib.dump()
def dm_onehot_gen():
    # 1 准备语料 vocabs
    vocabs = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}

    # 2 实例化词汇映射器Tokenizer, 使用映射器拟合现有文本数据 (内部生成 index_word word_index)
    # 2-1 注意idx序号-1
    mytokenizer = Tokenizer()
    mytokenizer.fit_on_texts(vocabs)

    # 3 查询单词idx 赋值 zero_list,生成onehot
    for vocab in vocabs:
        zero_list = [0] * len(vocabs)
        idx = mytokenizer.word_index[vocab] - 1
        zero_list[idx] = 1
        print(vocab, '的onehot编码是', zero_list)

    # 4 使用joblib工具保存映射器 joblib.dump()
    mypath = './mytokenizer'
    joblib.dump(mytokenizer, mypath)
    print('保存mytokenizer End')

    # 注意5-1 字典没有顺序 onehot编码没有顺序 []-有序 {}-无序 区别
    # 注意5-2 字典有的单词才有idx idx从1开始
    # 注意5-3 查询没有注册的词会有异常 eg: 狗蛋
    print(mytokenizer.word_index)
    print(mytokenizer.index_word)


# 思路分析
# 1 加载已保存的词汇映射器Tokenizer joblib.load(mypath)
# 2 查询单词idx 赋值zero_list,生成onehot 以token为'李宗盛'
# 3 token = "狗蛋" 会出现异常
def dm_onehot_use():
    vocabs = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}

    # 1 加载已保存的词汇映射器Tokenizer joblib.load(mypath)
    mypath = './mytokenizer'
    mytokenizer = joblib.load(mypath)

    # 2 编码token为"李宗盛"  查询单词idx 赋值 zero_list,生成onehot
    token = "李宗盛"
    zero_list = [0] * len(vocabs)
    idx = mytokenizer.word_index[token] - 1
    zero_list[idx] = 1
    print(token, '的onehot编码是', zero_list)


if __name__ == '__main__':
    # 函数不调用不执行
    dm_onehot_gen()
    dm_onehot_use()

3.2 word2vec(★)

✅ 目标

解决 one-hot 的问题:让相似词在向量空间中靠得近。

例如:"猫"和"狗"应该离得很近,"吃"和"喝"也该靠近。

🤔 原理

基于一个简单假设:

上下文决定词义 ------ 出现在相同语境中的词,意义相近。

它有两种方式:CBOW 和 skip-gram

3.2.1 CBOW(★)

CBOW(Continuous Bag of Words)
用上下文预测中心词

输入:[我, 爱, 狗] → 预测中间词:"爱"

👉 就像你在玩"猜词游戏":看到"我"和"狗",你能猜出中间可能是"爱"或"养"。

代码案例

python 复制代码
import torch
import torch.nn as nn

# 语料(一句话)
text = "I like NLP and I like coding".split()

# 构建词表
vocab = list(set(text))
word_to_idx = {w: i for i, w in enumerate(vocab)}
idx_to_word = {i: w for i, w in enumerate(vocab)}
vocab_size = len(vocab)
print("词表:", vocab)

# 超参数
embed_dim = 5
window = 2

# 准备训练数据:(上下文, 目标词)
data = []
for i in range(window, len(text) - window):
    context = [text[i - 2], text[i - 1], text[i + 1], text[i + 2]]
    target = text[i]
    data.append((context, target))

# 模型
class CBOW(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, embed_dim)
        self.proj = nn.Linear(embed_dim, vocab_size)
    def forward(self, ctx_idxs):
        emb = self.embed(ctx_idxs)          # [4, embed_dim]
        avg = torch.mean(emb, dim=0)        # [embed_dim]
        out = self.proj(avg)                # [vocab_size]
        return out

model = CBOW(vocab_size, embed_dim)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

# 训练
for epoch in range(200):
    total_loss = 0
    for ctx, tgt in data:
        ctx_ids = torch.tensor([word_to_idx[w] for w in ctx])
        tgt_id = torch.tensor([word_to_idx[tgt]])
        optimizer.zero_grad()
        output = model(ctx_ids)
        loss = criterion(output.unsqueeze(0), tgt_id)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    if epoch % 50 == 0:
        print(f"Epoch {epoch}, Loss: {total_loss:.2f}")

# 查看词向量
print("\n学到的词向量(每个词的向量):")
with torch.no_grad():
    for word in vocab:
        vec = model.embed.weight[word_to_idx[word]]
        print(f"{word}: {vec.numpy().round(2)}")

3.2.2 skip-gram(★)

用中心词预测上下文

输入:中心词"爱" → 预测上下文:"我"、"狗"

👉 就像你读了一句话:"我爱狗",然后问自己:"'爱'这个词通常跟哪些词一起出现?"

✅ 实际中,skip-gram 效果更好,尤其对低频词。

python 复制代码
import torch
import torch.nn as nn

# 语料(一句话)
text = "I like NLP and I like coding".split()

# 构建词表
vocab = list(set(text))
word_to_idx = {w: i for i, w in enumerate(vocab)}
idx_to_word = {i: w for i, w in enumerate(vocab)}
vocab_size = len(vocab)
print("词表:", vocab)

# 超参数
embed_dim = 5
window = 2

# 准备训练数据:(中心词, 上下文词) ------ 一个中心词对应多个上下文
data = []
for i in range(window, len(text) - window):
    center = text[i]
    for j in [i - 2, i - 1, i + 1, i + 2]:
        context = text[j]
        data.append((center, context))

# 模型:输入中心词,输出预测的上下文词
class SkipGram(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, embed_dim)
        self.proj = nn.Linear(embed_dim, vocab_size)
    def forward(self, center_idx):
        emb = self.embed(center_idx)      # [embed_dim]
        out = self.proj(emb)              # [vocab_size]
        return out

model = SkipGram(vocab_size, embed_dim)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

# 训练
for epoch in range(200):
    total_loss = 0
    for center, context in data:
        center_id = torch.tensor([word_to_idx[center]])
        context_id = torch.tensor([word_to_idx[context]])
        optimizer.zero_grad()
        output = model(center_id)
        loss = criterion(output, context_id)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    if epoch % 50 == 0:
        print(f"Epoch {epoch}, Loss: {total_loss:.2f}")

# 查看学到的词向量
print("\n学到的词向量(每个词的向量):")
with torch.no_grad():
    for word in vocab:
        vec = model.embed.weight[word_to_idx[word]]
        print(f"{word}: {vec.numpy().round(2)}")

3.3 word embedding(★)

✅ 定义

word embedding 是一个广义术语,指将词语映射为连续向量的过程。

所以:word2vec 是一种实现 word embedding 的方法。

🧠 举个栗子

"国王" - "男人" ≈ "女王" - "女人"

在 embedding 空间中,这个等式成立!

→ 说明模型学到了性别、角色的关系。

代码案例

python 复制代码
import torch
import torch.nn as nn

# 1. 准备语料(一句话)
sentence = "I love natural language processing".split()
print("句子:", sentence)

# 2. 构建词表(含 <PAD> 和 <UNK> 更规范,这里简化)
vocab = ["<PAD>"] + list(set(sentence))  # 加一个填充符(可选)
word_to_idx = {word: i for i, word in enumerate(vocab)}
idx_to_word = {i: word for i, word in enumerate(vocab)}

vocab_size = len(vocab)
embed_dim = 8  # 每个词用 8 维向量表示

print("\n词表:", vocab)
print("词到索引:", word_to_idx)

# 3. 创建 Embedding 层(随机初始化词向量)
embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embed_dim)

# 4. 把句子转成索引
input_ids = torch.tensor([word_to_idx[word] for word in sentence])
print("\n输入索引:", input_ids.tolist())

# 5. 获取词向量
word_vectors = embedding(input_ids)  # shape: [seq_len, embed_dim]
print("\n词向量形状:", word_vectors.shape)
print("第一个词 'I' 的向量:\n", word_vectors[0].detach().numpy().round(3))

3.4 fasttext(★)

✅ 由 Facebook 提出,扩展了 word2vec

🎯 特色功能

  • 支持子词(subword)

    → 把词拆成更小单元,比如 "unhappy" → "un", "happ", "y"

  • 特别适合稀疏词和拼写错误

    → 即使没见过"unhappiness",也能根据"un", "happ"推测含义

    中文也适用(虽然不如英文明显)

🌟 举个例子

"跑步" → 可分解为 "跑" + "步"

即使没学过"跑步",只要见过"跑"和"步",就能理解

✅ fasttext 在低资源语言和拼写纠错场景中表现优异!

window系统安装指令

python 复制代码
pip install fasttext-wheel

3.6 小结

方法 是否稠密 能否表达语义 是否考虑上下文 适合场景
one-hot ❌ 稀疏 ❌ 完全不行 ❌ 不考虑 初学者、小数据集
word2vec ✅ 稠密 ✅ 很好 ✅ 强依赖 大规模文本、语义分析
fasttext ✅ 稠密 ✅ 更强(子词) ✅ 强依赖 拼写错误、罕见词、多语言

扩展:画图 - Transorboard

4. 文本数据分析

4.1词云

"词云"不是什么魔法,它是把文本中出现频率高的词,用大小不一的字体展示出来------字越大,越热门!

✅ 举个例子:

你写了一篇关于"人工智能"的文章,生成词云后发现:"深度学习"、"模型"、"数据"这几个词特别大,说明它们是核心话题。

💡 实现思路(简化版):

python 复制代码
from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "人工智能 机器学习 深度学习 自然语言处理"

# Windows 中文黑体路径(几乎每台 Windows 都有)
font_path = "C:/Windows/Fonts/simhei.ttf"

wordcloud = WordCloud(
    font_path=font_path,
    background_color='white',
    width=800,
    height=400
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()

输出

🧠 小贴士:词云常用于文本分析可视化,比如社交媒体情绪分析、用户评论挖掘等。

4.2 回顾API(★)

4.2.1 可变参数,可变参数键值对(★)

python 复制代码
def demo(*args, **kwargs):
    print("args:", args)      # 位置参数 → 元组
    print("kwargs:", kwargs)  # 关键字参数 → 字典

demo(1, 2, name="Alice", age=25)

4.2.2 join()

把列表拼成字符串 → 字符串

python 复制代码
",".join(['a', 'b', 'c'])  # "a,b,c"

4.2.3 split()

把字符串按分隔符切开 → 列表

python 复制代码
"a,b,c".split(",")  # ['a', 'b', 'c']

4.2.4 set和list

set 无序,去重神器

list 有序,可重复

python 复制代码
lst = [1, 2, 2, 3]
s = set(lst)  # {1, 2, 3}

4.2.5 推导式

把循环写成一行,提升效率

python 复制代码
# 普通方式
squares = []
for i in range(5):
    squares.append(i**2)

# 推导式(更 Pythonic)
squares = [i**2 for i in range(5)]

4.2.6 生成器

不是一次性全算完,而是边走变算,节省内存。适合处理大数据流,比如读取超大文件时进行逐行处理

python 复制代码
def my_gen():
    yield 1
    yield 2
    yield 3

g = my_gen()
next(g)  # 1
next(g)  # 2

4.2.7chain

将多个对象"连接起来"当一个使用

python 复制代码
from itertools import chain
lists = [[1, 2], [3, 4], [5]]
flat = list(chain(*lists))  # [1, 2, 3, 4, 5]

4.2.8 zip打包和zip解包

将两个列表"拉链式"配对,常用于数据对齐、批量处理

python 复制代码
names = ["张三", "李四"]
scores = [90, 85]

# 打包:配对
for name, score in zip(names, scores):
    print(f"{name}: {score}")

# 解包:还原
unzipped = zip(*zip(names, scores))

4.2.9 map与lambda

map 对每个元素"统一加工"

lambda 简单计算就用它

python 复制代码
numbers = [1, 2, 3]
squared = list(map(lambda x: x**2, numbers))  # [1, 4, 9]

⚠️ 注意:map() 返回的是 map 对象,需要 list() 转换。

💡 lambda 通常和 map, filter, sorted 一起用

4.2.10 列表拼接extend 和 +

会修改原列表

python 复制代码
a = [1, 2]
b = [3, 4]

# 方法1:+
c = a + b

# 方法2:extend(原地修改)
a.extend(b)

# 方法3:*解包
d = [*a, *b]

4.2.11 串联为一行代码

⚠️ 提醒:别为了"一行"牺牲可读性,适度才是王道!

python 复制代码
# 传统写法
result = []
for i in range(10):
    if i % 2 == 0:
        result.append(i**2)
print(result)

# 一行搞定(推导式 + 条件)
print([i**2 for i in range(10) if i % 2 == 0])

# 用 lambda + map + filter
print(list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, range(10)))))

4.2.12 seaborn

Seaborn 是建立在 Matplotlib 之上的"高级画笔"------它把枯燥的数据绘图变成像杂志封面一样精致的图表。你可以把它想象成一个"视觉设计师",专为统计分析量身定制。

它特别擅长处理数据框(DataFrame),支持自动配色、样式美化和统计模型可视化。

🔧 核心特点

  • 基于 Pandas DataFrame
  • 内置主题和调色板
  • 支持常见统计图:热力图、箱线图、散点图、回归图等
  • 与 matplotlib 兼容
    代码
python 复制代码
import seaborn as sns
import matplotlib.pyplot as plt

# 加载内置数据集
iris = sns.load_dataset("iris")

# 设置绘图风格(可选)
sns.set(style="whitegrid")

# 1. 散点图 + 分类颜色(pairplot 自动分组)
sns.pairplot(iris, hue="species")
plt.show()

# 2. 相关性热力图
plt.figure(figsize=(8, 6))
sns.heatmap(iris.corr(), annot=True, cmap="coolwarm", center=0)
plt.title("Iris 特征相关性热力图")
plt.show()

4.3 文本数据分析案例

4.3.1 情感分析

情感分析是一种自然语言处理技术,用来判断一段文字的情绪倾向,比如:

  • 正面(开心)✅
  • 负面(生气)❌
  • 中性(无感)😐

💡 举个例子:

输入:"这部电影太棒了,演员演技炸裂!"

输出:"情感:正面,置信度:0.98"

4.3.2 案例流程

1)获取标签数量分布

2)获取句子长度分布

3)获取正负样本长度散点分布

4)获取不同词汇总数统计

5)获取训练集高频形容词词云

6)获取验证集形容词词云

python 复制代码
# -*- coding: utf-8 -*-
import re
import jieba  # 分词工具(需先安装:pip install jieba)
from collections import Counter
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import numpy as np

# ================================
# 📌 模拟数据:训练集和验证集评论(中文)
# ================================
train_texts = [
    "这部电影太好看了,演员演技很棒,剧情也很精彩。",
    "我觉得一般般,节奏有点慢,但音乐不错。",
    "非常推荐!特效震撼,导演功力深厚。",
    "无聊透顶,浪费时间,完全不值得一看。",
    "画面很美,情感真挚,看完哭了。"
]

val_texts = [
    "这部剧太燃了,看得我热血沸腾!",
    "平平无奇,没有亮点,建议别看。",
    "剧情反转太多,有点混乱,但还算好看。",
    "演员表现不错,可惜剧本拖沓。",
    "太感人了,是我今年看过最好的电影。"
]

# 标签:正面 / 负面 / 中性
train_labels = ['正面', '中性', '正面', '负面', '正面']
val_labels = ['正面', '负面', '中性', '中性', '正面']

# ================================
# 🔹 1. 获取标签数量分布
# ================================
print("=== 1. 标签数量分布 ===")
label_count = Counter(train_labels)
print(label_count)

# 可视化
plt.figure(figsize=(6, 4))
plt.bar(label_count.keys(), label_count.values())
plt.title("训练集标签分布")
plt.ylabel("数量")
plt.show()

# ================================
# 🔹 2. 获取句子长度分布
# ================================
print("\n=== 2. 句子长度分布 ===")
sentence_lengths = [len(text) for text in train_texts]
print(f"长度列表: {sentence_lengths}")

# 绘制直方图
plt.figure(figsize=(8, 5))
plt.hist(sentence_lengths, bins=5, edgecolor='black')
plt.title("训练集句子长度分布")
plt.xlabel("字符数")
plt.ylabel("频次")
plt.show()

# ================================
# 🔹 3. 获取正负样本长度散点分布
# ================================
print("\n=== 3. 正负样本长度散点分布 ===")
positive_lengths = [len(text) for text, label in zip(train_texts, train_labels) if label == '正面']
negative_lengths = [len(text) for text, label in zip(train_texts, train_labels) if label == '负面']

print(f"正面样本长度: {positive_lengths}")
print(f"负面样本长度: {negative_lengths}")

# 散点图
plt.figure(figsize=(8, 5))
plt.scatter(range(len(positive_lengths)), positive_lengths, color='green', label='正面')
plt.scatter(range(len(negative_lengths)), negative_lengths, color='red', label='负面')
plt.title("正负样本句子长度对比")
plt.xlabel("样本序号")
plt.ylabel("句子长度")
plt.legend()
plt.show()

# ================================
# 🔹 4. 获取不同词汇汇总统计
# ================================
print("\n=== 4. 不同词汇汇总统计 ===")

# 使用 jieba 进行中文分词
all_words = []
for text in train_texts + val_texts:
    words = jieba.lcut(text)  # 分词
    all_words.extend(words)

# 去除停用词(可选)
stopwords = {'的', '了', '是', '在', '我', '有', '这', '就', '也', '很', '都', '一', '个'}
filtered_words = [word for word in all_words if word not in stopwords and len(word) > 1]

# 统计词频
word_freq = Counter(filtered_words)
print("前10高频频词:")
for word, freq in word_freq.most_common(10):
    print(f"{word}: {freq}")

# ================================
# 🔹 5. 获取训练集/验证集形容词词云
# ================================
print("\n=== 5. 形容词词云 ===")

# 提取形容词(简单规则:常见形容词列表)
adjectives = {
    '好', '棒', '精彩', '震撼', '燃', '感人', '无聊', '差', '一般', '普通',
    '优秀', '出色', '失望', '失望', '失望', '感动', '喜欢', '讨厌', '推荐'
}

# 提取训练集中的形容词
train_adjectives = [word for word in filtered_words if word in adjectives]
train_text = " ".join(train_adjectives)

# 提取验证集中的形容词
val_adjectives = [word for word in jieba.lcut(" ".join(val_texts)) if word in adjectives]
val_text = " ".join(val_adjectives)

# 设置字体路径(Windows 示例)
font_path = "C:/Windows/Fonts/simhei.ttf"  # 修改为你的系统路径!

# 生成词云
plt.figure(figsize=(12, 5))

# 训练集词云
plt.subplot(1, 2, 1)
wc_train = WordCloud(font_path=font_path, background_color='white').generate(train_text)
plt.imshow(wc_train, interpolation='bilinear')
plt.axis("off")
plt.title("训练集形容词词云")

# 验证集词云
plt.subplot(1, 2, 2)
wc_val = WordCloud(font_path=font_path, background_color='white').generate(val_text)
plt.imshow(wc_val, interpolation='bilinear')
plt.axis("off")
plt.title("验证集形容词词云")

plt.tight_layout()
plt.show()

5. 文本特征处理

5.1 n-gram(★)

定义:n-gram 是一种基于统计的语言模型,通过计算连续 n 个词(或字)共同出现的概率,来建模语言的规律。

想象你是个刚学说话的小孩,只会听大人讲话,但还不懂语法。

有一天你听到大人说:

"今天天气真__。"

你会猜下一个词是什么?

"好"?"差"?"热"?......大概率不会是"冰箱"或"火箭"对吧?

为什么?因为你潜意识里记住了"天气真"后面常跟什么词------这就是 n-gram 的核心思想!

🔢 具体例子:

  • 1-gram(unigram):只看单个词。
    → "好" 出现很多次,所以概率高。但它完全不管上下文!(太孤僻)
  • 2-gram(bigram):看两个词的组合。
    → "天气 真" → 下一个词很可能是"好"。
    模型会记录:"真" 后面接 "好" 的频率特别高。
  • 3-gram(trigram):看三个词。
    → "今天 天气 真" → 更准!可能90%后面是"好",5%是"热",剩下乱码忽略。

💡 所以:n 越大,上下文越丰富,预测越准------但代价是数据稀疏(很多组合根本没见过)!

🛠️ 它能干啥?(应用场景)

输入法预测:你打"人工",它弹出"智能"------背后就是 bigram/trigram 在算概率。

拼写纠错:"我吃苹国" → "苹国"很少和"吃"一起出现,但"苹果"很常见 → 自动纠正。

早期机器翻译 & 语音识别:用 n-gram 评估哪句话更"像人话"。

文本生成(原始版):给定开头,不断选概率最高的下一个词------虽然容易跑偏成"天气真好好好好......"

⚠️ 小缺点

短视:n=5 也就看5个词,长距离依赖搞不定(比如指代:"小明...他...")。

数据饥渴:要覆盖所有 n-gram,语料库得超级大,否则全是"零概率"。

没理解语义:它只知道"猫吃鱼"常见,但不知道猫为啥不吃石头 😅

✅ 所以后来有了 RNN、Transformer------它们才是"读心高手"。但 n-gram?是它们的启蒙老师!

📝 小结(板书重点)

名称 看几个词 优点 缺点
unigram 1 简单 忽略上下文
bigram 2 能抓局部搭配 长程关系不行
trigram+ 3+ 更准 数据稀疏、计算量大

硅谷的图:描述的是2-gram语言模型

6. 文本数据增强

6.1 回译数据增强法

回译(Back-Translation) 是一种常用的文本数据增强技术,通过将源语言句子翻译成另一种语言,再翻译回来,从而生成语义相近但表达不同的新句子。

原始中文:我爱人工智能

→ 翻译成英文:I love artificial intelligence

→ 再翻译回中文:我喜欢人工智能

👉 得到一个同义但不同表达的新样本!

💡 为什么有效?

  • 保留原意(因为是来回翻译)
  • 改变措辞(如"爱" → "喜欢")
  • 扩充训练数据,提升模型泛化能力
  • 特别适合小样本 NLP 任务(如情感分析、命名实体识别)

缺点是翻译过程中会产生损失

在"中文 → 英文 → 中文"的过程中,可能出现:

原句 回译结果 问题
"他打了电话。" "He made a call." → "他打了一个电话。" ✅ 可接受(轻微变化)
"苹果发布了新 iPhone。" "Apple released a new iPhone." → "苹果公司推出了一款新的 iPhone。" ✅ 信息保留
"我有点不舒服。" "I feel a little unwell." → "我觉得不太舒服。" ⚠️ 语气弱化
"禁止吸烟!" "No smoking!" → "不要吸烟。" ❌ 语气丢失、指令弱化
"张三涉嫌贪污。" "Zhang San is suspected of embezzlement." → "张三被怀疑有贪污行为。" ❌ 法律语义模糊化

📉 这种语义偏移、细节丢失、语气弱化,就是 回译损失(Back-Translation Noise / Semantic Drift)。

✅ 那么,如何解决或缓解这种损失?

方法一:✅ 人工筛选 + 质量过滤(最可靠)

  • 对回译结果做置信度评分
  • 使用 BLEU、BERTScore、COMET 等指标衡量与原文的相似度
  • 设定阈值,只保留高相似度样本
python 复制代码
from bert_score import score

def is_high_quality(original, back_translated, threshold=0.85):
    P, R, F1 = score([back_translated], [original], lang="zh", verbose=False)
    return F1.item() > threshold

# 示例
if is_high_quality("禁止吸烟!", "不要吸烟。"):
    print("保留")
else:
    print("丢弃")  # 实际会丢弃,因为语义强度变了

💡 适用场景:小规模高质量数据增强(如医疗、法律 NLP)

方法二:🔄 多语言回译 + 投票融合

不只用"中→英→中",而是走多条路径:

中文

→ 英文 → 中文

→ 法文 → 中文

→ 日文 → 中文

然后:

  • 保留出现多次的回译结果
  • 或取语义最接近原始句的版本

🌐 利用不同语言的表达习惯"互相校正"

任务类型 是否建议回译
情感分析(正面/负面) ✅ 可用(只要极性不变)
医疗诊断文本分类 ⚠️ 谨慎
法律条款识别 ❌ 不建议
命名实体识别(NER) ✅ 可用(实体通常保留)
问答系统(答案生成) ❌ 高风险

方法三:🚫 避免在敏感任务中使用回译

有些任务绝对不能容忍语义损失:

📌 原则:如果任务对精确语义、语气、否定词、程度副词敏感,慎用回译!

✅ 一句话总结:

回译确实会带来语义损失,但我们可以通过"质量过滤 + 多路径校验 + 大模型修正 + 任务适配"来有效控制它,让它从"有损压缩"变成"智能扩增"。

补充内容

tensorboard

seaborn

pyecharts

相关推荐
霖大侠2 小时前
Squeeze-and-Excitation Networks
人工智能·算法·机器学习·transformer
天竺鼠不该去劝架2 小时前
财务自动化怎么做?财务RPA选型清单与路径
人工智能·科技·自动化
好奇龙猫2 小时前
人工智能学习-AI-MIT公开课-第三节:推理:目标树与基于规则的专家系统-笔记
人工智能·笔记·学习
正经人_x2 小时前
学习日记28:Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks
人工智能·深度学习·cnn
好奇龙猫2 小时前
【AI学习-comfyUI学习-第二十节-controlnet线稿+softedge线稿处理器工作流艺术线处理器工作流-各个部分学习】
人工智能·学习
陈橘又青2 小时前
vLLM-Ascend推理部署与性能调优深度实战指南:架构解析、环境搭建与核心配置
人工智能·后端·ai·架构·restful·数据·vllm
世优科技虚拟人2 小时前
AI数字人企业产品图谱解析:2D/3D数字人AI交互开发技术指南
人工智能·大模型·人机交互·数字人·智能交互
LiFileHub3 小时前
2025 AI驱动产业转型全景手册:从技术破局到价值重生(附8大转型案例)
人工智能
python机器学习ML3 小时前
论文复现-以动物图像分类为例进行多模型性能对比分析
人工智能·python·神经网络·机器学习·计算机视觉·scikit-learn·sklearn