本教程適用與第一次接觸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 pipelineclassifier = 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_datasetdataset = load_dataset("imdb")
print(dataset["train"][0]) # {'text': 'Great movie!', 'label': 1}
(3) Model Hub
? 托管 50,000+ 預訓練模型,涵蓋 NLP、CV、語音等領域。
? 支持社區共享:用戶可以上傳自己的模型供他人使用。
示例(下載 BERT 模型):
from transformers import BertModelmodel = 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 pipelineclassifier = 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 examplemaped_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])