社区供稿 | Hugging Face 使用 Dragonfly 加速分发模型和数据集

2023/11/14 18:00
阅读数 21

Dragonfly GitHub Repo:

https://github.com/dragonflyoss/Dragonfly2

本文将帮助您将 Dragonfly 与 Hugging Face 一起使用。当在 Hugging Face 下载数据集以及模型的时候,文件相对较大且会有并发下载文件的场景。这样很容易导致 Hugging Face 的 Git LFS 存储带宽被打满,从而引起下载过慢的情况,影响训练以及推理服务的使用。

这种方式比较好的解决方案是使用 Dragonfly 的 P2P 技术利用每个节点的闲置带宽缓解 Git LFS 存储的带宽压力,从而达到加速效果。在最理想的情况下 Dragonfly 可以让整个 P2P 集群中只有一个节点回源 Hugging Face 下载数据集或模型,其他节点流量均使用集群内 P2P 内网带宽。

Part.1


集成方案

1.1 依赖

所需软件

版本要求

文档

Kubernetes cluster

1.20+

kubernetes.io

Helm

3.8.0+

helm.sh

注意: 如果没有可用的 Kubernetes 集群进行测试,推荐使用 Kind

1.2 安装 Dragonfly

基于 Kubernetes cluster 详细安装文档可以参考 quick-start-kubernetes

  • 使用 Kind 安装 Kubernetes 集群

创建 Kind 多节点集群配置文件 kind-config.yaml,配置如下:

kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes:  - role: control-plane  - role: worker    extraPortMappings:      - containerPort: 30950        hostPort: 65001  - role: worker

使用配置文件创建 Kind 集群:

kind create cluster --config kind-config.yaml

切换 Kubectl 的 context 到 Kind 集群:

kubectl config use-context kind-kind
  • Kind 加载 Dragonfly 镜像

下载 Dragonfly latest 镜像:

docker pull dragonflyoss/scheduler:latestdocker pull dragonflyoss/manager:latestdocker pull dragonflyoss/dfdaemon:latest

Kind 集群加载 Dragonfly latest 镜像:

kind load docker-image dragonflyoss/scheduler:latestkind load docker-image dragonflyoss/manager:latestkind load docker-image dragonflyoss/dfdaemon:latest
  • 基于 Helm Charts 创建 Dragonfly P2P 集群

创建 Helm Charts 配置文件 charts-config.yaml 并且设置 dfdaemon.config.proxy.registryMirror.url 为 Hugging Face 的 LFS 服务的地址, 配置如下:

scheduler:  replicas: 1  metrics:    enable: true  config:    verbose: true    pprofPort: 18066
seedPeer: replicas: 1 metrics: enable: true config: verbose: true pprofPort: 18066
dfdaemon: metrics: enable: true hostNetwork: true config: verbose: true pprofPort: 18066 proxy: defaultFilter: 'Expires&Key-Pair-Id&Policy&Signature' security: insecure: true tcpListen: listen: 0.0.0.0 port: 65001 registryMirror: # When enable, using header "X-Dragonfly-Registry" for remote instead of url. dynamic: true # URL for the registry mirror. url: https://cdn-lfs.huggingface.co # Whether to ignore https certificate errors. insecure: true # Optional certificates if the remote server uses self-signed certificates. certs: [] # Whether to request the remote registry directly. direct: false # Whether to use proxies to decide if dragonfly should be used. useProxies: true proxies: - regx: repos.* useHTTPS: true
manager: replicas: 1 metrics: enable: true config: verbose: true pprofPort: 18066

使用配置文件部署 Dragonfly Helm Charts:

$ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/$ helm install --wait --create-namespace --namespace dragonfly-system dragonfly dragonfly/dragonfly -f charts-config.yamlNAME: dragonflyLAST DEPLOYED: Wed Oct 19 04:23:22 2022NAMESPACE: dragonfly-systemSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:1. Get the scheduler address by running these commands:  export SCHEDULER_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name})  export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")  kubectl --namespace dragonfly-system port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORT  echo "Visit http://127.0.0.1:8002 to use your scheduler"
2. Get the dfdaemon port by running these commands: export DFDAEMON_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name}) export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.
3. Configure runtime to use dragonfly: https://d7y.io/docs/getting-started/quick-start/kubernetes/

检查 Dragonfly 是否部署成功:

$ kubectl get po -n dragonfly-systemNAME                                 READY   STATUS    RESTARTS       AGEdragonfly-dfdaemon-rhnr6             1/1     Running   4 (101s ago)   3m27sdragonfly-dfdaemon-s6sv5             1/1     Running   5 (111s ago)   3m27sdragonfly-manager-67f97d7986-8dgn8   1/1     Running   0              3m27sdragonfly-mysql-0                    1/1     Running   0              3m27sdragonfly-redis-master-0             1/1     Running   0              3m27sdragonfly-redis-replicas-0           1/1     Running   1 (115s ago)   3m27sdragonfly-redis-replicas-1           1/1     Running   0              95sdragonfly-redis-replicas-2           1/1     Running   0              70sdragonfly-scheduler-0                1/1     Running   0              3m27sdragonfly-seed-peer-0                1/1     Running   2 (95s ago)    3m27s

