HuggingFace与自然语言处理(从框架学习到经典项目实践)[ 01 API操作 ]

发布于:2025-05-09 ⋅ 阅读:(10) ⋅ 点赞:(0)

本教程适用与第一次接触huggingface与相应框架和对nlp任务感兴趣的朋友,该栏目目前更新总结如下:

  1. ​​Tokenizer​​:
    支持单句/双句编码,自动处理特殊符号和填充。
    批量编码提升效率,适合训练数据预处理。
  2. Datasets​​:
    统一 API 处理多种格式数据(远程/本地)。
    内置排序、分桶、拆分等功能,简化数据准备流程。
  3. 应用场景​​:
    文本分类、NER 等任务的数据预处理。
    快速实验模型(如 BERT 微调)。
    通过 Hugging Face 工具,可高效完成 NLP 任务的 ​​数据编码 → 处理 → 训练​​ 全流程。

在这里插入图片描述

Hugging Face 与自然语言处理(NLP)介绍

Hugging Face 是一家专注于 自然语言处理(NLP)机器学习(ML) 的公司,以其开源库 Transformers 闻名。它提供了 预训练模型(Pre-trained Models)、数据集(Datasets)、训练工具(Trainer) 等,极大降低了 NLP 研究和应用的门槛。


1. Hugging Face 的核心产品

(1) Transformers 库

核心功能:提供 BERT、GPT、T5、RoBERTa 等预训练模型,支持 文本分类、翻译、问答、文本生成 等任务。
特点
PyTorch & TensorFlow 兼容:支持两种主流深度学习框架。
Pipeline API:几行代码即可完成 NLP 任务(如情感分析、命名实体识别)。
模型微调(Fine-tuning):可基于自己的数据调整预训练模型。

示例代码(情感分析)

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
result = classifier("I love Hugging Face!")
print(result)  # [{'label': 'POSITIVE', 'score': 0.9998}]

(2) Datasets 库

提供 10,000+ 数据集(如 GLUE、SQuAD、IMDb),支持快速加载和预处理。
特点
内存优化:流式加载大数据集(如 Wikipedia)。
数据预处理:内置 tokenization、批处理等功能。

示例代码(加载 IMDb 数据集)

from datasets import load_dataset

dataset = load_dataset("imdb")
print(dataset["train"][0])  # {'text': 'Great movie!', 'label': 1}

(3) Model Hub

托管 50,000+ 预训练模型,涵盖 NLP、CV、语音等领域。
支持社区共享:用户可以上传自己的模型供他人使用。

示例(下载 BERT 模型)

from transformers import BertModel

model = BertModel.from_pretrained("bert-base-uncased")

(4) Spaces(模型部署)

免费托管 AI 应用(如聊天机器人、文本生成器)。
支持 Gradio、Streamlit 等交互式 UI。


2. 自然语言处理(NLP)简介

NLP(Natural Language Processing)是 让计算机理解、生成人类语言 的技术,应用广泛:

(1) 主要任务

任务 示例
文本分类 情感分析(正面/负面)
命名实体识别(NER) 识别 “Apple” 是公司还是水果
机器翻译 中英互译
文本生成 GPT-3 写文章
问答系统 Siri、ChatGPT
文本摘要 自动生成新闻摘要

(2) 关键技术

词嵌入(Word Embeddings)(如 Word2Vec、GloVe)
Transformer 架构(如 BERT、GPT)
迁移学习(Transfer Learning):用预训练模型微调下游任务。

(3) Hugging Face 在 NLP 中的作用

降低 NLP 门槛:无需从头训练模型,直接使用预训练模型。
标准化流程:统一 API(如 AutoModelAutoTokenizer)。
社区驱动:研究者共享模型,推动 NLP 发展。


3. 典型 NLP 任务实战

(1) 文本分类(情感分析)

from transformers import pipeline

classifier = pipeline("text-classification", model="distilbert-base-uncased-finetuned-sst-2-english")
result = classifier("Hugging Face is awesome!")
print(result)  # [{'label': 'POSITIVE', 'score': 0.9998}]

(2) 命名实体识别(NER)

