全部文章

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:阿里开源的大模型平台,模型不是很全,速度比较快;

简介

image

认识 Hugging Face

你是不是经常刷到 “某团队发布开源 AI 模型” 的消息?觉得这些 “黑科技” 离自己很远?其实,这些模型大多都能在Hugging Face上找到 —— 这个近两年爆火的平台,连谷歌、微软这样的大厂都会把开源模型放在这里。无论是个人开发者、产品经理还是 AI 从业者,学会用它都是入门 AI 的第一步。
Hugging Face 是 AI 领域的 “GitHub”,集模型库、数据集、协作工具和社区于一体,提供超过 150 万个开源 AI 模型和 33 万个数据集,覆盖文本、图像、音频等多模态任务。其核心库 Transformers 简化了模型的下载、训练和部署流程,即使是小白也能快速上手。

Hugging Face:从 “聊天机器人” 到 AI 巨头的逆袭

Hugging Face 的故事充满 “无心插柳” 的巧合:
  • 2016 年:公司在纽约成立,最初想做 “给无聊人聊天的机器人”,但早期发展平平。
  • 2019 年转折点:为了训练聊天机器人的自然语言能力,团队在 GitHub 开源了Transformers 库。没想到这个库迅速火遍机器学习社区,成为 GitHub 史上增长最快的机器学习库。
  • 如今:平台不仅因 Transformers 库成名,更成为机器学习领域的 “模型超市”,覆盖 NLP、计算机视觉、语音、生物学、强化学习等多个领域,帮助科学家和开发者高效连接 “模型创造” 与 “应用落地”。
用 CEO 的话说,Hugging Face 的核心价值是 **“弥补科学与生产之间的鸿沟”**—— 让造模型的大神和用模型的开发者高效协作。聊天机器人转向模型托管与开源社区,成功连接了“学术研究”与“产业应用”。

注册和安装

 注册 Hugging Face 账户

国内访问与注册

  • 注册:访问huggingface.co,使用邮箱注册(需科xue上网)。
  • 镜像站点:国内用户可通过HF-Mirror访问,速度更快。
  • 订阅问题:Pro 会员需国际信用卡,国内用户可尝试野卡(如虚拟卡)解决。
  • 注册成功后,你可以访问模型库、数据集和文档,也可以管理你的个人模型和项目。

安装 Hugging Face

Hugging Face 提供了 transformers 库,用于加载和使用模型。你可以使用以下命令来安装它:(电脑须安装基础环境:Anaconda,CUDA,cuDNN,pytorch

如果你还需要安装其他依赖库,如 datasets 和 tokenizers ,可以使用以下命令:

pip install transformers

pip install transformers datasets tokenizers
 

数据集下载&本地加载

直接使用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]
输出内容解读
out是一个字典,包含了模型输入所需的各种信息,例如:
  • input_ids:文本转换后的 token 索引序列(数字形式,模型直接接收的输入)就是编码后的词
  • attention_mask:1 表示有效 token,0 表示填充的[PAD],pad的位置是0,其他位置是1
  • token_type_ids:区分不同句子(单句场景下基本都是 0)第一个句子和特殊符号的位置是0,第二个句子的位置是1
  • special_tokens_mask 特殊符号的位置是1,其他位置是0
  • length 返回句子长度
  • 其他标记信息(特殊符号位置、实际长度等)

总结

这段代码的核心作用是文本预处理:将原始中文文本转换为 BERT 模型能够接受的数字序列格式,包含了分词、添加特殊符号、截断 / 填充、生成辅助标记等操作,为后续使用 BERT 模型进行分类、情感分析等任务做准备。

使用词汇表转换数据集

在微调 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])

代码的核心作用详解

