机器读不懂人话?用NLP自然语言处理让AI理解文本

机器读不懂人话?用NLP自然语言处理让AI理解文本

关键词:NLP、自然语言处理、分词、词向量、BERT、文本分类


目录

  • 一、NLP技术体系全景
    • [1.1 什么是自然语言处理?](#1.1 什么是自然语言处理? "#11-%E4%BB%80%E4%B9%88%E6%98%AF%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86")
    • [1.2 NLP技术分层架构](#1.2 NLP技术分层架构 "#12-nlp%E6%8A%80%E6%9C%AF%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84")
  • 二、中文分词:让机器认识"词"的边界
    • [2.1 为什么中文需要分词?](#2.1 为什么中文需要分词? "#21-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%AD%E6%96%87%E9%9C%80%E8%A6%81%E5%88%86%E8%AF%8D")
    • [2.2 分词工具实战](#2.2 分词工具实战 "#22-%E5%88%86%E8%AF%8D%E5%B7%A5%E5%85%B7%E5%AE%9E%E6%88%98")
  • 三、词性标注与命名实体识别
    • [3.1 词性标注:给每个词贴"功能标签"](#3.1 词性标注:给每个词贴"功能标签" "#31-%E8%AF%8D%E6%80%A7%E6%A0%87%E6%B3%A8%E7%BB%99%E6%AF%8F%E4%B8%AA%E8%AF%8D%E8%B4%B4%E5%8A%9F%E8%83%BD%E6%A0%87%E7%AD%BE")
    • [3.2 命名实体识别:找出文本中的关键信息](#3.2 命名实体识别:找出文本中的关键信息 "#32-%E5%91%BD%E5%90%8D%E5%AE%9E%E4%BD%93%E8%AF%86%E5%88%AB%E6%89%BE%E5%87%BA%E6%96%87%E6%9C%AC%E4%B8%AD%E7%9A%84%E5%85%B3%E9%94%AE%E4%BF%A1%E6%81%AF")
  • 四、词向量:从One-Hot到BERT的演进之路
    • [4.1 One-Hot编码的致命缺陷](#4.1 One-Hot编码的致命缺陷 "#41-one-hot%E7%BC%96%E7%A0%81%E7%9A%84%E8%87%B4%E5%91%BD%E7%BC%BA%E9%99%B7")
    • [4.2 Word2Vec:稠密词向量的里程碑](#4.2 Word2Vec:稠密词向量的里程碑 "#42-word2vec%E7%A8%A0%E5%AF%86%E8%AF%8D%E5%90%91%E9%87%8F%E7%9A%84%E9%87%8C%E7%A8%8B%E7%A2%91")
    • [4.3 BERT:双向预训练的革命](#4.3 BERT:双向预训练的革命 "#43-bert%E5%8F%8C%E5%90%91%E9%A2%84%E8%AE%AD%E7%BB%83%E7%9A%84%E9%9D%A9%E5%91%BD")
  • 五、文本分类实战:情感分析项目
    • [5.1 项目需求与数据准备](#5.1 项目需求与数据准备 "#51-%E9%A1%B9%E7%9B%AE%E9%9C%80%E6%B1%82%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%87%86%E5%A4%87")
    • [5.2 构建分类模型](#5.2 构建分类模型 "#52-%E6%9E%84%E5%BB%BA%E5%88%86%E7%B1%BB%E6%A8%A1%E5%9E%8B")
    • [5.3 模型训练与评估](#5.3 模型训练与评估 "#53-%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83%E4%B8%8E%E8%AF%84%E4%BC%B0")
  • 常见问题
  • 和AI大模型开发的关系
  • 总结

一、NLP技术体系全景

1.1 什么是自然语言处理?

自然语言处理(Natural Language Processing,简称NLP)是人工智能的核心分支之一,致力于让计算机能够理解、生成和处理人类语言

人类每天产生海量文本数据:社交媒体动态、客服对话记录、新闻报道、产品评论、合同文档......如果只能靠人工阅读分析,效率极低且容易遗漏关键信息。NLP技术的价值就在于:将非结构化的文本转化为机器可理解的结构化数据,从而支撑智能客服、舆情监控、内容审核、机器翻译等实际应用。

NLP的核心挑战在于人类语言的复杂性:

  • 歧义性:"苹果"可以指水果,也可以指科技公司
  • 上下文依赖:"他打了一场比赛"和"他打了一个电话",同一个"打"含义完全不同
  • 隐含信息:"这家餐厅不错"可能是正面评价,也可能是委婉的负面评价(取决于语境)

1.2 NLP技术分层架构

图一:NLP技术体系全景

(图一:NLP技术体系从基础层到应用层逐层递进,基础层处理语言单元,核心层完成语义理解,应用层面向实际业务场景)

NLP技术体系通常分为三个层次:

基础层:处理语言的基本单元

  • 分词:将连续文本切分为词语(中文特有需求)
  • 词性标注:识别每个词的语法角色(名词、动词、形容词等)
  • 句法分析:分析句子中词语之间的语法关系
  • 语义理解:理解词语和句子的真实含义

核心层:完成具体的NLP任务

  • 文本分类:将文本归类到预定义类别(如新闻分类、垃圾邮件识别)
  • 情感分析:判断文本表达的情感倾向(正面/负面/中性)
  • 命名实体识别:从文本中提取人名、地名、机构名等关键实体
  • 关系抽取:识别实体之间的语义关系

应用层:面向实际业务场景

  • 机器翻译:自动将一种语言翻译为另一种语言
  • 文本生成:自动撰写报告、摘要、对话回复
  • 问答系统:理解用户问题并给出准确答案

二、中文分词:让机器认识"词"的边界

2.1 为什么中文需要分词?

英文等拉丁语系语言中,单词之间天然有空格分隔,计算机可以直接识别词语边界。但中文是连续字符序列,没有显式的词边界标记。

例如:"自然语言处理技术应用于智能客服系统"

这句话对人类来说很容易理解,但计算机看到的是:

复制代码
自 然 语 言 处 理 技 术 应 用 于 智 能 客 服 系 统

如果不分词,计算机无法知道"自然语言"是一个词还是"自然"+"语言"两个词,更无法理解整句话的含义。

分词的核心目标:将连续的中文字符序列切分为有意义的词语单元,为后续的NLP任务奠定基础。

2.2 分词工具实战

图二:中文分词流程示意

(图二:中文分词将连续字符序列切分为词语单元,并可进一步进行词性标注,为后续NLP任务提供结构化输入)

目前主流的中文分词工具包括:

工具 特点 适用场景
jieba 轻量级、易上手、支持自定义词典 快速原型开发、中小型项目
HanLP 功能全面、支持多种NLP任务 企业级应用、学术研究
LTP 哈工大出品、分词准确率高 中文信息处理、学术研究
spaCy 多语言支持、工业级性能 多语言项目、生产环境

下面用 jieba 演示分词和词性标注的完整流程:

python 复制代码
import jieba
import jieba.posseg as pseg

# ========== 基础分词 ==========
text = "自然语言处理技术广泛应用于智能客服系统"

# 精确模式(推荐):最准确的切分方式
words_precise = jieba.cut(text, cut_all=False)
print("精确模式分词:", " / ".join(words_precise))
# 输出: 自然语言 / 处理 / 技术 / 广泛 / 应用 / 于 / 智能 / 客服 / 系统

# 全模式:扫描所有可能的词语
words_full = jieba.cut(text, cut_all=True)
print("全模式分词:", " / ".join(words_full))
# 输出: 自然 / 自然语言 / 语言 / 处理 / 技术 / 广泛 / 应用 / 于 / 智能 / 客服 / 系统

# 搜索引擎模式:在精确模式基础上对长词再次切分
words_search = jieba.cut_for_search(text)
print("搜索引擎模式:", " / ".join(words_search))

# ========== 词性标注 ==========
# 使用 posseg 模块同时进行分词和词性标注
words_with_pos = pseg.cut(text)
print("\n词性标注结果:")
for word, flag in words_with_pos:
    print(f"  {word:6s} -> {flag}")
    # 输出示例:
    #   自然语言 -> nz (其他专名)
    #   处理     -> v  (动词)
    #   技术     -> n  (名词)
    #   广泛     -> a  (形容词)
    #   应用     -> v  (动词)
    #   于       -> p  (介词)
    #   智能     -> a  (形容词)
    #   客服     -> n  (名词)
    #   系统     -> n  (名词)

# ========== 自定义词典 ==========
# 对于领域专有名词,可以添加自定义词典提高分词准确率
jieba.add_word("RAG检索增强")
jieba.add_word("LangChain框架")

custom_text = "RAG检索增强和LangChain框架是大模型应用的核心技术"
print("\n添加自定义词典后:")
print("分词结果:", " / ".join(jieba.cut(custom_text)))
# 输出: RAG检索增强 / 和 / LangChain框架 / 是 / 大模型 / 应用 / 的 / 核心 / 技术

分词常见问题与解决方案

python 复制代码
# 问题1:新词识别不准(如"大语言模型"被切分为"大/语言/模型")
# 解决:添加自定义词典或使用支持新词发现的模式
jieba.add_word("大语言模型")
jieba.add_word("提示词工程")

# 问题2:领域专有名词切分错误
# 解决:加载领域词典
jieba.load_userdict("domain_dict.txt")  # 每行一个词

# 问题3:歧义切分(如"南京市长江大桥")
# 解决:使用更高级的分词工具如HanLP
import hanlp
tokenizer = hanlp.load(hanlp.pretrained.tokenizers.COARSE_ELECTRA_SMALL_ZH)
result = tokenizer("南京市长江大桥")
print(result)  # ['南京市', '长江大桥']

三、词性标注与命名实体识别

3.1 词性标注:给每个词贴"功能标签"

词性标注(Part-of-Speech Tagging,POS Tagging)的目标是为文本中的每个词语分配一个词性标签,如名词、动词、形容词等。

词性标注的价值在于:

  • 理解句子结构:知道哪些是主语、谓语、宾语
  • 消歧:同一个词在不同语境下词性不同(如"打"可以是动词"打球",也可以是量词"一打")
  • 支撑下游任务:句法分析、信息抽取等任务都依赖词性标注结果
python 复制代码
import jieba.posseg as pseg

# 词性标注示例
text = "北京大学的学生在图书馆学习自然语言处理课程"
words = pseg.cut(text)

print("词性标注详细结果:")
print(f"{'词语':<8} {'词性':<5} {'说明'}")
print("-" * 30)

# 常见词性标签对照
pos_explain = {
    'n': '名词', 'v': '动词', 'a': '形容词', 'd': '副词',
    'p': '介词', 'c': '连词', 'r': '代词', 'm': '数词',
    'q': '量词', 'u': '助词', 'f': '方位词', 's': '处所词',
    't': '时间词', 'nr': '人名', 'ns': '地名', 'nt': '机构名',
    'nz': '其他专名'
}

for word, flag in words:
    explain = pos_explain.get(flag, '其他')
    print(f"{word:<8} {flag:<5} ({explain})")

# 输出:
# 词语       词性    说明
# ------------------------------
# 北京大学   ns     (地名)
# 的         u      (助词)
# 学生       n      (名词)
# 在         p      (介词)
# 图书馆     n      (名词)
# 学习       v      (动词)
# 自然语言处理 nz   (其他专名)
# 课程       n      (名词)

3.2 命名实体识别:找出文本中的关键信息

命名实体识别(Named Entity Recognition,NER)是NLP中的核心任务之一,目标是从文本中识别出具有特定意义的实体,如人名、地名、机构名、时间、数字等。

python 复制代码
import jieba.posseg as pseg

def extract_entities(text):
    """从文本中提取命名实体"""
    words = pseg.cut(text)
    
    entities = {
        '人名': [],
        '地名': [],
        '机构名': [],
        '时间': [],
        '其他专名': []
    }
    
    # 词性标签到实体类型的映射
    entity_map = {
        'nr': '人名',
        'ns': '地名',
        'nt': '机构名',
        't': '时间',
        'nz': '其他专名'
    }
    
    for word, flag in words:
        if flag in entity_map:
            entities[entity_map[flag]].append(word)
    
    return entities

# 测试
text = "2024年3月,阿里巴巴在杭州发布了最新的大语言模型通义千问," \
       "该模型在自然语言处理领域取得了重大突破。"

entities = extract_entities(text)
print("命名实体识别结果:")
for entity_type, words in entities.items():
    if words:
        print(f"  {entity_type}: {', '.join(words)}")

# 输出:
#   时间: 2024年, 3月
#   机构名: 阿里巴巴
#   地名: 杭州
#   其他专名: 大语言模型, 通义千问, 自然语言处理

进阶:使用深度学习模型进行NER

对于更复杂的实体识别场景,可以使用预训练模型:

python 复制代码
from transformers import pipeline

# 使用预训练的中文NER模型
ner_pipeline = pipeline(
    "token-classification",
    model="uer/roberta-base-finetuned-jd-binary-chinese",
    tokenizer="uer/roberta-base-finetuned-jd-binary-chinese"
)

text = "张三在北京的阿里巴巴总部参加了技术峰会"
results = ner_pipeline(text)

# 提取实体
entities = {}
current_entity = ""
current_type = ""

for result in results:
    word = result['word']
    entity_type = result['entity']
    
    if entity_type.startswith('B-'):  # 实体开始
        if current_entity:
            entities[current_type] = entities.get(current_type, []) + [current_entity]
        current_type = entity_type[2:]  # 去掉"B-"前缀
        current_entity = word.replace('##', '')
    elif entity_type.startswith('I-'):  # 实体继续
        current_entity += word.replace('##', '')

if current_entity:
    entities[current_type] = entities.get(current_type, []) + [current_entity]

print("深度学习NER结果:", entities)

四、词向量:从One-Hot到BERT的演进之路

4.1 One-Hot编码的致命缺陷

在深度学习兴起之前,NLP领域最常用的文本表示方法是One-Hot编码

One-Hot编码的原理很简单:假设词表大小为10000,每个词用一个10000维的向量表示,该词对应的位置为1,其余位置为0。

python 复制代码
import numpy as np

# 假设词表:{"我": 0, "喜欢": 1, "自然": 2, "语言": 3, "处理": 4}
vocab = {"我": 0, "喜欢": 1, "自然": 2, "语言": 3, "处理": 4}
vocab_size = len(vocab)

def one_hot_encode(word, vocab):
    """将词语转换为One-Hot向量"""
    vector = np.zeros(vocab_size)
    vector[vocab[word]] = 1
    return vector

# 示例
print("One-Hot编码示例:")
for word in ["我", "喜欢", "自然", "语言", "处理"]:
    vector = one_hot_encode(word, vocab)
    print(f"  {word}: {vector}")

# 输出:
#   我: [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.]

One-Hot编码的三大缺陷

  1. 维度灾难:词表越大,向量维度越高。工业级词表通常有几十万甚至上百万词,向量维度极高
  2. 稀疏性:每个向量只有一个位置为1,其余全为0,信息密度极低
  3. 无法表达语义相似度:"猫"和"狗"的One-Hot向量正交(点积为0),但语义上它们很相似;"猫"和"桌子"同样正交,但语义完全不同
python 复制代码
# 语义相似度问题演示
cat_vector = one_hot_encode("自然", vocab)
dog_vector = one_hot_encode("语言", vocab)
desk_vector = one_hot_encode("处理", vocab)

# 计算余弦相似度
from sklearn.metrics.pairwise import cosine_similarity

sim_cat_dog = cosine_similarity([cat_vector], [dog_vector])[0][0]
sim_cat_desk = cosine_similarity([cat_vector], [desk_vector])[0][0]

print(f"\n语义相似度(One-Hot):")
print(f"  '自然' vs '语言': {sim_cat_dog:.4f}")  # 0.0000(正交)
print(f"  '自然' vs '处理': {sim_cat_desk:.4f}")  # 0.0000(正交)
# 问题:无法区分语义相似和语义不同的词对!

4.2 Word2Vec:稠密词向量的里程碑

2013年,Google研究员Mikolov提出了Word2Vec模型,彻底改变了词向量表示方式。

图三:词向量技术演进路线

(图三:词向量技术从One-Hot稀疏编码演进到Word2Vec稠密向量,再到BERT双向预训练,最终进入LLM时代)

Word2Vec的核心思想:用上下文预测目标词(或反之),在训练过程中学习词的稠密向量表示

Word2Vec包含两种架构:

图四:Word2Vec CBOW vs Skip-gram对比

(图四:CBOW用上下文预测目标词,适合小规模数据;Skip-gram用目标词预测上下文,适合大规模数据和罕见词)

架构 原理 适用场景
CBOW(连续词袋模型) 用上下文词语预测目标词 训练速度快,适合小规模数据
Skip-gram(跳字模型) 用目标词预测上下文词语 对罕见词效果好,适合大规模数据

Word2Vec实战示例

python 复制代码
import gensim
from gensim.models import Word2Vec

# 准备训练语料(模拟AI技术文档)
corpus = [
    ["自然语言", "处理", "是", "人工智能", "的", "核心", "技术"],
    ["深度", "学习", "模型", "在", "图像", "识别", "中", "表现", "优异"],
    ["大", "语言", "模型", "如", "GPT", "和", "BERT", "推动", "了", "NLP", "发展"],
    ["RAG", "检索", "增强", "生成", "技术", "结合", "了", "向量", "数据库", "和", "大模型"],
    ["提示词", "工程", "是", "优化", "大模型", "输出", "质量", "的", "关键", "方法"],
    ["Transformer", "架构", "通过", "自", "注意力", "机制", "捕获", "长距离", "依赖"],
]

# 训练Word2Vec模型
model = Word2Vec(
    sentences=corpus,
    vector_size=50,      # 词向量维度
    window=3,            # 上下文窗口大小
    min_count=1,         # 最小词频
    workers=4,           # 并行线程数
    sg=0                 # 0=CBOW, 1=Skip-gram
)

# 查看词向量
print("词向量示例:")
print(f"  '自然语言' 的向量维度: {model.wv['自然语言'].shape}")
print(f"  '自然语言' 的向量(前5维): {model.wv['自然语言'][:5]}")

# 计算词相似度
print("\n词相似度计算:")
similar_words = model.wv.most_similar("自然语言", topn=3)
for word, score in similar_words:
    print(f"  {word}: {score:.4f}")

# 词类比推理(经典测试)
# "国王" - "男人" + "女人" ≈ "女王"
try:
    analogy = model.wv.most_similar(
        positive=["学习", "模型"],
        negative=["深度"],
        topn=3
    )
    print("\n词类比推理:")
    for word, score in analogy:
        print(f"  {word}: {score:.4f}")
except KeyError as e:
    print(f"\n词类比推理需要更大的语料库,当前语料不足")

# 保存和加载模型
model.save("word2vec_ai_model.model")
loaded_model = Word2Vec.load("word2vec_ai_model.model")

Word2Vec的优势

  • 向量维度大幅降低(通常50-300维),解决了维度灾难
  • 语义相似的词在向量空间中距离更近
  • 支持词相似度计算和类比推理

Word2Vec的局限

  • 静态词向量:同一个词在不同语境下向量相同,无法处理一词多义
  • 局部上下文:只考虑固定窗口内的上下文,无法捕获长距离依赖

4.3 BERT:双向预训练的革命

2018年,Google提出了BERT(Bidirectional Encoder Representations from Transformers)模型,开启了NLP的预训练时代。

图五:BERT双向编码结构

(图五:BERT通过多层Transformer编码器实现双向上下文感知,每个token的表示同时融合了左右两侧的信息)

BERT的核心创新:

  1. 双向上下文:传统语言模型只能从左到右(或从右到左)单向建模,BERT通过**Masked Language Modeling(MLM)**实现真正的双向编码
  2. Transformer架构:使用自注意力机制捕获任意距离的依赖关系
  3. 预训练+微调范式:先在大规模无标注语料上预训练,再在下游任务上微调

BERT预训练的两个任务

python 复制代码
# BERT预训练任务1:Masked Language Modeling(掩码语言建模)
# 随机掩盖输入序列中15%的token,让模型预测被掩盖的词

input_text = "自然[MASK]处理技术广泛应用于智能客服"
# 模型需要预测 [MASK] 位置应该是 "语言"

# BERT预训练任务2:Next Sentence Prediction(下一句预测)
# 给定两个句子A和B,判断B是否是A的下一句

sentence_a = "自然语言处理是人工智能的核心技术"
sentence_b = "它包括分词、词性标注、命名实体识别等任务"
# 标签:IsNext(是下一句)

sentence_c = "深度学习在图像识别领域也取得了巨大进展"
# 标签:NotNext(不是下一句)

使用Hugging Face Transformers调用BERT

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

# 加载预训练的中文BERT模型
model_name = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)

# 输入文本
text = "自然语言处理技术让机器理解人类语言"

# 分词并转换为模型输入
inputs = tokenizer(
    text,
    return_tensors="pt",        # 返回PyTorch张量
    padding=True,               # 填充到批次最大长度
    truncation=True,            # 截断超长序列
    max_length=512              # BERT最大序列长度
)

print("Tokenization结果:")
print(f"  输入IDs: {inputs['input_ids']}")
print(f"  注意力掩码: {inputs['attention_mask']}")
print(f"  Token序列: {tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])}")

# 前向传播获取隐藏状态
with torch.no_grad():
    outputs = model(**inputs)

# outputs.last_hidden_state 形状: [batch_size, sequence_length, hidden_size]
# 对于bert-base-chinese,hidden_size = 768
hidden_states = outputs.last_hidden_state
print(f"\n隐藏状态形状: {hidden_states.shape}")
# 输出: torch.Size([1, 15, 768])
# 1个样本,15个token,每个token的表示维度为768

# 获取[CLS] token的表示(通常用于句子级任务)
cls_embedding = hidden_states[:, 0, :]
print(f"[CLS]向量维度: {cls_embedding.shape}")
# 输出: torch.Size([1, 768])

BERT vs Word2Vec 对比

特性 Word2Vec BERT
向量类型 静态(一词一向量) 动态(上下文感知)
上下文 局部窗口 全局双向
一词多义 无法处理 可以处理
训练方式 从 scratch 训练 预训练+微调
计算资源 轻量级 需要GPU
适用场景 词相似度、简单分类 复杂NLP任务、问答、推理

五、文本分类实战:情感分析项目

5.1 项目需求与数据准备

场景 :某电商平台需要自动分析用户评论的情感倾向,将评论分为正面负面中性三类,用于产品口碑监控和客服优先级排序。

python 复制代码
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, confusion_matrix
import jieba

# ========== 模拟电商评论数据 ==========
# 实际项目中应从数据库或CSV文件加载
reviews_data = [
    # 正面评论
    ("这款AI音箱的语音识别非常准确,响应速度也很快,强烈推荐!", "正面"),
    ("客服态度很好,问题解决得很及时,购物体验很棒", "正面"),
    ("产品质量超出预期,包装精美,物流也很快", "正面"),
    ("大语言模型的对话能力令人惊叹,几乎像真人一样", "正面"),
    ("RAG技术让搜索结果更精准,用户体验提升明显", "正面"),
    ("界面设计简洁美观,操作流畅,五星好评", "正面"),
    ("智能客服回答专业,帮我快速解决了问题", "正面"),
    ("这款学习机的AI辅导功能很实用,孩子成绩提高了", "正面"),
    
    # 负面评论
    ("语音识别准确率太低了,经常听不懂我说的话", "负面"),
    ("客服态度恶劣,问题拖了一周都没解决", "负面"),
    ("收到货就发现屏幕有划痕,质量太差", "负面"),
    ("大模型生成的内容经常出错,不可靠", "负面"),
    ("搜索功能形同虚设,根本找不到想要的东西", "负面"),
    ("APP经常崩溃,体验极差,已卸载", "负面"),
    ("智能客服答非所问,完全是人工智障", "负面"),
    ("学习机用了一周就坏了,售后也不管", "负面"),
    
    # 中性评论
    ("产品还行吧,中规中矩,没什么特别的", "中性"),
    ("客服回复速度一般,问题最终解决了", "中性"),
    ("物流速度正常,包装一般,产品符合描述", "中性"),
    ("大模型的能力有限,有些问题回答不了", "中性"),
    ("搜索功能基本可用,但结果不够精准", "中性"),
    ("APP偶尔卡顿,但不影响正常使用", "中性"),
    ("智能客服能解决简单问题,复杂的还得转人工", "中性"),
    ("学习机功能一般,性价比不高", "中性"),
]

# 转换为DataFrame
df = pd.DataFrame(reviews_data, columns=["review", "sentiment"])
print("数据集概览:")
print(df.head(10))
print(f"\n数据集大小: {len(df)} 条")
print(f"类别分布:\n{df['sentiment'].value_counts()}")

# ========== 文本预处理 ==========
def preprocess_text(text):
    """中文文本预处理:分词 + 去除停用词"""
    # 分词
    words = jieba.cut(text)
    
    # 常见停用词列表
    stop_words = {
        "的", "了", "在", "是", "我", "有", "和", "就", "不", "人",
        "都", "一", "一个", "上", "也", "很", "到", "说", "要", "去",
        "你", "会", "着", "没有", "看", "好", "自己", "这", "那", "着"
    }
    
    # 过滤停用词和单字
    filtered_words = [
        word for word in words 
        if word not in stop_words and len(word) > 1
    ]
    
    return " ".join(filtered_words)

# 应用预处理
df["processed_review"] = df["review"].apply(preprocess_text)
print("\n预处理示例:")
for i in range(3):
    print(f"  原文: {df['review'].iloc[i]}")
    print(f"  处理后: {df['processed_review'].iloc[i]}")
    print()

# ========== 划分训练集和测试集 ==========
X = df["processed_review"]
y = df["sentiment"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

print(f"训练集大小: {len(X_train)}")
print(f"测试集大小: {len(X_test)}")

5.2 构建分类模型

python 复制代码
# ========== 方法1:TF-IDF + 朴素贝叶斯(基线模型) ==========

# TF-IDF特征提取
tfidf = TfidfVectorizer(
    max_features=5000,    # 最大特征数
    ngram_range=(1, 2),   # 使用unigram和bigram
    min_df=2,             # 最小文档频率
    max_df=0.95           # 最大文档频率(过滤过于常见的词)
)

X_train_tfidf = tfidf.fit_transform(X_train)
X_test_tfidf = tfidf.transform(X_test)

print(f"TF-IDF特征维度: {X_train_tfidf.shape}")

# 训练朴素贝叶斯分类器
nb_classifier = MultinomialNB(alpha=0.1)  # alpha为平滑参数
nb_classifier.fit(X_train_tfidf, y_train)

# 预测
y_pred_nb = nb_classifier.predict(X_test_tfidf)

print("\n=== 朴素贝叶斯分类结果 ===")
print(classification_report(y_test, y_pred_nb))

# ========== 方法2:使用预训练模型(进阶方案) ==========
# 实际项目中推荐使用BERT等预训练模型获得更高准确率

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import TrainingArguments, Trainer
import torch

# 加载预训练的中文分类模型
model_name = "hfl/chinese-macbert-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 标签映射
label2id = {"负面": 0, "中性": 1, "正面": 2}
id2label = {v: k for k, v in label2id.items()}

# 数据编码函数
def encode_data(texts, labels=None):
    """将文本编码为模型输入格式"""
    encodings = tokenizer(
        texts.tolist(),
        truncation=True,
        padding=True,
        max_length=128,
        return_tensors="pt"
    )
    if labels is not None:
        encodings["labels"] = torch.tensor([label2id[l] for l in labels])
    return encodings

# 编码训练集和测试集
train_encodings = encode_data(X_train, y_train)
test_encodings = encode_data(X_test, y_test)

# 定义数据集类
class SentimentDataset(torch.utils.data.Dataset):
    def __init__(self, encodings):
        self.encodings = encodings
    
    def __getitem__(self, idx):
        return {key: val[idx] for key, val in self.encodings.items()}
    
    def __len__(self):
        return len(self.encodings["input_ids"])

train_dataset = SentimentDataset(train_encodings)
test_dataset = SentimentDataset(test_encodings)

# 加载预训练模型(用于序列分类)
model = AutoModelForSequenceClassification.from_pretrained(
    model_name,
    num_labels=3,
    id2label=id2label,
    label2id=label2id
)

# 定义评估函数
def compute_metrics(eval_pred):
    from sklearn.metrics import accuracy_score, f1_score
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return {
        "accuracy": accuracy_score(labels, predictions),
        "f1": f1_score(labels, predictions, average="weighted")
    }

# 训练参数
training_args = TrainingArguments(
    output_dir="./sentiment_model",
    num_train_epochs=3,
    per_device_train_batch_size=8,
    per_device_eval_batch_size=16,
    warmup_steps=50,
    weight_decay=0.01,
    logging_dir="./logs",
    logging_steps=10,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
    metric_for_best_model="f1",
)

# 创建Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=test_dataset,
    compute_metrics=compute_metrics,
)

# 开始训练(实际运行时取消注释)
# trainer.train()

# 保存模型
# trainer.save_model("./sentiment_model_final")
# tokenizer.save_pretrained("./sentiment_model_final")

print("\n预训练模型方案已配置完成")
print("调用 trainer.train() 即可开始训练")

5.3 模型训练与评估

python 复制代码
# ========== 模型评估与部署 ==========

# 使用训练好的朴素贝叶斯模型进行预测示例
def predict_sentiment(review_text, classifier, vectorizer):
    """预测单条评论的情感倾向"""
    # 预处理
    processed = preprocess_text(review_text)
    
    # 特征提取
    features = vectorizer.transform([processed])
    
    # 预测
    prediction = classifier.predict(features)[0]
    probabilities = classifier.predict_proba(features)[0]
    
    return prediction, probabilities

# 测试新评论
new_reviews = [
    "这款产品的AI功能太强大了,完全超出我的预期!",
    "客服态度很差,问题一直没人解决,太失望了",
    "产品一般般,没什么特别的,勉强能用",
    "大模型的对话能力确实不错,但偶尔会胡说八道",
]

print("\n=== 新评论情感预测 ===")
for review in new_reviews:
    sentiment, probs = predict_sentiment(review, nb_classifier, tfidf)
    print(f"\n评论: {review}")
    print(f"预测情感: {sentiment}")
    print(f"各类别概率: ", end="")
    for label, prob in zip(nb_classifier.classes_, probs):
        print(f"{label}={prob:.2%} ", end="")
    print()

# ========== 模型部署(Flask API示例) ==========
"""
from flask import Flask, request, jsonify
import joblib

app = Flask(__name__)

# 加载训练好的模型
classifier = joblib.load("sentiment_classifier.pkl")
vectorizer = joblib.load("tfidf_vectorizer.pkl")

@app.route("/predict", methods=["POST"])
def predict():
    data = request.get_json()
    review = data.get("review", "")
    
    sentiment, probs = predict_sentiment(review, classifier, vectorizer)
    
    return jsonify({
        "sentiment": sentiment,
        "probabilities": {
            label: float(prob) 
            for label, prob in zip(classifier.classes_, probs)
        }
    })

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

# 调用示例:
# curl -X POST http://localhost:5000/predict \
#   -H "Content-Type: application/json" \
#   -d '{"review": "这款产品非常好用"}'
"""

常见问题

Q1:分词工具选哪个?jieba还是HanLP?

:取决于项目需求:

  • 快速原型/中小型项目 :选 jieba,轻量级、API简单、社区活跃
  • 企业级应用/高准确率要求 :选 HanLP,功能全面、支持多种NLP任务、准确率高
  • 学术研究 :选 LTP(哈工大)或 spaCy,可定制性强

实际项目中可以先用 jieba 快速验证,再根据需求升级到 HanLP

Q2:词向量维度怎么选?越大越好吗?

:不是越大越好,需要根据任务和数据量权衡:

场景 推荐维度 原因
小型数据集(<10万句) 50-100 避免过拟合
中型数据集(10万-100万句) 100-200 平衡表达力和泛化
大型数据集(>100万句) 200-300 充分捕获语义信息
预训练模型(BERT等) 768/1024 模型架构固定

维度太小会导致语义信息丢失,维度太大会增加计算开销且容易过拟合。

Q3:BERT模型太大,部署困难怎么办?

:有以下几种方案:

  1. 使用轻量级变体 :如 DistilBERT(参数量减少40%,性能保持97%)、TinyBERT
  2. 模型量化:将FP32权重量化为INT8,模型大小减少75%
  3. 知识蒸馏:用大模型训练小模型,小模型继承大模型的能力
  4. 按需加载:只加载任务相关的层,减少内存占用
python 复制代码
# 使用轻量级DistilBERT示例
from transformers import DistilBertTokenizer, DistilBertModel

tokenizer = DistilBertTokenizer.from_pretrained("distilbert-base-chinese")
model = DistilBertModel.from_pretrained("distilbert-base-chinese")
# 参数量约66M,而BERT-base为110M

Q4:如何处理领域专有名词的分词问题?

:三种常用方法:

python 复制代码
import jieba

# 方法1:添加自定义词典(推荐)
jieba.add_word("RAG检索增强")
jieba.add_word("LangChain框架")
jieba.add_word("提示词工程")

# 方法2:加载领域词典文件
# domain_dict.txt 格式:每行一个词,可选词频和词性
# RAG检索增强 1000 n
# LangChain框架 1000 n
jieba.load_userdict("domain_dict.txt")

# 方法3:强制不切分(使用jieba.suggest_freq)
jieba.suggest_freq("大语言模型", tune=True)

和AI大模型开发的关系

NLP技术是AI大模型开发的基础能力,大模型的许多核心功能都建立在NLP技术之上。以下是NLP在大模型开发中的4个典型应用场景:

场景1:大模型输入预处理(分词+清洗)

大模型接收用户输入前,需要进行文本清洗和分词处理:

python 复制代码
import jieba
import re

class LLMInputProcessor:
    """大模型输入预处理器"""
    
    def __init__(self):
        # 加载领域词典
        jieba.add_word("RAG")
        jieba.add_word("LangChain")
        jieba.add_word("Coze")
        jieba.add_word("Dify")
    
    def clean_text(self, text):
        """清洗文本:去除特殊字符、标准化空格"""
        # 去除HTML标签
        text = re.sub(r'<[^>]+>', '', text)
        # 去除多余空白
        text = re.sub(r'\s+', ' ', text).strip()
        return text
    
    def tokenize(self, text):
        """分词并过滤停用词"""
        stop_words = {"的", "了", "在", "是", "我", "有", "和"}
        words = jieba.cut(text)
        return [w for w in words if w not in stop_words and len(w) > 1]
    
    def process(self, user_input):
        """完整处理流程"""
        cleaned = self.clean_text(user_input)
        tokens = self.tokenize(cleaned)
        return {
            "original": user_input,
            "cleaned": cleaned,
            "tokens": tokens,
            "token_count": len(tokens)
        }

# 使用示例
processor = LLMInputProcessor()
result = processor.process("请问RAG和LangChain有什么区别?")
print(f"原始输入: {result['original']}")
print(f"清洗后: {result['cleaned']}")
print(f"分词结果: {result['tokens']}")
print(f"Token数量: {result['token_count']}")

场景2:意图识别(文本分类)

智能客服需要先识别用户意图,再路由到对应的处理模块:

python 复制代码
from transformers import pipeline

class IntentClassifier:
    """用户意图分类器"""
    
    def __init__(self):
        # 使用预训练的中文文本分类模型
        self.classifier = pipeline(
            "text-classification",
            model="uer/roberta-base-finetuned-jd-binary-chinese"
        )
        
        # 意图映射(实际项目中根据业务定义)
        self.intent_map = {
            "product_query": "产品咨询",
            "technical_support": "技术支持",
            "billing": "账单问题",
            "complaint": "投诉建议",
            "general": "其他"
        }
    
    def classify(self, user_input):
        """识别用户意图"""
        result = self.classifier(user_input)[0]
        intent_code = result["label"]
        confidence = result["score"]
        
        return {
            "intent": self.intent_map.get(intent_code, "其他"),
            "intent_code": intent_code,
            "confidence": confidence,
            "routing": self._get_routing(intent_code)
        }
    
    def _get_routing(self, intent_code):
        """根据意图返回路由目标"""
        routing_map = {
            "product_query": "product_agent",
            "technical_support": "tech_agent",
            "billing": "billing_agent",
            "complaint": "human_agent",
            "general": "general_agent"
        }
        return routing_map.get(intent_code, "general_agent")

# 使用示例
classifier = IntentClassifier()
test_inputs = [
    "这个AI音箱怎么连接WiFi?",
    "我的订单还没收到,已经等了三天了",
    "你们的产品价格太贵了",
]

for text in test_inputs:
    result = classifier.classify(text)
    print(f"\n输入: {text}")
    print(f"意图: {result['intent']} (置信度: {result['confidence']:.2%})")
    print(f"路由到: {result['routing']}")

场景3:实体抽取(信息提取)

从用户对话中提取关键实体,用于后续处理:

python 复制代码
import jieba.posseg as pseg

class EntityExtractor:
    """对话实体抽取器"""
    
    def __init__(self):
        # 添加领域词典
        jieba.add_word("大语言模型")
        jieba.add_word("提示词工程")
        jieba.add_word("RAG技术")
    
    def extract(self, text):
        """从文本中提取实体"""
        words = pseg.cut(text)
        
        entities = {
            "product": [],      # 产品名
            "feature": [],      # 功能特性
            "time": [],         # 时间
            "location": [],     # 地点
            "person": [],       # 人名
        }
        
        # 词性到实体类型的映射
        entity_map = {
            "nr": "person",     # 人名
            "ns": "location",   # 地名
            "nt": "product",    # 机构/产品名
            "t": "time",        # 时间
            "nz": "feature",    # 专有名词/特性
        }
        
        for word, flag in words:
            if flag in entity_map:
                entities[entity_map[flag]].append(word)
        
        # 基于规则的额外抽取
        # 抽取价格信息
        import re
        prices = re.findall(r'[\d]+元', text)
        if prices:
            entities["price"] = prices
        
        return entities

# 使用示例
extractor = EntityExtractor()
dialogue = "我想在北京买一个大语言模型的开发课程,大概3000元左右"
entities = extractor.extract(dialogue)

print("实体抽取结果:")
for entity_type, values in entities.items():
    if values:
        print(f"  {entity_type}: {values}")
# 输出:
#   location: ['北京']
#   product: ['大语言模型']
#   feature: ['开发课程']
#   price: ['3000元']

场景4:RAG系统中的文本处理

RAG(检索增强生成)系统需要对文档进行分块和向量化:

python 复制代码
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np

class RAGTextProcessor:
    """RAG系统文本处理器"""
    
    def __init__(self, chunk_size=200, chunk_overlap=50):
        self.chunk_size = chunk_size
        self.chunk_overlap = chunk_overlap
        self.vectorizer = TfidfVectorizer(
            tokenizer=jieba.lcut,
            max_features=10000
        )
    
    def chunk_document(self, document):
        """将长文档切分为小块"""
        # 按句子切分
        sentences = re.split(r'[。!?;]', document)
        sentences = [s.strip() for s in sentences if s.strip()]
        
        chunks = []
        current_chunk = []
        current_length = 0
        
        for sentence in sentences:
            sentence_length = len(sentence)
            
            if current_length + sentence_length > self.chunk_size:
                # 当前块已满,保存并开启新块
                chunks.append("。".join(current_chunk) + "。")
                # 保留重叠部分
                overlap_sentences = current_chunk[-self.chunk_overlap:]
                current_chunk = overlap_sentences + [sentence]
                current_length = sum(len(s) for s in current_chunk)
            else:
                current_chunk.append(sentence)
                current_length += sentence_length
        
        # 添加最后一个块
        if current_chunk:
            chunks.append("。".join(current_chunk) + "。")
        
        return chunks
    
    def build_index(self, documents):
        """构建向量索引"""
        # 切分文档
        all_chunks = []
        for doc in documents:
            all_chunks.extend(self.chunk_document(doc))
        
        # 向量化
        chunk_vectors = self.vectorizer.fit_transform(all_chunks)
        
        return {
            "chunks": all_chunks,
            "vectors": chunk_vectors,
            "chunk_count": len(all_chunks)
        }
    
    def search(self, query, index, top_k=3):
        """检索相关文档块"""
        # 查询向量化
        query_vector = self.vectorizer.transform([query])
        
        # 计算相似度
        from sklearn.metrics.pairwise import cosine_similarity
        similarities = cosine_similarity(query_vector, index["vectors"])[0]
        
        # 返回top-k结果
        top_indices = np.argsort(similarities)[-top_k:][::-1]
        
        results = []
        for idx in top_indices:
            results.append({
                "chunk": index["chunks"][idx],
                "similarity": float(similarities[idx])
            })
        
        return results

# 使用示例
import re

processor = RAGTextProcessor(chunk_size=150, chunk_overlap=30)

# 模拟文档
documents = [
    "自然语言处理是人工智能的核心技术之一。它包括分词、词性标注、命名实体识别等基础任务。"
    "现代NLP技术已经从传统的统计方法发展到深度学习时代。Word2Vec、BERT等模型大幅提升了语义理解能力。",
    
    "RAG(检索增强生成)技术结合了向量检索和大语言模型。"
    "它通过检索相关文档片段,为大模型提供上下文信息,从而生成更准确的回答。"
    "RAG系统通常包含文档切分、向量化、检索和生成四个核心模块。"
]

# 构建索引
index = processor.build_index(documents)
print(f"文档切分结果: {index['chunk_count']} 个块")

# 检索
query = "什么是RAG技术?"
results = processor.search(query, index, top_k=2)

print(f"\n查询: {query}")
print("相关文档块:")
for i, result in enumerate(results, 1):
    print(f"  {i}. [相似度: {result['similarity']:.4f}] {result['chunk'][:50]}...")

总结

本文系统梳理了NLP自然语言处理的核心技术体系:

基础层:中文分词是NLP的基石,jieba、HanLP等工具让机器能够识别词语边界;词性标注和命名实体识别进一步为文本添加结构化信息。

表示层:词向量技术从One-Hot编码演进到Word2Vec稠密向量,再到BERT双向预训练模型,语义表达能力呈指数级提升。BERT的"预训练+微调"范式已成为现代NLP的标准做法。

应用层:文本分类、情感分析等任务将NLP技术落地到实际业务场景,支撑智能客服、舆情监控、内容审核等应用。

与大模型的关系:NLP是大模型开发的底层能力,大模型的输入预处理、意图识别、实体抽取、RAG检索等核心功能都建立在NLP技术之上。掌握NLP基础,是理解和使用大模型的前提。

#NLP #自然语言处理 #分词 #词向量 #BERT #文本分类 #情感分析 #AI大模型

相关推荐
博、、17 分钟前
酒吧点餐小程序开发:从场景痛点到系统落地的全流程拆解
人工智能·架构·需求分析
Q一件事18 分钟前
防风固沙服务真的能沿直线“送达”北京吗?——对一项区域关联度研究的思考
大数据·人工智能
七牛云行业应用21 分钟前
DSH Desktop 桌面版实测:Win/macOS 一键安装,“桌面也是插件“怎么理解,和 npx 起 Web UI 差在哪
人工智能·agent·ai编程
空堂与归22 分钟前
文本喂给模型前要做啥?文本预处理与张量表示全解析
人工智能
小海豚儿24 分钟前
你写的 Prompt,可能有一半是安慰剂
人工智能
AI的探索之旅30 分钟前
97 个 OpenCV 实例(十七):图片读写,FileStorage 与 JPEG 采样
人工智能·opencv·计算机视觉
Forerror202633 分钟前
API网关怎么选?MAI Gateway vs 开源方案全面对比
人工智能·ai工具·maigateway·企业ai网关·企业级大模型网关
Likeadust36 分钟前
一键创建,说开就开:私有化音视频系统EasyDSS视频会议的入会、协作与AI纪要全流程
人工智能·音视频·easydss
小陈phd36 分钟前
电商客服智能体状态机编排:基于 LangGraph 的双轨意图识别与分级降级工程实践
人工智能