分析问题
问题的起源
事情起因是这样的,我们要使用huggingface,但是日常因为联网问题无法加载模型。因为是服务器集群,搭梯子让我们在docker里弄,所以像我这种懒人,docker?算了,我还是直接手动下载吧,具体看这:服务器huggingface联网通用方案
-
问题:不能联网下模型,不想搭梯子
-
解决:缓存模型
但是我是个懒人,我解决的方法只直接在系统默认缓存位置的.cache
里缓存文件。
这会带来另一个问题。模型加载过程中会有很大一部分在加载模型。
因为如果你没修改过from_pretrained
的force_download (bool, optional, defaults to False)
,那么他加载模型的逻辑是这样的:
需要加载模型的时候:
去缓存里找-->找不到-->去huggingface下载-->下载不下来-->去缓存里找-->没找到-->报错
| |
-->找到-->加载模型 -->找到-->加载模型
我的实验用到4个预训练模型,以及n个huggingface的评价指标。
这个过程会耗费大量的时间。
耗费大量时间,根本无所谓,毕竟我懒,根本不想改代码。
但是。问题来了。在我6卡V100跑了两个多周之后,师兄跑来问我是不是实验卡了。
因为他采样时候是这样的,只占一捏捏缓存,GPU是不用的。
为了不挨骂,我决定改一下代码。
修改
评价指标
先说评价指标:
def compute_perplexity(all_texts_list, model_id='gpt2-large'):
torch.cuda.empty_cache()
perplexity = load("perplexity", module_type="metric")
results = perplexity.compute(predictions=all_texts_list, model_id=model_id, device='cuda')
return results['mean_perplexity']
我这段代码里需要加载评价指标perplexity和加载模型gpt2-large。
现看加载评价指标的load:
这里写的:
a local path to processing script or the directory containing the script (if the script has the same name as the directory),e.g.
'./metrics/rouge'
or'./metrics/rouge/rouge.py'
需要你把评价指标外嵌套一个同名的文件夹。所以文件应该如下放置。
为了确保准确,我这里传入绝对位置,需要借助os
# 获得当前文件夹的目录
import os
cur_dir = os.path.split(__file__)[0]
os.path.split(__file__)
: __file__
是Python中的一个内置变量,它指向当前脚本文件的路径。os.path.split()
函数将这个路径分为两部分,第一部分是文件所在的目录,第二部分是文件名。例如,如果 __file__
的值是'https://b2.7b2.com/path/to/script.py'
,那么 os.path.split(__file__)
将返回 ('/path/to', 'script.py')
。
cur_dir = os.path.split(__file__)[0]
: 这一行代码获取了当前脚本文件所在的目录,即 '/path/to'
。它将目录部分提取出来并存储在变量 cur_dir
中。
f"{cur_dir}/perplexity"
即可获得我当前目录存的perplexity。
模型
因为perplexity需要用到预训练模型,我们可以看到我们传入的model_id
是模型名,点进这个计算函数看一下。
可以看到传入的model_id
直接在这里进行了模型加载。
from_pretrained
实现代码注释这样写。
A string, the model id of a predefined tokenizer hosted inside a model repo on huggingface.co. Valid model ids can be located at the root-level, like
bert-base-uncased
, or namespaced under auser or organization name, likedbmdz/bert-base-german-cased
.
传入本地文件夹包含模型文件即可。
所以我们只需要将缓存中的模型及其配置新建个文件夹放进去即可。
看一下文件夹嵌套就知道为什么传入绝对地址了。
compute_perplexity
是evaluation.py的函数,但是这个函数引用了同目录下的perplexity/perplexity.py,但是这个perplexity.py要load上上级文件夹下的模型。为了防止混乱,我们还是使用os.path
获取位置比较方便。
import os
model_id = "gpt2-large"
cur_dir = os.path.split(__file__)[0]
model_dir = '/'.join(cur_dir.split('/')[:-1])
print(f"{model_dir}/assist_model/{model_id}")
这段代码用于获取当前脚本文件所在目录的上级目录(parent directory)的路径,通常用于构建文件路径或定位相关资源文件。
cur_dir.split('/')
: 这一行代码使用斜杠 /
分割 cur_dir
中的路径字符串,将其拆分成一个列表。例如,如果 cur_dir
的值是 '/path/to'
,那么 cur_dir.split('/')
将返回 ['', 'path', 'to']
。
[:-1]
: 这是一个切片操作,它用于获取列表中除了最后一个元素之外的所有元素。在这里,[:-1]
表示获取拆分后列表中的所有元素,除了最后一个元素 'to'
。
'/'
:这是字符串,用于将列表中的元素连接起来,形成一个新的路径字符串。
最终,model_dir
的值将是当前脚本文件所在的目录的上级目录的路径,具体取决于当前脚本文件的位置和文件系统的路径分隔符。
这样我们就能在evaluation.py获取到上级文件夹的模型。
修改之后的代码如下,我们就可以全部从本地文件夹直接加载了。
import os
cur_dir = os.path.split(__file__)[0]
model_dir = '/'.join(cur_dir.split('/')[:-1])
def compute_perplexity(all_texts_list, model_id='gpt2-large'):
torch.cuda.empty_cache()
perplexity = load(f"{cur_dir}/perplexity", module_type="metric")
model_id = f"{model_dir}/assist_model/{model_id}"
results = perplexity.compute(predictions=all_texts_list, model_id=model_id, device='cuda')
return results['mean_perplexity']
总结
-
找到
load
和from_pretrained
-
把模型丢个文件夹里
-
用
os.path
传入绝对路径
其他
问题1
提问:
为什么要在项目里搞个本地文件夹加载模型和评价指标?直接load的时候从本地缓存加载不就行了。
回答:
我都要load本地地址了,我为什么不搞个好load的,还要搞那一长串缓存地址?
C:Usersann.cachehuggingfacehubmodels–gpt2-largesnapshots97935fc1a406f447320c3db70fe9e9875dca2595
D:ld4_offmodelgpt2-large
1和2我选2.
问题2
提问: 嫌弃默认的.cache
,为什么不改个缓存地址?
回答: 确实,
from transformers import set_cache_dir, AutoModel
# 设置全局的缓存目录
set_cache_dir('/path/to/cache/directory')
# 使用from_pretrained函数加载模型(确保引用全局缓存目录)
model = AutoModel.from_pretrained('bert-base-uncased',cache_dir='/path/to/model/cache')
# 此时模型将从缓存加载,如果缓存不存在,它将尝试从缓存加载,而不重新下载
这样可以修改缓存位置,但是,他好像还是缓存啊……,还是从缓存里读的那个逻辑,还是要走他那个从cache加载的逻辑,不如直接load本地模型吧。我个人感觉是直接加载本地模型更快。
信我,速改,改完以后极其丝滑: