ML-For-Beginners NLP 课程:从分词、N-gram 到用 TextBlob 构建情感感知对话机器人
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
本篇是 ML-For-Beginners 课程"6-NLP"章节的第 2 课,系统讲解自然语言处理(NLP)的常见任务与技术:分词、词嵌入、句法分析与词性标注、词频/短语频率、N-gram、名词短语抽取、情感分析、屈折变化与词形还原,并介绍 WordNet 词库与 TextBlob 库。读完本篇,你将能够使用 TextBlob 的 ConllExtractor 进行名词短语抽取,并把上一课只能随机应答的"Marvin"机器人升级为具备四档情感感知、还能针对识别出的名词短语追问的对话机器人。
1. 本课定位与环境准备
ML-For-Beginners 是一门"12 weeks, 26 lessons, 52 quizzes"的经典机器学习入门课程。NLP 章节(6-NLP)的总览见 6-NLP/README.md,该章以"Marvin"对话机器人为主线、以简·奥斯汀小说《傲慢与偏见》的文本和欧洲酒店评论为语料,逐课引入 NLP 技术。课程列表为:
- Introduction to natural language processing(NLP 入门)
- Common NLP tasks and techniques(本课)
- Translation and sentiment analysis with machine learning(机器翻译与情感分析)
- Preparing your data(酒店评论数据准备)
- NLTK for Sentiment Analysis(用 NLTK 做情感分析)
上一课中,学习者先构建了一个极简的 Marvin 机器人。其参考解答 6-NLP/1-Introduction-to-NLP/solution/bot.py 的核心逻辑是:对任何用户输入,都从 6 句固定回复里随机挑一句返回。
random_responses = ["That is quite interesting, please tell me more.", "I see. Do go on.", "Why do you say that?", "Funny weather we've been having, isn't it?", "Let's change the subject.", "Did you catch the game last night?"] # ... response = random.choices(random_responses)[0]本课的任务正是让应答"没那么随机":机器人要分析用户输入的情感(polarity),输出匹配情感的回应;如果检测到名词短语,还要把它变成复数并围绕该话题追问。
环境准备
NLP 章节对环境的唯一硬性要求是 Python 3(示例基于 Python 3.8),编辑器推荐 VS Code + Python 扩展。本课依赖 TextBlob 库,安装命令(来自上一课的前置条件小节)为:
pip install -U textblob python -m textblob.download_corpora第二条命令不可省略:TextBlob 的词性标注、名词短语抽取等能力依赖 NLTK 语料,需要显式下载。
2. NLP 常见任务全景
对大多数 NLP 任务而言,待处理文本必须被拆分(break down)、检查(examined),并且结果要存储起来或与规则和数据集做交叉比对。通过这些任务,程序员可以推导出文本的含义(meaning)或意图(intent),或者仅仅得到某些词项的频率(frequency)。这些任务通常按顺序执行。
2.1 分词(Tokenization)
大多数 NLP 算法首先要做的事就是把文本切分成 token(词)。这听起来简单,但一旦要考虑标点、不同语言的词界与句界,就会变得棘手,你可能需要用多种方法来判断分界位置。
对《傲慢与偏见》中一句话的分词示意。信息图作者为 Jen Looper。
2.2 词嵌入(Embeddings)
词嵌入(Word embeddings)是把文本数据数值化的一种方式。嵌入的构造使得含义相近、或经常一起使用的词在向量空间中彼此聚集。
《傲慢与偏见》句子 "I have the highest respect for your nerves, they are my old friends." 的词嵌入聚类。信息图作者为 Jen Looper。
TensorFlow 提供的 TensorBoard 嵌入投影器(Projector)是实验词嵌入的有趣工具:点选某个词会展示与之聚类的相似词,例如 'toy' 会与 'disney'、'lego'、'playstation'、'console' 聚在一起。
2.3 句法分析与词性标注(Parsing & Part-of-speech Tagging)
每个被分词后的单词都可以被标注为某个词性——名词、动词或形容词。例如句子the quick red fox jumped over the lazy brown dog可以标注为 fox = 名词(noun)、jumped = 动词(verb)。
而 Parsing(句法分析/分块)是识别句子中哪些词彼此相关联——比如the quick red fox jumped是一个"形容词-名词-动词"序列,与lazy brown dog序列是分开的两个块。
2.4 词与短语频率(Word and Phrase Frequencies)
分析大量文本时,一个有用的程序是构建一个字典:记录每个感兴趣的词或短语及其出现次数。例如句子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。
2.5 N-gram
文本可以被切分成固定长度的连续词序列:单个词(unigram)、两个词(bigram)、三个词(trigram),或任意个数(n-gram)。
以the quick red fox jumped over the lazy brown dog为例,n-gram 长度为 2 时得到以下 bigram:
- the quick
- quick red
- red fox
- fox jumped
- jumped over
- over the
- the lazy
- lazy brown
- brown dog
把它想象成在句子上滑动的窗口更直观。下面是 n-gram 长度为 3 的滑动过程,每句中加粗的即为当前 n-gram:
- the quick redfox jumped over the lazy brown dog
- thequick red foxjumped over the lazy brown dog
- the quickred fox jumpedover the lazy brown dog
- the quick redfox jumped overthe lazy brown dog
- the quick red foxjumped over thelazy brown dog
- the quick red fox jumpedover the lazybrown dog
- the quick red fox jumped overthe lazy browndog
- the quick red fox jumped over thelazy brown dog
N-gram 取值为 3 的滑动窗口示意。信息图作者为 Jen Looper。
2.6 名词短语抽取(Noun Phrase Extraction)
大多数句子中都有一个作为主语或宾语的名词。在英语中,它前面常常跟着 'a'、'an' 或 'the',因此可以通过"抽取名词短语"来识别句子的主语或宾语——这是在试图理解句子含义时 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."(同样出自《傲慢与偏见》)中找出名词短语。以the quick red fox jumped over the lazy brown dog为例,其中包含 2 个名词短语:quick red fox和lazy brown dog。
2.7 情感分析(Sentiment Analysis)
一个句子或文本可以被分析其情感,即它有多积极或多消极。情感从*极性(polarity)和客观性/主观性(objectivity/subjectivity)*两个维度度量:polarity 取值范围是 -1.0 到 1.0(消极到积极),objectivity 取值范围是 0.0 到 1.0(最客观到最主观)。
确定情感的方式不止一种:一种经典做法是由人类专家维护一份按"积极/消极"分类的词与短语列表,把该模型应用到文本上计算极性得分。这种方式在有些场景下有效,在另一些场景下则力不从心——例如讽刺与反语,这正是后几课要借助机器学习情感分类器解决的。
2.8 屈折变化与词形还原(Inflection & Lemmatization)
屈折变化(Inflection):给定一个词,能得到它的单数或复数形式。
词形还原(Lemmatization):lemma是一组词词形的词根或主题词(headword)。例如flew、flies、flying的词根都是动词fly。
2.9 WordNet 词库
NLP 研究者还有可用的现成数据库,其中最著名的是 WordNet:一个涵盖多门语言中大量词汇的数据库,记录每个词的同义词、反义词以及其他多种词法/语义关系。它在构建翻译、拼写检查或任何类型的语言工具时都非常有用。
3. 用 TextBlob 落地上述任务
不必自己实现全部这些技术——Python 有优秀的 NLP 库。课文以 TextBlob 为例,它"站在 [NLTK] 和 [pattern] 两个巨人的肩膀上,并良好地与两者协作",且 API 中内嵌了相当多的机器学习能力。TextBlob 官方提供了一份面向有经验的 Python 开发者的 Quick Start 指南,课程建议配合查阅。
练习:使用 TextBlob 的 ConllExtractor 抽取名词短语
识别名词短语时,TextBlob 提供了多种抽取器(extractor)。课文要求使用ConllExtractor:
from textblob import TextBlob from textblob.np_extractors import ConllExtractor # import and create a Conll extractor to use later extractor = ConllExtractor() # later when you need a noun phrase extractor: user_input = input("> ") user_input_blob = TextBlob(user_input, np_extractor=extractor) # note non-default extractor specified np = user_input_blob.noun_phrases关键点在于构造TextBlob时通过np_extractor=指定了非默认抽取器。课文解释了ConllExtractor的来历:它"使用以 ConLL-2000 训练语料训练的 chunk parsing(分块解析)来抽取名词短语"。ConLL-2000 指 2000 年的国际计算自然语言学习会议(Conference on Computational Natural Language Learning),该会议每年主办一个攻克棘手 NLP 问题的工作坊,2000 年的主题是名词分块(noun chunking):模型在《华尔街日报》语料上训练,"以 sections 15-18 作为训练数据(211727 tokens),section 20 作为测试数据(47377 tokens)"。
4. 挑战:用 NLP 升级 Marvin 机器人
4.1 任务拆解
上一课你构建了一个非常简单的问答机器人。本课要利用情感分析让 Marvin 更有"同理心":分析输入的情感并输出匹配的回应;同时识别出一个noun_phrase并围绕它追问。构建这个更好对话机器人的步骤(与课文一致):
- 打印指引,告诉用户如何与机器人交互
- 开始循环
- 接收用户输入
- 如果用户要求退出,则退出
- 处理用户输入并确定合适的情感回应
- 如果检测到名词短语,将其变为复数并围绕该话题追问
- 打印回应
- 回到第 2 步
情感判断的代码片段如下(课文只给了四个梯度的情感回应,你也可以加更多):
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. "4.2 参考解答逐段解析
课程给出的一个可行解答是 6-NLP/2-Tasks/solution/bot.py,全文约 44 行,可完整对照上面 5 个步骤:
import random from textblob import TextBlob from textblob.np_extractors import ConllExtractor extractor = ConllExtractor()bot.py 第 1-4 行 在模块级创建了一次ConllExtractor。从源码结构看,extractor只构造一次、在所有对话轮次间复用,这是合理的——它封装的模型不需要每轮重建;而文件顶部的import random在解答中并未被使用,可以推断它是从上一课"随机回复版"演化而来时留下的痕迹。
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?")bot.py 第 6-10 行 对应步骤 1:打印交互指引。注意开场白从上一课的 "the simple robot" 改成了 "the friendly robot"——语气本身就是本次升级的一部分。
while True: # wait for the user to enter some text user_input = input("> ") if user_input.lower() == "bye": # if they typed in 'bye' (or even BYE, ByE, byE etc.), break out of the loop breakbot.py 第 12-18 行 是循环入口(步骤 2):input("> ")接收输入;user_input.lower() == "bye"使退出判断不区分大小写(BYE、ByE 等写法都能退出)。
else: # Create a TextBlob based on the user input. Then extract the noun phrases 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. "bot.py 第 20-31 行 是每轮处理的核心(步骤 3):
- 每轮用
TextBlob(user_input, np_extractor=extractor)新建一个 TextBlob 对象,并指定第 3 节练习中的 Conll 抽取器; user_input_blob.noun_phrases返回名词短语列表,赋给局部变量np(注意:这里的np是局部变量,与习惯上代表 NumPy 的np只是重名);- 四级
polarity分支与课文给出的片段完全一致。从源码结构看有一个细节值得留意:第二个分支是polarity <= 0,即极性恰为 0.0 的中性输入会落入 "Hmm, that's not great." 这一"不太乐观"的档位,而非中性回应——四档设计覆盖了 (-∞, -0.5]、(-0.5, 0]、(0, 0.5]、(0.5, 1] 四个区间。
if len(np) != 0: # There was at least one noun phrase detected, so ask about that and pluralise it # e.g. cat -> cats or mouse -> mice response = response + "Can you tell me more about " + np[0].pluralize() + "?" else: response = response + "Can you tell me more?" print(response)bot.py 第 33-39 行 是名词短语追问逻辑(步骤 4):
- 只有检测到至少一个名词短语(
len(np) != 0)才追问,否则退化为通用的 "Can you tell me more?"; - 取第一个名词短语
np[0],调用 TextBlob 的pluralize()变复数——源码注释明确举例cat -> cats or mouse -> mice; - 最终
print(response)(步骤 5),循环回到第 2 步;输入bye跳出后打印 bot.py 第 41 行 的告别语。
4.3 预期输出
课文给出了引导用的样例对话(以>开头的是用户输入):
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!对照代码可以验证每一步:I am ok未检测到名词短语,走 "Can you tell me more?" 通用分支;saw a lovely cat抽取出lovely cat并复数化为lovely cats;cool dog→cool dogs;old hounddog(单复数同形)→old hounddogs,且sick使极性落入 "not great" 档。机器人看似"理解"了用户,实际上只做了情感打分、名词短语抽取与词形变化——这正是本课程的思考点:
✅Knowledge Check
- 你认为这种"富有同情心"的回应会"骗"过某人,让他们以为机器人真的理解了自己吗?
- 识别名词短语会让机器人更"可信"吗?
- 从句子中抽取"名词短语"为什么是有用的事情?
4.4 🚀 Challenge
把 Knowledge Check 中的任务实现出来,并找一位朋友测试你的机器人。它能"骗"过对方吗?你能否让它更"可信"?
5. 作业:让机器人会"接话"
配套的作业是 6-NLP/2-Tasks/assignment.md,题为Make a bot talk back。要求如下:
- 在之前几课你已经编程实现了一个可以聊天的基础机器人:它会随机回复,直到你说 'bye' 为止。能否让回复"没那么随机"——例如当用户说 'why' 或 'how' 这类特定词时触发特定回复?
- 思考一下随着机器人不断扩展,机器学习如何能让这类工作更少依赖手工规则;
- 可以使用 NLTK 或 TextBlob 库来简化任务。
评分标准(Rubric):
| 标准 | 优秀(Exemplary) | 合格(Adequate) | 需改进(Needs Improvement) |
|---|---|---|---|
| 呈现 | 提交一个新的、有文档注释的 bot.py 文件 | 提交了新的 bot 文件但含有 bug | 未提交文件 |
6. 延伸阅读与下一步
- 后续几课将深入情感分析。课文建议通过 KDNuggets 社区的 NLP 专题文章研究这一有趣的技术。
- 下一步课程是 6-NLP/3-Translation-Sentiment/README.md:用机器学习做机器翻译与情感分析,把本课介绍的"词典打分"式情感判断升级为数据驱动的分类器。
本篇涉及的全部仓库资源:
| 资源 | 路径 |
|---|---|
| 本课原文 | 6-NLP/2-Tasks/README.md |
| 参考解答 bot.py | 6-NLP/2-Tasks/solution/bot.py |
| 作业与评分标准 | 6-NLP/2-Tasks/assignment.md |
| 上一课基础机器人解答 | 6-NLP/1-Introduction-to-NLP/solution/bot.py |
| NLP 章节总览 | 6-NLP/README.md |
| 分词示意图 | 6-NLP/2-Tasks/images/tokenization.png |
| 词嵌入示意图 | 6-NLP/2-Tasks/images/embedding.png |
| N-gram 滑动窗口动画 | 6-NLP/2-Tasks/images/n-grams.gif |
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考