自然语言处理NLP的数据预处理:从原始文本到模型输入(MindSpore版)

自然语言处理(NLP)任务中,数据预处理的质量直接影响模型性能。即使是最先进的大模型,在未经处理的原始文本上也难以发挥作用。本文将详细介绍 NLP 数据预处理的核心步骤,并基于 MindSpore 框架提供代码实现,帮助你构建完整的预处理流水线。

为什么数据预处理至关重要?

原始文本数据通常存在噪声(如错别字、特殊符号)、格式不一致(如大小写混合)、冗余信息(如重复段落)等问题。预处理的目标是:

  • 统一数据格式,降低模型学习难度
  • 去除噪声,减少干扰信息
  • 将文本转化为模型可理解的数值形式
  • 适应特定任务需求(如分类、翻译、摘要)

下面以情感分析任务为例(输入文本,输出正面 / 负面标签),演示完整预处理流程。

数据预处理核心步骤

1. 数据加载与探索

首先需要加载数据并初步探索,了解数据分布(如文本长度、标签分布),为后续处理提供依据。

示例数据格式(CSV)

python 复制代码
text,label
"这部电影太精彩了!推荐大家去看",1
"剧情拖沓,浪费时间...",0
"演员演技在线,但结局有点仓促",1
...

MindSpore 加载代码

python 复制代码
import mindspore.dataset as ds
import pandas as pd
import matplotlib.pyplot as plt

# 加载CSV数据
df = pd.read_csv("reviews.csv")
print(f"数据集规模:{len(df)}条")
print(f"标签分布:{df['label'].value_counts().to_dict()}")

# 可视化文本长度分布
df["text_len"] = df["text"].apply(lambda x: len(x))
plt.hist(df["text_len"], bins=50)
plt.title("文本长度分布")
plt.xlabel("长度")
plt.ylabel("数量")
plt.show()

# 转换为MindSpore数据集
dataset = ds.NumpySlicesDataset(df[["text", "label"]].values, column_names=["text", "label"])

2. 文本清洗

去除无关字符、统一格式,减少噪声干扰。常见操作包括:

  • 去除特殊符号、标点
  • 大小写转换(通常转为小写)
  • 去除多余空格
  • 处理中英文混合场景(如保留中文,清理无意义符号)

清洗函数实现

python 复制代码
import re

def clean_text(text):
    # 去除URL
    text = re.sub(r"http\S+", "", text)
    # 去除特殊符号和标点(保留中文、字母、数字)
    text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", "", text)
    # 转为小写(英文)
    text = text.lower()
    # 去除多余空格
    text = re.sub(r"\s+", " ", text).strip()
    return text

# 应用清洗函数
dataset = dataset.map(operations=lambda x: (clean_text(x[0]), x[1]), input_columns=["text", "label"])

3. 分词(Tokenization)

将连续文本拆分为最小语义单位(词语或子词),是 NLP 的基础步骤。中文通常使用 jieba、THULAC 等工具,英文可直接按空格分词(或使用更复杂的子词分词)。

中文分词实现

python 复制代码
import jieba

def tokenize(text):
    # 中文分词,过滤空字符串
    tokens = [token for token in jieba.cut(text) if token.strip()]
    return tokens

# 应用分词
dataset = dataset.map(operations=lambda x: (tokenize(x[0]), x[1]), input_columns=["text", "label"])

示例效果

  • 原始文本:"这部电影太精彩了!推荐大家去看"
  • 清洗后:"这部电影太精彩了推荐大家去看"
  • 分词后:["这部", "电影", "太", "精彩", "了", "推荐", "大家", "去看"]

4. 去除停用词

停用词是指在文本中频繁出现但语义贡献小的词(如 "的"、"是"、"在"),去除它们可以减少冗余,提升效率。

停用词处理

python 复制代码
# 加载停用词表(可自定义或使用公开表)
with open("stopwords.txt", "r", encoding="utf-8") as f:
    stopwords = set(f.read().splitlines())

def remove_stopwords(tokens):
    return [token for token in tokens if token not in stopwords]

# 应用去停用词
dataset = dataset.map(operations=lambda x: (remove_stopwords(x[0]), x[1]), input_columns=["text", "label"])

示例效果

  • 分词后:["这部", "电影", "太", "精彩", "了", "推荐", "大家", "去看"]
  • 去停用词后:["电影", "精彩", "推荐", "大家"]

5. 词表构建与数值映射

模型无法直接处理文本,需要将词语转换为数值。这一步需要:

  • 构建词表(记录词语与索引的映射)
  • 将分词后的文本转换为索引序列

词表构建实现

python 复制代码
from collections import Counter

# 收集所有词,统计频率
all_tokens = []
for data in dataset.create_dict_iterator():
    all_tokens.extend(data["text"])

# 按频率排序,保留前5000个词(超参数)
word_counts = Counter(all_tokens).most_common(5000)
# 构建词表:预留0(PAD)、1(UNK)
vocab = {"<PAD>": 0, "<UNK>": 1}
for word, _ in word_counts:
    vocab[word] = len(vocab)

print(f"词表大小:{len(vocab)}")

# 保存词表
import json
with open("vocab.json", "w", encoding="utf-8") as f:
    json.dump(vocab, f, ensure_ascii=False)

数值映射

python 复制代码
def tokens_to_ids(tokens):
    # 未在词表中的词用<UNK>代替
    return [vocab.get(token, 1) for token in tokens]

# 应用映射
dataset = dataset.map(operations=lambda x: (tokens_to_ids(x[0]), x[1]), input_columns=["text", "label"])

