是否可以从 GCS bucket URL 加载预训练的 Pytorch 模型而不首先在本地持久化?
Is it possible to load a pretrained Pytorch model from a GCS bucket URL without first persisting locally?
我是在 Google 数据流的上下文中问这个问题,但也是一般的。
使用 PyTorch,我可以引用包含多个文件的本地目录,这些文件构成了预训练模型。我碰巧在使用 Roberta 模型,但其他人的界面是相同的。
ls some-directory/
added_tokens.json
config.json
merges.txt
pytorch_model.bin
special_tokens_map.json vocab.json
from pytorch_transformers import RobertaModel
# this works
model = RobertaModel.from_pretrained('/path/to/some-directory/')
但是,我的预训练模型存储在 GCS 存储桶中。我们称它为 gs://my-bucket/roberta/
。
在 Google Dataflow 中加载此模型的上下文中,我试图保持无状态并避免持久保存到磁盘,因此我更倾向于直接从 GCS 获取此模型。据我了解,PyTorch 通用接口方法 from_pretrained()
可以采用本地目录或 URL 的字符串表示形式。但是,我似乎无法从 GCS URL.
加载模型
# this fails
model = RobertaModel.from_pretrained('gs://my-bucket/roberta/')
# ValueError: unable to parse gs://mahmed_bucket/roberta-base as a URL or as a local path
如果我尝试使用目录 blob 的 public https URL,它也会失败,尽管这可能是由于 lack of authentication,因为 python 可以创建客户端的环境不会转换为 public 对 https://storage.googleapis
的请求
# this fails, probably due to auth
bucket = gcs_client.get_bucket('my-bucket')
directory_blob = bucket.blob(prefix='roberta')
model = RobertaModel.from_pretrained(directory_blob.public_url)
# ValueError: No JSON object could be decoded
# and for good measure, it also fails if I append a trailing /
model = RobertaModel.from_pretrained(directory_blob.public_url + '/')
# ValueError: No JSON object could be decoded
我知道 GCS doesn't actually have subdirectories 它实际上只是存储桶名称下的平面命名空间。但是,似乎我被身份验证的必要性和 PyTorch 不说话所阻止 gs://
.
我可以通过先在本地保存文件来解决这个问题。
from pytorch_transformers import RobertaModel
from google.cloud import storage
import tempfile
local_dir = tempfile.mkdtemp()
gcs = storage.Client()
bucket = gcs.get_bucket(bucket_name)
blobs = bucket.list_blobs(prefix=blob_prefix)
for blob in blobs:
blob.download_to_filename(local_dir + '/' + os.path.basename(blob.name))
model = RobertaModel.from_pretrained(local_dir)
但这似乎是个 hack,我一直认为我一定遗漏了一些东西。当然有一种方法可以保持无状态并且不必依赖磁盘持久性!
- 那么有没有办法加载存储在 GCS 中的预训练模型?
- 在此上下文中执行 public URL 请求时,是否有验证方法?
- 即使有认证方式,子目录不存在还会是问题吗?
感谢您的帮助!我也很高兴被指出任何重复的问题,因为我确实找不到任何问题。
编辑和澄清
我的 Python 会话已经通过 GCS 的身份验证,这就是为什么我能够在本地下载 blob 文件然后指向该本地目录 load_frompretrained()
load_frompretrained()
需要目录引用,因为它需要问题顶部列出的所有文件,而不仅仅是 pytorch-model.bin
为了澄清问题 #2,我想知道是否有某种方法可以向 PyTorch 方法提供一个请求 URL,该请求 URL 嵌入了加密凭据或类似的东西。有点远景,但我想确保我没有错过任何东西。
为了澄清问题 #3(除了对下面一个答案的评论之外),即使有一种方法可以在我不知道的 URL 中嵌入凭据,我仍然需要引用 目录 而不是单个 blob,而且我不知道 GCS 子目录是否会被识别,因为(如 Google 文档状态) GCS 中的子目录是一种错觉,它们并不代表真实的目录结构。所以我认为这个问题无关紧要,或者至少被问题 #2 阻止了,但这是我追逐的一个线程,所以我仍然很好奇。
如您正确所述,开箱即用的 pytorch-transformers
似乎不支持此功能,但主要是因为它无法将文件 link 识别为 URL。
经过一番查找,我在this source file的第144-155行附近找到了相应的错误信息。
当然,您可以尝试将 'gs'
标记添加到第 144 行,然后将您与 GCS 的连接解释为 http
请求(第 269-272 行)。如果 GCS 接受这一点,那应该是唯一需要改变才能工作的东西。
如果这不起作用,唯一直接的解决方法是实现类似于 Amazon S3 存储桶功能的东西,但我对 S3 和 GCS 存储桶的了解还不够多,无法在这里做出任何有意义的判断。
我不太了解 Pytorch 或 Roberta 模型,但我会尝试回答您关于 GCS 的问题:
1.- "So is there a way to load a pretrained model stored in GCS?"
如果您的模型可以直接从二进制文件加载 Blob:
from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket("bucket name")
blob = bucket.blob("path_to_blob/blob_name.ext")
data = blob.download_as_string() # you will have your binary data transformed into string here.
2.- "Is there a way to authenticate when doing the public URL request in this context?"
这是棘手的部分,因为根据您使用的上下文 运行 脚本,它将使用默认服务帐户进行身份验证。因此,当您使用官方 GCP 库时,您可以:
A.- 授予该默认服务帐户访问您的 bucket/objects.
的权限
B.- 创建一个新的服务帐户并在脚本中使用它进行身份验证(您还需要为该服务帐户生成身份验证令牌):
from google.cloud import storage
from google.oauth2 import service_account
VISION_SCOPES = ['https://www.googleapis.com/auth/devstorage']
SERVICE_ACCOUNT_FILE = 'key.json'
cred = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=VISION_SCOPES)
client = storage.Client(credentials=cred)
bucket = client.get_bucket("bucket_name")
blob = bucket.blob("path/object.ext")
data = blob.download_as_string()
然而,这是可行的,因为官方库在后台处理对 API 调用的身份验证,因此在 from_pretrained() 函数的情况下不起作用。
所以替代方法是创建对象 public,这样您就可以在使用 public url.
时访问它
3.- "Even if there is a way to authenticate, will the non-existence of subdirectories still be an issue?"
不确定你的意思,你的存储桶中可以有文件夹。
主要编辑:
您可以在 Dataflow worker 上安装 wheel 文件,也可以使用 worker temp storage 在本地持久化二进制文件!
确实(截至 2019 年 11 月)您不能通过提供 --requirements
参数来做到这一点。相反,您必须像这样使用 setup.py
。假设大写的任何常量都在别处定义。
REQUIRED_PACKAGES = [
'torch==1.3.0',
'pytorch-transformers==1.2.0',
]
setup(
name='project_dir',
version=VERSION,
packages=find_packages(),
install_requires=REQUIRED_PACKAGES)
运行 脚本
python setup.py sdist
python project_dir/my_dataflow_job.py \
--runner DataflowRunner \
--project ${GCP_PROJECT} \
--extra_package dist/project_dir-0.1.0.tar.gz \
# SNIP custom args for your job and required Dataflow Temp and Staging buckets #
在作业中,这里是在自定义数据流运算符的上下文中从 GCS 下载和使用模型。为了方便起见,我们将一些实用方法包装在一个单独的模块中(对于解决数据流依赖上传很重要)并将它们导入自定义运算符的本地范围,而不是全局范围。
class AddColumn(beam.DoFn):
PRETRAINED_MODEL = 'gs://my-bucket/blah/roberta-model-files'
def get_model_tokenizer_wrapper(self):
import shutil
import tempfile
import dataflow_util as util
try:
return self.model_tokenizer_wrapper
except AttributeError:
tmp_dir = tempfile.mkdtemp() + '/'
util.download_tree(self.PRETRAINED_MODEL, tmp_dir)
model, tokenizer = util.create_model_and_tokenizer(tmp_dir)
model_tokenizer_wrapper = util.PretrainedPyTorchModelWrapper(
model, tokenizer)
shutil.rmtree(tmp_dir)
self.model_tokenizer_wrapper = model_tokenizer_wrapper
logging.info(
'Successfully created PretrainedPyTorchModelWrapper')
return self.model_tokenizer_wrapper
def process(self, elem):
model_tokenizer_wrapper = self.get_model_tokenizer_wrapper()
# And now use that wrapper to process your elem however you need.
# Note that when you read from BQ your elements are dictionaries
# of the column names and values for each BQ row.
代码库中独立模块中的实用函数。在我们的项目根目录中,这是在 dataflow_util/init.py 中,但您不必那样做。
from contextlib import closing
import logging
import apache_beam as beam
import numpy as np
from pytorch_transformers import RobertaModel, RobertaTokenizer
import torch
class PretrainedPyTorchModelWrapper():
def __init__(self, model, tokenizer):
self.model = model
self.tokenizer = tokenizer
def download_tree(gcs_dir, local_dir):
gcs = beam.io.gcp.gcsio.GcsIO()
assert gcs_dir.endswith('/')
assert local_dir.endswith('/')
for entry in gcs.list_prefix(gcs_dir):
download_file(gcs, gcs_dir, local_dir, entry)
def download_file(gcs, gcs_dir, local_dir, entry):
rel_path = entry[len(gcs_dir):]
dest_path = local_dir + rel_path
logging.info('Downloading %s', dest_path)
with closing(gcs.open(entry)) as f_read:
with open(dest_path, 'wb') as f_write:
# Download the file in chunks to avoid requiring large amounts of
# RAM when downloading large files.
while True:
file_data_chunk = f_read.read(
beam.io.gcp.gcsio.DEFAULT_READ_BUFFER_SIZE)
if len(file_data_chunk):
f_write.write(file_data_chunk)
else:
break
def create_model_and_tokenizer(local_model_path_str):
"""
Instantiate transformer model and tokenizer
:param local_model_path_str: string representation of the local path
to the directory containing the pretrained model
:return: model, tokenizer
"""
model_class, tokenizer_class = (RobertaModel, RobertaTokenizer)
# Load the pretrained tokenizer and model
tokenizer = tokenizer_class.from_pretrained(local_model_path_str)
model = model_class.from_pretrained(local_model_path_str)
return model, tokenizer
这就是伙计们!可以在此处找到更多详细信息:https://beam.apache.org/documentation/sdks/python-pipeline-dependencies/
我发现,这整个提问链是无关紧要的,因为 Dataflow 只允许您在 worker 上安装源分发包,这意味着您实际上无法安装 PyTorch。
当您提供 requirements.txt
文件时,Dataflow 将使用 --no-binary
标志进行安装,该标志会阻止安装 Wheel (.whl) 包并且只允许源代码分发 (.tar.gz) .我决定尝试在 Google Dataflow 上推出我自己的 PyTorch 源代码分发,其中一半是 C++,一部分是 Cuda,还有一部分谁知道这是傻瓜的差事。
感谢大家的参与。
目前我没有和 Roberta 一起玩,而是和 Bert 一起玩 Token classNER 化,但我认为它具有相同的机制..
下面是我的代码:
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'your_gcs_auth.json'
# initiate storage
client = storage.Client()
en_bucket = client.get_bucket('your-gcs-bucketname')
# get blob
en_model_blob = en_bucket.get_blob('your-modelname-in-gcsbucket.bin')
en_model = en_model_blob.download_as_string()
# because model downloaded into string, need to convert it back
buffer = io.BytesIO(en_model)
# prepare loading model
state_dict = torch.load(buffer, map_location=torch.device('cpu'))
model = BertForTokenClassification.from_pretrained(pretrained_model_name_or_path=None, state_dict=state_dict, config=main_config)
model.load_state_dict(state_dict)
我不确定 download_as_string()
方法是否将数据保存到本地磁盘,但根据我的经验,如果我执行 download_to_filename()
该函数会将模型下载到我的本地。
另外,如果您修改了 transformers 网络的配置(并将其放入 GCS 并且还需要加载),您还需要修改 class PretrainedConfig
,因为它可以处理download_as_string()
函数生成的文件。
干杯,希望对你有帮助
我是在 Google 数据流的上下文中问这个问题,但也是一般的。
使用 PyTorch,我可以引用包含多个文件的本地目录,这些文件构成了预训练模型。我碰巧在使用 Roberta 模型,但其他人的界面是相同的。
ls some-directory/
added_tokens.json
config.json
merges.txt
pytorch_model.bin
special_tokens_map.json vocab.json
from pytorch_transformers import RobertaModel
# this works
model = RobertaModel.from_pretrained('/path/to/some-directory/')
但是,我的预训练模型存储在 GCS 存储桶中。我们称它为 gs://my-bucket/roberta/
。
在 Google Dataflow 中加载此模型的上下文中,我试图保持无状态并避免持久保存到磁盘,因此我更倾向于直接从 GCS 获取此模型。据我了解,PyTorch 通用接口方法 from_pretrained()
可以采用本地目录或 URL 的字符串表示形式。但是,我似乎无法从 GCS URL.
# this fails
model = RobertaModel.from_pretrained('gs://my-bucket/roberta/')
# ValueError: unable to parse gs://mahmed_bucket/roberta-base as a URL or as a local path
如果我尝试使用目录 blob 的 public https URL,它也会失败,尽管这可能是由于 lack of authentication,因为 python 可以创建客户端的环境不会转换为 public 对 https://storage.googleapis
# this fails, probably due to auth
bucket = gcs_client.get_bucket('my-bucket')
directory_blob = bucket.blob(prefix='roberta')
model = RobertaModel.from_pretrained(directory_blob.public_url)
# ValueError: No JSON object could be decoded
# and for good measure, it also fails if I append a trailing /
model = RobertaModel.from_pretrained(directory_blob.public_url + '/')
# ValueError: No JSON object could be decoded
我知道 GCS doesn't actually have subdirectories 它实际上只是存储桶名称下的平面命名空间。但是,似乎我被身份验证的必要性和 PyTorch 不说话所阻止 gs://
.
我可以通过先在本地保存文件来解决这个问题。
from pytorch_transformers import RobertaModel
from google.cloud import storage
import tempfile
local_dir = tempfile.mkdtemp()
gcs = storage.Client()
bucket = gcs.get_bucket(bucket_name)
blobs = bucket.list_blobs(prefix=blob_prefix)
for blob in blobs:
blob.download_to_filename(local_dir + '/' + os.path.basename(blob.name))
model = RobertaModel.from_pretrained(local_dir)
但这似乎是个 hack,我一直认为我一定遗漏了一些东西。当然有一种方法可以保持无状态并且不必依赖磁盘持久性!
- 那么有没有办法加载存储在 GCS 中的预训练模型?
- 在此上下文中执行 public URL 请求时,是否有验证方法?
- 即使有认证方式,子目录不存在还会是问题吗?
感谢您的帮助!我也很高兴被指出任何重复的问题,因为我确实找不到任何问题。
编辑和澄清
我的 Python 会话已经通过 GCS 的身份验证,这就是为什么我能够在本地下载 blob 文件然后指向该本地目录
load_frompretrained()
load_frompretrained()
需要目录引用,因为它需要问题顶部列出的所有文件,而不仅仅是pytorch-model.bin
为了澄清问题 #2,我想知道是否有某种方法可以向 PyTorch 方法提供一个请求 URL,该请求 URL 嵌入了加密凭据或类似的东西。有点远景,但我想确保我没有错过任何东西。
为了澄清问题 #3(除了对下面一个答案的评论之外),即使有一种方法可以在我不知道的 URL 中嵌入凭据,我仍然需要引用 目录 而不是单个 blob,而且我不知道 GCS 子目录是否会被识别,因为(如 Google 文档状态) GCS 中的子目录是一种错觉,它们并不代表真实的目录结构。所以我认为这个问题无关紧要,或者至少被问题 #2 阻止了,但这是我追逐的一个线程,所以我仍然很好奇。
如您正确所述,开箱即用的 pytorch-transformers
似乎不支持此功能,但主要是因为它无法将文件 link 识别为 URL。
经过一番查找,我在this source file的第144-155行附近找到了相应的错误信息。
当然,您可以尝试将 'gs'
标记添加到第 144 行,然后将您与 GCS 的连接解释为 http
请求(第 269-272 行)。如果 GCS 接受这一点,那应该是唯一需要改变才能工作的东西。
如果这不起作用,唯一直接的解决方法是实现类似于 Amazon S3 存储桶功能的东西,但我对 S3 和 GCS 存储桶的了解还不够多,无法在这里做出任何有意义的判断。
我不太了解 Pytorch 或 Roberta 模型,但我会尝试回答您关于 GCS 的问题:
1.- "So is there a way to load a pretrained model stored in GCS?"
如果您的模型可以直接从二进制文件加载 Blob:
from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket("bucket name")
blob = bucket.blob("path_to_blob/blob_name.ext")
data = blob.download_as_string() # you will have your binary data transformed into string here.
2.- "Is there a way to authenticate when doing the public URL request in this context?"
这是棘手的部分,因为根据您使用的上下文 运行 脚本,它将使用默认服务帐户进行身份验证。因此,当您使用官方 GCP 库时,您可以:
A.- 授予该默认服务帐户访问您的 bucket/objects.
的权限B.- 创建一个新的服务帐户并在脚本中使用它进行身份验证(您还需要为该服务帐户生成身份验证令牌):
from google.cloud import storage
from google.oauth2 import service_account
VISION_SCOPES = ['https://www.googleapis.com/auth/devstorage']
SERVICE_ACCOUNT_FILE = 'key.json'
cred = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=VISION_SCOPES)
client = storage.Client(credentials=cred)
bucket = client.get_bucket("bucket_name")
blob = bucket.blob("path/object.ext")
data = blob.download_as_string()
然而,这是可行的,因为官方库在后台处理对 API 调用的身份验证,因此在 from_pretrained() 函数的情况下不起作用。
所以替代方法是创建对象 public,这样您就可以在使用 public url.
时访问它3.- "Even if there is a way to authenticate, will the non-existence of subdirectories still be an issue?"
不确定你的意思,你的存储桶中可以有文件夹。
主要编辑:
您可以在 Dataflow worker 上安装 wheel 文件,也可以使用 worker temp storage 在本地持久化二进制文件!
确实(截至 2019 年 11 月)您不能通过提供 --requirements
参数来做到这一点。相反,您必须像这样使用 setup.py
。假设大写的任何常量都在别处定义。
REQUIRED_PACKAGES = [
'torch==1.3.0',
'pytorch-transformers==1.2.0',
]
setup(
name='project_dir',
version=VERSION,
packages=find_packages(),
install_requires=REQUIRED_PACKAGES)
运行 脚本
python setup.py sdist
python project_dir/my_dataflow_job.py \
--runner DataflowRunner \
--project ${GCP_PROJECT} \
--extra_package dist/project_dir-0.1.0.tar.gz \
# SNIP custom args for your job and required Dataflow Temp and Staging buckets #
在作业中,这里是在自定义数据流运算符的上下文中从 GCS 下载和使用模型。为了方便起见,我们将一些实用方法包装在一个单独的模块中(对于解决数据流依赖上传很重要)并将它们导入自定义运算符的本地范围,而不是全局范围。
class AddColumn(beam.DoFn):
PRETRAINED_MODEL = 'gs://my-bucket/blah/roberta-model-files'
def get_model_tokenizer_wrapper(self):
import shutil
import tempfile
import dataflow_util as util
try:
return self.model_tokenizer_wrapper
except AttributeError:
tmp_dir = tempfile.mkdtemp() + '/'
util.download_tree(self.PRETRAINED_MODEL, tmp_dir)
model, tokenizer = util.create_model_and_tokenizer(tmp_dir)
model_tokenizer_wrapper = util.PretrainedPyTorchModelWrapper(
model, tokenizer)
shutil.rmtree(tmp_dir)
self.model_tokenizer_wrapper = model_tokenizer_wrapper
logging.info(
'Successfully created PretrainedPyTorchModelWrapper')
return self.model_tokenizer_wrapper
def process(self, elem):
model_tokenizer_wrapper = self.get_model_tokenizer_wrapper()
# And now use that wrapper to process your elem however you need.
# Note that when you read from BQ your elements are dictionaries
# of the column names and values for each BQ row.
代码库中独立模块中的实用函数。在我们的项目根目录中,这是在 dataflow_util/init.py 中,但您不必那样做。
from contextlib import closing
import logging
import apache_beam as beam
import numpy as np
from pytorch_transformers import RobertaModel, RobertaTokenizer
import torch
class PretrainedPyTorchModelWrapper():
def __init__(self, model, tokenizer):
self.model = model
self.tokenizer = tokenizer
def download_tree(gcs_dir, local_dir):
gcs = beam.io.gcp.gcsio.GcsIO()
assert gcs_dir.endswith('/')
assert local_dir.endswith('/')
for entry in gcs.list_prefix(gcs_dir):
download_file(gcs, gcs_dir, local_dir, entry)
def download_file(gcs, gcs_dir, local_dir, entry):
rel_path = entry[len(gcs_dir):]
dest_path = local_dir + rel_path
logging.info('Downloading %s', dest_path)
with closing(gcs.open(entry)) as f_read:
with open(dest_path, 'wb') as f_write:
# Download the file in chunks to avoid requiring large amounts of
# RAM when downloading large files.
while True:
file_data_chunk = f_read.read(
beam.io.gcp.gcsio.DEFAULT_READ_BUFFER_SIZE)
if len(file_data_chunk):
f_write.write(file_data_chunk)
else:
break
def create_model_and_tokenizer(local_model_path_str):
"""
Instantiate transformer model and tokenizer
:param local_model_path_str: string representation of the local path
to the directory containing the pretrained model
:return: model, tokenizer
"""
model_class, tokenizer_class = (RobertaModel, RobertaTokenizer)
# Load the pretrained tokenizer and model
tokenizer = tokenizer_class.from_pretrained(local_model_path_str)
model = model_class.from_pretrained(local_model_path_str)
return model, tokenizer
这就是伙计们!可以在此处找到更多详细信息:https://beam.apache.org/documentation/sdks/python-pipeline-dependencies/
我发现,这整个提问链是无关紧要的,因为 Dataflow 只允许您在 worker 上安装源分发包,这意味着您实际上无法安装 PyTorch。
当您提供 requirements.txt
文件时,Dataflow 将使用 --no-binary
标志进行安装,该标志会阻止安装 Wheel (.whl) 包并且只允许源代码分发 (.tar.gz) .我决定尝试在 Google Dataflow 上推出我自己的 PyTorch 源代码分发,其中一半是 C++,一部分是 Cuda,还有一部分谁知道这是傻瓜的差事。
感谢大家的参与。
目前我没有和 Roberta 一起玩,而是和 Bert 一起玩 Token classNER 化,但我认为它具有相同的机制..
下面是我的代码:
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'your_gcs_auth.json'
# initiate storage
client = storage.Client()
en_bucket = client.get_bucket('your-gcs-bucketname')
# get blob
en_model_blob = en_bucket.get_blob('your-modelname-in-gcsbucket.bin')
en_model = en_model_blob.download_as_string()
# because model downloaded into string, need to convert it back
buffer = io.BytesIO(en_model)
# prepare loading model
state_dict = torch.load(buffer, map_location=torch.device('cpu'))
model = BertForTokenClassification.from_pretrained(pretrained_model_name_or_path=None, state_dict=state_dict, config=main_config)
model.load_state_dict(state_dict)
我不确定 download_as_string()
方法是否将数据保存到本地磁盘,但根据我的经验,如果我执行 download_to_filename()
该函数会将模型下载到我的本地。
另外,如果您修改了 transformers 网络的配置(并将其放入 GCS 并且还需要加载),您还需要修改 class PretrainedConfig
,因为它可以处理download_as_string()
函数生成的文件。
干杯,希望对你有帮助