这段代码的本质是 “文本预处理”—— 将数据集中的原始文本(如 “酒店环境很好”),转换为 BERT 模型能直接接收的数字格式(如 input_idsattention_mask

dataset.map(...) 做了什么?

map 是 Dataset 对象的核心方法,作用是 “对数据集中的每条(或每批)数据执行自定义函数”,这里的自定义函数就是 tokenizer(x['text'], ...)(分词器处理文本)。

具体来说,它会遍历数据集中的每一条数据,对 x['text'](原始文本)执行分词操作,生成 BERT 必需的 3 个核心字段(默认输出):

生成的字段 作用
input_ids 文本分词后每个 token 对应的 “词典索引”(如 “阳光”→ 21128),是模型的核心输入
attention_mask 标记 “有效 token” 和 “填充 token([PAD])”:1 表示有效,0 表示填充,避免模型关注无效数据
token_type_ids 标记 “句子边界”(单句场景下全为 0,句子对场景下区分第一句 / 第二句)

处理后,dataset 的每条数据会新增这 3 个字段,结构变为:
{
  "text": "酒店环境很好",  # 原始文本(保留)
  "label": 1,             # 原始标签(保留)
  "input_ids": tensor([[101, 3791, 2644, 2523, 3299, 102]]),  # 新增:token 索引
  "attention_mask": tensor([[1, 1, 1, 1, 1, 1]]),             # 新增:注意力掩码
  "token_type_ids": tensor([[0, 0, 0, 0, 0, 0]])              # 新增:句子类型标记
}

batched=True 和 return_tensors="pt" 的作用

  • return_tensors="pt":指定分词器输出 PyTorch 张量(tensor),而不是默认的列表(list)。因为 BERT 模型(PyTorch 版本)只接收张量作为输入,这一步避免了后续手动转换格式的麻烦。
  • batched=True:开启 “批量处理” 模式。分词器会一次性处理多条文本(而非单条),效率远高于单条处理(尤其当数据集有几万 / 几十万条数据时,能显著节省时间)。

处理后的 dataset 能用来做什么?

处理后的 dataset 已经具备 BERT 模型训练 / 推理的 “输入资格”,后续可直接配合 PyTorch 的 DataLoader 批量加载数据,送入模型:
from torch.utils.data import DataLoader

# 将处理后的 dataset 转换为 DataLoader(PyTorch 训练必备,支持批量、洗牌、多线程)
dataloader = DataLoader(
    dataset,
    batch_size=32,  # 每次加载 32 条数据
    shuffle=True    # 训练前洗牌,避免数据顺序影响模型
)

# 训练时循环读取批量数据
for batch in dataloader:
    # 从 batch 中提取模型需要的输入
    input_ids = batch["input_ids"].squeeze()  # 去除多余维度(batch_size=32 → [32, seq_len])
    attention_mask = batch["attention_mask"].squeeze()
    labels = batch["label"]
    
    # 送入模型训练(伪代码)
    # outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
    # loss = outputs.loss
    # loss.backward()
 

转换PyTorch  Dataset 数据集

核心思路:把分词逻辑写进 __getitem__

Hugging Face Dataset 的 map 是 “批量遍历所有数据,一次性完成分词并保存结果”;而 PyTorch 原生 Dataset 的 __getitem__ 是 “当 DataLoader 读取某条数据时,实时对这条数据做分词”。两者最终目的都是把文本转成模型能认的格式,只是执行时机不同。

具体改造代码如下(以情感分析数据集为例):
# 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 文件中

1. vocab.txt 是静态文件,不会被动态修改

vocab.txt 是预训练模型在发布时就固定好的词汇表文件,存储了模型原始的所有 token。当你通过代码 tokenizer.add_tokens() 或 add_special_tokens() 添加新词时,这些操作只会在内存中动态扩展分词器的词汇表,而不会修改硬盘上的 vocab.txt 文件本身。

也就是说:
  • 原始 vocab.txt 始终保持模型发布时的状态,不会新增内容;
  • 新增的 “阳光”“大地”“[EOS]” 只存在于运行时的 tokenizer 对象中,不会被持久化到 vocab.txt
 如果需要持久化这些新增词汇,需要手动保存修改后的分词器:
# 保存修改后的分词器(会生成新的词汇表相关文件,而非修改原始vocab.txt)
tokenizer.save_pretrained("path/to/save/modified_tokenizer")

保存后会在指定路径生成新的词汇表文件(如 vocab.txttokenizer_config.json 等),其中包含你新增的词汇。

 
 
 

Transformers 库和 Pipeline 工具快速上手

Hugging Face 的易用性,很大程度来自Transformers 库Pipeline 工具

1. 安装 Transformers 库

一行代码即可安装:
pip install transformers

2. Pipeline:AI 任务的 “一键调用神器”

Pipeline 是封装好的端到端工具,帮你搞定 “数据预处理→模型处理→数据后处理” 全流程,无需关心底层细节。举几个例子:
 

pipline调用

  • 情感分析:输入句子,直接返回 “积极 / 消极” 标签及置信度。
from transformers import pipeline  
classifier = pipeline("sentiment-analysis")  
result = classifier("Today is a nice day")  
# 输出:[{'label': 'POSITIVE', 'score': 0.9998}]
  • 文本生成:指定模型,让 AI 续写文本。
generator = pipeline("text-generation", model="gpt2")
result = generator("The future of AI is", max_length=30, num_return_sequences=2)
print(result)
  • 零样本分类:给文本和候选标签,AI 自动判断匹配度(比如 “这篇文章讲教育、政治还是商业?”)。
classifier = pipeline("zero-shot-classification")
result = classifier(
    "This is a course about AI and machine learning.",
    candidate_labels=["education", "politics", "business"]
)
print(result)
# 输出: 最可能标签为 'education',置信度超过96%
Pipeline 支持的任务超丰富:音频分类、语音识别、图像分类、翻译、摘要…… 基本覆盖主流 AI 场景。
 

在线API访问(很多API访问不了,了解即可)

说明:调用API地址格式为:
API_URL = "https://api-inference.huggingface.co/models/+模型名称"
#例如
API_URL = "https://api-inference.huggingface.co/models/google-bert/bert-base-chinese"
image
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())

3 步搭建 “图生故事 + 语音”AI 应用

上一期我们初步认识了 Hugging Face 及其 Transformers 库,还通过小实验体验了 Pipeline 工具的便捷。这一期,我们直接进入实战 —— 用 Hugging Face 模型、OpenAI API 和 LangChain 工具,搭建一个 “上传图片→生成故事→输出语音” 的完整小应用。哪怕你只懂一点点开发,跟着步骤走也能轻松实现!

一、先明确:我们要做什么?

这个应用的核心逻辑很直观:
输入一张图片 → 用 Hugging Face 模型把图片转成文字描述 → 把文字传给 OpenAI GPT-3.5 生成指定风格的故事 → 再用 Hugging Face 文字转语音模型,把故事变成可播放的音频文件。

整个过程会串联 3 个关键技术点,学会这一套,后续再用 Hugging Face 上 200 多万个模型开发应用,基本就能举一反三:
  1. 用 Transformers 库实现 “图片转文字”(Image to Text);
  2. 用 LangChain 串联 OpenAI API,让 GPT-3.5 生成定制化故事;
  3. 调用 Hugging Face 在线模型 API,实现 “文字转语音”(Text to Speech)。

二、环境准备:用 Colab 避坑,零基础也能搭环境

上一期有小伙伴反馈在 VS Code 里部署环境时遇到各种包安装问题,这次我们用 Google Colab 来演示 —— 它相当于一个 “干净的云端虚拟机”,无需本地配置,直接在线跑代码,下载依赖速度还快,对新手特别友好。

