Hugging Face系统学习
认知:将来我们做的大部分是AI应用开发,也就是基于别人训练好的模型做应用,而非AI模型(炼丹师)开发。为什么叫AI模型开发叫炼丹师,因为模型训练时,需要多少“层”,各种超参数只能靠大量的尝试,充满了不确定性。
大模型为什么叫大模型,其中一个大的体现就是参数非常大,一般大模型的参数都是10亿起步,比如GPT3 的参数规模是175B,1B(1 Bilion)等于10亿(即(10^9)),所以GPT3 有1750 亿个参数,属于千亿级的参数,GPT4 参数更夸张,是1.8万亿参数,训练一次就得 6300万美元!
大模型:参数超过10亿也就是1B的模型。背景:起初2018年谷歌发表论文《Attention Is All You Need》,彻底将RNN淘汰,进入了transfromer时代()。但是openai却是最早拿着论文做实践的,并且openai发现,随着模型“参数量”的增加,模型的智能程度,越来越高,所以就在堆模型大小上死磕,最终将模型参数提升到10亿以上。于是就有了现在的ChatGPT和很多“大”模型。
我们现在做微调一般选择7B参数的模型,20GB的显存就可以进行微调。
目前主流的两个开源大模型平台分别是
HuggingFace:类似于github,模型比较全,但是需要科xue上网;
ModelScope:阿里开源的大模型平台,模型不是很全,速度比较快;
简介
认识 Hugging Face
注册和安装
注册 Hugging Face 账户
- 注册成功后,你可以访问模型库、数据集和文档,也可以管理你的个人模型和项目。
安装 Hugging Face 库
Hugging Face 提供了 transformers 库,用于加载和使用模型。你可以使用以下命令来安装它:(电脑须安装基础环境:Anaconda,CUDA,cuDNN,pytorch)
如果你还需要安装其他依赖库,如 datasets 和 tokenizers ,可以使用以下命令:
pip install transformers
pip install transformers datasets tokenizers
temperature、top_k、top_p的区别含义与作用机制:
temperature(温度):控制模型输出的随机性。从概率分布角度理解,在计算下一个词的概率分布时,temperature 会对原始概率进行变换。数值越低,生成结果越确定,模型更倾向于选择概率最高的词 ,输出较为保守、常规;数值越高,概率分布越平滑,低概率词被选中的可能性增加,输出结果的随机性增强,模型创造性更高。
top_k:采样时,模型只考虑概率最高的 k 个词作为下一个词的候选,其余词的概率设为 0。这是一种固定数量筛选的方式,限制了候选词范围。
top_p(核采样):设定一个概率阈值 p,从概率最高的词开始依次累加概率,当累加到的概率总和达到或超过 p 时,就将这些词作为候选词集合,而不考虑候选词的固定数量。
应用场景偏好:
temperature:处理需要准确答案的场景,如知识问答、信息提取,宜用低温;需要创意的场景,如文本创作、角色扮演,适合高温。
top_k:适用于对生成结果有一定确定性要求,且不太需要极高多样性的场景。但因存在局限性,实际应用没有 top_p 广泛。
top_p:在各种文本生成场景中应用广泛,能较好平衡多样性和合理性,尤其在希望生成自然流畅且多样文本的场景中表现出色 。
加载Bert分类模型
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import pipeline
# 加载模型和分词器
# model_name = "ckiplab/bert-base-chinese-pos"
model_name = r"D:\learn\learncode\huggingface\demo_4\trasnFormers_test\model\bert-base-chinese\models--bert-base-chinese\snapshots\c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
model = BertForSequenceClassification.from_pretrained(model_name)
tokenizer = BertTokenizer.from_pretrained(model_name)
# 创建分类 pipeline
classifier = pipeline("text-classification", model=model, tokenizer=tokenizer,device="cpu")
# 进行分类
result = classifier("你好,我是一款语言模型")
print(result)
print(model)
点击查看打印结果
[{'label': 'LABEL_1', 'score': 0.5526784062385559}]
BertForSequenceClassification(
(bert): BertModel(
(embeddings): BertEmbeddings(
(word_embeddings): Embedding(21128, 768, padding_idx=0)
(position_embeddings): Embedding(512, 768)
(token_type_embeddings): Embedding(2, 768)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(encoder): BertEncoder(
(layer): ModuleList(
(0-11): 12 x BertLayer(
(attention): BertAttention(
(self): BertSdpaSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
(intermediate_act_fn): GELUActivation()
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
)
(pooler): BertPooler(
(dense): Linear(in_features=768, out_features=768, bias=True)
(activation): Tanh()
)
)
(dropout): Dropout(p=0.1, inplace=False)
(classifier): Linear(in_features=768, out_features=2, bias=True)
)
输出结果解析
[{'label': 'LABEL_1', 'score': 0.5526784062385559}]
模型返回的是基于原来的分类标签的一个标签及对应的概率,我们是完全不能拿来直接用的。我们需要进行下游任务的开发。后面讲到了就理解了。
百度AI开放平台情感分类案例


数据集下载&本地加载
直接使用huggingface自带的datasets工具加载数据集
方式1:直接加载成熟的数据集使用
from datasets import load_dataset, load_from_disk
data_dir = "D:\data\datasets"
dataset = load_dataset("lansinuote/ChnSentiCorp", cache_dir=data_dir)
print(dataset)
# 必须要执行本地保存, 直接缓存的数据集不满足本地磁盘加载的格式
dataset.save_to_disk(r"D:\data\datasets\ChnSentiCorp")
print("save completion")
# 目录是包含dataset_dict.json的目录
dataset = load_from_disk(r"D:\data\datasets\ChnSentiCorp")
train = dataset["train"]
for i in range(10):
print(train[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
})
})
Saving the dataset (1/1 shards): 100%|██████████| 9600/9600 [00:00<00:00, 1319611.92 examples/s]
Saving the dataset (1/1 shards): 100%|██████████| 1200/1200 [00:00<00:00, 602413.50 examples/s]
Saving the dataset (1/1 shards): 100%|██████████| 1200/1200 [00:00<00:00, 405573.31 examples/s]
save completion
{'text': '选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般', 'label': 1}
{'text': '15.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错', 'label': 1}
{'text': '房间太小。其他的都一般。。。。。。。。。', 'label': 0}
{'text': '1.接电源没有几分钟,电源适配器热的不行. 2.摄像头用不起来. 3.机盖的钢琴漆,手不能摸,一摸一个印. 4.硬盘分区不好办.', 'label': 0}
{'text': '今天才知道这书还有第6卷,真有点郁闷:为什么同一套书有两种版本呢?当当网是不是该跟出版社商量商量,单独出个第6卷,让我们的孩子不会有所遗憾。', 'label': 1}
{'text': '机器背面似乎被撕了张什么标签,残胶还在。但是又看不出是什么标签不见了,该有的都在,怪', 'label': 0}
{'text': '呵呵,虽然表皮看上去不错很精致,但是我还是能看得出来是盗的。但是里面的内容真的不错,我妈爱看,我自己也学着找一些穴位。', 'label': 0}
{'text': '这本书实在是太烂了,以前听浙大的老师说这本书怎么怎么不对,哪些地方都是误导的还不相信,终于买了一本看一下,发现真是~~~无语,这种书都写得出来', 'label': 0}
{'text': '地理位置佳,在市中心。酒店服务好、早餐品种丰富。我住的商务数码房电脑宽带速度满意,房间还算干净,离湖南路小吃街近。', 'label': 1}
{'text': '5.1期间在这住的,位置还可以,在市委市政府附近,要去商业区和步行街得打车,屋里有蚊子,虽然空间挺大,晚上熄灯后把窗帘拉上简直是伸手不见五指,很适合睡觉,但是会被该死的蚊子吵醒!打死了两只,第二天早上还是发现又没打死的,卫生间挺大,但是设备很老旧。', 'label': 1}
进程已结束,退出代码为 0
方式2:自己自定义数据集
from datasets import Dataset
# 制作 Dataset
dataset = Dataset.from_dict({
'text': ['位置尚可,但距离海边的位置比预期的要差的多', '5月8日付款成功,当当网显示5月10日发货,可是至今还没看到货物,也没收到任何通知,简不知怎么说好!!!', '整体来说,本书还是不错的。至少在书中描述了许多现实中存在的司法系统方面的问题,这是值得每个法律工作者去思考的。尤其是让那涉世深的想加入到律师队伍中的年青人,看到了社会特别是中国司法界真实的一面。缺点是:书中引用了大量的法律条文和司法解释,对于已经是律师或有一定工作经验的法律工作者来说有点多余,而且所占的篇幅不少,有凑字数的嫌疑。整体来说还是不错的。不要对一本书提太高的要求。'],
'label': [0, 1, 1] # 0 表示负向评价,1 表示正向评价
})
# 查看数据集信息
print(dataset)
for i in range(2):
print(dataset[i])
Dataset({
features: ['text', 'label'],
num_rows: 3
})
{'text': '位置尚可,但距离海边的位置比预期的要差的多', 'label': 0}
{'text': '5月8日付款成功,当当网显示5月10日发货,可是至今还没看到货物,也没收到任何通知,简不知怎么说好!!!', 'label': 1}
数据集字段
在制作 Dataset 时,需定义数据集的字段。在本案例中,定义了两个字段: text (文本)和label (情感标签)。每个字段都需要与模型的输入和输出匹配。
数据集信息
制作 Dataset 后,可以通过 dataset.info 等方法查看其大小、字段名称等信息,以确保数据集的正确性和完整性。
使用PyTorch 原生的 Dataset 加载数据(重点,常用)
from torch.utils.data import Dataset
from datasets import load_from_disk
class Mydataset(Dataset):
#初始化数据
def __init__(self,split):
#从磁盘加载数据
self.dataset = load_from_disk(r"D:\learn\learncode\huggingface\demo_4\data\ChnSentiCorp")
if split =="train":
self.dataset = self.dataset["train"]
elif split == "validation":
self.dataset = self.dataset["validation"]
elif split=="test":
self.dataset = self.dataset["test"]
else:
print("数据集名称错误!")
#获取数据集大小
def __len__(self):
return len(self.dataset)
#对数据做定制化处理
def __getitem__(self, item):
text = self.dataset[item]["text"]
label = self.dataset[item]["label"]
return text,label
if __name__ == '__main__':
dataset = Mydataset("validation")
for data in dataset:
print(data)
('位置尚可,但距离海边的位置比预期的要差的多,只能远远看大海,没有停车场', 0)
('整体来说,本书还是不错的。至少在书中描述了许多现实中存在的司法系统方面的问题,这是值得每个法律工作者去思考的。尤其是让那些涉世不深的想加入到律师队伍中的年青人,看到了社会特别是中国司法界真实的一面。缺点是:书中引用了大量的法律条文和司法解释,对于已经是律师或有一定工作经验的法律工作者来说有点多余,而且所占的篇幅不少,有凑字数的嫌疑。整体来说还是不错的。不要对一本书提太高的要求。', 1)
('5月8日付款成功,当当网显示5月10日发货,可是至今还没看到货物,也没收到任何通知,简不知怎么说好!!!', 0)
说明:huggingface的datasets和Pytorch的
Dataset的区别
词汇表操作
批量文本编码
from transformers import BertTokenizer
#加载字典和分词工具
# 加载分词器
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
print(tokenizer)
# 准备了 4 条中文文本
sents = ["酒店太旧了, 大堂感觉象三星级的, 房间也就是的好点的三星级的条件, 在青岛这样的酒店是绝对算不上四星标准, 早餐走了两圈也没有找到可以吃的, 太差了",
"已经贴完了,又给小区的妈妈买了一套。最值得推荐",
"屏幕大,本本薄。自带数字小键盘,比较少见。声音也还过得去。usb接口多,有四个。独显看高清很好。运行速度也还可以,性价比高!",
"酒店环境很好 就是有一点点偏 交通不是很便利 去哪都需要达车 关键是不好打 酒店应该想办法解决一下"]
# #批量编码句子,batch_encode_plus是分词器的批量处理方法,用于将多个文本转换为模型输入格式,主要参数和作用如下:
out = tokenizer.batch_encode_plus(
batch_text_or_text_pairs=[sents[0],sents[1]],#指定要处理的文本,这里选择了前两条评价
add_special_tokens=True,#自动添加 BERT 要求的特殊符号([CLS]开头,[SEP]结尾)
#当句子长度大于max_length时,截断
truncation=True,
# 一律补零到max_length长度
padding="max_length",###将所有文本统一填充到max_length长度(不足的用[PAD]填充)
max_length=30,#指定最大序列长度为 30 个 token
# 返回值类型,可取值tf,pt,np,默认为返回list
return_tensors=None,
# 各种return_*参数:控制输出的信息,包括:
#返回attention_mask,标记哪些位置是有效 token(非[PAD])
return_attention_mask=True,
#返回token_type_ids,区分句子对中的不同句子(这里单句,所以主要是 0)
return_token_type_ids=True,
#返回special_tokens_mask 特殊符号标识,标记哪些是特殊符号([CLS]/[SEP]/[PAD])
return_special_tokens_mask=True,
#返回offset_mapping 标识每个词的起止位置,这个参数只能BertTokenizerFast使用
return_offsets_mapping=True,
#返回length 标识长度,每个文本的实际长度(不含填充)
return_length=True,
)
print(out)
for k,v in out.items():
print(k,":",v)
print(tokenizer.decode(out["input_ids"][0]),tokenizer.decode(out["input_ids"][1]))
#input_ids 就是编码后的词
#token_type_ids 第一个句子和特殊符号的位置是0,第二个句子的位置是1
#special_tokens_mask 特殊符号的位置是1,其他位置是0
#attention_mask pad的位置是0,其他位置是1
#length 返回句子长度
查看打印结果
BertTokenizer(name_or_path='bert-base-chinese', vocab_size=21128, model_max_length=512, is_fast=False, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True, added_tokens_decoder={
0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}
)
{'input_ids': [[101, 6983, 2421, 1922, 3191, 749, 8024, 1920, 1828, 2697, 6230, 6496, 676, 3215, 5277, 4638, 8024, 2791, 7313, 738, 2218, 3221, 4638, 1962, 4157, 4638, 676, 3215, 5277, 102], [101, 2347, 5307, 6585, 2130, 749, 8024, 1348, 5314, 2207, 1277, 4638, 1968, 1968, 743, 749, 671, 1947, 511, 3297, 966, 2533, 2972, 5773, 102, 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]], 'special_tokens_mask': [[1, 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, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]], 'length': [30, 25], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 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, 0]]}
input_ids : [[101, 6983, 2421, 1922, 3191, 749, 8024, 1920, 1828, 2697, 6230, 6496, 676, 3215, 5277, 4638, 8024, 2791, 7313, 738, 2218, 3221, 4638, 1962, 4157, 4638, 676, 3215, 5277, 102], [101, 2347, 5307, 6585, 2130, 749, 8024, 1348, 5314, 2207, 1277, 4638, 1968, 1968, 743, 749, 671, 1947, 511, 3297, 966, 2533, 2972, 5773, 102, 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]]
special_tokens_mask : [[1, 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, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]]
length : [30, 25]
attention_mask : [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 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, 0]]
[CLS] 酒 店 太 旧 了 , 大 堂 感 觉 象 三 星 级 的 , 房 间 也 就 是 的 好 点 的 三 星 级 [SEP] [CLS] 已 经 贴 完 了 , 又 给 小 区 的 妈 妈 买 了 一 套 。 最 值 得 推 荐 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD]
输出内容解读
使用词汇表转换数据集
在微调 BERT 模型之前,需要将模型的词汇表(vocab)与数据集中的文本匹配。这一步骤确保输入的文本能够被正确转换为模型的输入格式。
词汇表(vocab)
BERT 模型使用词汇表(vocab)将文本转换为模型可以理解的输入格式。词汇表包含所有模型已知的单词及其对应的索引。确保数据集中的所有文本都能找到对应的词汇索引是至关重要的。
文本转换
使用 tokenizer 将文本分割成词汇表中的单词,并转换为相应的索引。此步骤需要确保文本长度、特殊字符处理等都与 BERT 模型的预训练设置相一致。
转换huggingface自带的datasets数据集
# 1. 导入必要的库
from datasets import load_from_disk
from transformers import BertTokenizer
# 2. 加载数据集(DatasetDict → 提取train子集)
dataset = load_from_disk(r"D:\learn\learncode\huggingface\demo_4\data\ChnSentiCorp")
dataset = dataset["train"] # 此时为单子集 Dataset
# 3. 加载分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
# 4. 修正:添加截断、填充、统一最大长度
dataset = dataset.map(
lambda x: tokenizer(
x['text'], # 待分词的文本字段
return_tensors="pt", # 返回PyTorch张量
truncation=True, # 关键:截断超长文本(超过max_length的部分删掉)
padding="max_length", # 关键:填充短文本到max_length(用[PAD]填充)
max_length=512 # 关键:统一长度为512(BERT的最大支持长度)
),
batched=True # 批量处理(此时所有文本长度统一,可正常生成张量)
)
# 5. 查看处理后的数据集(此时应无报错,且input_ids长度均为512)
print(dataset)
print(dataset[0])
代码的核心作用详解
转换PyTorch Dataset 数据集
# 1. 导入必要的库
from torch.utils.data import Dataset
from datasets import load_from_disk
from transformers import BertTokenizer
# 2. 改造自定义 Mydataset:在 __getitem__ 中加入分词逻辑
class Mydataset(Dataset):
def __init__(self, split, tokenizer):
# ① 加载数据集(Hugging Face 格式,包含 text 和 label)
self.dataset = load_from_disk(r"D:\data\datasets\ChnSentiCorp")[split] # 替换为你的数据集路径
# ② 传入分词器(提前初始化,避免在 __getitem__ 中重复创建,节省资源)
self.tokenizer = tokenizer
# ③ 可提前定义分词参数(避免硬编码)
self.max_length = 128 # 根据任务设定合适的最大序列长度
def __len__(self):
# 返回数据集总长度(DataLoader 需要)
return len(self.dataset)
def __getitem__(self, item):
# ① 读取单条原始数据(text + label)
raw_data = self.dataset[item]
text = raw_data["text"] # 原始文本
label = raw_data["label"] # 原始标签
# ② 核心:对当前文本做分词(替代 Hugging Face Dataset 的 map 方法)
tokenized_result = self.tokenizer(
text=text,
truncation=True, # 超过 max_length 则截断
padding="max_length", # 不足 max_length 则用 [PAD] 填充
max_length=self.max_length, # 统一序列长度
return_tensors="pt" # 返回 PyTorch 张量(适配模型输入)
)
# ③ 处理张量维度(分词器返回的是 [1, max_length],需要去掉多余的 batch 维度 → [max_length])
input_ids = tokenized_result["input_ids"].squeeze(0) # 从 (1,128) 变成 (128)
attention_mask = tokenized_result["attention_mask"].squeeze(0) # 同理
token_type_ids = tokenized_result["token_type_ids"].squeeze(0) # 同理(单句场景下可省略)
# ④ 返回模型需要的所有数据(input_ids + attention_mask + label)
return {
"input_ids": input_ids,
"attention_mask": attention_mask,
"label": label
}
# 3. 使用改造后的 Mydataset
if __name__ == "__main__":
# ① 初始化分词器(只创建一次,传入 Mydataset)
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
# ② 加载训练集(split 可选 "train"/"test"/"validation")
train_dataset = Mydataset(split="train", tokenizer=tokenizer)
# ③ 用 DataLoader 批量加载数据(PyTorch 训练标准流程)
from torch.utils.data import DataLoader
train_dataloader = DataLoader(
train_dataset,
batch_size=32, # 每次加载 32 条数据
shuffle=True # 训练前洗牌,避免数据顺序影响
)
# ④ 测试:查看批量数据格式(和用 map 处理后的结果一致)
for batch in train_dataloader:
print("input_ids 形状:", batch["input_ids"].shape) # torch.Size([32, 128])
print("attention_mask 形状:", batch["attention_mask"].shape) # torch.Size([32, 128])
print("label 形状:", batch["label"].shape) # torch.Size([32])
break # 只看第一批
修改词汇表
#加载字典和分词工具
# 加载分词器
tokenizer = BertTokenizer.from_pretrained(r"D:\learn\learncode\huggingface\demo_4\trasnFormers_test\model\bert-base-chinese\models--bert-base-chinese\snapshots\c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f")
# print(tokenizer)
# 准备了 4 条中文文本
sents = ["酒店太旧了, 大堂感觉象三星级的, 房间也就是的好点的三星级的条件, 在青岛这样的酒店是绝对算不上四星标准, 早餐走了两圈也没有找到可以吃的, 太差了",
"已经贴完了,又给小区的妈妈买了一套。最值得推荐",
"屏幕大,本本薄。自带数字小键盘,比较少见。声音也还过得去。usb接口多,有四个。独显看高清很好。运行速度也还可以,性价比高!",
"酒店环境很好 就是有一点点偏 交通不是很便利 去哪都需要达车 关键是不好打 酒店应该想办法解决一下"]
#获取字典
vocab = tokenizer.get_vocab()
# print(vocab)
# 查看字典长度
print(len(vocab))#21128
# 判断某个词是否在字典内
print("阳" in vocab)#True
print("光" in vocab)#True
print("阳光" in vocab)#False
#添加新词
tokenizer.add_tokens(new_tokens=["阳光","大地"])
#添加新的特殊符号
tokenizer.add_special_tokens({"eos_token":"[EOS]"})
vocab = tokenizer.get_vocab()
print(len(vocab))##21131
print("阳光" in vocab,"大地" in vocab,"[EOS]" in vocab)#True True True
#编码新句子
out = tokenizer.encode(text="阳光照在大地上[EOS]",
text_pair=None,
truncation=True,
padding="max_length",
max_length=10,
add_special_tokens=True,
return_tensors=None)
print(out)#[101, 21128, 4212, 1762, 21129, 677, 21130, 102, 0, 0]
#解码为原字符串
print(tokenizer.decode(out))#[CLS] 阳光 照 在 大地 上 [EOS] [SEP] [PAD] [PAD]
在代码中通过tokenizer.add_tokens()添加的 “阳光” 等新词,并不会被写入到原始的vocab.txt文件中
如果需要持久化这些新增词汇,需要手动保存修改后的分词器:# 保存修改后的分词器(会生成新的词汇表相关文件,而非修改原始vocab.txt) tokenizer.save_pretrained("path/to/save/modified_tokenizer")保存后会在指定路径生成新的词汇表文件(如
vocab.txt、tokenizer_config.json等),其中包含你新增的词汇。
Transformers 库和 Pipeline 工具快速上手
在线API访问(很多API访问不了,了解即可)
API_URL = "https://api-inference.huggingface.co/models/+模型名称"
#例如
API_URL = "https://api-inference.huggingface.co/models/google-bert/bert-base-chinese"

