Hub 로컬 캐시

Hub 로컬 캐시 (Hub Local Cache)

이 문서는 HF Hub 로컬 캐시의 디스크 레이아웃을 설명해요. 어떤 언어든 캐시 시스템을 재구현하기 위한 참고 자료로 작성된 거예요.

출처: 문서

본문

이 캐시 레이아웃을 사용하는 라이브러리와 애플리케이션의 일부 목록이에요. 자신을 추가하려면 PR을 열어주세요.

라이브러리

라이브러리 언어 참고
huggingface_hub Python 그리고 그것에 의존하는 모든 라이브러리 (예: transformers, diffusers, datasets, mlx, vllm …)
hf-hub Rust
swift-huggingface Swift
@huggingface/hub JavaScript Node.js 전용
SMILE Java HuggingFaceHub 문서 참고

애플리케이션

애플리케이션 언어 참고
llama.cpp C++ #20775 이후
LlamaBarn Swift macOS 앱, llama.cpp 기반
HuggingFaceModelDownloader Go
oMLX Python macOS MLX 추론 서버, v0.4.0 이후

캐시 위치

기본 캐시 디렉토리는 다음과 같아요.

~/.cache/huggingface/hub

환경 변수로 덮어쓸 수 있어요.

  • HF_HUB_CACHE - 캐시 디렉토리의 직접 경로 (우선순위를 가짐)
  • HF_HOME - Hugging Face 홈 디렉토리 경로; 설정하면 캐시는 $HF_HOME/hub에 있음

개요

<CACHE_DIR>/
├── .locks/                                  # 동시 다운로드 안전을 위한 잠금 파일
├── models--<org>--<repo>/                   # 캐시된 모델 저장소
├── datasets--<org>--<repo>/                 # 캐시된 데이터셋 저장소
└── spaces--<org>--<repo>/                   # 캐시된 space 저장소

다운로드된 각 저장소는 단일 플랫 폴더를 얻어요. 각 저장소 폴더 안에서 파일은 content-addressed blobs/ 디렉토리에 한 번 저장되고 snapshots/ 심볼릭 링크로 접근돼요. 명명된 참조(branch, tag)는 refs/에서 추적돼요.

스키마

                     ┌──────────────────────────────────────────┐
                     │           Repository folder              │
                     │  models--julien-c--EsperBERTo-small      │
                     └──────────────┬───────────────────────────┘
                                    │
              ┌─────────────┬───────┴───────┬──────────────┐
              │             │               │              │
              v             v               v              v
          ┌───────┐    ┌────────┐    ┌────────────┐   ┌──────────┐
          │ refs/ │    │ blobs/ │    │ snapshots/ │   │.no_exist/│
          └───┬───┘    └────┬───┘    └──────┬─────┘   └────┬─────┘
              │             │               │              │
              │             │               │              │
   "main" contains     Files stored     One folder     Empty marker
   commit hash         by content       per commit     files for
   e.g. "aaaaaa"       hash (SHA-1      hash, e.g.     files known
                       or SHA-256)      aaaaaa/        not to exist
   Resolves a                           bbbbbb/
   branch/tag to                            │
   a snapshot ──────────────────────►  Contains symlinks
                                       to ../../blobs/{hash}

저장소 폴더 이름

저장소는 캐시 루트에 플랫 디렉토리로 저장돼요. 폴더 이름은 저장소 유형과 저장소 ID를 인코딩해요.

{type}s--{repo_id_with_slashes_replaced_by_--}

규칙:

  • 저장소 유형은 복수형: models, datasets, spaces
  • 저장소 ID의 슬래시(/)는 --로 대체
  • 모든 부분 사이의 구분자는 --
  • 대소문자는 보존

예시:

Hub 저장소 ID 저장소 유형 캐시 폴더 이름
julien-c/EsperBERTo-small model models--julien-c--EsperBERTo-small
huggingface/DataMeasurementsFiles dataset datasets--huggingface--DataMeasurementsFiles
dalle-mini/dalle-mini space spaces--dalle-mini--dalle-mini

[!NOTE] Bucket은 git 기반이 아니므로 이 캐시로 처리되지 않아요. 전용 hf buckets sync 명령을 대신 사용하세요.

저장소 폴더 내부

모든 캐시된 저장소는 같은 내부 구조를 가져요.

<repo_folder>/
├── blobs/
├── refs/
├── snapshots/
└── .no_exist/    # 항상 존재하지는 않을 수 있음

blobs/: 콘텐츠 주소형 파일 저장

blobs/ 디렉토리는 실제 파일 내용을 저장해요. 각 파일은 Hub의 파일 etag 이름을 따서 명명돼요.

  • Git 추적 파일: SHA-1 해시(16진수 40자)로 명명
  • Git LFS 파일: SHA-256 해시(16진수 64자)로 명명

이것은 플랫 디렉토리예요 — 하위 디렉토리가 없어요. 서로 다른 리비전 간에 동일한 파일은 한 번만 저장돼요.

blobs/
├── 403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd   # SHA-256 (LFS)
├── 7cb18dc9bafbfcf74629a4b760af1b160957a83e                           # SHA-1 (git)
└── d7edf6bd2a681fb0175f7735299831ee1b22b812                           # SHA-1 (git)

refs/: branch 및 tag 참조

refs/ 디렉토리는 사람이 읽을 수 있는 참조(branch 이름, tag, PR 번호)를 커밋 해시에 매핑해요.

각 참조는 한 줄(전체 커밋 해시, 16진수 40자)을 포함하는 일반 텍스트 파일이에요.