Colab 的基础使用可以自行了解(比如如何新建笔记本、运行代码块,详参:Google Colab:你的云端 Python 开发神器 全方位指南

三、实战步骤:3 步实现完整功能

第一步:图片转文字(Image to Text)—— 用 Transformers 库搞定

核心是调用 Hugging Face 上的开源图片描述模型,通过 Pipeline 工具一键实现 “图转文”。

1. 安装依赖

首先在 Colab 里安装 Transformers 库(Google 服务器下载,速度很快):
# 安装 Transformers 库
!pip install transformers

2. 写代码:定义 “图转文” 函数

从 Transformers 库导入 Pipeline,定义一个接收图片路径 / URL 的函数,自动调用模型生成文字描述:
# 从 Transformers 导入 Pipeline 工具
from transformers import pipeline

def image_to_text(image_path_or_url):
    # 加载图片转文字的 Pipeline,指定模型(这里用 Salesforce 热门模型,效果稳定)
    image_to_text_pipeline = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base")
    # 调用模型处理图片,返回文字描述列表(列表中第一个结果最关键)
    results = image_to_text_pipeline(image_path_or_url)
    # 提取并返回核心描述文本
    caption = results[0]["generated_text"]
    print("图片描述:", caption)
    return caption

# 调用函数(示例:可传在线图片 URL,或 Colab 中上传的本地图片路径)
# 比如上传图片到 Colab 后,路径类似 "/content/your-image.jpg"
image_caption = image_to_text("https://example.com/your-image.jpg") 

3. 运行效果

比如输入一张 “老人捧着金色书本微笑” 的图片,模型可能返回:
"A man is smiling while holding a golden book"
文字描述有了,接下来就能用它生成故事了。

第二步:文字转故事 —— 用 LangChain 串联 OpenAI API

这一步要实现 “把图片描述 + 定制指令传给 GPT-3.5,让它生成故事”。我们用 LangChain 工具来简化流程 —— 它能轻松串联多个组件(比如模型、模板、API),避免写复杂的接口调用逻辑。

1. 安装依赖

需要安装 LangChain 和 OpenAI 官方库:
# 安装 LangChain 和 OpenAI 库
!pip install langchain openai

2. 写代码:定义 “生成故事” 函数

核心是用 LangChain 的模板类(PromptTemplate)定义故事生成规则,再用 OpenAI 类调用 GPT-3.5 接口:
# 导入 LangChain 相关工具和 OpenAI 接口
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.llms import OpenAI

# 注意:需要替换成你的 OpenAI API Key(从 OpenAI 官网获取)
openai_api_key = "your-openai-api-key"

def generate_story(image_caption):
    # 1. 定义故事生成模板(指定角色、风格、字数,让 GPT 输出更可控)
    prompt_template = PromptTemplate(
        input_variables=["context"],  # 输入变量:图片描述
        template="""你是一位擅长讲短故事的老人,风格要温暖带点小幽默,字数控制在 100 字以内。
根据下面的内容创作故事:
Context: {context}
故事:"""
    )

    # 2. 初始化 OpenAI 模型(调用 GPT-3.5 Turbo)
    llm = OpenAI(
        model_name="gpt-3.5-turbo-instruct",  # GPT-3.5 模型名
        api_key=openai_api_key,
        temperature=0.7  # 随机性:0 偏严谨,1 偏创意
    )

    # 3. 用 LLMChain 串联“模板”和“模型”
    story_chain = LLMChain(llm=llm, prompt=prompt_template)

    # 4. 调用链条生成故事
    story = story_chain.run(context=image_caption)
    print("生成的故事:", story)
    return story

# 调用函数:用第一步得到的图片描述生成故事
story_text = generate_story(image_caption)

3. 关键说明

  • API Key 问题:需要从 OpenAI 官网申请 API Key,国内用户若做公司级应用,需先了解合规性,个人学习测试可正常使用;
  • 模板定制:你可以修改 template 里的内容,比如把 “温暖幽默” 改成 “惊悚”“猎奇”,字数也能调整,GPT 会按指令生成对应风格的故事;
  • 运行效果:基于第一步的图片描述,GPT-3.5 可能生成这样的故事:“老人捧着金书笑,说这是他藏了十年的‘快乐笔记’。路人问写了啥,他翻开:‘今天捡了片好看的叶子’‘小猫蹭了我的手’。原来财富,早藏在小日子里啦~”

第三步:文字转语音(Text to Speech)—— 调用 Hugging Face 在线模型 API

最后一步,把生成的故事转成音频。我们直接调用 Hugging Face 上的文字转语音模型 API,无需本地部署模型,返回的音频数据可直接保存为文件。

1. 准备 Hugging Face Token

首先需要一个 Hugging Face 账号(免费注册),然后在账号设置里生成一个 Access Token(用于调用模型 API 时验证身份,权限选 “Read” 即可)。

2. 写代码:定义 “文转音” 函数

用 requests 库发送 POST 请求到模型 API,接收音频数据并保存为 FLAC 文件(可后续转成 MP3 播放):
import requests

def text_to_speech(message, api_url, token):
    # 1. 设置请求头(包含 Token 验证)
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    # 2. 准备请求参数(传入要转语音的文本)
    payload = {
        "inputs": message
    }

    # 3. 发送 POST 请求调用模型 API
    response = requests.post(api_url, headers=headers, json=payload)

    # 4. 保存音频文件(若请求成功)
    if response.status_code == 200:
        # 保存为 FLAC 格式(Hugging Face 模型常返回此格式,可在线转 MP3)
        with open("/content/story_audio.flac", "wb") as f:
            f.write(response.content)
        print("音频生成成功!文件路径:/content/story_audio.flac")
    else:
        print("音频生成失败,错误码:", response.status_code)

# 调用函数:替换为你的 Token 和目标模型 API URL
# 模型 API URL 从 Hugging Face 模型页面获取(比如选 "tts-1" 类模型,复制其 Inference API 地址)
huggingface_token = "your-huggingface-token"
tts_model_api_url = "https://api-inference.huggingface.co/models/facebook/fastspeech2-en-ljspeech"
text_to_speech(story_text, tts_model_api_url, huggingface_token)

3. 运行效果

Colab 左侧文件栏会出现 story_audio.flac 文件,下载后可在线转成 MP3 播放。虽然示例模型的中文语音效果一般(可换成 Hugging Face 上的中文 TTS 模型,如 “bakerk123/tts”),但核心逻辑已跑通 —— 只要替换模型 API 地址,就能切换不同语音风格。

 

通过此案例掌握:​​多模型协调能力​​ > 单一模型精度,这正是AI应用开发的核心竞争力。

 
 

发布模型

创建空间

http://huggingface.co/spaces

image

image

image

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​​ 并确保你的使用方式符合其规定。忽略许可证可能会导致法律风险。如果不确定,最好直接联系模型作者咨询。

 

查看自己的模型

image

image

创建或上传模型文件

image

模型代码示例

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
    )