创建 Peer Service 配置文件 peer-service-config.yaml 配置如下:

apiVersion: v1kind: Servicemetadata:  name: peer  namespace: dragonfly-systemspec:  type: NodePort  ports:    - name: http-65001      nodePort: 30950      port: 65001  selector:    app: dragonfly    component: dfdaemon    release: dragonfly

使用配置文件部署 Peer Service:

kubectl apply -f peer-service-config.yaml


1.3 通过 Dragonfly 分发 Hub Python Library 的下载文件流量

任何 Hub Python Library 的 API 使用 Requests 库下载文件,都可以通过设置 DragonflyAdapter 将流量使用 Dragonfly 分发。

  • 使用 Dragonfly 下载单个文件

下载单个文件可以使用 hf_hub_download, 并且通过 Dragonfly 分发流量。


创建 hf_hub_download_dragonfly.py 文件,使用 DragonflyAdapter 将下载流量转发至 Dragonfly HTTP Proxy。这样可以通过 P2P 网络分发流量,内容如下:

import requestsfrom requests.adapters import HTTPAdapterfrom urllib.parse import urlparsefrom huggingface_hub import hf_hub_downloadfrom huggingface_hub import configure_http_backend
class DragonflyAdapter(HTTPAdapter): def get_connection(self, url, proxies=None): # Change the schema of the LFS request to download large files from https:// to http://, # so that Dragonfly HTTP proxy can be used. if url.startswith('https://cdn-lfs.huggingface.co'): url = url.replace('https://', 'http://') return super().get_connection(url, proxies)
def add_headers(self, request, **kwargs): super().add_headers(request, **kwargs)
# If there are multiple different LFS repositories, you can override the # default repository address by adding X-Dragonfly-Registry header. if request.url.find('example.com') != -1: request.headers["X-Dragonfly-Registry"] = 'https://example.com'
# Create a factory function that returns a new Session.def backend_factory() -> requests.Session: session = requests.Session() session.mount('http://', DragonflyAdapter()) session.mount('https://', DragonflyAdapter()) session.proxies = {'http': 'http://127.0.0.1:65001'} return session
# Set it as the default session factoryconfigure_http_backend(backend_factory=backend_factory)
hf_hub_download(repo_id="tiiuae/falcon-rw-1b", filename="pytorch_model.bin")

通过 Dragonfly 基于 LFS 协议下载单个文件:

$ python3 hf_hub_download_dragonfly.py(…)YkNX13a46FCg__&Key-Pair-Id=KVTP0A1DKRTAX: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.62G/2.62G [00:52<00:00, 49.8MB/s]
  • 验证基于 Dragonfly 下载单个文件

执行命令:

# find podskubectl -n dragonfly-system get pod -l component=dfdaemon# find logspod_name=dfdaemon-xxxxxkubectl -n dragonfly-system exec -it ${pod_name} -- grep "peer task done" /var/log/dragonfly/daemon/core.log

日志输出:

peer task done, cost: 28349ms   {"peer": "89.116.64.101-77008-a95a6918-a52b-47f5-9b18-cec6ada03daf", "task": "2fe93348699e07ab67823170925f6be579a3fbc803ff3d33bf9278a60b08d901", "component": "PeerTask", "trace": "b34ed802b7afc0f4acd94b2cedf3fa2a"}
  • 使用 Dragonfly 下载仓库快照

下载仓库快照可以使用 snapshot_download, 并且通过 Dragonfly 分发流量。


创建 snapshot_download_dragonfly.py 文件,使用 DragonflyAdapter 将下载流量转发至 Dragonfly HTTP Proxy。只有 Git LFS 协议的大文件流量会通过 P2P 网络分发,内容如下:

import requestsfrom requests.adapters import HTTPAdapterfrom urllib.parse import urlparsefrom huggingface_hub import snapshot_downloadfrom huggingface_hub import configure_http_backend
class DragonflyAdapter(HTTPAdapter): def get_connection(self, url, proxies=None): # Change the schema of the LFS request to download large files from https:// to http://, # so that Dragonfly HTTP proxy can be used. if url.startswith('https://cdn-lfs.huggingface.co'): url = url.replace('https://', 'http://') return super().get_connection(url, proxies)
def add_headers(self, request, **kwargs): super().add_headers(request, **kwargs)
# If there are multiple different LFS repositories, you can override the # default repository address by adding X-Dragonfly-Registry header. if request.url.find('example.com') != -1: request.headers["X-Dragonfly-Registry"] = 'https://example.com'
# Create a factory function that returns a new Session.def backend_factory() -> requests.Session: session = requests.Session() session.mount('http://', DragonflyAdapter()) session.mount('https://', DragonflyAdapter()) session.proxies = {'http': 'http://127.0.0.1:65001'} return session
# Set it as the default session factoryconfigure_http_backend(backend_factory=backend_factory)
snapshot_download(repo_id="tiiuae/falcon-rw-1b")

