ML-For-Beginners 课程实战:NLP 常见任务与 TextBlob 情感感知聊天机器人构建指南
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
自然语言处理(NLP)任务的核心,是先把待处理的文本分解、检查,再与规则和数据集交叉比对,从而让程序推导出文本中词与短语的含义、意图或频率。本文对应 ML-For-Beginners 课程 NLP 单元的第二课 6-NLP/2-Tasks(原文为西班牙语翻译版),系统讲解分词、词嵌入、句法解析、词频统计、n-gram、名词短语抽取、情感分析等 NLP 基础技术,并结合仓库中的 参考实现 bot.py,带你用 TextBlob 把上一课"只会随机应答"的 Marvin 机器人升级为能感知情绪、能围绕名词短语追问的"共情"聊天机器人。学完本课,你将掌握 NLP 预处理的标准任务清单,并能独立完成一次"情感分析 + 名词短语抽取"的端到端编码实践。
NLP 任务的通用套路:先拆解,再分析
对大多数自然语言处理任务而言,待处理的文本必须被分解(broken down)、检查(examined),结果要么被存储,要么与规则和数据集交叉引用。正是这一系列任务,让程序员得以从文本中推导出词与术语的含义(meaning)、意图(intent),或者仅仅只是频率(frequency)。
这些技术一旦与机器学习结合,就能高效分析海量文本。但在把 ML 应用到这些任务之前,理解 NLP 工程师所面对的问题本身同样重要。通常,这些任务会按照固定的先后顺序依次执行——下面逐一介绍。
Tokenization(分词)
绝大多数 NLP 算法的第一步,都是把文本切分成tokens(词元),也就是一个个单词。听起来简单,但要处理标点符号、不同语言各自的词与句分隔符,就会变得棘手——你可能需要借助多种方法才能确定词的边界。
对《傲慢与偏见》中的句子进行分词。Infografía por Jen Looper
例如图中把句子 "Angry people are not always wise." 逐个单词高亮拆开,每个单词成为一个 token,供下游任务使用。分词是词性标注、情感分析、机器翻译等一切需要单词级输入的 NLP 流水线的基础。
Embeddings(词嵌入)
词嵌入(Word embeddings)是把文本数据转换为数值的一种方式。嵌入的构造方式使得语义相近的词、或经常一起出现的词会在向量空间中聚拢成群。
"I have the highest respect for your nerves, they are my old friends." —— 来自《傲慢与偏见》一句话的词嵌入。Infografía por Jen Looper
图中每个单词(I, have, respect, the, highest...)被映射为向量,语义关联的词(如 respect 连接 highest 与 nerves)在向量空间中距离更近。这种分布式表示是文本分类、命名实体识别、问答系统等需要语义理解任务的基础。可以用 TensorFlow 的 Embedding Projector 之类的工具亲自体验:点击一个单词,就会显示出相似词的聚类,例如 'toy'(玩具)会和 'disney'、'lego'、'playstation'、'console' 聚在一起。
Parsing 与词性标注(Part-of-Speech Tagging)
每个被分词后的单词都可以被标注为一种词性(part of speech)——名词、动词或形容词。例如句子el rápido zorro rojo saltó sobre el perro marrón perezoso(敏捷的红狐狸跳过懒棕狗)可以被标注为:zorro(狐狸)= 名词,saltó(跳过)= 动词。
对《傲慢与偏见》中的句子进行解析。Infografía por Jen Looper
图中对句子 "Elizabeth was too much embarrassed to say a word." 的每个成分都标上了语法类别:Elizabeth = 专有名词(Proper noun)、was = 动词(verb)、too much = 形容词修饰语(mod. adj.)、embarrassed = 形容词(adjective)、to say = 动词不定式(v. infinitive)、a word = 名词(noun)。
而Parsing(句法解析)则是识别句子中哪些词彼此相关:例如el rápido zorro rojo saltó是一个"形容词-名词-动词"序列,与后面的el perro marrón perezoso序列相分离。机器很擅长套用形式化规则,这也是为什么"像人一样解析句子"对计算机来说并不难——真正的难点在于理解句子的含义与情感。
词与短语频率(Word and Phrase Frequencies)
分析大段文本时,一个非常实用的操作是:为每一个感兴趣的词或短语构建词典(dictionary),记录它出现了多少次。例如the quick red fox jumped over the lazy brown dog中,the的词频是 2。
看一个真实示例。Rudyard Kipling 的诗《The Winners》中有这样一节:
What the moral? Who rides may read. When the night is thick and the tracks are blind A friend at a pinch is a friend, indeed, But a fool to wait for the laggard behind. Down to Gehenna or up to the Throne, He travels the fastest who travels alone.短语频率统计可以根据需要区分大小写或不区分大小写:这首诗中,短语a friend的频率是 2,the的频率是 6,travels的频率是 2。
N-grams(N 元组)
文本可以按固定长度切分成词的序列:一个词叫unigram(一元组),两个词叫bigrams(二元组),三个词叫trigrams(三元组),任意 n 个词统称n-grams。
例如el rápido zorro rojo saltó sobre el perro marrón perezoso以 n=2 切分,得到如下二元组:
- el rápido
- rápido zorro
- zorro rojo
- rojo saltó
- saltó sobre
- sobre el
- el perro
- perro marrón
- marrón perezoso
把它想象成在句子上滑动的盒子会更直观。以下是 n=3 的滑动窗口(每句中加粗下划线的部分就是该三元组):
- el rápido zorrosaltó sobre el perro marrón perezoso
- elrápido zorro rojosaltó sobre el perro marrón perezoso
- el rápidozorro rojo saltósobre el perro marrón perezoso
- el rápido zorrorojo saltó sobreel perro marrón perezoso
- el rápido zorro rojosaltó sobre elperro marrón perezoso
- el rápido zorro rojo saltósobre el perromarrón perezoso
- el rápido zorro rojo saltó sobreel perro marrónperezoso
- el rápido zorro rojo saltó sobre elperro marrón perezoso
n-gram 值为 3 时的滑动窗口效果。Infografía por Jen Looper
n-gram 用于捕捉局部上下文信息,是拼写检查、文本生成、关键词提取和统计语言模型(如根据前 n-1 个词预测下一个词)等任务的基础特征。
名词短语抽取(Noun Phrase Extraction)
大多数句子中都有一个作为主语或宾语的名词。在英语里,它前面往往有 'a'、'an' 或 'the' 这样的冠词。通过**抽取名词短语(noun phrase extraction)**来识别句子的主语或宾语,是 NLP 中理解句子含义时的常见任务。
✅ 在句子 "I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation. It is too long ago. I was in the middle before I knew that I had begun." 中,你能找出名词短语吗?
以el rápido zorro rojo saltó sobre el perro marrón perezoso为例,这里共有 2 个名词短语:rápido zorro rojo(红狐狸)和perro marrón perezoso(懒棕狗)。
情感分析(Sentiment Analysis)
一个句子或文本可以被分析出情感(sentiment),即它有多积极或多消极。情感通过两个维度衡量:极性(polarity)与客观性/主观性(objectivity/subjectivity)。
- 极性(polarity):取值范围 -1.0 到 1.0,从消极到积极;
- 客观性(objectivity):取值范围 0.0 到 1.0,从最客观到最主观。
✅ 后面你会学到用机器学习判断情感的多种方法,其中一种朴素做法是:由人类专家准备一份被归类为积极或消极的词与短语清单,再把这个"模型"应用到文本上,计算极性得分。想一想:这种方法在哪些场景下会奏效,在哪些场景下效果不佳?(例如反讽、幽默、上下文反转等场景就很难处理。)
屈折与词形还原:处理词的形态变化
- Inflection(屈折变化):让你拿到一个词后得到它的单数或复数形式,例如 cat → cats、mouse → mice。这是英语等语言的形态学核心机制。
- Lemmatization(词形还原):lemma(词元)是一组词的根或主词形,例如voló(飞了)、vuela(飞)、volando(飞着)的词元都是动词volar(飞)。词形还原把各种屈折形式归并到词典中的基本形,便于后续统计与检索。
实用资源:WordNet 词汇数据库
NLP 研究者还有可用的现成数据库,其中特别值得一提:
WordNet是一个词库数据库,收录了多种语言中每个词的近义词、反义词以及大量其他细节。在构建翻译系统、拼写检查器或任何类型的语言工具时,它都极其有用。
NLP 库:为什么你不必从零实现
幸运的是,你不需要亲手实现上述所有技术——Python 生态中有出色的现成库,让不专精于 NLP 或 ML 的开发者也能轻松上手。后续课程会给出更多示例,本课先掌握几个马上要用到的实用技巧。
练习:使用 TextBlob 库
本课选用TextBlob,因为它包含了处理这类任务的实用 API。TextBlob "站在 NLTK 和 pattern 的巨人肩膀上,并与两者良好协作",其 API 中内嵌了相当多的机器学习能力。
在识别名词短语时,TextBlob 提供多种抽取器可选。来看ConllExtractor:
from textblob import TextBlob from textblob.np_extractors import ConllExtractor # 导入并创建一个 Conll 抽取器,供稍后使用 extractor = ConllExtractor() # 之后需要名词短语抽取器时: user_input = input("> ") user_input_blob = TextBlob(user_input, np_extractor=extractor) # 注意指定了非默认抽取器 np = user_input_blob.noun_phrases这段代码做了什么?ConllExtractor是"一个使用 ConLL-2000 训练语料训练的组块解析(chunk parsing)名词短语抽取器"。ConLL-2000 指 2000 年举办的"计算自然语言学习会议"(Conference on Computational Natural Language Learning)。该会议每年设一个工作坊解决一个棘手的 NLP 问题,2000 年的题目正是名词组块(noun chunking)。当时的模型使用《华尔街日报》语料训练:"第 15–18 节作为训练数据(211727 个 token),第 20 节作为测试数据(47377 个 token)"。
提示:TextBlob 官方提供了一份便于快速上手的 Quick Start 指南,推荐给有经验的 Python 开发者阅读。
挑战:用 NLP 升级你的聊天机器人
上一课(6-NLP/1-Introduction-to-NLP/README.md)你构建了一个只会随机应答的简单问答机器人(参考实现见 1-Introduction-to-NLP 的 solution,核心只是从random_responses列表中随机挑一句话)。现在,我们要让 Marvin 变得更"共情":
- 打印说明,指导用户如何与机器人交互;
- 启动循环:
- 接收用户输入;
- 如果用户要求退出,则退出;
- 处理用户输入,确定合适的情感回应;
- 如果在输入中检测到名词短语,将其复数化(pluralize)并就这个话题追问更多信息;
- 打印回应;
- 回到第 2 步继续循环。
以下是使用 TextBlob 判定情感的代码片段。注意这里只设了四档情感回应梯度(如果你愿意,完全可以更多):
if user_input_blob.polarity <= -0.5: response = "Oh dear, that sounds bad. " elif user_input_blob.polarity <= 0: response = "Hmm, that's not great. " elif user_input_blob.polarity <= 0.5: response = "Well, that sounds positive. " elif user_input_blob.polarity <= 1: response = "Wow, that sounds great. "仓库参考实现:完整的 Marvin 源码
本仓库提供了该挑战的完整参考实现 6-NLP/2-Tasks/solution/bot.py,其关键逻辑如下,可直接对照学习:
import random from textblob import TextBlob from textblob.np_extractors import ConllExtractor extractor = ConllExtractor() def main(): print("Hello, I am Marvin, the friendly robot.") print("You can end this conversation at any time by typing 'bye'") print("After typing each answer, press 'enter'") print("How are you today?") while True: # 等待用户输入文本 user_input = input("> ") if user_input.lower() == "bye": # 如果输入了 'bye'(或 BYE、ByE、byE 等),跳出循环 break else: # 基于用户输入创建 TextBlob,然后抽取名词短语 user_input_blob = TextBlob(user_input, np_extractor=extractor) np = user_input_blob.noun_phrases response = "" if user_input_blob.polarity <= -0.5: response = "Oh dear, that sounds bad. " elif user_input_blob.polarity <= 0: response = "Hmm, that's not great. " elif user_input_blob.polarity <= 0.5: response = "Well, that sounds positive. " elif user_input_blob.polarity <= 1: response = "Wow, that sounds great. " if len(np) != 0: # 至少检测到一个名词短语,就复数化并追问 # 例如 cat -> cats 或 mouse -> mice response = response + "Can you tell me more about " + np[0].pluralize() + "?" else: response = response + "Can you tell me more?" print(response) print("It was nice talking to you, goodbye!") # 启动程序 main()从源码结构可以看到几个值得注意的实现细节:
user_input.lower() == "bye"对用户输入做了小写归一化,因此无论用户输入bye、BYE还是ByE都能正确退出循环;TextBlob(user_input, np_extractor=extractor)显式传入非默认的ConllExtractor,使noun_phrases使用 ConLL-2000 训练的组块解析器;- 极性判断是级联的
elif分支,把polarity(-1.0 ~ 1.0)切成四档:<= -0.5、<= 0、<= 0.5、<= 1,分别对应"糟糕、不太好、积极、很棒"四档回应; - 名词短语处理使用了
np[0].pluralize():只取第一个检测到的名词短语,并调用 TextBlob 的屈折变化能力将其复数化(如 cat → cats、mouse → mice),再嵌入追问语句; - 当
len(np) == 0(没有检测到名词短语)时,回退为通用的 "Can you tell me more?",保证任何输入都有合理回应。
示例输出
下面是一段引导性的示例输出(以>开头的是用户输入):
Hello, I am Marvin, the friendly robot. You can end this conversation at any time by typing 'bye' After typing each answer, press 'enter' How are you today? > I am ok Well, that sounds positive. Can you tell me more? > I went for a walk and saw a lovely cat Well, that sounds positive. Can you tell me more about lovely cats? > cats are the best. But I also have a cool dog Wow, that sounds great. Can you tell me more about cool dogs? > I have an old hounddog but he is sick Hmm, that's not great. Can you tell me more about old hounddogs? > bye It was nice talking to you, goodbye!注意对话流:用户每次输入后,Marvin 先按极性给出情感回应("Well, that sounds positive."、"Wow, that sounds great."、"Hmm, that's not great."),再把检测到的名词短语复数化后追问("lovely cats" → "cool dogs" → "old hounddogs"),形成一个表面上看"理解"了话题的连贯对话。
运行前提
运行本课代码需要先安装 TextBlob 并下载其语料库(上一课 1-Introduction-to-NLP/README.md 中有明确安装步骤):
pip install -U textblob python -m textblob.download_corpora课程环境要求具备 Python 3 基础(input、循环、数组等),并使用 Visual Studio Code 及 Python 扩展(或你习惯的任何 Python IDE)。注意ConllExtractor等组块解析功能依赖已下载的语料数据,首次运行前务必执行download_corpora。
知识检验
- 你觉得这些"共情式"回应会让人误以为机器人真的理解了他们吗?
- 识别出名词短语会让机器人显得更"可信"吗?
- 从句子中抽取"名词短语"为什么是有用的操作?
把上面的机器人实现出来,找朋友测试一下:它能骗过他们吗?你能让机器人更"可信"吗?
🚀 挑战
从上面的知识检验中任选一项尝试实现。用朋友来测试这个机器人:它能骗到他们吗?你能否让机器人更"可信"?例如,可以增加更多情感梯度档位、识别 'why' / 'how' 等触发词给出特定回应,或者让追问句式更多样。
复习与延伸
接下来的课程(3-Translation-Sentiment/README.md 及后续 4-Hotel-Reviews-1、5-Hotel-Reviews-2)将深入讲解情感分析——包括用机器学习做翻译与情感分析、数据准备、以及用 NLTK 做酒店评论情感分析。你可以进一步研究 KDNuggets 等社区上关于 NLP 的技术文章。
课后作业
完成 作业:让机器人开口回应(Make a bot talk back):
在之前的课程中,你编写了一个可以聊天的基础机器人,它会给出随机回答直到你说 'bye'。你能让回答不那么随机吗?能否在你说了特定内容(如 'why' 或 'how')时触发特定回答?想一想在扩展机器人时,机器学习如何让这类工作减少人工。你可以使用 NLTK 或 TextBlob 库来简化任务。
评分标准如下:
| 标准 | 优秀 | 合格 | 待改进 |
|---|---|---|---|
| 提交了有说明文档的全新 bot.py 文件 | 提交了新 bot 文件但存在 bug | 未提交文件 |
本课在 ML-For-Beginners 课程体系中的位置:它是 NLP 单元的第二课(6-NLP/README.md 列出了全部五课),承接第一课"NLP 导论 + 随机应答机器人",为第三课起的"翻译与情感分析"打下任务级基础——先掌握"有哪些 NLP 任务",再学会"用现成库完成这些任务",最终走向"用机器学习自动完成这些任务"。
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考