refs/
├── main            # 예: "bbc77c8132af1cc5cf678da3f1ddf2de43606d48" 포함
├── 2.4.0           # tag
└── refs/
    └── pr/
        └── 1       # pull request 참조

브랜치나 태그 이름으로 파일을 다운로드하면 해당 ref 파일이 최신 커밋 해시로 생성되거나 업데이트돼요.

snapshots/: 리비전 뷰

snapshots/ 디렉토리는 캐시된 각 리비전(커밋 해시)당 하나의 하위 디렉토리를 포함해요. 각 리비전 디렉토리는 Hub의 저장소 파일 구조를 반영하지만, 파일은 ../../blobs/{hash}를 가리키는 심볼릭 링크예요.

snapshots/
├── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/
│   ├── README.md -> ../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812
│   └── pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
└── bbc77c8132af1cc5cf678da3f1ddf2de43606d48/
    ├── README.md -> ../../blobs/7cb18dc9bafbfcf74629a4b760af1b160957a83e
    └── pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd

주요 속성:

  • 심볼릭 링크는 상대 경로: ../../blobs/{hash} 사용
  • 두 리비전 사이에 파일이 변하지 않으면 두 심볼릭 링크 모두 같은 blob을 가리켜 데이터 중복이 없음
  • Hub의 하위 디렉토리 파일은 snapshot의 하위 디렉토리로 표현됨(전체 상대 경로 보존)

snapshot 전환은 로컬 git 저장소에서 git checkout을 사용하는 것과 비슷해요.

.no_exist/: 비존재 캐시

./no_exist/ 디렉토리는 요청되었지만 Hub에 존재하지 않는 파일을 추적해요. 선택적 파일에 대한 반복 HTTP 요청을 피하게 해줘요.

구조는 snapshots/를 반영해요. 커밋 해시당 하나의 하위 디렉토리이며, 없어진 파일 이름을 딴 (심볼릭 링크가 아닌) 빈 파일을 포함해요.

.no_exist/
└── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/
    └── config_that_does_not_exist.json    # empty file

이것들은 빈 마커 파일일 뿐이라 디스크 사용량은 무시할 수 있어요.

잠금 파일

잠금 파일은 동시 프로세스가 같은 blob을 동시에 다운로드하지 못하게 해요. 캐시 루트의 .locks/ 디렉토리(repo 폴더 안이 아님)에 저장돼요.

<CACHE_DIR>/.locks/<repo_folder_name>/<blob_hash>.lock

예시:

<CACHE_DIR>/.locks/models--julien-c--EsperBERTo-small/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd.lock

전체 예시

~/.cache/huggingface/hub/
├── .locks/
│   └── models--julien-c--EsperBERTo-small/
│       └── 403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd.lock
│
└── models--julien-c--EsperBERTo-small/
    ├── blobs/
    │   ├── [321M]  403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
    │   ├── [ 398]  7cb18dc9bafbfcf74629a4b760af1b160957a83e
    │   └── [1.4K]  d7edf6bd2a681fb0175f7735299831ee1b22b812
    │
    ├── refs/
    │   └── main    # "bbc77c8132af1cc5cf678da3f1ddf2de43606d48" 포함
    │
    ├── snapshots/
    │   ├── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/
    │   │   ├── README.md         -> ../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812
    │   │   └── pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
    │   │
    │   └── bbc77c8132af1cc5cf678da3f1ddf2de43606d48/
    │       ├── README.md         -> ../../blobs/7cb18dc9bafbfcf74629a4b760af1b160957a83e
    │       └── pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
    │
    └── .no_exist/
        └── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/
            └── optional_config.json    # empty file

pytorch_model.bin이 두 리비전 모두에서 같은 blob을 가리키는 방식을 주목하세요. 321MB 파일은 디스크에 한 번만 저장돼요.

파일 해석 로직

캐시된 파일을 디스크에서 찾으려면:

  1. 리비전을 커밋 해시로 해석

    • 리비전이 이미 40자 16진수 문자열이면 그대로 사용
    • 아니면 refs/{revision}에서 파일을 읽어 커밋 해시를 얻음
  2. snapshot 확인

    • snapshots/{commit_hash}/{relative_path}를 찾음
    • 존재하면(심볼릭 링크든 파일이든) 파일이 캐시된 것. 심볼릭 링크를 따라가 내용을 얻음
  3. 비존재 확인

    • .no_exist/{commit_hash}/{relative_path}를 찾음
    • 존재하면 이 리비전에서 파일이 Hub에 없다는 것을 알고 있음
  4. 캐시 미스

    • 두 경로 모두 없으면 파일이 아직 캐시되지 않음

Windows 동작

캐시는 심볼릭 링크에 의존해요. 심볼릭 링크를 사용할 수 없는 Windows 시스템에서는 캐시가 저하 모드(degraded mode) 로 동작해요. 심볼릭 링크 대신 실제 파일 복사본이 snapshots/에 직접 배치돼요. 이 모드에서는 blobs/ 디렉토리가 사용되지 않아요.

즉 같은 파일 내용이 리비전 간에 중복되어 디스크 사용량이 늘어날 수 있어요. Windows에서 심볼릭 링크 지원을 활성화하려면 Developer Mode를 활성화하거나 관리자로 실행하세요.

더 알아보기 (Learn more)

캐시는 blobs/(콘텐츠 주소형 실제 데이터), snapshots/(리비전별 심볼릭 링크 뷰), refs/(브랜치→커밋 매핑), .no_exist/(없는 파일 캐시)로 구성돼요. 같은 파일은 리비전이 달라도 blob 하나로 공유돼 디스크를 아껴요. Windows는 심볼릭 링크가 없으면 저하 모드로 동작해 데이터가 중복될 수 있어요.