示例效果

  • 去停用词后:["电影", "精彩", "推荐", "大家"]
  • 数值映射后:[56, 128, 34, 92](假设词表中对应索引)

6. 序列长度对齐(Padding/Truncation)

模型输入需要固定长度,因此需对序列进行截断(超长)或填充(不足)。

长度对齐实现

python 复制代码
import mindspore.dataset.transforms as C
import mindspore.dataset.vision as vision

# 设定最大长度(根据前面的长度分布统计结果)
max_seq_len = 32

def pad_sequence(ids):
    # 截断超长序列
    if len(ids) > max_seq_len:
        return ids[:max_seq_len]
    # 填充短序列(用<PAD>的索引0)
    else:
        return ids + [0] * (max_seq_len - len(ids))

# 应用长度对齐
dataset = dataset.map(operations=lambda x: (pad_sequence(x[0]), x[1]), input_columns=["text", "label"])

7. 数据划分与格式转换

最后将数据集划分为训练集、验证集,并转换为模型可接受的格式(如 Tensor)。

最终处理步骤

python 复制代码
# 划分训练集(80%)和验证集(20%)
dataset = dataset.shuffle(buffer_size=len(df))  # 打乱数据
train_size = int(0.8 * len(df))
train_dataset, val_dataset = dataset.split([train_size, len(df) - train_size])

# 转换为Tensor格式
train_dataset = train_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["text"])
train_dataset = train_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["label"])
val_dataset = val_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["text"])
val_dataset = val_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["label"])

# 批量处理(设置batch_size)
train_dataset = train_dataset.batch(batch_size=32)
val_dataset = val_dataset.batch(batch_size=32)

# 查看最终数据格式
for batch in train_dataset.create_dict_iterator():
    print("文本张量形状:", batch["text"].shape)  # (32, 32)
    print("标签张量形状:", batch["label"].shape)  # (32,)
    break

完整预处理流水线总结

将上述步骤整合,可构建一个可复用的预处理函数:

python 复制代码
def preprocess_pipeline(file_path, max_seq_len=32, vocab_path=None):
    # 1. 加载数据
    df = pd.read_csv(file_path)
    dataset = ds.NumpySlicesDataset(df[["text", "label"]].values, column_names=["text", "label"])
    
    # 2. 文本清洗
    dataset = dataset.map(operations=lambda x: (clean_text(x[0]), x[1]), input_columns=["text", "label"])
    
    # 3. 分词
    dataset = dataset.map(operations=lambda x: (tokenize(x[0]), x[1]), input_columns=["text", "label"])
    
    # 4. 去停用词
    dataset = dataset.map(operations=lambda x: (remove_stopwords(x[0]), x[1]), input_columns=["text", "label"])
    
    # 5. 词表构建/加载与映射
    if vocab_path:
        with open(vocab_path, "r", encoding="utf-8") as f:
            vocab = json.load(f)
    else:
        all_tokens = []
        for data in dataset.create_dict_iterator():
            all_tokens.extend(data["text"])
        word_counts = Counter(all_tokens).most_common(5000)
        vocab = {"<PAD>": 0, "<UNK>": 1}
        for word, _ in word_counts:
            vocab[word] = len(vocab)
        with open("vocab.json", "w", encoding="utf-8") as f:
            json.dump(vocab, f, ensure_ascii=False)
    
    dataset = dataset.map(operations=lambda x: (tokens_to_ids(x[0]), x[1]), input_columns=["text", "label"])
    
    # 6. 长度对齐
    dataset = dataset.map(operations=lambda x: (pad_sequence(x[0], max_seq_len), x[1]), input_columns=["text", "label"])
    
    # 7. 划分与转换
    dataset = dataset.shuffle(buffer_size=len(df))
    train_size = int(0.8 * len(df))
    train_dataset, val_dataset = dataset.split([train_size, len(df) - train_size])
    
    train_dataset = train_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["text", "label"]).batch(32)
    val_dataset = val_dataset.map(operations=C.TypeCast(mstype.int32), input_columns=["text", "label"]).batch(32)
    
    return train_dataset, val_dataset, vocab

注意事项

  1. 预处理的任务相关性:不同任务(如机器翻译、命名实体识别)可能需要调整步骤(如翻译需保留句子结构,NER 需保留特殊符号)。
  2. 超参数调整:max_seq_len、词表大小等需根据数据分布调整,过长会增加计算量,过短会丢失信息。
  3. 效率优化 :MindSpore 的 map 操作支持多线程加速(通过num_parallel_workers参数),大规模数据可开启。
  4. 可复现性:保存词表等中间结果,确保测试 / 部署时使用相同的预处理规则。
相关推荐
deephub29 分钟前
AI智能体落地:Agent-Assist vs 全自动化完整决策指南
人工智能·大语言模型·agent
Danceful_YJ1 小时前
36.优化方法
人工智能·pytorch·python·深度学习·优化器算法
C116111 小时前
Jupyter中选择不同的python 虚拟环境
开发语言·人工智能·python
golang学习记1 小时前
TRAE AI 真强,连外国人都在用这些AI技巧
人工智能
化作星辰1 小时前
深度学习_神经网络_损失函数基础
人工智能·深度学习·神经网络
oak隔壁找我1 小时前
Spring AI 实现MCP简单案例
java·人工智能·后端
星光一影1 小时前
SpringBoot+Vue3无人机AI巡检系统
人工智能·spring boot·websocket·mysql·intellij-idea·mybatis·无人机
hxj..1 小时前
如何进行AI作图(架构图,流程图等)
人工智能·ai·ai作画
飞哥数智坊1 小时前
初级岗正在消失!1.8亿岗位数据让我看清:AI协同时代已经来了
人工智能