ner = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
result = ner("Apple is headquartered in Cupertino.")
print(result)  # [{'entity': 'I-ORG', 'word': 'Apple'}, ...]

(3) 文本生成(GPT-2)

generator = pipeline("text-generation", model="gpt2")
result = generator("Once upon a time,", max_length=30)
print(result[0]["generated_text"])

4. 学习资源

Hugging Face 官方课程https://huggingface.co/course
Transformers 文档https://huggingface.co/docs/transformers
NLP 经典书籍:《Speech and Language Processing》(Jurafsky & Martin)


总结

方面 Hugging Face 的贡献
模型 提供 BERT、GPT 等预训练模型
数据 托管大量 NLP 数据集
工具 简化训练、推理、部署流程
社区 推动开源 NLP 生态

Hugging Face 已成为 NLP 领域的 “GitHub”,无论是研究者还是开发者,都能快速构建 NLP 应用。🚀

下面专栏将一步一步学习huggingface框架的基础操作:

编码

新建编码器

from transformers import BertTokenizer
tokenizers = BertTokenizer.from_pretrained(
    pretrained_model_name_or_path='google-bert/bert-base-chinese',
    cache_dir=None, // 默认缓存,也可以指定目录
    force_download=False // 强制下载
)

编码处理

encode() 一次编码一个句子或者一对句子

# 基本编码
out = tokenizers.encode_plus(
    text=sents[0],
    text_pair=sents[1],
    max_length=25,
    padding='max_length',
    truncation=True,
    return_tensors=None
)
print(out)
print(tokenizers.decode(token_ids=out['input_ids']))


# 输出
{'input_ids': [101, 872, 4991, 1762, 3441, 677, 4692, 7599, 3250, 102, 4692, 7599, 3250, 4638, 782, 1762, 3517, 677, 4692, 872, 102, 0, 0, 0, 0], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]}
[CLS] 你 站 在 桥 上 看 风 景 [SEP] 看 风 景 的 人 在 楼 上 看 你 [SEP] [PAD] [PAD] [PAD] [PAD]

(1)参数text和text_pair分别为两个句子,如果只想编码一个句子,则可让text_pair传None。

(2)参数truncation=True表明当句子长度大于max_length时,截断句子。

(3)参数padding= 'max_length’表明当句子长度不足max_length时,在句子的后面补充PAD,直到max_length长度。

(4)参数add_special_tokens=True表明需要在句子中添加特殊符号。

(5)参数max_length=25定义了max_length的长度。

(6)参数return_tensors=None表明返回的数据类型为list格式,也可以赋值为tf、pt、np,分别表示TensorFlow、PyTorch、NumPy数据格式。

输出解释:

从输出可以看出,编码工具把两个句子前后拼接在一起,中间使用[SEP]符号分隔,在整个句子的头部添加符号[CLS],在整个句子的尾部添加符号[SEP],因为句子的长度不足max_length,所以补充了4个[PAD]。

进阶编码函数 encode_plus()

# 进阶编码函数
out = tokenizers.encode_plus(
    text=sents[2],
    text_pair=sents[3],
    max_length=25,
    padding='max_length',
    truncation=True,
    add_special_tokens=True,
    return_tensors=None,
    return_token_type_ids=True,
    return_attention_mask=True,
    return_special_tokens_mask=True,
    return_length=True,
)
# input_ids 编码后的词
# token_type_ids 第1个句子和特殊符号的位置是0,第2个句子的位置是1
# special_tokens_mask 特殊符号的位置是1,其他位置是0
# attention_mask PAD的位置是0,其他位置是1
# length 返回句子长度

for key, value in out.items():
    print(key, value)

print(tokenizers.decode(token_ids=out['input_ids'][0]))