import requests
# 替换为你的实际API Token
API_TOKEN = "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"
headers = {"Authorization": f"Bearer {API_TOKEN}"}
# 第一个示例:使用Hugging Face Inference API
API_URL = "https://api-inference.huggingface.co/models/google-bert/bert-base-chinese"
response = requests.post(API_URL, headers=headers, json={"inputs": "巴黎是[MASK]国的首都。"})
print("BERT模型结果:", response.json())

License说明
Hugging Face 模型库中配置项里的 “License” 是一个非常关键的信息,它定义了其他人可以如何使用、修改、分发这个模型的法定权限和限制。
简单来说,它就是模型的 “使用说明书”或“法律条款”。
1. 为什么需要 License?
开源模型虽然免费,但“免费”不等于“可以为所欲为”。模型的创作者(作者或机构)通过选择特定的许可证,来明确告知用户:
•我允许你用这个模型做什么? (例如:商用、修改、分发)
•我需要你做什么? (例如:署名、开源你的衍生作品、使用相同的许可证)
•我不允许你做什么? (例如:用于军事用途、用于生成虚假信息)
2. 常见的许可证类型有哪些?
Hugging Face 模型库中常见的许可证可以分为几大类:
a) 宽松开源许可证 (Permissive)
这类许可证限制非常少,几乎允许任何用途,是最“友好”的许可证。
•Apache License 2.0: 非常流行且宽松的许可证。允许商用、修改、私有化分发,只需保留版权和许可声明即可。Hugging Face 自家的很多模型(如 BERT, DistilBERT)都使用此许可证。
•MIT License: 比 Apache 2.0 更简短,但同样非常宽松。要求非常简单的署名。
•BSD-3-Clause: 与 MIT 许可证类似,也是非常宽松的选择。
b) Copyleft 许可证 (Copyleft / Reciprocal)
这类许可证要求任何基于该模型的衍生作品(例如:微调后的模型)也必须在相同的开源许可证下发布。这确保了开源生态的延续性。
•GNU General Public License (GPL): 具有很强的“传染性”。如果你的项目使用了 GPL 许可的模型,那么你的整个项目可能也需要开源。
•Creative Commons Attribution-ShareAlike (CC BY-SA): 要求署名,并且任何衍生作品必须使用相同的 (SA) 许可证发布。
c) 非商业许可证 (Non-Commercial)
明确禁止将模型用于商业目的。
•Creative Commons Attribution-NonCommercial (CC BY-NC): 允许修改和分发,但只能用于非商业目的,并且需要署名。
•自定义非商业许可证: 很多研究机构(如 Meta)会发布一些仅限非商用的强大模型,例如 LLaMA 2 使用的自定义许可证,允许研究和有限度的商用(需要申请),但禁止某些特定用途。
d) 其他特殊许可证
•OpenRAIL (Open Responsible AI License): 这是近年来AI领域出现的一种新许可证。它在传统开源许可(如 MIT, Apache)的基础上,增加了一份“使用限制”清单。它允许自由使用、修改和分发,但明确禁止将模型用于有害目的,例如生成仇恨言论、虚假信息、监控等。
•
BigScience OpenRAIL-M: BLOOM 模型使用的许可证。•
BigCode OpenRAIL-M: StarCoder 模型使用的许可证。•自定义许可证 (Custom): 作者可能会制定自己的使用条款,使用时务必仔细阅读。
•未知 (Unknown): 如果作者没有指定,则会显示为未知,这意味着法律风险不明确,使用时需非常谨慎。
3. 在哪里查看 License?
在 Hugging Face 模型库的任何一个模型主页上,你都可以在右侧的信息卡中找到
License这一项。4. 如何为我的模型选择 License?
当你上传自己的模型到 Hugging Face 时,认真选择许可证非常重要。你可以问自己几个问题:
1.我希望别人能免费商用我的模型吗? 如果希望 -> 选择 Apache 2.0, MIT。
2.我希望任何基于我模型的改进都必须开源吗? 如果希望 -> 选择 GPL 或 CC BY-SA。
3.我不希望别人用我的模型赚钱吗? 如果不希望 -> 选择 CC BY-NC。
4.我希望模型被自由使用,但不能用于作恶吗? 如果希望 -> 选择 OpenRAIL 系列的许可证。
总结
许可证类型
核心特点
常见例子
宽松型 (Apache 2.0, MIT)
几乎无限制,允许商用,只需署名
BERT, DistilBERT, many others
Copyleft 型 (GPL)
衍生作品必须开源
Some older models
非商业型 (CC BY-NC)
禁止商业用途
Many research models (e.g., early LLaMA)
责任型 (OpenRAIL)
允许自由使用但禁止有害用途
BLOOM, StarCoder
最重要的建议:在使用任何模型之前,务必检查其 License 并确保你的使用方式符合其规定。忽略许可证可能会导致法律风险。如果不确定,最好直接联系模型作者咨询。
查看自己的模型