通过 Dragonfly 基于 LFS 协议下载仓库快照:

$ python3 snapshot_download_dragonfly.py(…)03165eb22f0a867d4e6a64d34fce19/README.md: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7.60k/7.60k [00:00<00:00, 374kB/s](…)7d4e6a64d34fce19/configuration_falcon.py: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6.70k/6.70k [00:00<00:00, 762kB/s](…)f0a867d4e6a64d34fce19/modeling_falcon.py: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 56.9k/56.9k [00:00<00:00, 5.35MB/s](…)3165eb22f0a867d4e6a64d34fce19/merges.txt: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 456k/456k [00:00<00:00, 9.07MB/s](…)867d4e6a64d34fce19/tokenizer_config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 234/234 [00:00<00:00, 106kB/s](…)eb22f0a867d4e6a64d34fce19/tokenizer.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.11M/2.11M [00:00<00:00, 27.7MB/s](…)3165eb22f0a867d4e6a64d34fce19/vocab.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 798k/798k [00:00<00:00, 19.7MB/s](…)7d4e6a64d34fce19/special_tokens_map.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 99.0/99.0 [00:00<00:00, 45.3kB/s](…)67d4e6a64d34fce19/generation_config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 115/115 [00:00<00:00, 5.02kB/s](…)165eb22f0a867d4e6a64d34fce19/config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.05k/1.05k [00:00<00:00, 75.9kB/s](…)eb22f0a867d4e6a64d34fce19/.gitattributes: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.48k/1.48k [00:00<00:00, 171kB/s](…)t-oSSW23tawg__&Key-Pair-Id=KVTP0A1DKRTAX: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.62G/2.62G [00:50<00:00, 52.1MB/s]Fetching 12 files: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 12/12 [00:50<00:00,  4.23s/it]
  • 验证基于 Dragonfly 下载仓库快照

执行命令:

# find podskubectl -n dragonfly-system get pod -l component=dfdaemon# find logspod_name=dfdaemon-xxxxxkubectl -n dragonfly-system exec -it ${pod_name} -- grep "peer task done" /var/log/dragonfly/daemon/core.log

日志输出:

peer task done, cost: 28349ms   {"peer": "89.116.64.101-77008-a95a6918-a52b-47f5-9b18-cec6ada03daf", "task": "2fe93348699e07ab67823170925f6be579a3fbc803ff3d33bf9278a60b08d901", "component": "PeerTask", "trace": "b34ed802b7afc0f4acd94b2cedf3fa2a"}


Part.2


性能测试

测试 Hugging Face Python Library 的 hf_hub_download API 与 Dragonfly 集成后的单机模型文件下载的性能。


由于机器本身网络环境、配置等影响,实际下载时间不具有参考价值, 但是不同场景下载时间所提升的比率是有重要意义的。


  • Hugging Face Python Library: 使用 hf_hub_download API 直接下载模型文件。

  • Hugging Face Python Library & Dragonfly Cold Boot: 使用 hf_hub_download API 直接下载模型文件,没有命中任何缓存。

  • Hit Dragonfly Remote Peer Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Dragonfly 的远端 Peer 缓存。

  • Hit Dragonfly Remote Local Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Dragonfly 的本地 Peer 缓存。

  • Hit Hugging Face Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Hugging Face 的缓存。

测试结果表明 Hugging Face Python Library 和 Dragonfly 集成,能够有效减少模型文件下载时间。测试是在单机情况下基本在缓存命中情况下, 性能瓶颈在于磁盘。如果在多节点并发下载数据集或者模型的情况下, Dragonfly 效果会更加明显。

Part.3


相关链接

Dragonfly 社区

  • Website:

    https://d7y.io/

  • Github Repo:

    https://github.com/dragonflyoss/Dragonfly2

  • Slack Channel:

    #dragonfly on CNCF Slack

  • Discussion Group:

    dragonfly-discuss@googlegroups.com

  • Twitter:

    @dragonfly_oss

Hugging Face

  • Website:

    https://huggingface.co/

  • Github Repo:

    https://github.com/huggingface/huggingface_hub

  • Document: 

    https://huggingface.co/docs

  • Hub Python Library: 

    https://huggingface.co/docs/huggingface_hub/index




本文由 Hugging Face 中文社区内容共建项目提供,稿件由社区成员投稿,经授权发布于 Hugging Face 公众号。文章内容不代表官方立场,文中介绍的产品和服务等均不构成投资建议。了解更多请关注公众号:

如果你有与开源 AI、Hugging Face 相关的技术和实践分享内容,以及最新的开源 AI 项目发布,希望通过我们分享给更多 AI 从业者和开发者们,请通过下面的链接投稿与我们取得联系:

https://hf.link/tougao

本文分享自微信公众号 - Hugging Face(gh_504339124f0f)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部