NLP自然语言处理硬核实战笔记

📖 导读

这份笔记整合了Code05/NLP下的所有代码,每一行都讲清楚是干啥的。看完能直接上手。

  • 零遗漏原则:所有关键代码完整覆盖
  • 显微镜式解析:逐行/逐参数讲解
  • 场景化映射:代码对应实际应用

🗺️ 一、文本预处理

1.1 jieba分词

python 复制代码
import jieba

# ========== 精确模式 ========
text = "传智教育是一家上市公司"
words = jieba.lcut(text)
print(words)
# ['传智教育', '是', '一家', '上市公司']

💡 代码解析

  • jieba.lcut():返回列表形式
  • 精确模式:尽量保持词语完整
python 复制代码
# ========== 全模式 ========
words_all = jieba.lcut(text, cut_all=True)
print(words_all)
# ['传智', '教育', '是', '一', '家', '上市', '公司']

# ========== 搜索引擎模式 ========
words_search = jieba.lcut_for_search(text)
print(words_search)
# ['传智', '教育', '上市', '公司', '传智教育', '上市公司']

📊 三种模式对比

复制代码
原句: "传智教育是一家上市公司"

精确模式:   ['传智教育', '是', '一家', '上市公司']  ← 推荐
全模式:    ['传智', '教育', '是', '一', '家', '上市', '公司']
搜索引擎:  ['传智', '教育', '上市', '公司', '传智教育', '上市公司']

1.2 词性标注

python 复制代码
import jieba.posseg as pseg

text = "我爱自然语言处理"
result = pseg.lcut(text)

for word, flag in result:
    print(f"{word}: {flag}")

# 输出:
# 我: r    (代词)
# 爱: v    (动词)
# 自然语言: n  (名词)
# 处理: vn   (动名词)

💡 代码解析

  • jieba.posseg:分词+词性一次性搞定

1.3 自定义词典

python 复制代码
# ====== 方式1: 代码中添加 ======
jieba.add_word("传智教育")
jieba.add_word("黑马程序员")

# ====== 方式2: 词典文件 ======
# my_dict.txt 内容:
# 传智教育 10 n
# 黑马程序员 8 n

jieba.load_userdict("my_dict.txt")

text = "传智教育旗下有黑马程序员"
print(jieba.lcut(text))
# ['传智教育', '旗下', '有', '黑马程序员']

💡 代码解析

  • add_word():临时添加
  • load_userdict():从文件批量加载

1.4 停用词过滤

python 复制代码
# 常见停用词列表
stopwords = {
    '的', '了', '是', '在', '我', '有', '和',
    '就', '不', '人', '都', '一', '一个', '上', '也', '很'
}

text = "我是一名程序员,在北京工作"
words = jieba.lcut(text)

# 过滤停用词
filtered = [w for w in words if w not in stopwords and len(w) > 1]
print(filtered)
# ['程序员', '北京', '工作']

💻 二、文本向量化

2.1 One-Hot编码

python 复制代码
from tensorflow.keras.preprocessing.text import Tokenizer

# 词表
vocabs = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

tokenizer = Tokenizer()
tokenizer.fit_on_texts(vocabs)

# 词表索引
print(tokenizer.word_index)
# {'苹果': 1, '香蕉': 2, '橙子': 3, '葡萄': 4, '西瓜': 5}

# 转One-Hot矩阵
one_hot_matrix = tokenizer.texts_to_matrix(vocabs, mode='binary')
print(one_hot_matrix)

# 输出:
# [[1. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0.]
#  [0. 0. 1. 0. 0.]
#  [0. 0. 0. 1. 0.]
#  [0. 0. 0. 0. 1.]]

💡 代码解析

  • fit_on_texts():构建词表
  • texts_to_matrix():转One-Hot

⚠️ One-Hot问题

复制代码
稀疏: 10000个词 = 10000维向量,几乎全是0
无法计算相似度: 苹果(1,0,0...) 香蕉(0,1,0...) 距离=√2

2.2 Word2Vec

python 复制代码
import fasttext

# ====== 训练词向量 ======
# 需要准备大量文本文件: corpus.txt
model = fasttext.train_unsupervised(
    'corpus.txt',
    model='cbow',      # cbow或skipgram
    dim=100,           # 向量维度
    epoch=5,           # 训练轮数
    lr=0.1,           # 学习率
    thread=10         # 线程数
)