# 输出 === === === === === === === ===
input_ids [101, 3209, 3299, 6163, 7652, 749, 872, 4638, 4970, 2094, 102, 872, 6163, 7652, 749, 1166, 782, 4638, 3457, 102, 0, 0, 0, 0, 0]
token_type_ids [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
special_tokens_mask [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
attention_mask [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
length 25
  • input_ids 编码后的词
  • token_type_ids 第1个句子和特殊符号的位置是0,第2个句子的位置是1
  • special_tokens_mask 特殊符号的位置是1,其他位置是0
  • attention_mask PAD的位置是0,其他位置是1
  • length 返回句子长度

批量编码函数

print("# 批量编码函数 ===============")
# 批量编码函数
batch_sents = [sents[0], sents[1], sents[2], sents[3]]
out = tokenizers.batch_encode_plus(
    batch_text_or_text_pairs=batch_sents,
    max_length=25,
    padding='max_length',
    truncation=True,
    add_special_tokens=True,
    return_tensors=None,
    return_token_type_ids=True,
    return_attention_mask=True,
    return_special_tokens_mask=True,
    return_length=True,
)
print(out)
for key, value in out.items():
    print(key, value)
print(tokenizers.decode(token_ids=out['input_ids'][0]))

## 输出 ========
input_ids [[101, 872, 4991, 1762, 3441, 677, 4692, 7599, 3250, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [101, 4692, 7599, 3250, 4638, 782, 1762, 3517, 677, 4692, 872, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [101, 3209, 3299, 6163, 7652, 749, 872, 4638, 4970, 2094, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [101, 872, 6163, 7652, 749, 1166, 782, 4638, 3457, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
token_type_ids [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
special_tokens_mask [[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
length [10, 12, 11, 10]
attention_mask [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
[CLS] 你 站 在 桥 上 看 风 景 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]

可以看到,这里的输出都是二维的list了,表明这是一个批量的编码。这个函数在后续章节中会多次用到。

字典

获取字典

添加字典元素

from transformers import BertTokenizer
tokenizers = BertTokenizer.from_pretrained(
    pretrained_model_name_or_path='google-bert/bert-base-chinese',
    cache_dir=None,
    force_download=False
)

# 查看字典
vocab = tokenizers.get_vocab()
print(vocab)
print(type(vocab))
print(len(vocab))
print('沐浴' in vocab)


# 添加字典元素
print("添加字典元素========")
tokenizers.add_tokens(new_tokens=["明月","装饰","窗子"])
tokenizers.add_special_tokens({'eos_token': '[EOS]'})
out = tokenizers.encode(text='明月装饰了你的窗子[EOS]'
, text_pair=None ,add_special_tokens=True,truncation=True,padding="max_length",max_length=10,return_tensors=None)
print(out)
print(tokenizers.decode(out))



# 输出 ============
添加字典元素========
[101, 21128, 21129, 749, 872, 4638, 21130, 21131, 102, 0]
[CLS] 明月 装饰 了 你 的 窗子 [EOS] [SEP] [PAD]

可以看到,“明月”已经被识别为一个词,而不是两个词,新的特殊符号[EOS]也被正确识别。

数据集工具

在以往的自然语言处理任务中会花费大量的时间在数据处理上,针对不同的数据集往往需要不同的处理过程,各个数据集的格式差异大,处理起来复杂又容易出错。针对以上问题,HuggingFace提供了统一的数据集处理工具,让开发者在处理各种不同的数据集时可以通过统一的API处理,大大降低了数据处理的工作量。

远程加载并且保存

# 加载数据集
from datasets import load_dataset,load_from_disk
dataset = load_dataset(path="lansinuote/ChnSentiCorp",trust_remote_code=True)
print(dataset)

# 加载数据集并且保存
load_dataset(path="glue",name="sst2",split='train')
dataset.save_to_disk(dataset_dict_path='./data/ChnSentiCorp')

磁盘加载


# 从磁盘加载数据集并且查看
dataset = load_from_disk(dataset_path="./data/ChnSentiCorp")
print(dataset)
dataset = dataset["train"]
print(dataset)
for i in [12, 17, 20, 26, 56]: print(dataset[i])
    
## 输出 =================
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 9600
    })
    validation: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
})
Dataset({
    features: ['text', 'label'],
    num_rows: 9600
})
{'text': '轻便,方便携带,性能也不错,能满足平时的工作需要,对出差人员来说非常不错', 'label': 1}
{'text': '很好的地理位置,一蹋糊涂的服务,萧条的酒店。', 'label': 0}
{'text': '非常不错,服务很好,位于市中心区,交通方便,不过价格也高!', 'label': 1}
{'text': '跟住招待所没什么太大区别。 绝对不会再住第2次的酒店!', 'label': 0}
{'text': '价格太高,性价比不够好。我觉得今后还是去其他酒店比较好。', 'label': 0}


数据集的分析

数据排序

# 数据排序
print(dataset['label'][:10])
# 让数据按照label排序
sorted_dataset = dataset.sort("label")
print(sorted_dataset['label'][:10])
print(sorted_dataset['label'][-10:])
# “和sort()函数相对应,可以使用shuffle()函数再次打乱数据,”
shuffled_dataset = dataset.shuffle()
print(shuffled_dataset["label"][:10])

数据抽样

# 数据集的抽样 抽样后形成新的数据子集
print(shuffled_dataset.select([1,2,3,4,99]))
for i in range(5): print(shuffled_dataset[i])

{‘text’: ‘指纹机。价格略高。散热有待加强。播放720P高清电影还是有点卡。略重。’, ‘label’: 0}

{‘text’: ‘轻便、小巧、配置不错! 送货速度快,当天下午四点多下单,次日上午十点到货。’, ‘label’: 1}

{‘text’: ‘我借给一个朋友看的时候,问她看的时候会不会想哭,她说会。我也是一样的感受。为什么又说不上。只是感觉程然描绘得很细腻,很真实。禅学,我是看了这本书才有所了解,因为当时心很乱,需要这类书的安慰。看来以后,在当当网等了很久才终于没缺货,终于买到了。捧在手里,感动和悲伤同在。但是这本书真的很适合我们去读。在这纷乱的世界里,能有这本书作伴,谢谢程然了!’, ‘label’: 1}

{‘text’: ‘穿越的书我买了好几套了 在当当网上看见《蔓蔓清萝》的评论还多好了 就买来看看 看了文章后真的让人有些失望 写得不是那么生动 感觉太简单化了 反而我比较喜欢《步步惊心》这本书 也是穿越的 o(∩_∩)o…’, ‘label’: 0}

升级版数据集拆分

# 训练集测试集拆分
train_dataset,test_dataset = dataset.train_test_split(test_size=0.2).values()
print(train_dataset)
print(test_dataset)

# 输出 === === === ===
Dataset({
    features: ['text', 'label'],
    num_rows: 7680
})
Dataset({
    features: ['text', 'label'],
    num_rows: 1920
})

数据分桶可以使用shared ()函数把数据均匀地分为n部分,代码如下:

dataset.shard(num_shards=4, index=0)

(1)参数num_shards表明要把数据均匀地分为几部分,例子中分为4部分。

(2)参数index表明要取出第几份数据,例子中为取出第0份。

运行结果如下:Dataset({features: ['text', 'label'],num_rows: 2400})

过滤数据

字段 操作


# 字段重命名
dataset_Sentence_label = dataset.rename_column("text","sentence")
print(dataset_Sentence_label)


# 字段删除
dataset.remove_columns("label")
print(dataset)

映射与数据格式


# 映射函数map
def add_prefix(example):
    example["sentence"] = "prefix:" + example["sentence"]
    return example

maped_dataset = dataset_Sentence_label.map(add_prefix)
print(maped_dataset['sentence'][:10])
print(maped_dataset)

# 设置数据格式
maped_dataset = maped_dataset.set_format(type="pandas",columns=['label'],output_all_columns=True)
print(maped_dataset)

数据导出

导出为 csv 文件

# 数据导出
dataset.to_csv("./data_csv/ChnSentiCorp.csv")
csv_dataset = load_dataset("csv",data_files="./data_csv/ChnSentiCorp.csv",split='train')
print(csv_dataset)
print(csv_dataset[:10])

导出为 json

dataset=load_dataset(path='seamew/ChnSentiCorp', split='train')
dataset.to_json(path_or_buf='./data/ChnSentiCorp.json')
#加载JSON格式数据
json_dataset=load_dataset(path='json',data_files='./data/ChnSentiCorp.json',split='train')
print(json_dataset[20])

网站公告

今日签到

点亮在社区的每一天
去签到