Hugging Face 介绍
Hugging Face 是一家专注于人工智能和自然语言处理(NLP)的公司,成立于 2016 年。它提供了一个广受欢迎的开源平台,允许开发者和研究人员访问和使用先进的机器学习模型。
- Transformers 库:Hugging Face 核心库,包含了大量预训练模型(BERT、GPT、T5 等)。
- Datasets 库:提供了一个数据集库,方便用户查找、加载和使用各种常见的机器学习数据集。
- Model Hub:一个在线平台,用户可以分享和下载模型,提供了多种预训练模型.
- Hugging Face Hub:Hugging Face 云平台,允许协作和托管模型,可在云端进行训练和推理。
Hugging Face 相关库的安装
- 安装前需要准备基础环境:Anaconda,CUDA,pytorch
- Hugging Face 提供了
transformers
库,用于加载和使用模型。 pip install transformers datasets tokenizers
Hugging Face 模型访问
Hugging Face 提供了一个庞大的模型库;Models - Hugging Face 在模型页中使用左侧的过滤器按任务、框架、语言等条件筛选你需要的模型。
py
import requests
from transformers import pipeline
from dotenv import load_dotenv
import os
load_dotenv()
API_URL = f"{os.getenv('HF_BASE_URL')}/models/uer/gpt2-chinese-cluecorpussmall"
API_KEY = f"{os.getenv('HF_KEY')}"
# 在线访问模型
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.post(API_URL, headers=headers, json={
"inputs": "你好,Hugging face"})
print(response.json())
# 离线访问模型,第一次需要下载模型
generator = pipeline("text-generation", model="gpt2")
# truncation:输入文本超过 模型限制的最大输入 时会被截断
# num_return_sequences:希望从模型中获得多少个独立生成的文本序列
output = generator("I am a text generation model.", max_length=50, truncation=True, num_return_sequences=2)
print(output)
GPT 和 BERT
BERT 和 GPT 是基于 Transformer 结构的两个不同方向的模型 ,而 Transformer 由编码器
和解码器
两个部分构成;
- BERT 是一个 双向编码器 ,主要用于 理解任务,例如文本分类、命名实体识别、问答等。BERT 的目标是捕捉上下文信息,理解句子的含义。
- GPT 是一个 自回归解码器 ,主要用于 生成任务,例如文本生成、对话生成、创作等。GPT 的目标是根据给定的上下文生成流畅的文本
Transformer 模型
输入处理:词向量 与 位置向量
- embeddings:每个词 通过 embedding 转为词向量
- positional embedding:每个输入的词向量都加上了一个位置向量(确定每个单词的位置、句子中不同单词之间的距离)
- embedding with time signal:将这些表示位置的向量添加到词向量中,得到的新向量,可以为模型提供更多有意义的信息,比如词的位置,词之间的距离等。
Encoder 、Decoder
- encoders:编码部分由多层编码器(Encoder)组成
- decoders:解码部分由多层的解码器(Decoder)组成
每层编码器网络结构是一样的,每层解码器网络结构也是一样的。不同层编码器和解码器网络结构不共享参数。
单层的编码器和解码器 注意组成
- Self-Attention Layer:处理一个词时,关注输入序列中其他位置的词。
- Feed Forward Neural Network(前馈神经网络,缩写为 FFNN):全连接层,每个自注意力模块后都跟随一个全连接层,通常包括两个线性变换和一个激活函数 (例如 ReLU)激活函数直接会影响模型的输出。
- Encoder-Decoder Attention:也称 Cross-Attention 允许解码器在生成输出时,能够动态地关注编码器生成的隐藏状态。
GPT 生成模型
- tokenizer 配置介绍
json
{
"do_lower_case": false, # 是否将输入转为小写
"unk_token": "[UNK]", # 未知标记:模型未能识别字符
"sep_token": "[SEP]", # 分割标记:分割两个句子或者文本片段
"pad_token": "[PAD]", # 填充标记:填充输入序列,使得所有序列的长度一致
"cls_token": "[CLS]", # 分类标记:输入序列的起始标记
"mask_token": "[MASK]", # 遮蔽标记:在训练时,部分单词会被替换为[MASK],模型需预测被掩蔽的词汇
"tokenize_chinese_chars": true, # 中文文本是否需要分割,为 true 表示会将中文字符单独作为一个 Token 来处理
"strip_accents": null, # 是否移除文本中的重音符号(例如,é 变为 e)
"model_max_length": 1024 # 限制模型输入的最大长度
}
- 示例介绍
python
from transformers import BertTokenizer, BertForSequenceClassification, pipeline
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
from huggingface_hub.constants import HF_HUB_CACHE
# 下载模型和分词器下载到本地
model_name = "uer/gpt2-chinese-cluecorpussmall"
# 模型缓存地址,如果不指定,默认为 ~/.cache/huggingface/hub 目录下
cache_dir = HF_HUB_CACHE
# 下载并加载模型和分词器(一般下载模型的时候连同分词器也会一并下载) 模型与分词器一一对应
model = AutoModelForCausalLM.from_pretrained(model_name, cache_dir=cache_dir)
tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir=cache_dir)
# "text-generation" 文本生成 pipeline
generator = pipeline("text-generation", model=model,tokenizer=tokenizer, device="cuda")
output = generator(
# 输入种子文本(prompt)模型根据这个初始文本,生成后续的文本
"这是很久之前的事情了,",
# 生成文本的最大长度(最多包含 50 个 tokens)
max_length=50,
# 从模型中获得多少个独立生成的文本序列
num_return_sequences=1,
# 输入文本超过模型所能支持的最大输入长度(tokenizer_config中定义的model_max_length)时会被截断
truncation=True,
temperature=0.7,top_k=50,top_p=0.9,
# 生成的文本中是否清理分词时引入的空格。值为 True,生成的文本会清除多余的空格;
clean_up_tokenization_spaces=True
)
为何模型和分词工具是一一对应的?
- 分词工具依赖词典或者规则把文本切分成词语或小单元(比如字或子词),然后转成数字。每个模型训练时,都是用特定的分词工具和方式来学习的。
- 模型会根据它训练时使用的分词方式来理解文本。如果你换了分词工具,文本的切分和数字表示就会变,模型可能就无法正确理解这些新的数据
python
model = AutoModelForCausalLM.from_pretrained(model_name, cache_dir=cache_dir)
tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir=cache_dir)
top_k、temperature、top_p 理解
- 假设模型所能识别的字符大小为 21128,对于文本生成模型来说,取到每个字符的概率为 1/21128;
- top_k=50:表示模型生成每个词只考虑概率最高的 50 个候选词;如果希望每次生成的结果都一样,可以将top_k 设置为 1
- temperature=0.7:表示从 top_k 指定的候选词中选择候选词;越小->越保守(选择概率较高的词汇);越大->越多样(选择越多样的词汇)。
- top_p=0.9:核采样,通过控制概率的累积和来筛选词汇
- 候选词 A:0.5 B:0.4 C:0.2 D:0.1 而 A 与 B 的累积概率已经到达0.9(0.5+0.4)所以,候选词只能在A与B之前选。
Bert 模型
py
from transformers import BertTokenizer, BertForSequenceClassification, pipeline
from transformers import AutoModel, AutoTokenizer, pipeline
from huggingface_hub.constants import HF_HUB_CACHE
# 模型缓存地址,如果不指定,默认为 ~/.cache/huggingface/hub 目录下
cache_dir = HF_HUB_CACHE
# 加载模型和分词器
model = BertForSequenceClassification.from_pretrained(
"bert-base-chinese", cache_dir=cache_dir)
tokenizer = BertTokenizer.from_pretrained(
"bert-base-chinese", cache_dir=cache_dir)
# 创建分类pipleine
classifier = pipeline("text-classification", model=model,
tokenizer=tokenizer, device="cuda")
# 进行文本分类
result = classifier("你好,我是一款语言模型")
print(result)
print(model)
打印结果如下,可以看到,并没有给出我们想要的结果,原因就是因为 我们并没有实现它的分类层
py
[{'label': 'LABEL_0', 'score': 0.5539349317550659}]
BertForSequenceClassification(
(bert): BertModel(
# embeddings:向量化,将文本 转为 模型可输入的向量
(embeddings): BertEmbeddings(
)
# encoder:特征提取,根据 词向量 提取、理解 特征
(encoder): BertEncoder(
)
(pooler): BertPooler(
)
)
(dropout): Dropout
# classifier:模型的分类层,out_features=2 表示一个2分类模型
(classifier): Linear(in_features=768, out_features=2, bias=True)
)
Tokenizer 模型是如何处理字符数据?
-
分词器 根据预定的词表
vocabulary
将文本转化为 tokens ,并通常将其转化为 数字 ID 。 例如:"你好,世界!"=>["你好", ",", "世界", "!"]
-
Tokenizer 会将每个 token 映射到一个 数字 ID(每个 token 在词表中的索引行)。
-
向量化 (将 tokens 转化为向量)是由模型的 嵌入层(Embedding layer) 来完成的,它根据 tokens 的 ID 获取其对应的向量表示。这些向量再传递给模型的其他层进行进一步处理。
AI模型的本质是基于矩阵运算,输入的数据通常是高维矩阵(张量),本质上也都是数字。
AI 模型处理的核心任务可分为图像视觉、音频处理和自然语言处理(NLP)。
- 图像和音频较为简单,因为它们本身就是数字(如图像像素值和音频采样值),可以直接输入模型。
- NLP 最难的地方就是如何将一句话变成一串数字?
加载字典和分词器
ini
模型所能识别的字符都是有限的
vocab_size=21128
vocab.txt(字典)中字符的数量;
模型不能识别的字符就用 special_tokens 中的 unk_token 指定的特殊标记来表示
vocab.txt
以 ## 开头的字符:表示带有上下文编码的字符;
单个字符:是大多数的预训练模型采用中文分词,优点:扩展性强;中文中有很多 多音字
因此 严格的来讲,大多数预训练模型所能识别的字符为 vocab_size/2
token 字符编码的逻辑就是将输入的文本 转为 vocab.txt 中每个字符所对应的索引下标
py
from transformers import BertTokenizer
from huggingface_hub.constants import HF_HUB_CACHE
cache_dir = HF_HUB_CACHE
# 加载字典和分词器
token = BertTokenizer.from_pretrained("bert-base-chinese", cache_dir=cache_dir)
#准备要编码的文本数据
sents = ["吃饭、睡觉、打豆豆,","打卡、发呆、摸虾鱼。"]
#批量编码句子
out = token.batch_encode_plus(
batch_text_or_text_pairs=[sents[0],sents[1]],
# 编码时添加 special_tokens 为了给模型看
add_special_tokens=True,
# 当句子长度大于max_length(上限是model_max_length)时,截断
truncation=True,
# 编码之后的长度
max_length=6,
# 补0 对齐文本 到 max_length;不够6就用0补齐,超出6就截断
padding="max_length",
# 编码后返回的数值类型 可取值为tf,pt,np,默认为list
return_tensors=None,
# 编码之后需要给到 transformer 模型的输入
return_attention_mask=True,
return_token_type_ids=True,
return_special_tokens_mask=True,
# 返回序列长度
return_length=True
)
#解码文本数据
print(token.decode(out["input_ids"][0]),token.decode(out["input_ids"][1]))
dataset 数据集
py
from datasets import load_dataset,load_from_disk
# 加载数据集,未指定cache_dir 默认缓存到 ~/.cache/huggingface/datasets
dataset = load_dataset(path="lansinuote/ChnSentiCorp")
print(dataset)
# 将数据保存到指定的磁盘中 注意:这里使用的都是相对路径
dataset.save_to_disk(".cache/data/lansinuote/ChnSentiCorp")
# 加载保存后的数据集
dataset = load_from_disk(".cache/data/lansinuote/ChnSentiCorp")
print(dataset)
# 打印数据集
train_data = dataset["test"]
for data in train_data:
print(data)
# 转为csv格式(实际项目大多使用的csv数据集)
dataset['train'].to_csv(".cache/data/lansinuote/ChnSentiCorp/csv/train.csv")
# 直接从CSV文件加载数据集
dataset = load_dataset('csv', data_files='.cache/data/lansinuote/ChnSentiCorp/csv/train.csv')
print(dataset)