# 保存模型
model.save_model('word2vec.bin')

# ====== 使用 ======
# 获取词向量
vec = model.get_word_vector('苹果')
print(vec.shape)  # (100,)

# 找相似词
similar = model.get_nearest_neighbors('苹果')
print(similar)
# [(0.92, '香蕉'), (0.87, '水果'), (0.82, '橙子')]

💡 代码解析

  • cbow:用上下文预测中间词
  • skipgram:用中间词预测上下文
  • dim:词向量维度

🍎 Word2Vec vs One-Hot

复制代码
One-Hot: 苹果=[1,0,0,0]  香蕉=[0,1,0,0]  → 距离=√2
Word2Vec: 苹果≈[0.8,0.2]  香蕉≈[0.75,0.25]  → 距离≈0.07

结论:相似词向量距离更近!

2.3 词嵌入Embedding层

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

# 假设词表大小10000,嵌入成128维
embedding = nn.Embedding(num_embeddings=10000, embedding_dim=128)

# 输入: 词的索引 (batch内)
word_indices = torch.tensor([1, 5, 10, 99])  # 4个词

# 输出: 4个词的128维向量
vectors = embedding(word_indices)
print(vectors.shape)  # (4, 128)

💡 代码解析

  • num_embeddings:词表大小
  • embedding_dim:向量维度

📊 三、文本数据分析

3.1 标签分布

python 复制代码
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 读取数据
train_df = pd.read_csv('train.tsv', sep='\t')

# 统计
print(train_df['label'].value_counts())

# 画图
sns.countplot(x='label', data=train_df)
plt.title('Label Distribution')
plt.xlabel('Label')
plt.ylabel('Count')
plt.show()

3.2 句子长度分布

python 复制代码
# 计算每句长度
train_df['length'] = train_df['text'].apply(lambda x: len(x))

# 统计
print(train_df['length'].describe())

# 按标签看
sns.boxplot(x='label', y='length', data=train_df)
plt.title('Text Length by Label')
plt.show()

3.3 词频统计

python 复制代码
from collections import Counter
import jieba

# 统计词频
all_words = []
for text in train_df['text']:
    words = jieba.lcut(text)
    all_words.extend(words)

word_count = Counter(all_words)

# Top 30
print(word_count.most_common(30))

🧠 四、RNN系列

4.1 RNN实现

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

# 参数
input_size = 10    # 输入维度
hidden_size = 20   # 隐藏状态维度
num_layers = 2     # 层数
batch_size = 3     # batch大小

# 创建RNN
rnn = nn.RNN(
    input_size=input_size,
    hidden_size=hidden_size,
    num_layers=num_layers,
    batch_first=True
)

# 输入: (batch, seq_len, input_size)
x = torch.randn(batch_size, 5, input_size)

# 初始隐藏状态: (num_layers, batch, hidden_size)
h0 = torch.zeros(num_layers, batch_size, hidden_size)

# 前向传播
output, hn = rnn(x, h0)

print(f"output形状: {output.shape}")  # (3, 5, 20)
print(f"hn形状: {hn.shape}")         # (2, 3, 20)

💡 代码解析

  • output:所有时间步的输出
  • hn:最后一个隐藏状态

4.2 LSTM实现

python 复制代码
# LSTM: 比RNN多一个细胞状态
lstm = nn.LSTM(
    input_size=10,
    hidden_size=20,
    num_layers=2,
    batch_first=True,
    bidirectional=True  # 双向
)

# 初始: 隐藏状态 + 细胞状态
h0 = torch.zeros(4, 3, 20)  # num_layers*2 for bidirectional
c0 = torch.zeros(4, 3, 20)

output, (hn, cn) = lstm(x, (h0, c0))

print(f"output: {output.shape}")  # (3, 5, 40) 双向=20*2
print(f"hn: {hn.shape}")         # (4, 3, 20)

💡 代码解析

  • 三个门:遗忘门、输入门、输出门
  • 解决RNN的长序列梯度消失问题

4.3 GRU实现

python 复制代码
# GRU: LSTM的简化版,只有两个门
gru = nn.GRU(
    input_size=10,
    hidden_size=20,
    num_layers=2,
    batch_first=True,
    bidirectional=True
)

output, hn = gru(x, h0)
print(f"output: {output.shape}")  # (3, 5, 40)

💡 代码解析

  • 两个门:更新门、重置门
  • 比LSTM参数量少

