本教程适用与第一次接触huggingface与相应框架和对nlp任务感兴趣的朋友,该栏目目前更新总结如下:
- Tokenizer:
支持单句/双句编码,自动处理特殊符号和填充。
批量编码提升效率,适合训练数据预处理。 - Datasets:
统一 API 处理多种格式数据(远程/本地)。
内置排序、分桶、拆分等功能,简化数据准备流程。 - 应用场景:
文本分类、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(如 AutoModel
、AutoTokenizer
)。
• 社区驱动:研究者共享模型,推动 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])