编写依赖文件

requirements.txt
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文件自带的配置不要删除,否则项目启动报错:

image

点击查看
# 🌟 图片转故事与音频生成器

这是一个基于 **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
效果预览:
image
为什么每次执行代码都那么久?不应该有缓存吗

在 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. 解决思路

  1. 减少 requirements.txt 改动:每次修改都会触发重新安装。

  2. 用小模型:Qwen1.5B、DeepSeek 6B 这类会比 70B 模型快很多。

  3. 使用 hf_transfer 加速下载(在 requirements.txt 里加 hf-transfer,再在代码里设置环境变量)。

  4. 升级到 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]

核心对比总结 (优化版)​

类别

任务头类型

典型架构

输出示例

适用场景

AutoModel

Encoder/Decoder

隐藏状态 ([batch, seq, dim])

特征提取、自定义任务

AutoModelForCausalLM

因果语言建模头

Decoder-only

下一个token概率 ([batch, seq, vocab])

文本生成、对话系统

AutoModelForSequenceClassification

序列分类头

Encoder-based

类别logits ([batch, labels])

情感分析、主题分类

AutoModelForQuestionAnswering

答案起止位置头

Encoder-based

起止logits ([batch, seq])

抽取式问答

AutoModelForTokenClassification

Token分类头

Encoder-based

每个token的标签 ([batch, seq])

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. 分词过程示例

假设我们的句子是:“乌鸦的羽毛是乌黑的”。

  1. ​首先,进行基础分词​​(可能按空格和 punctuation):["乌鸦", "的", "羽毛", "是", "乌黑的"]

  2. ​然后,对每个词应用WordPiece算法​​:

    • 乌鸦” -> 被拆分为:["乌", "##鸦"]

      • 乌” 是一个独立token。

      • 鸦” 不是一个常见的独立词,所以被标记为子词 ##鸦

    • 的” -> 是一个常见词,作为独立token:["的"]

    • 羽毛” -> 被拆分为:["羽", "##毛"]

    • 是” -> 独立token:["是"]

    • 乌黑的” -> 被拆分为:["乌", "##黑", "##的"]

      • 乌” 是词的开头,用独立token

      • 黑” 在这里是词的中间部分,用子词token ##黑

      • 的” 在这里是词的尾部,用子词token ##的

所以,整个句子的最终token序列是:

[“乌”, “##鸦”, “的”, “羽”, “##毛”, “是”, “乌”, “##黑”, “##的”]

总结与为什么重要

特征

独立 Token (如 )

子词 Token (如 ##黑)

​用途​

作为词语的开头部分

​仅​​作为词语的非开头部分(中间或尾部)

​前缀​

无前缀

有特定前缀(如WordPiece用 ##, SentencePiece用 

​例子​

“黑” -> [黑]; “黑色” -> [黑, ##色]

“乌黑” -> [乌, ##黑]; “黑黑” -> [黑, ##黑]

​设计目的​

代表常见的、可以独立成词或引领一个复合词的词汇单元

代表常见的词缀、字根,可以无限组合,​​极大地增强模型处理未知词汇的能力​

​这种机制的优势在于:​

模型只需要学习“黑”这一个字根的含义,它就能理解所有包含“黑”的词语(如“黑暗”、“黑客”、“抹黑”、“乌黑”等),即使这些词没有全部出现在训练数据中。这极大地提高了模型的泛化能力和词汇表的效率。

所以,vocab.txt中存在这两个token并不是错误或冗余,而是模型高效处理语言的关键设计。你在使用tokenizer时,它会自动帮你处理好这些规则,你通常不需要手动去选择使用 还是 ##黑

 

体原因:

  1. 解决未登录词问题
    中文虽然以字为基本单位,但存在大量组合词。WordPiece 通过将词拆分为子词(如 “巧克力” 拆分为 “巧”+“克”+“# 力”),既能减少词汇表大小,又能让模型处理未见过的组合词(通过子词拼接理解)。
  2. 保留词边界信息
    #符号本质是 “词边界标记”,告诉模型 “# 黑” 中的 “黑” 不是一个独立词的开头,而是前一个子词的延续。这有助于模型理解词语的结构和语义(例如 “黑板” 和 “板黑” 含义不同,通过子词拆分和#标记能区分)。

举例说明:

对于 “黑科技” 这个词,可能的切分结果是:["黑", "科", "#技"]

  • “黑” 作为词的开头,单独出现;
  • “科” 作为中间部分,单独出现;
  • “# 技” 中的#表示它是 “科” 的延续,合起来构成 “科技”。

这种设计让分词器既能灵活处理各种词汇组合,又能为模型保留足够的结构信息,提升对复杂文本的理解能力。
 
 
 

huggingface的datasets和Pytorch的Dataset的区别

教程中额外定义 Mydataset 类(继承自 torch.utils.data.Dataset),核心原因是 datasets 库的 Dataset 和 PyTorch 原生的 Dataset 定位不同—— 前者更擅长 “数据加载与管理”,后者是 “PyTorch 训练流水线的标准接口”,自定义类是为了在两者之间搭建桥梁,满足 PyTorch 训练的灵活需求。下面从 3 个关键角度拆解必要性:

1. 本质区别:datasets.Dataset vs torch.utils.data.Dataset

首先要明确:你用 load_from_disk 加载的是 datasets 库的 Dataset 对象(或 DatasetDict),而教程定义的 Mydataset 是 PyTorch 原生的 Dataset 子类。两者的核心定位完全不同:

对比维度 datasets.Dataset(Hugging Face) torch.utils.data.Dataset(PyTorch 原生)
所属生态 Hugging Face datasets 库(数据管理专用) PyTorch 生态(训练流水线核心接口)
核心作用 加载、缓存、预处理公开 / 自定义数据集(如切分、过滤) 定义 “数据读取规则”,为 PyTorch DataLoader 提供标准输入
能否直接用 DataLoader 支持但不灵活(需适配) 必须用(DataLoader 只认原生子类)

简单说:PyTorch 的训练核心工具 DataLoader(负责批量加载、多线程读取数据),只接受 PyTorch 原生 Dataset 子类作为输入。虽然 datasets.Dataset 也能勉强配合 DataLoader,但自定义 Mydataset 能更灵活地控制 “数据读取 + 预处理” 的全流程 —— 这是教程写这个类的核心原因。

2. 核心需求:为 PyTorch 训练做 “定制化适配”

你的代码(print(train[i]))只是 “查看数据”,而实际训练模型需要做 “数据→模型输入” 的定制化处理Mydataset 类正是为这个需求设计的。举几个教程可能隐含的场景:

场景 1:统一 “数据读取接口”,适配 DataLoader

训练时需要用 DataLoader 批量加载数据(比如一次加载 32 条数据),而 DataLoader 依赖 PyTorch 原生 Dataset 的 3 个核心方法:

  • __init__:初始化数据(加载数据集、指定子集如 train/test);
  • __len__:告诉 DataLoader 数据集总长度(用于计算批次数量);
  • __getitem__:告诉 DataLoader“如何读取单条数据”(如取 text 和 label)。
教程的 Mydataset 正好实现了这 3 个方法,让 DataLoader 能无缝读取数据:
from torch.utils.data import DataLoader

# 1. 用自定义 Mydataset 加载训练集
train_dataset = Mydataset("train")
# 2. 用 DataLoader 批量加载(PyTorch 训练必备)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

# 3. 训练时循环读取批次数据(标准 PyTorch 流程)
for batch in train_loader:
    texts, labels = batch  # 直接拿到批量的 text 和 label
    # 后续:用模型处理 texts,计算损失...

如果直接用 datasets.Dataset 配合 DataLoader,虽然能运行,但无法灵活控制 __getitem__ 中的逻辑(比如下面的预处理)。

场景 2:在 __getitem__ 中嵌入 “数据预处理”

模型需要的不是 raw 文本(text),而是 分词后的 input_idsattention_mask 等格式。这些预处理逻辑可以直接写在 __getitem__ 里,让数据读取时自动完成预处理:
class Mydataset(Dataset):
    def __init__(self, split, tokenizer):
        self.dataset = load_from_disk(r"D:\data\datasets\ChnSentiCorp")[split]
        self.tokenizer = tokenizer  # 传入分词器(如 BertTokenizer)
    
    def __len__(self):
        return len(self.dataset)
    
    def __getitem__(self, item):
        text = self.dataset[item]["text"]
        label = self.dataset[item]["label"]
        
        # 关键:读取数据时自动分词(无需单独写预处理代码)
        inputs = self.tokenizer(
            text,
            truncation=True,
            padding="max_length",
            max_length=512,
            return_tensors="pt"  # 直接返回 PyTorch 张量
        )
        
        # 返回模型能直接用的格式(inputs + label)
        return inputs["input_ids"].squeeze(), inputs["attention_mask"].squeeze(), label
 
这种方式能让 “数据加载” 和 “预处理” 无缝衔接,代码更简洁,也避免了预处理后的数据占用大量内存(预处理在读取单条数据时实时执行)。

场景 3:灵活处理特殊需求(过滤、特征新增等)

如果需要对数据做特殊处理(如过滤短文本、新增特征),可以在 __getitem__ 或 __init__ 中直接实现:
def __getitem__(self, item):
    text = self.dataset[item]["text"]
    label = self.dataset[item]["label"]
    
    # 需求1:过滤长度小于 5 的无效文本
    if len(text) < 5:
        raise IndexError("跳过短文本")  # 后续可配合 DataLoader 的 drop_last 处理
    
    # 需求2:新增“文本长度”特征(假设模型需要)
    text_length = len(text)
    
    return text, label, text_length
如果直接用 datasets.Dataset,虽然也能做过滤,但需要额外写代码生成新的数据集;而自定义 Mydataset 能在读取时实时处理,更灵活。

3. 总结:你的代码 vs 教程代码的目标不同

  • 你的代码:目标是 “加载并查看数据集”,只需要 datasets 库的加载能力,不需要适配训练流程;
  • 教程代码:目标是 “为 PyTorch 训练准备数据”,需要通过自定义 Mydataset 类,实现:
    1. 符合 PyTorch DataLoader 的标准接口;
    2. 嵌入数据预处理逻辑(分词、格式转换);
    3. 支持灵活的特殊需求(过滤、特征新增)。
简单说:load_from_disk 是 “把数据拿出来”,Mydataset 是 “把数据改成模型能吃的样子,并按训练要求喂给模型”—— 前者是数据管理,后者是训练适配,两者缺一不可。
 
 

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需要有读或写权限:

imageimage

image

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调用​权限说明

pipeline 在很多情况下确实可以不指定 token 直接使用,这取决于你使用的模型类型和来源:
  1. 默认模型(如情感分析的默认模型)
    当你不指定 model 参数时(如 pipeline("sentiment-analysis")),Hugging Face 会使用一个官方提供的默认模型(通常是 distilbert-base-uncased-finetuned-sst-2-english)。这类模型是公开可访问的,不需要认证,因此可以直接使用,无需提供 token
  2. 需要权限的模型
    当你明确指定某些模型(如 gpt2bert-base-chinese 等),或者使用私有模型、需要同意使用条款的模型时,就需要提供 token 进行身份验证。例如:
    • 某些大模型(如 meta-llama/Llama-2-7b)需要先在 Hugging Face 官网申请访问权限,再用 token 验证
    • 私有仓库中的模型需要 token 才能访问
  3. 版本差异
    早期的 transformers 版本中,即使是公开模型,有时也需要 token 进行基本的 API 调用验证,但现在大多数公开模型已经不需要了。
你的示例代码之所以能直接运行,是因为使用了默认的公开模型。如果将其改为需要权限的模型,就必须提供 token 了,例如:
from transformers import pipeline

# 使用默认模型(无需token)
classifier = pipeline("sentiment-analysis")
print(classifier("Today is a nice day"))

# 使用需要验证的模型(需token)
classifier = pipeline("sentiment-analysis", model="cardiffnlp/twitter-roberta-base-sentiment", token="你的token")
print(classifier("Today is a nice day"))
 
总结来说:公开的默认模型可以直接使用 pipeline 而无需 token,但特定模型或私有模型需要提供 token 进行身份验证

 

posted @ 2025-08-26 23:12  指尖下的世界  阅读(18)  评论(0)    收藏  举报