以下内容将从 基本概念 到 实用代码 分步骤、分场景地详细介绍 NLP 常见文本预处理 方法及其背后的思想。如果无法从外部导入数据,我们会模拟一份简易文本数据(如字符串列表),并在此基础上演示预处理代码及详细解释,确保在常规 Python 环境下可以运行。
在自然语言处理(NLP)任务(如机器学习、深度学习、大模型开发)中,原始文本数据通常会包含各种噪声,例如:
以下方法往往会按需组合使用:
. , ! ? : ; " ' -
等。the, a, an, of, is
等。
,或保持数字原状,视任务需要。以下我们将使用 Python 及相关库演示以上方法的大部分实现。
我们先模拟一段文本数据(用一个字符串列表表示)。请注意,此示例仅用于演示,真实场景可来自数据库、爬虫、csv 等形式。
# 模拟一段文本数据
text_corpus = [
"I love Machine Learning! It's awesome.",
"Data science is an interdisciplinary field. The quick brown fox jumps over the lazy dog, doesn't it?",
"Visit us at https://example.com or send an email to [email protected]!",
"This is a sample HTML snippet. NLP can be fun :)"
]
下面演示从最基础的清洗开始,逐步进行各种预处理操作。为了演示效果,我们使用一些常见 Python 库(re
, nltk
),并保证无需外部数据文件即可运行。若你的环境没有安装 nltk
,请先执行 pip install nltk
。
说明:如果环境中无法使用 NLTK,也可根据需求自行编写简单函数或使用其他库(如
spaCy
,但需pip install spacy
并下载语言模型)。
目标:
import re
def basic_cleaning(text: str) -> str:
# 1) 转小写
text = text.lower()
# 2) 移除 HTML 标签,如 , 等
text = re.sub(r"<.*?>", " ", text)
# 3) 移除 URL (http://xxx 或 https://xxx)
text = re.sub(r"http\S+|www\S+", " ", text)
# 4) 移除邮箱 ([email protected])
text = re.sub(r"\S+@\S+\.\S+", " ", text)
# 5) 去除多余的标点符号,仅保留字母、数字、常见的符号
# 视任务而定,这里演示删除大部分标点,只保留句子间空格
text = re.sub(r"[^a-z0-9\s.,!?]", " ", text)
# 6) 去除多余空格
text = re.sub(r"\s+", " ", text).strip()
return text
# 测试:
cleaned_corpus = [basic_cleaning(sentence) for sentence in text_corpus]
for i, (orig, c) in enumerate(zip(text_corpus, cleaned_corpus)):
print(f"原文 {i}: {orig}")
print(f"清洗后: {c}")
print("-"*50)
re.sub(r"<.*?>", " ", text)
:用空格替换任何形如 <...>
的 HTML 标签。re.sub(r"http\S+|www\S+", " ", text)
:匹配并移除所有 URL。re.sub(r"\S+@\S+\.\S+", " ", text)
:匹配并移除邮箱格式(极简匹配)。re.sub(r"[^a-z0-9\s.,!?]", " ", text)
:只保留字母、数字、空格和部分标点(.,!?
),其余替换为空格。text.lower()
:统一转为小写。\s+
匹配连续空格,将其替换成单个空格;然后 .strip()
去掉首尾空格。此时得到的是一个基本“干净”、大小写统一的语句列表。
常见做法:
re.split(r"\W+", text)
等。此处演示 nltk.tokenize.word_tokenize:
import nltk
# 第一次使用需要下载 punkt 包:
# nltk.download('punkt')
from nltk.tokenize import word_tokenize
def tokenize_text(text: str) -> list:
tokens = word_tokenize(text)
return tokens
tokenized_corpus = [tokenize_text(sentence) for sentence in cleaned_corpus]
for i, (clean_sen, toks) in enumerate(zip(cleaned_corpus, tokenized_corpus)):
print(f"清洗后文本 {i}: {clean_sen}")
print(f"分词结果: {toks}")
print("-"*50)
word_tokenize
会把标点符号、撇号等也当成单独词元拆分,比如 "doesn't"
会被拆成 ["does", "n't"]
,这在英文 NLP 里比较常见。停用词指在文本中出现频率极高,但对主语义贡献不大或会干扰主题建模的词汇,如英语中的 the, a, an, of, is, are, was, were, ...
等。在 NLTK 中,可以使用内置的停用词表(需要 stopwords
数据包)。
# nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english')) # 这里是英文停用词表
def remove_stopwords(tokens: list) -> list:
filtered_tokens = [tok for tok in tokens if tok not in stop_words]
return filtered_tokens
no_stopwords_corpus = [remove_stopwords(toks) for toks in tokenized_corpus]
for i, (toks, fs) in enumerate(zip(tokenized_corpus, no_stopwords_corpus)):
print(f"分词结果 {i}: {toks}")
print(f"去停用词后: {fs}")
print("-"*50)
set(stopwords.words('english'))
来加速 “是否在停用词表中” 的判断。Stemming:用算法直接截断或替换单词末尾,得到词干(例如 using PorterStemmer
、SnowballStemmer
)。
Lemmatization:基于词典规则,还原到词的原型(例如 studies
→study
,went
→go
)。英文中常用 WordNetLemmatizer
。
下面以 NLTK 为例。
# nltk.download('wordnet')
from nltk.stem import PorterStemmer, WordNetLemmatizer
stemmer = PorterStemmer()
lemmatizer = WordNetLemmatizer()
def stem_and_lemmatize(tokens: list):
stemmed = [stemmer.stem(tok) for tok in tokens]
# WordNetLemmatizer 需要指定词性(pos),默认为名词,此处示例只做简单还原
lemmed = [lemmatizer.lemmatize(tok) for tok in tokens]
return stemmed, lemmed
for i, tokens in enumerate(no_stopwords_corpus):
s, l = stem_and_lemmatize(tokens)
print(f"去停用词后:{tokens}")
print(f"Stem 结果:{s}")
print(f"Lemma结果:{l}")
print("-"*50)
lemmatizer.lemmatize(word, pos='v')
来处理动词)。具体业务中,可二选一,或在不同场景尝试哪种更有效。
在大模型(如 BERT、GPT)中,子词切分是非常重要的一步。常见算法:
这里示例一个简单的 BPE 思想演示(并不完整,只作概念说明)。现实中常用库如 tokenizers
(HuggingFace) 或 sentencepiece
(Google)来训练和使用子词模型。
BPE 核心思路:
下面的示例仅演示核心过程的简化版本(不涉及多轮迭代训练),让你了解大致机制。
# 一个极简BPE演示:基于当前句子的字符频率合并
from collections import Counter, defaultdict
def bpe_tokenize(sentence, merges=2):
"""
sentence: 输入字符串(假设已清洗、转小写)
merges: 合并次数(越大,子词越长)
此函数仅用于演示BPE核心合并机制,不是完整实现
"""
# 初始:把句子视作字符列表,用空格隔开
# 例如 "data" -> ["d", "a", "t", "a"]
tokens = list(sentence.replace(" ", "▁")) # 用下划线表示空格
#▁可以视为空格符号,用来和字符区分
for _ in range(merges):
# 1. 统计所有相邻字符对频率
pairs = Counter()
for i in range(len(tokens)-1):
pair = (tokens[i], tokens[i+1])
pairs[pair] += 1
if not pairs:
break
# 2. 找到出现次数最多的字符对
best_pair = max(pairs, key=pairs.get)
# 3. 合并该字符对
# 在 tokens 中出现 (a,b) 的地方替换成 ab
merged = []
skip = False
for i in range(len(tokens)):
if skip:
skip = False
continue
if i < len(tokens)-1 and (tokens[i], tokens[i+1]) == best_pair:
merged.append(tokens[i] + tokens[i+1])
skip = True
else:
merged.append(tokens[i])
tokens = merged
return tokens
# 示例句子
example_sentence = "i love machine"
print("原句:", example_sentence)
bpe_result = bpe_tokenize(example_sentence, merges=3)
print("BPE切分结果:", bpe_result)
结合前面的步骤,可以组合成为一个 pipeline。示例:
def preprocess_pipeline(text_list):
"""
综合使用:
1) 基础清洗 (含大小写转化、去HTML/URL/邮箱、去标点等)
2) 分词
3) 去停用词
4) Lemmatization
"""
# 1) 基础清洗
cleaned = [basic_cleaning(t) for t in text_list]
# 2) 分词
tokenized = [word_tokenize(t) for t in cleaned]
# 3) 去停用词
filtered = []
for tokens in tokenized:
filtered_tokens = [tok for tok in tokens if tok not in stop_words]
filtered.append(filtered_tokens)
# 4) 词形还原 (Lemmatization) 简化示例
final_output = []
for tokens in filtered:
lemmed = [lemmatizer.lemmatize(tok) for tok in tokens]
final_output.append(lemmed)
return final_output
processed_corpus = preprocess_pipeline(text_corpus)
for i, (orig, pro) in enumerate(zip(text_corpus, processed_corpus)):
print(f"原文 {i}: {orig}")
print(f"最终预处理结果: {pro}")
print("="*70)
basic_cleaning
,word_tokenize
,stop_words
和 WordNetLemmatizer
。!
、?
)可能帮助模型理解情绪表达;去除或保留停用词会影响模型效果,应当视情况调参。jieba
, pkuseg
, spaCy
中文模型等)。在 NLP 中,无论是传统机器学习、深度学习还是大模型(如 BERT/GPT)开发,都需要对文本数据做一定程度的预处理。这些预处理操作往往包括:
具体使用哪种预处理步骤,需根据 任务目标、数据特点 来决定,并结合实验结果进行调整和优化。
以上所有代码都可以本地直接运行,所需外部数据仅用 Python 列表 text_corpus
模拟。同时,为了更好地运用预处理结果,建议在实际场景中对完整语料执行这些步骤,并做相应的性能评估。
至此,我们详尽介绍了 NLP 常见的文本预处理方法及其思路,并提供可运行的示例代码。希望能帮助你在文本数据清理、模型训练中快速上手并灵活改进。
【哈佛博后带小白玩转机器学习】 哔哩哔哩_bilibili
总课时超400+,时长75+小时