🎯 五、注意力机制

5.1 原理

复制代码
🏀 比喻:
  翻译 "I saw a saw" (我看到一把锯子)
  → 第一个saw(看到) vs 第二个saw(锯子)
  → 需要关注不同位置!
  
Attention = 告诉模型"看哪里"

5.2 实现

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

class Attention(nn.Module):
    def __init__(self, hidden_size):
        super().__init__()
        self.attn = nn.Linear(hidden_size * 2, hidden_size)
        self.v = nn.Parameter(torch.rand(hidden_size))
    
    def forward(self, hidden, encoder_outputs):
        # hidden: (batch, hidden_size)
        # encoder_outputs: (seq_len, batch, hidden_size)
        
        seq_len = encoder_outputs.size(0)
        
        # 复制hidden到每个时间步
        hidden = hidden.unsqueeze(0).repeat(seq_len, 1, 1)
        
        # 计算能量
        energy = torch.tanh(
            self.attn(torch.cat([hidden, encoder_outputs], dim=2))
        )
        
        # (batch, hidden, seq_len)
        energy = energy.permute(1, 2, 0)
        
        # v是学习到的"查询向量"
        v = self.v.repeat(encoder_outputs.size(1), 1).unsqueeze(1)
        scores = torch.bmm(v, energy).squeeze(1)
        
        # 归一化
        attn_weights = F.softmax(scores, dim=1)
        
        # 加权求和
        context = torch.bmm(
            attn_weights.unsqueeze(1),
            encoder_outputs.permute(1, 0, 2)
        ).squeeze(1)
        
        return context, attn_weights

💡 代码解析

  • 计算相似度 → softmax归一化 → 加权求和

🔥 六、Transformer

6.1 核心组件

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

class SelfAttention(nn.Module):
    def __init__(self, embed_size, heads):
        super().__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.head_dim = embed_size // heads
        
        self.values = nn.Linear(embed_size, embed_size)
        self.keys = nn.Linear(embed_size, embed_size)
        self.queries = nn.Linear(embed_size, embed_size)
        self.fc_out = nn.Linear(embed_size, embed_size)
    
    def forward(self, values, keys, query, mask):
        N = query.shape[0]
        
        # 分头
        values = self.values(values).view(N, -1, self.heads, self.head_dim)
        keys = self.keys(keys).view(N, -1, self.heads, self.head_dim)
        queries = self.queries(query).view(N, -1, self.heads, self.head_dim)
        
        # 注意力计算
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
        
        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))
        
        attention = torch.softmax(energy / (self.embed_size ** (1/2)), dim=3)
        
        out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
            N, -1, self.heads * self.head_dim
        )
        
        return self.fc_out(out)

💡 代码解析

  • 多头注意力:并行计算多个注意力
  • einsum:爱因斯坦求和约定,简洁高效

6.2 完整Transformer

python 复制代码
class Transformer(nn.Module):
    def __init__(self, src_vocab, trg_vocab, src_pad_idx, trg_pad_idx,
                 embed_size=256, num_layers=6, heads=8, forward_expansion=4,
                 dropout=0.1):
        super().__init__()
        self.src_pad_idx = src_pad_idx
        self.trg_pad_idx = trg_pad_idx
        
        self.src_embedding = nn.Embedding(src_vocab, embed_size)
        self.trg_embedding = nn.Embedding(trg_vocab, embed_size)
        
        self.transformer = nn.Transformer(
            embed_size, heads, num_layers, forward_expansion, dropout
        )
        
        self.fc_out = nn.Linear(embed_size, trg_vocab)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, src, trg):
        src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)
        trg_mask = torch.tril(torch.ones(trg.size(1), trg.size(1))).unsqueeze(0)
        
        src_emb = self.dropout(self.src_embedding(src))
        trg_emb = self.dropout(self.trg_embedding(trg))
        
        out = self.transformer(
            src_emb, trg_emb,
            src_key_padding_mask=~src_mask,
            tgt_mask=~trg_mask
        )
        
        return self.fc_out(out)

🚀 七、迁移学习

7.1 FastText分类

python 复制代码
import fasttext

# 训练文本分类模型
# 格式: __label__positive 这是一个好产品
#       __label__negative 这个很糟糕

model = fasttext.train_supervised(
    'train.txt',
    lr=0.1,
    epoch=5,
    word_ngrams=2,
    dim=100,
    loss='softmax'
)