创建或上传模型文件

模型代码示例
app.py
import gradio as gr
from transformers import BlipProcessor, BlipForConditionalGeneration, AutoTokenizer, AutoModelForCausalLM, pipeline
import torch
from gtts import gTTS
import os
import uuid # 生成唯一音频文件名,防止覆盖
from datetime import datetime # 辅助清理旧文件
# 确保中文显示正常
os.environ["PYTHONUTF8"] = "1"
# ---------------------- 关键:创建音频持久化存储目录 ----------------------
AUDIO_SAVE_DIR = "./generated_audio_files"
os.makedirs(AUDIO_SAVE_DIR, exist_ok=True) # 目录不存在则自动创建
# ---------------------- 模型加载 ----------------------
# 图像描述模型(BLIP)
processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base")
# 适配 CPU/GPU 环境的 dtype(避免内存报错)
dtype = torch.float16 if torch.cuda.is_available() else torch.float32
model = BlipForConditionalGeneration.from_pretrained(
"Salesforce/blip-image-captioning-base",
torch_dtype=dtype
)
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
# 故事生成模型(Qwen2-0.5B-Instruct,CPU 可稳定运行)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct", trust_remote_code=True)
story_model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2-0.5B-Instruct",
trust_remote_code=True,
torch_dtype=dtype # 减少 CPU 内存占用
).to(device)
# 确保 tokenizer 有 pad_token(避免生成时报错)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# ---------------------- 核心功能函数 ----------------------
def generate_caption(image):
"""根据图片生成中文描述"""
if image is None:
return "请上传一张图片"
inputs = processor(image, return_tensors="pt").to(device, dtype=dtype)
out = model.generate(**inputs, max_length=50, num_beams=3) # Beam Search 提升描述质量
caption = processor.decode(out[0], skip_special_tokens=True)
# 确保描述为中文(BLIP 基础版可能输出英文)
if not any('\u4e00' <= c <= '\u9fff' for c in caption):
try:
translator = pipeline("translation", model="Helsinki-NLP/opus-mt-en-zh")
caption = translator(caption, max_length=50)[0]['translation_text']
except Exception as e:
print(f"翻译失败: {e}")
caption = "图片内容无法识别,请尝试另一张图片"
return caption
def generate_story(caption, max_length, style, creativity):
"""根据描述生成指定风格的中文故事"""
invalid_captions = ["请上传一张图片", "图片内容无法识别,请尝试另一张图片"]
if not caption or caption in invalid_captions:
return "请先上传有效图片生成描述"
# 创意值映射为 temperature(0→保守,10→奔放)
temperature = max(0.3, min(2.0, 0.3 + (creativity / 10) * 1.7))
# 优化提示词(适配 Qwen 模型,避免跑题)
prompt = f"""任务:根据图片描述写{style}风格故事
要求:1. 纯中文;2. 字数控制在{max_length}字左右;3. 符合风格特点,语句通顺
图片描述:{caption}
故事:"""
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512).to(device)
outputs = story_model.generate(
**inputs,
max_length=len(inputs["input_ids"][0]) + max_length,
temperature=temperature,
do_sample=True,
top_p=0.9, # 平衡创意与逻辑
pad_token_id=tokenizer.pad_token_id,
eos_token_id=tokenizer.eos_token_id
)
story = tokenizer.decode(outputs[0], skip_special_tokens=True).replace(prompt, "").strip()
# 截断过长故事并补充结尾
if len(story) > max_length:
story = story[:max_length].rsplit('。', 1)[0] + "......(故事已截断以符合字数要求)"
return story
def text_to_speech(text):
"""将故事文本转为 MP3 音频(持久化存储,支持试听/下载)"""
if not text or text == "请先上传有效图片生成描述":
return None
try:
# 生成唯一文件名(避免多轮生成覆盖,格式:时间+随机ID.mp3)
unique_id = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}"
audio_path = os.path.join(AUDIO_SAVE_DIR, f"story_audio_{unique_id}.mp3")
# 生成中文音频(slow=False 语速正常,提升听感)
tts = gTTS(text=text, lang='zh-CN', slow=False)
tts.save(audio_path)
# 清理3天前的旧音频(避免目录占用过大)
clean_old_audio(days=3)
return audio_path # 返回文件路径,Gradio 自动识别并渲染控件
except Exception as e:
print(f"音频生成失败: {str(e)}")
return None
def clean_old_audio(days=3):
"""清理指定天数前的旧音频文件"""
now = datetime.now()
for filename in os.listdir(AUDIO_SAVE_DIR):
if filename.endswith(".mp3"):
file_path = os.path.join(AUDIO_SAVE_DIR, filename)
# 获取文件修改时间
file_mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
# 超过指定天数则删除
if (now - file_mtime).days >= days:
os.remove(file_path)
print(f"已清理旧音频:{filename}")
def process_image(image, max_length, style, creativity):
"""端到端处理:图片→描述→故事→音频"""
try:
caption = generate_caption(image)
story = generate_story(caption, max_length, style, creativity)
audio = text_to_speech(story)
return caption, story, audio
except Exception as e:
error_msg = f"处理出错: {str(e)[:100]}" # 限制错误信息长度,避免界面混乱
return error_msg, "", None
# ---------------------- Gradio 界面(重点配置音频控件) ----------------------
with gr.Blocks(title="图片转故事与音频", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🌟 图片转故事与音频生成器")
gr.Markdown("上传图片 → 生成中文描述 → 定制风格故事 → 试听/下载音频")
with gr.Row():
# 左侧:输入与参数配置区
with gr.Column(scale=1, min_width=300):
image_input = gr.Image(
type="pil",
label="上传图片",
height=280, # 增大图片上传区域,提升体验
show_label=True,
interactive=True
)
# 参数配置折叠面板(更简洁)
with gr.Accordion("📝 故事参数设置", open=True):
max_length = gr.Slider(
minimum=50, # 最低字数调整为50,避免生成过短内容
maximum=1000,
value=300,
step=50,
label="故事最大字数",
info="建议 200-500 字(生成更快)"
)
style = gr.Dropdown(
choices=["搞笑", "武侠", "爱情", "动作", "科幻", "悬疑", "奇幻", "温馨", "恐怖", "冒险", "童话"],
value="温馨",
label="故事风格",
interactive=True
)
creativity = gr.Slider(
minimum=0,
maximum=10,
value=5,
step=1,
label="创意值(0-10)",
info="0=保守常规 | 10=脑洞大开"
)
generate_btn = gr.Button(
"🚀 生成描述+故事+音频",
variant="primary",
size="lg" # 增大按钮,提升辨识度
)
# 右侧:输出区(重点配置音频控件)
with gr.Column(scale=2, min_width=500):
caption_output = gr.Textbox(
label="🖼️ 图片描述",
lines=2,
interactive=False,
show_copy_button=True # 增加复制按钮,方便用户使用
)
story_output = gr.Textbox(
label="📖 生成的故事",
lines=12,
interactive=False,
show_copy_button=True
)
# 关键:配置音频控件支持试听和下载
audio_output = gr.Audio(
label="🎵 故事音频(可试听/下载)",
type="filepath", # 必须设为 filepath,Gradio 才能识别文件并渲染控件
interactive=True,
show_download_button=True, # 显式开启下载按钮
autoplay=False # 关闭自动播放,避免打扰用户
)
# 绑定生成按钮事件(显示进度条,提升体验)
generate_btn.click(
fn=process_image,
inputs=[image_input, max_length, style, creativity],
outputs=[caption_output, story_output, audio_output],
show_progress="full", # 显示完整进度条,告知用户处理状态
queue=True # 开启任务队列,支持多轮生成不卡顿
)
# 示例数据(帮助用户快速测试功能)
gr.Examples(
examples=[
["./example_images/dog.jpg", 300, "温馨", 5],
["./example_images/fangfengzheng.jpeg", 400, "童话", 8],
["./example_images/jiehun.jpg", 200, "爱情", 7]
],
inputs=[image_input, max_length, style, creativity],
outputs=[caption_output, story_output, audio_output],
fn=process_image,
label="💡 点击示例快速测试"
)
# ---------------------- 启动配置 ----------------------
if __name__ == "__main__":
# 本地运行:自动打开浏览器,端口 7860
demo.launch(
debug=False,
share=False, # 本地运行无需公网链接,部署到 Hugging Face 时会自动忽略
server_name="0.0.0.0",
server_port=7860
)
编写依赖文件
gradio>=3.40.0
transformers>=4.35.0
torch>=2.0.0
gtts>=2.3.2
Pillow>=9.5.0
sentencepiece>=0.1.99
编写README.md
注意:原生的Readme文件自带的配置不要删除,否则项目启动报错:

点击查看
# 🌟 图片转故事与音频生成器
这是一个基于 **Gradio + Transformers + gTTS** 的中文图片故事生成工具。用户上传图片后,系统会:
1. 自动生成图片中文描述;
2. 根据描述生成指定风格的中文故事;
3. 将故事转换成可试听和下载的 MP3 音频。
---
## 功能特点
- **图片识别与描述**:使用 [BLIP](https://huggingface.co/Salesforce/blip-image-captioning-base) 模型生成图片内容描述。
- **故事生成**:使用 [Qwen2-0.5B-Instruct](https://huggingface.co/Qwen/Qwen2-0.5B-Instruct) 模型生成不同风格的故事,可自定义字数、风格和创意值。
- **文本转语音**:利用 [gTTS](https://pypi.org/project/gTTS/) 将故事文本生成中文音频,并支持试听/下载。
- **音频持久化管理**:自动保存生成音频,支持按天数清理旧音频文件。
- **用户友好的 Gradio 界面**:支持拖拽图片上传、参数设置、示例快速测试、生成进度提示。
---
## 环境依赖
- Python >= 3.10
- [PyTorch](https://pytorch.org/)
- [Transformers](https://huggingface.co/docs/transformers/index)
- [Gradio](https://gradio.app/)
- [gTTS](https://pypi.org/project/gTTS/)
- PIL (Python Imaging Library)
安装依赖示例:
```bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers gradio gTTS pillow
```
---
## 文件结构
```
project/
├─ app.py # 主程序
├─ generated_audio_files/ # 自动生成音频存放目录
├─ example_images/ # 示例图片
│ ├─ dog.jpg
│ ├─ fangfengzheng.jpeg
│ └─ jiehun.jpg
└─ README.md # 项目说明
```
---
## 使用说明
### 1. 启动应用
```bash
python app.py
```
- 默认访问地址:http://localhost:7860
- 支持本地浏览器访问,也可设置 `share=True` 生成公网链接。
### 2. 上传图片
- 支持 JPG/PNG 等常见图片格式。
- 上传后系统自动生成图片描述。
### 3. 设置故事参数
- **故事最大字数**:控制生成故事的长度。
- **故事风格**:如温馨、搞笑、武侠、爱情、科幻等。
- **创意值**:0-10,数值越高生成故事越有创意。
### 4. 生成故事与音频
- 点击 `生成描述+故事+音频` 按钮,系统会依次生成图片描述、故事文本和可下载音频。
- 音频控件支持试听和下载。
### 5. 示例快速测试
- 页面底部提供示例图片和参数,一键生成效果,方便新用户体验。
---
## 注意事项
1. **性能要求**
- GPU 推荐:可加速模型推理,尤其是故事生成。
- CPU 运行也可,但生成速度较慢。
2. **音频存储**
- 默认保存路径:`./generated_audio_files/`
- 自动清理超过 3 天的旧音频,避免目录占用过大。
3. **中文输出**
- BLIP 模型基础版可能生成英文描述,系统会自动调用翻译接口转换为中文。
4. **依赖版本问题**
- 请确保 `transformers`、`torch` 与 `tokenizers` 版本兼容。
---
## 致谢
- [Salesforce BLIP](https://github.com/salesforce/BLIP)
- [Qwen2-0.5B-Instruct](https://huggingface.co/Qwen/Qwen2-0.5B-Instruct)
- [Gradio](https://gradio.app/)
- [gTTS](https://pypi.org/project/gTTS/)
---
## 开源协议
MIT License

为什么每次执行代码都那么久?不应该有缓存吗在 Hugging Face Space 上跑代码,确实有缓存机制,但要分清楚几种情况:
🚀 1. 依赖安装(requirements.txt)
第一次部署,会把
requirements.txt里的包都下载并安装(这一步比较慢)。后续部署时,如果
requirements.txt没变,Hugging Face 会使用缓存的依赖,不会每次都重新下载。⚠️ 但如果你改动了
requirements.txt,就会触发 重新安装,时间会比较久。
🚀 2. 模型下载
你用到的 Hugging Face 模型(比如
Qwen2.5,deepseek, 或者 TTS 模型),第一次调用会从 Hub 下载,放在容器的缓存里。只要 Space 没被重启(或自动清理),下次运行就会直接从本地缓存加载,速度会快很多。
⚠️ 但是 Hugging Face 的 免费 Space 容器会休眠(大约 15 分钟无人访问就会休眠),被唤醒时缓存可能会丢失 → 又得重新下载模型,所以你会觉得“每次都很久”。
🚀 3. 解决思路
减少 requirements.txt 改动:每次修改都会触发重新安装。
用小模型:Qwen1.5B、DeepSeek 6B 这类会比 70B 模型快很多。
使用
hf_transfer加速下载(在 requirements.txt 里加hf-transfer,再在代码里设置环境变量)。升级到 Pro Space(付费版有持久缓存,免费版容器随时可能重置)。
🔑 总结:
免费 Space 上,依赖缓存通常能保留,但 模型缓存不稳定,容器休眠后可能会清空 → 所以你才感觉“每次执行代码都很久”。要不要我帮你把 demo 改成 优先使用本地缓存,如果没缓存再去下载 Hugging Face 模型?这样体验会更快。
补充
AutoModel和 AutoModelForXXX
Hugging Face Transformers 库中的 AutoModel和 AutoModelForXXX系列类是针对不同任务设计的模型加载工具,其核心区别在于是否包含任务专用的输出层(Model Head)。以下按类别系统说明其区别与应用场景:
1. AutoModel:基础模型(无任务头)
•功能
加载预训练模型的主干架构(如 Transformer 编码器/解码器),不包含任何任务专用的输出层。输出为原始隐藏状态(Hidden States),需用户自定义下游任务头。
•适用场景
•特征提取(获取文本嵌入)
•自定义任务头(如添加私有分类器、回归层)
•多任务学习或迁移学习中灵活适配下游任务
from transformers import AutoModel
model = AutoModel.from_pretrained("bert-base-uncased")
outputs = model(**inputs) # 输出形状: [batch_size, seq_len, hidden_size]
核心对比总结 (优化版)
|
类别 |
任务头类型 |
典型架构 |
输出示例 |
适用场景 |
|---|---|---|---|---|
|
|
无 |
Encoder/Decoder |
隐藏状态 ( |
特征提取、自定义任务 |
|
|
因果语言建模头 |
Decoder-only |
下一个token概率 ( |
文本生成、对话系统 |
|
|
序列分类头 |
Encoder-based |
类别logits ( |
情感分析、主题分类 |
|
|
答案起止位置头 |
Encoder-based |
起止logits ( |
抽取式问答 |
|
|
Token分类头 |
Encoder-based |
每个token的标签 ( |
NER、词性标注 |
选择指南 (优化版)
1. 任务驱动选择
-
生成文本 →
AutoModelForCausalLM -
分类整段文本 →
AutoModelForSequenceClassification -
定位答案 →
AutoModelForQuestionAnswering -
标注每个词 →
AutoModelForTokenClassification -
自定义任务 →
AutoModel+ 自建输出层。
2. 架构适配性
-
ncoder-decoder模型(如T5)适用条件生成任务(
AutoModelForSeq2SeqLM),与上述类别独立。
3. 代码灵活性
AutoModelForXXX自动匹配模型架构(如BERT/RoBERTa),避免硬编码,提升跨模型兼容性。
提示: 通过正确选择模型类,可避免“加载基础模型却无法获得任务结果”的常见问题。实际使用时需配合
AutoTokenizer预处理文本,并参考模型配置(如config.id2label)解析输出。
子词分割
现代Transformer模型(如BERT)分词器的核心机制:子词分割(Subword Tokenization)。
简单直接的答案是:
-
“黑”:这是一个独立、完整的 token。当“黑”作为一个词单独出现或在词首时,会被识别为此 token。
-
“#黑”:这是一个子词 token(在WordPiece算法中通常以
##开头)。它表示“黑”这个字不是词语的开头部分,而是作为词语的中间或尾部出现。
详细解释
这种现象源于BERT等模型所使用的 WordPiece 分词算法。它的设计目的是解决大规模词汇表(Vocabulary)与未知词(Out-of-Vocabulary, OOV)问题之间的平衡。
1. 核心思想:平衡词汇表大小与覆盖度
-
问题:如果每个词都作为一个独立的token(比如“中国”、“黑色”、“黑客”),词汇表会变得极其庞大,且无法处理训练时没见过的新词。
-
解决方案:将词拆分成更小的、可重复使用的“子词”单元。常见的字或字母组合作为独立token,不常见的词则被拆分成这些子词组合。
2. “黑” vs “#黑” 的具体区别
-
“黑” (独立token)
-
角色:作为词的开头。
-
例子:词汇“黑” (
黑)、“黑色” (黑,##色)、“黑客” (黑,##客)。 -
分词时,如果“黑”字位于一个词的开始位置,分词器就会使用
黑这个token。
-
-
“#黑” (子词token, 通常表示为
##黑)-
角色:作为词的中间或尾部,不能独立成词或开头。
-
例子:“乌黑” (
乌,##黑)、“黑黑” (黑,##黑)、“抹黑” (抹,##黑)。 -
词器永远不会用
##黑来开始一个词。它前面一定跟着另一个没有##前缀的token。
-
3. 分词过程示例
假设我们的句子是:“乌鸦的羽毛是乌黑的”。
-
首先,进行基础分词(可能按空格和 punctuation):
["乌鸦", "的", "羽毛", "是", "乌黑的"] -
然后,对每个词应用WordPiece算法:
-
乌鸦” -> 被拆分为:
["乌", "##鸦"]-
乌” 是一个独立token。
-
鸦” 不是一个常见的独立词,所以被标记为子词
##鸦。
-
-
的” -> 是一个常见词,作为独立token:
["的"] -
羽毛” -> 被拆分为:
["羽", "##毛"] -
是” -> 独立token:
["是"] -
乌黑的” -> 被拆分为:
["乌", "##黑", "##的"]-
乌” 是词的开头,用独立token
乌。 -
黑” 在这里是词的中间部分,用子词token
##黑。 -
的” 在这里是词的尾部,用子词token
##的。
-
-
所以,整个句子的最终token序列是:
[“乌”, “##鸦”, “的”, “羽”, “##毛”, “是”, “乌”, “##黑”, “##的”]
总结与为什么重要
|
特征 |
独立 Token (如 |
子词 Token (如 |
|---|---|---|
|
用途 |
作为词语的开头部分 |
仅作为词语的非开头部分(中间或尾部) |
|
前缀 |
无前缀 |
有特定前缀(如WordPiece用 |
|
例子 |
“黑” -> |
“乌黑” -> |
|
设计目的 |
代表常见的、可以独立成词或引领一个复合词的词汇单元 |
代表常见的词缀、字根,可以无限组合,极大地增强模型处理未知词汇的能力 |
这种机制的优势在于:
模型只需要学习“黑”这一个字根的含义,它就能理解所有包含“黑”的词语(如“黑暗”、“黑客”、“抹黑”、“乌黑”等),即使这些词没有全部出现在训练数据中。这极大地提高了模型的泛化能力和词汇表的效率。
所以,vocab.txt中存在这两个token并不是错误或冗余,而是模型高效处理语言的关键设计。你在使用tokenizer时,它会自动帮你处理好这些规则,你通常不需要手动去选择使用 黑还是 ##黑。
体原因:
举例说明:
1. 本质区别:datasets.Dataset vs torch.utils.data.Dataset
2. 核心需求:为 PyTorch 训练做 “定制化适配”
场景 1:统一 “数据读取接口”,适配 DataLoader
场景 2:在 __getitem__ 中嵌入 “数据预处理”
场景 3:灵活处理特殊需求(过滤、特征新增等)
3. 总结:你的代码 vs 教程代码的目标不同
Hugging Face API 使用
匿名访问 API(了解)
你可以通过 Hugging Face Inference API 匿名使用预训练模型(注意:匿名访问的模型受限于公开权限)使用 Hugging Face API 时,大部分模型需要认证,不能匿名访问,例如下面的代码执行就报错无权限:
import requests
API_URL = "https://api-inference.huggingface.co/models/bert-base-chinese"
# 不使用 Authorization 头以进行匿名访问
response = requests.post(API_URL, json={"inputs": "你好,Hugging Face!"})
print(response.json())
{'error': 'Invalid username or password.'}
使用Access Tokens调用API
注册并获取 API Token 后,你可以使用自己的 API Token 进行访问:
需在账户设置获取API Token
注意,调用API接口的话,创建的Access Token需要有读或写权限:



import requests
from transformers import pipeline
# 替换为你的实际API Token
API_TOKEN = "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"
headers = {"Authorization": f"Bearer {API_TOKEN}"}
# 第一个示例:使用Hugging Face Inference API
API_URL = "https://api-inference.huggingface.co/models/google-bert/bert-base-chinese"
response = requests.post(API_URL, headers=headers, json={"inputs": "巴黎是[MASK]国的首都。"})
print("BERT模型结果:", response.json())
# 第二个示例:使用pipeline
# 使用token参数替代use_auth_token
generator = pipeline("text-generation", model="gpt2", token=API_TOKEN)
# 添加truncation=True以消除警告
output = generator("The future of AI is", max_length=50, truncation=True)
print("GPT2生成结果:", output)
BERT模型结果: [{'score': 0.9911912083625793, 'token': 3791, 'token_str': '法', 'sequence': '巴 黎 是 法 国 的 首 都 。'}, {'score': 0.002847270341590047, 'token': 2548, 'token_str': '德', 'sequence': '巴 黎 是 德 国 的 首 都 。'}, {'score': 0.0016626743599772453, 'token': 5739, 'token_str': '英', 'sequence': '巴 黎 是 英 国 的 首 都 。'}, {'score': 0.0009207695838995278, 'token': 5401, 'token_str': '美', 'sequence': '巴 黎 是 美 国 的 首 都 。'}, {'score': 0.0007634416106157005, 'token': 6421, 'token_str': '该', 'sequence': '巴 黎 是 该 国 的 首 都 。'}]
GPT2生成结果: [{'generated_text': 'The future of AI is very bright, and our future is very bright," he said.\n\n"I think as long as we don\'t get to the point where we\'re going to be able to do that, it\'s going to take us a long time to do that."\n\nRotherham\'s chief executive, Ian Murray, said: "We believe AI is an important issue for the future of our city, and we expect to see a rapid rise in the number of people in the city and the number of engineers in the world that can work on it.\n\n"We also believe that the future of the UK is very bright.\n\n"We are investing £5m over the next 10 years to drive our economy and technology forward, and in the coming years, we intend to invest in the next generation of robotics, smart robots, and the future of the digital economy."'}]
Hugging Face API调用权限说明







浙公网安备 33010602011771号