# 预测
pred, prob = model.predict('这个产品太好了!')
print(pred)   # __label__positive
print(prob)   # 0.98

💡 代码解析

  • 训练数据格式:__label__标签 文本
  • word_ngrams:n-gram特征

7.2 Hugging Face BERT

python 复制代码
from transformers import AutoModel, AutoTokenizer, AutoModelForSequenceClassification
import torch

# 加载预训练模型
tokenizer = AutoTokenizer.from_pretrained('bert-base-chinese')
model = AutoModel.from_pretrained('bert-base-chinese')

# 分类模型
model = AutoModelForSequenceClassification.from_pretrained(
    'bert-base-chinese',
    num_labels=2
)

# 编码
inputs = tokenizer("你好世界", return_tensors="pt")

# 前向
outputs = model(**inputs)
logits = outputs.logits
print(logits)

💡 代码解析

  • AutoTokenizer:自动选择分词器
  • AutoModel:加载预训练模型

7.3 完整微调流程

python 复制代码
from transformers import Trainer, TrainingArguments
from datasets import Dataset

# 准备数据
train_dataset = Dataset.from_pandas(train_df)

# 预处理
def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        padding="max_length",
        truncation=True,
        max_length=128
    )

train_dataset = train_dataset.map(tokenize_function, batched=True)

# 训练参数
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    learning_rate=2e-5,
    weight_decay=0.01,
)

# 训练
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
)

trainer.train()

💡 代码解析

  • TrainingArguments:训练超参数
  • Trainer:高级训练封装

🐛 八、避坑指南

❌ 错误1: 维度顺序

python 复制代码
# batch_first=True vs False
rnn1 = nn.RNN(10, 20, batch_first=True)
input1 = torch.randn(3, 5, 10)  # (batch, seq, feature)

rnn2 = nn.RNN(10, 20, batch_first=False)
input2 = torch.randn(5, 3, 10)  # (seq, batch, feature)

❌ 错误2: mask没设置

python 复制代码
# Transformer的mask很重要
# 否则会看到PAD位置的信息

def make_src_mask(src, pad_idx):
    return (src != pad_idx).unsqueeze(1).unsqueeze(2)

❌ 错误3: 学习率太大

复制代码
BERT微调:学习率2e-5左右
从头训练:学习率1e-3左右

差100倍!用错直接不收敛

📝 九、总结

核心要点

模块 关键点
预处理 jieba分词、停用词
向量化 One-Hot→Word2Vec→Embedding
RNN 记忆、梯度消失
LSTM 门控、解决长序列
Attention 加权求和、找重点
Transformer 自注意力、并行
迁移学习 FastText、BERT

代码对照表

功能 代码位置
分词 01_文本预处理/01_文本加载与特征提取
向量 01_文本预处理/02_文本向量化表示
分析 01_文本预处理/03_文本数据分析
RNN 02_RNN循环神经网络/01_RNN与LSTM与GRU
Attention 02_RNN循环神经网络/02_注意力机制
Transformer 03_Transformer
迁移学习 04_迁移学习

学完这篇,NLP实战就入门了! 🚀

相关推荐
2501_943124052 小时前
实测数据:矩阵跃动小陌GEO+龙虾机器人,助力企业AI搜索曝光提升3倍+的技术实践
大数据·人工智能
PieroPc2 小时前
电脑DIY组装报价系统 用MiMo V2 Pro 写html ,再用opencode(选MiMo 作模型) 当录入口
前端·html
jkyy20142 小时前
家庭智能饮食健康:智能冰箱联动健康数据,实现个性化饮食指导
人工智能·语言模型·自动化·健康医疗
K姐研究社2 小时前
EdgeClaw Box体验 – 开源端云协同AI硬件,断网也能跑Agent
人工智能·aigc
科德航空的张先生2 小时前
飞行错觉(空间定向障碍)地面模拟训练系统
人工智能·算法
打破砂锅问到底0072 小时前
Claude Code(终端 AI 编程代理)安装、对接网关与高效使用
人工智能
格林威2 小时前
工业相机图像采集处理:从 RAW 数据到 AI 可读图像,附basler相机 C#实战代码
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
工程师老罗2 小时前
lvgl有哪些布局?
前端·javascript·html
北京耐用通信2 小时前
工业现场通信互通 耐达讯自动化CC-Link IE转Modbus RTU网关
人工智能·物联网·网络协议·自动化·信息与通信