캐싱 이해하기
huggingface_hub은 Hub에서 다운로드한 개별 파일을 저장하고, 저장소가 업데이트될 때 변경되지 않은 파일을 다시 다운로드하지 않도록 로컬 파일 기반 캐시를 사용합니다. 또한 hf_xet은 업로드를 최적화하기 위해 샤드 캐시와 스테이징 데이터를 저장합니다(자세한 내용은 Xet 캐싱을 참조).
이 가이드는
huggingface_hub에서 제공하는 Python 전용 캐시 관리 도구를 다룹니다. Hugging Face Hub 캐시 시스템의 작동 방식에 대한 언어에 구애받지 않는 개요는 로컬 캐싱에 대한 Hub 문서를 참고하세요.
파일 기반 캐싱
Hugging Face Hub의 캐시 시스템은 Hub에 의존하는 라이브러리 간에 공유되는 중앙 캐시로 설계되었습니다. v0.8.0에서 리비전 간 동일한 파일의 재다운로드를 방지하도록 업데이트되었습니다.
캐싱 시스템은 다음과 같이 설계되어 있습니다:
<CACHE_DIR>
├─ <MODELS>
├─ <DATASETS>
├─ <SPACES>
├─ <KERNELS>기본 <CACHE_DIR>은 ~/.cache/huggingface/hub입니다. 그러나 모든 메서드의 cache_dir 인수를 통해, 또는 HF_HOME 또는 HF_HUB_CACHE 환경 변수를 지정하여 사용자 정의할 수 있습니다.
모델, 데이터셋, 스페이스, 커널은 공통된 루트를 공유합니다. 이러한 저장소 각각에는 저장소 유형, 존재하는 경우 네임스페이스(조직 또는 사용자 이름), 저장소 이름이 포함되어 있습니다:
<CACHE_DIR>
├─ models--julien-c--EsperBERTo-small
├─ models--lysandrejik--arxiv-nlp
├─ models--bert-base-cased
├─ datasets--glue
├─ datasets--huggingface--DataMeasurementsFiles
├─ spaces--dalle-mini--dalle-mini이 폴더 안으로 이제 Hub에서 모든 파일이 다운로드됩니다. 캐싱은 이미 존재하고 업데이트되지 않은 파일이 두 번 다운로드되지 않도록 보장합니다. 하지만 파일이 업데이트되었고 최신 파일을 요청하는 경우, 최신 파일을 다운로드합니다(다시 필요할 경우를 대비해 이전 파일은 그대로 유지됩니다).
이를 위해 모든 폴더에는 동일한 구조가 포함되어 있습니다:
<CACHE_DIR>
├─ datasets--glue
│ ├─ refs
│ ├─ blobs
│ ├─ snapshots
│ ├─ trees
...각 폴더에는 다음 내용이 포함되도록 설계되어 있습니다:
참고
refs 폴더에는 주어진 참조의 최신 리비전을 나타내는 파일이 포함되어 있습니다. 예를 들어, 저장소의 main 브랜치에서 파일을 가져온 적이 있다면, refs 폴더에는 main이라는 이름의 파일이 포함되며, 이 파일에는 현재 헤드 커밐 식별자가 저장됩니다.
main의 최신 커밋 식별자가 aaaaaa인 경우, aaaaaa를 포함합니다.
같은 브랜치가 bbbbbb 식별자를 가진 새로운 커밋으로 업데이트되면, 해당 참조에서 파일을 다시 다운로드하면 refs/main 파일이 bbbbbb를 포함하도록 업데이트됩니다.
Blobs
blobs 폴더에는 다운로드한 실제 파일이 포함되어 있습니다. 각 파일의 이름은 해당 파일의 해시입니다.
스냅샷
snapshots 폴더에는 위에서 언급한 blob에 대한 심볼릭 링크가 포함되어 있습니다. 이 폴더 자체는 여러 하위 폴더로 구성되어 있으며, 알려진 각 리비전별로 하나씩 존재합니다!
위 설명에서 aaaaaa 리비전의 파일을 가져온 후 bbbbbb 리비전의 파일을 가져왔습니다. 이 경우 snapshots 폴더에는 aaaaaa와 bbbbbb 두 개의 폴더가 존재하게 됩니다.
각 폴더에는 다운로드한 파일의 이름을 가진 라이브 심볼릭 링크가 있습니다. 예를 들어, 리비전 aaaaaa에서 README.md 파일을 다운로드했다면 다음과 같은 경로가 생성됩니다:
<CACHE_DIR>/<REPO_NAME>/snapshots/aaaaaa/README.mdREADME.md 파일은 실제로 해당 파일의 해시를 가진 blob으로 연결되는 심볼릭 링크입니다.
이렇게 스켈레톤을 생성하면 파일 공유 메커니즘이 열립니다. 동일한 파일이 bbbbbb 리비전에서 가져와졌다면 동일한 해시를 가지므로 파일을 다시 다운로드할 필요가 없습니다.
트리
trees 폴더는 특정 커밋 시점에 저장소가 포함하는 파일 목록을 캐시합니다. 커밋은 불변이므로 해당 파일 목록은 절대 변경되지 않습니다. 이는 Hub와 다시 대조할 필요가 없이 목록을 영구적으로 캐시할 수 있음을 의미합니다.
각 캐시된 목록은 커밋 해시로 이름이 지정되며 trees/aaaaaa.json과 같은 JSON 파일로 저장됩니다. 해당 커밋 시점의 저장소 내 모든 파일에 대해 파일을 다운로드하는 데 필요한 정보, 즉 경로, 크기 및 해시를 기록합니다. 이는 Hub가 일반적으로 반환하는 정보와 동일하지만, 이를 가져오려면 일반적으로 파일당 네트워크 호출 한 번이 필요합니다.
이 캐시는 snapshot_download()에 의해 작성됩니다. 커밋을 처음 다운로드할 때 파일 목록이 한 번 가져와져 여기에 저장됩니다. 동일한 커밋을 다시 다운로드할 때는 다시 가져오지 않고 디스크에서 목록을 읽습니다. 그 결과, 모든 것이 이미 캐시된 상태에서 다운로드를 다시 실행하면 네트워크 호출이 한 번만 발생합니다. 이는 브랜치 또는 태그 이름을 커밋 해시로 해석하는 데 필요한 호출입니다.
snapshot_download()과 hf_hub_download() 모두 네트워크 호출을 피하기 위해 이 캐시를 읽습니다. 커밋 해시를 리비전으로 사용하여 파일을 다운로드할 때(snapshot_download()가 내부적으로 모든 파일에 대해 정확히 수행하는 작업입니다), 다운로드 메타데이터는 캐시된 파일 목록에서 읽히고 파일별 네트워크 호출이 건너뜁니다. 이는 단일 파일에 대한 hf_hub_download() 호출도 동일한 커밋에 대해 이전 snapshot_download()가 저장한 파일 목록의 이점을 누린다는 것을 의미합니다.
캐시된 파일 목록은 커밋에 정확히 어떤 내용이 포함되어야 하는지를 설명하므로, snapshot_download()는 로컬 스냅샷이 완전한지 여부도 확인할 수 있습니다. Hub에 연결할 수 없는 경우(오프라인 상태, 연결 실패, 또는 local_files_only=True를 전달한 경우)에 로컬 스냅샷에서 일부 예상 파일이 누락되어 있으면, snapshot_download()는 부분적인 폴더를 반환하는 대신 IncompleteSnapshotError를 발생시킵니다. 이전에는 불완전한 스냅샷이 조용히 반환되어, 파일 누락 사실을 모른 채로 작업하게 될 수 있었습니다. allow_patterns 또는 ignore_patterns로 제외된 파일은 누락된 파일로 집계되지 않습니다. 이 예외는 snapshot_path 속성을 통해 불완전한 스냅샷의 경로를 노출하므로, 필요 시 부분적으로 캐시된 파일을 찾을 수 있습니다.
.no_exist (고급)
blobs, refs, snapshots 폴더 외에도 캐시에 .no_exist 폴더가 있을 수 있습니다. 이 폴더는 한 번 다운로드를 시도했지만 Hub에 존재하지 않는 파일들을 추적합니다. 구조는 snapshots 폴더와 동일하며, 알려진 각 revision마다 하위 폴더가 하나씩 있습니다:
<CACHE_DIR>/<REPO_NAME>/.no_exist/aaaaaa/config_that_does_not_exist.jsonsnapshots 폴더와 달리, 파일들은 단순한 빈 파일(심볼릭 링크 없음)입니다. 이 예시에서 파일 "config_that_does_not_exist.json"은 Hub의 리비전 "aaaaaa"에 존재하지 않습니다. 빈 파일만 저장하므로 이 폴더는 디스크 사용량 측면에서 무시할 수 있습니다.
그렇다면 이 정보가 왜 관련이 있을까요?
일부 경우, 프레임워크는 모델의 선택적 파일을 로드하려고 시도합니다. 선택적 파일의 부재를 저장해 두면, 가능한 각 선택적 파일당 1회의 HTTP 호출을 절약하므로 모델 로딩이 더 빨라집니다.
예를 들어 transformers에서는 각 토크나이저가 추가 파일을 지원할 수 있습니다.
머신에서 토크나이저를 처음 로드할 때, 다음 초기화 시 로딩 시간을 단축시키기 위해 어떤 선택적 파일이 존재하는지(그리고 존재하지 않는지)를 캐시에 저장합니다.
To test if a file is cached locally (without making any HTTP request), you can use the try_to_load_from_cache() helper. It will either return the filepath (if exists and cached), the object _CACHED_NO_EXIST (if non-existence
is cached) or None (if we don’t know).
from huggingface_hub import try_to_load_from_cache, _CACHED_NO_EXIST
filepath = try_to_load_from_cache()
if isinstance(filepath, str):
# file exists and is cached
...
elif filepath is _CACHED_NO_EXIST:
# non-existence of file is cached
...
else:
# file is not cached
...실제로
실제로, 캐시는 다음과 같은 트리 구조를 가져야 합니다:
[ 96] .
└── [ 160] models--julien-c--EsperBERTo-small
├── [ 160] blobs
│ ├── [321M] 403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
│ ├── [ 398] 7cb18dc9bafbfcf74629a4b760af1b160957a83e
│ └── [1.4K] d7edf6bd2a681fb0175f7735299831ee1b22b812
├── [ 96] refs
│ └── [ 40] main
├── [ 128] snapshots
│ ├── [ 128] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f
│ │ ├── [ 52] README.md -> ../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812
│ │ └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
│ └── [ 128] bbc77c8132af1cc5cf678da3f1ddf2de43606d48
│ ├── [ 52] README.md -> ../../blobs/7cb18dc9bafbfcf74629a4b760af1b160957a83e
│ └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
└── [ 96] trees
├── [ 521] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f.json
└── [ 521] bbc77c8132af1cc5cf678da3f1ddf2de43606d48.jsonCACHEDIR.TAG
huggingface_hub은 캐시 디렉터리 안에 CACHEDIR.TAG 파일을 자동으로 생성합니다. 이 태그는 Cache Directory Tagging Standard를 따르며, 백업 도구(예: Borg, restic, rsync)에 해당 디렉터리에 재다운로드 가능한 캐시 데이터가 포함되어 있어 백업에서 안전하게 제외할 수 있음을 알려줍니다.
한계
효율적인 캐시 시스템을 위해 huggingface-hub는 심볼릭 링크를 사용합니다. 그러나 모든 머신에서 심볼릭 링크가 지원되지는 않습니다. 특히 Windows에서 이는 알려진 제한 사항입니다. 이 경우 huggingface_hub는 blobs/ 디렉토리를 사용하지 않고 대신 파일을 snapshots/ 디렉토리에 직접 저장합니다. 이 우회 방법은 사용자가 Hub에서 파일을 다운로드하고 캐시하는 방식을 정확히 동일하게 유지할 수 있게 합니다. 캐시를 검사하고 삭제하는 도구(아래 참조)도 지원됩니다. 그러나 동일한 저장소의 여러 리비전이 다운로드되면 단일 파일이 여러 번 다운로드될 수 있으므로 캐시 시스템의 효율성이 떨어집니다.
Windows 머신에서 심볼릭 링크 기반 캐시 시스템의 이점을 활용하려면 개발자 모드를 활성화하거나 관리자로 Python을 실행해야 합니다.
no-symlink 캐시 모드를 능동적으로 사용하려는 경우(예: 심볼릭 링크를 잘 처리하지 않는 공유 파일 시스템) HF_HUB_DISABLE_SYMLINKS 환경 변수를 1로 설정할 수 있습니다. 이 경우 파일은 blobs/에 심볼릭 링크를 만드는 대신 snapshots/에 직접 복사됩니다.
symlink이 지원되지 않는 경우, 캐시 시스템의 저하된 버전을 사용 중임을 알리는 경고 메시지가 사용자에게 표시됩니다. 이 경고는 HF_HUB_DISABLE_SYMLINKS_WARNING 환경 변수를 true로 설정하여 비활성화할 수 있습니다.
레포지토리 간 공유된 블롭
기본적으로 Xet 파일은 저장소 간에도 중복 제거됩니다. hf_xet를 통해 다운로드된 Xet 파일은 <CACHE_DIR>/blobs/<prefix>/<xet_hash>에 한 번만 저장되며, 해당 저장소의 blobs/<etag> 항목은 이를 가리키는 상대 심볼릭 링크입니다. 다른 저장소에서 동일한 파일이 필요할 경우 다운로드 대신 심볼릭 링크가 생성되므로, 바이트 전송이 발생하지 않고 추가 공간도 사용하지 않습니다. 스냅샷 레이아웃은 변경되지 않습니다. 마커 파일이 스토어를 식별하므로, huggingface_hub에 의해 생성되지 않은 blobs 디렉토리는 절대 수정되지 않습니다.
각 공유 파일에는 해당 파일을 사용하는 저장소 블롭을 나열하는 <xet_hash>.refs 매니페스트가 있습니다. hf cache rm는 삭제 작업에 영향을 받는 공유 파일만 확인하기 위해 이 매니페스트를 읽으며, hf cache prune는 더 이상 캐시된 저장소에서 사용되지 않는 공유 파일을 제거합니다. 매니페스트는 힌트일 뿐이며, 모든 항목은 파일시스템과 대조하여 확인됩니다. 메타데이터가 누락되었거나 읽을 수 없는 파일은 유지됩니다. 정리 과정에서는 유효한 캐시 항목을 손상시키는 것보다 복구 가능한 데이터를 남겨두는 것을 우선시합니다.
리포지토리별 크기는 논리적이며, 공유 파일은 해당 파일을 사용하는 모든 리포지토리에 대해 각각 계산됩니다. 캐시 전체의 size_on_disk는 물리적이며, 더 이상 어떤 리포지토리에서도 사용하지 않는 파일을 포함하여 각 공유 파일은 한 번만 계산됩니다.
이전 버전의 클라이언트(huggingface_hub, huggingface.js, hf-hub, llama.cpp 및 심볼릭 링크를 따르는 모든 것)는 동일한 저장소 폴더에서 읽기와 다운로드를 정상적으로 계속 수행합니다. 두 가지 제한 사항이 있습니다:
huggingface_hub<=1.31은 삭제된 저장소의 공유 파일을 스토어에 그대로 두며,hf cache ls가 이를 표시하고hf cache prune가 이를 제거합니다. 다른 클라이언트의 캐시 삭제 도구는 다른 저장소에서 여전히 사용 중인 공유 파일을 삭제할 수 있으며, 영향을 받은 파일은 다음 사용 시점에 다시 다운로드됩니다.hf cache rm,hf cache prune및 프로그래밍 방식의 삭제를 위해 최신huggingface_hub를 사용하세요.- 캐시 스캐너가 최상위
blobs디렉토리를 알 수 없는 항목으로 보고할 수 있습니다.
store는 symlink 기반의 캐시 레이아웃을 요구하며, HF_HUB_DISABLE_XET=1으로 비활성화할 수 있습니다. 파일 공유 실패(지원되지 않는 파일 시스템, 권한 오류, 기존에 마킹되지 않은 blobs 디렉터리 등)가 발생하면 조용히 일반적인 repo-local 저장소로 폴백됩니다. 완전히 제외하려면 HF_HUB_DISABLE_SHARED_BLOBS=1을 설정하세요.
리비전 고정 (고급)
ML 라이브러리에 Hub를 통합하는 경우에도 단일 snapshot_download() 호출이 여전히 권장되는 방법입니다. 이 방법은 리비전을 한 번만 해결하고, 모든 파일을 병렬로 다운로드하며, 파일 목록을 캐시합니다. 아래 내용은 구성 요소(설정, 가중치, 토크나이저, 프로세서, 어댑터 등)를 개별적으로 다운로드하고 로드하는 복잡한 라이브러리에서만 유용하며, 단일 호출을 사용할 수 없는 경우에 해당합니다.
라이브러리가 여러 파일을 하나씩 다운로드할 때, 각 호출마다 revision="main"을 커밋 해시로 다시 해석해야 합니다. 이로 인해 파일당 HTTP 호출 한 번이 발생하며, 더 나쁜 것은 몇 초 간격으로 이루어진 두 호출이 그 사이에 저장소가 업데이트되면 서로 다른 두 커밋에 도달할 수 있다는 점입니다.
HfApi.resolve_revision()은 리비전을 한 번만 해석하여 ResolvedRevision을 반환합니다:
>>> from huggingface_hub import resolve_revision
>>> revision = resolve_revision("openai-community/gpt2")
>>> revision
ResolvedRevision(initial=None, resolved='607a30d783dfa663caf39e06633721c8d4cfcd7e')ResolvedRevision은 str의 하위 클래스이므로, revision 인수를 받는 huggingface_hub의 어떤 메서드에든 전달할 수 있습니다. 문자열 값은 사용자가 처음 요청한 값이며(여기서는 "main"이므로 가독성 있는 오류 메시지를 제공합니다), .resolved에는 커밋 해시가 저장됩니다:
>>> revision == "main"
True
>>> revision.resolved
'607a30d783dfa663caf39e06633721c8d4cfcd7e'다운로드 헬퍼(hf_hub_download(), snapshot_download(), get_cached_repo_tree())는 ResolvedRevision을 감지하고 커밋 해시를 직접 사용합니다. 모든 파일이 동일한 커밋에서 가져오는 것이 보장되며, 파일이 캐시된 후에는 HTTP 호출이 전혀 필요하지 않습니다:
>>> from huggingface_hub import hf_hub_download
>>> config = hf_hub_download("openai-community/gpt2", "config.json", revision=revision)
>>> weights = hf_hub_download("openai-community/gpt2", "model.safetensors", revision=revision)revision -> commit hash 매핑은 캐시의 refs/ 폴더에도 기록됩니다(Refs 참조). 이는 나중에 Hub에 연결할 수 없는 경우(오프라인 모드, 연결 오류, 타임아웃, Hub 장애)에 HfApi.resolve_revision()가 캐시된 값으로 투명하게 폴백한다는 것을 의미합니다. 캐시된 값도 없으면 RevisionResolutionError가 발생됩니다.
커밋 해시는 해당 해시가 해석된 저장소에서만 의미를 가지며, 다운로드 헬퍼는 이를 그대로 사용합니다. 따라서 ResolvedRevision은 반드시 해당 객체가 해석된 저장소에만 전달되어야 합니다. 라이브러리가 다른 저장소(베이스 모델, 어댑터, 자체 저장소에 존재하는 컴포넌트 등)에서도 다운로드해야 하는 경우, 해당 저장소에 대해 해석된 리비전이 필요합니다. ResolvedRevision을 HfApi.resolve_revision()에 다시 전달하기만 하면 됩니다. 이 메서드는 해당 리비전이 어떤 저장소에 속하는지 기억하고, 처음 요청한 리비전(여기서는 "main")을 새로운 저장소에 대해 다시 해석합니다.
>>> other_revision = resolve_revision("openai-community/gpt2-medium", revision=revision) # resolves "main" again
>>> other_revision.resolved
'6dcaa7a952f72f9298047fd5137cd6e4f05f41da'
>>> config = hf_hub_download("openai-community/gpt2-medium", "config.json", revision=other_revision)Xet 캐싱
더 효율적인 업로드를 제공하기 위해 hf_xet은 기존 huggingface_hub 캐시에 xet 디렉터리를 추가합니다. 여기에는 샤드(파일을 청크에 매핑하는 데이터 구조)와 업로드 중복 제거 및 재개를 위해 사용되는 스테이징 데이터가 저장됩니다. Xet Storage 시스템에 대한 자세한 내용은 이 섹션을 참고하세요.
hf_xet는 더 이상 다운로드에 로컬 청크 캐시를 사용하지 않습니다. HF_XET_CHUNK_CACHE_SIZE_BYTES를 설정해도 로컬 청크 캐시가 활성화되지 않습니다. 다운로드된 파일은 여전히 파일 기반 캐시에 저장됩니다.
기본적으로 ~/.cache/huggingface/xet에 위치한 xet 디렉터리는 HF_XET_CACHE로 설정할 수 있습니다. 구조는 다음과 같습니다:
<CACHE_DIR> ├─ xet │ ├─ environment_identifier │ │ ├─ shard-cache │ │ ├─ staging
environment_identifier 디렉토리는 인코딩된 문자열입니다(로컬 머신에서는 https___cas_serv-tGqkUaZf_CBPHQ6h와 같이 나타날 수 있습니다). 이는 개발 과정에서 로컬 버전과 프로덕션 버전의 캐시가 동시에 공존할 수 있도록 하는 데 사용됩니다. 또한 서로 다른 스토리지 리전에 있는 저장소에서 다운로드할 때에도 사용됩니다. xet 디렉토리에서 이러한 항목이 여러 개 보일 수 있으며, 각각은 서로 다른 환경을 나타내지만 내부 구조는 동일합니다.
내부 디렉터리는 다음 목적을 위해 사용됩니다:
shard-cache에는 업로드 경로에서 사용되는 캐시된 샤드가 포함되어 있습니다.staging는 재개 가능한 업로드를 지원하도록 설계된 워크스페이스입니다.
이들은 아래에 문서화되어 있습니다.
hf_xet는 업로드를 위한 최적화 레이어로 샤드 캐시와 스테이징 데이터를 관리합니다. 다운로드된 파일을 관리하려면 huggingface_hub의 파일 기반 캐시 API를 사용하세요.
shard-cache
이 캐시는 Hub에 콘텐츠를 업로드할 때 사용됩니다. 이 디렉터리는 평탄한 구조로, 샤드 파일만 포함하며 각 샤드 파일은 샤드 이름으로 ID를 사용합니다.
<CACHE_DIR> ├─ xet │ ├─ environment_identifier │ │ ├─ shard-cache │ │ │ ├─ 1fe4ffd5cf0c3375f1ef9aec5016cf773ccc5ca294293d3f92d92771dacfc15d.mdb │ │ │ ├─ 906ee184dc1cd0615164a89ed64e8147b3fdccd1163d80d794c66814b3b09992.mdb │ │ │ ├─ ceeeb7ea4cf6c0a8d395a2cf9c08871211fbbd17b9b5dc1005811845307e6b8f.mdb │ │ │ ├─ e8535155b1b11ebd894c908e91a1e14e3461dddd1392695ddc90ae54a548d8b2.mdb
shard-cache에는 다음 조건을 충족하는 샤드가 포함되어 있습니다:
- 로컬에서 생성되어 CAS에 성공적으로 업로드되었습니다
- global deduplication algorithm의 일부로 CAS에서 다운로드됨
Shards는 파일과 청크 간의 매핑을 제공합니다. 업로드 중 각 파일은 청크로 분할되고, 청크의 해시가 저장됩니다. 이후 캐시의 모든 shard가 조회됩니다. 업로드 중인 로컬 파일에 존재하는 청크 해시를 shard가 포함하고 있다면, 해당 청크는 이미 CAS에 저장되어 있으므로 폐기할 수 있습니다.
모든 샤드는 다운로드 시점부터 3~4주 후 만료됩니다. 만료된 샤드는 업로드 시 로드되지 않으며, 만료 후 1주일이 지나면 삭제됩니다.
staging
업로드가 새 콘텐츠가 저장소에 커밋되기 전에 종료된 경우, 파일 전송을 재개해야 합니다. 그러나 중단되기 전에 일부 청크가 성공적으로 업로드되었을 수 있습니다.
처음부터 다시 시작하지 않도록, staging 디렉토리는 업로드 동안 워크스페이스 역할을 하며 성공적으로 업로드된 청크의 메타데이터를 저장합니다. staging 디렉토리의 구조는 다음과 같습니다:
<CACHE_DIR>
├─ xet
│ ├─ environment_identifier
│ │ ├─ staging
│ │ │ ├─ shard-session
│ │ │ │ ├─ 906ee184dc1cd0615164a89ed64e8147b3fdccd1163d80d794c66814b3b09992.mdb
│ │ │ │ ├─ xorb-metadata
│ │ │ │ │ ├─ 1fe4ffd5cf0c3375f1ef9aec5016cf773ccc5ca294293d3f92d92771dacfc15d.mdb파일이 처리되고 청크가 성공적으로 업로드되면, 해당 메타데이터가 xorb-metadata에 샤드로 저장됩니다. 업로드 세션을 재개할 때 각 파일이 다시 처리되고 이 디렉토리의 샤드가 참조됩니다. 성공적으로 업로드된 콘텐츠는 건너뛰고, 새로운 콘텐츠는 업로드되며(그 메타데이터는 저장됩니다).
한편, shard-session은 처리된 파일의 파일 및 청크 정보를 저장합니다. 업로드가 성공적으로 완료되면, 이 샤드들의 콘텐츠는 보다 영구적인 shard-cache로 이동됩니다.
한계 및 제한 사항
shard-cache에는 16GB의 소프트 제한이 있으며, HF_XET_SHARD_CACHE_SIZE_LIMIT 환경 변수를 통해 설정할 수 있습니다. 이 캐시를 관리하는 고수준 API는 없습니다.
shard cache와 staging 데이터는 업로드를 최적화하는 데 사용됩니다. 자산 자체와 상호 작용하려면 huggingface_hub 캐시 시스템 API를 사용하는 것이 권장됩니다.
Xet이 사용하는 공간을 되찾거나 캐시 관련 잠재적 문제를 디버깅해야 하는 경우, <cache_dir>에 rm -rf ~/<cache_dir>/xet을 실행하여 xet 캐시를 완전히 제거하면 됩니다. 여기서 <cache_dir>은 Hugging Face 캐시의 위치이며, 일반적으로 ~/.cache/huggingface입니다.
xet 캐시 디렉터리 트리 예시:
<CACHE_DIR> ├─ xet │ ├─ environment_identifier │ │ ├─ shard-cache │ │ │ ├─ 1fe4ffd5cf0c3375f1ef9aec5016cf773ccc5ca294293d3f92d92771dacfc15d.mdb │ │ │ ├─ 906ee184dc1cd0615164a89ed64e8147b3fdccd1163d80d794c66814b3b09992.mdb │ │ │ ├─ ceeeb7ea4cf6c0a8d395a2cf9c08871211fbbd17b9b5dc1005811845307e6b8f.mdb │ │ │ ├─ e8535155b1b11ebd894c908e91a1e14e3461dddd1392695ddc90ae54a548d8b2.mdb │ │ ├─ staging │ │ │ ├─ shard-session │ │ │ │ ├─ 906ee184dc1cd0615164a89ed64e8147b3fdccd1163d80d794c66814b3b09992.mdb │ │ │ │ ├─ xorb-metadata │ │ │ │ │ ├─ 1fe4ffd5cf0c3375f1ef9aec5016cf773ccc5ca294293d3f92d92771dacfc15d.mdb
Xet Storage에 대해 자세히 알아보려면 이 섹션을 참조하세요.
에셋 캐싱
Hub에서 파일을 캐싱하는 것 외에도, 하위 라이브러리는 huggingface_hub가 직접 처리하지는 않지만 HF와 관련된 다른 파일(예: GitHub에서 다운로드한 파일, 전처리된 데이터, 로그 등)을 캐싱해야 하는 경우가 많습니다. 이러한 파일은 assets라고 불리며, 이를 캐싱하려면 cached_assets_path()를 사용할 수 있습니다. 이 작은 헬퍼는 요청하는 라이브러리의 이름에 기반하여, 선택적으로 네임스페이스와 서브폴더 이름을 기반으로 HF 캐시 내의 경로를 통일된 방식으로 생성합니다. 그 목적은 올바른 assets 폴더 내에 유지되는 한, 각 하위 라이브러리가 자신의 assets를 원하는 방식으로 관리할 수 있도록 하는 것입니다(예: 구조에 대한 규칙 없음). 그러면 이러한 라이브러리는 huggingface_hub의 도구를 활용하여 캐시를 관리할 수 있으며, 특히 CLI 명령을 통해 assets의 일부를 스캔하고 삭제할 수 있습니다.
from huggingface_hub import cached_assets_path
assets_path = cached_assets_path(library_name="datasets", namespace="SQuAD", subfolder="download")
something_path = assets_path / "something.json" # Do anything you like in your assets folder !cached_assets_path()은 에셋을 저장하는 권장 방법이지만 필수 사항은 아닙니다. 라이브러리가 이미 자체 캐시를 사용 중이라면 이를 자유롭게 사용하세요!
실무에서의 자산
실제로, 자산 캐시는 다음과 같은 트리 구조를 가져야 합니다:
assets/
└── datasets/
│ ├── SQuAD/
│ │ ├── downloaded/
│ │ ├── extracted/
│ │ └── processed/
│ ├── Helsinki-NLP--tatoeba_mt/
│ ├── downloaded/
│ ├── extracted/
│ └── processed/
└── transformers/
├── default/
│ ├── something/
├── bert-base-cased/
│ ├── default/
│ └── training/
hub/
└── models--julien-c--EsperBERTo-small/
├── blobs/
│ ├── (...)
│ ├── (...)
├── refs/
│ └── (...)
└── [ 128] snapshots/
├── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/
│ ├── (...)
└── bbc77c8132af1cc5cf678da3f1ddf2de43606d48/
└── (...)파일 기반 캐시 관리
캐시 검사
현재 로컬 디렉터리에서 캐시된 파일은 절대 삭제되지 않습니다. 브랜치의 새 리비전을 다운로드할 때, 다시 필요할 수 있으므로 이전 파일이 유지됩니다. 따라서 디스크 공간을 가장 많이 차지하는 저장소와 리비전을 파악하려면 캐시 디렉터리를 검사하는 것이 유용할 수 있습니다. huggingface_hub는 hf CLI 또는 Python에서 사용할 수 있는 헬퍼를 제공합니다.
터미널에서 캐시 검사
hf cache ls를 실행하여 로컬에 저장된 내용을 확인합니다. 기본적으로 이 명령은 저장소별로 정보를 집계합니다:
➜ hf cache ls ID SIZE LAST_ACCESSED LAST_MODIFIED REFS ------------------------------------ ------- ------------- ------------- ------------------- dataset/glue 116.3K 4 days ago 4 days ago 2.4.0 main 1.17.0 dataset/google/fleurs 64.9M 1 week ago 1 week ago main refs/pr/1 model/Jean-Baptiste/camembert-ner 441.0M 2 weeks ago 16 hours ago main model/bert-base-cased 1.9G 1 week ago 2 years ago model/t5-base 10.1K 3 months ago 3 months ago main model/t5-small 970.7M 3 days ago 3 days ago main refs/pr/1 Found 6 repo(s) for a total of 12 revision(s) and 3.4G on disk.
--revisions를 추가하여 모든 캐시된 스냅샷을 나열하고, 필터를 체인하여 중요한 항목에 집중할 수 있습니다. 필터는 사람이 이해하기 쉬운 크기와 기간을 지원하므로 size>1GB 또는 accessed>30d와 같은 표현을 바로 사용할 수 있습니다:
➜ hf cache ls --revisions --filter "size>1GB" --filter "accessed>30d" ID REVISION SIZE LAST_MODIFIED REFS ------------------------------------ ------------------ ------- ------------- ------------------- model/bert-base-cased 6d1d7a1a2a6cf4c2 1.9G 2 years ago model/t5-small 1c610f6b3f5e7d8a 1.1G 3 months ago main Found 2 repo(s) for a total of 2 revision(s) and 3.0G on disk.
머신 친화적인 출력이 필요하신가요? --format json을 사용하면 구조화된 객체를, --format csv을 사용하면 스프레드시트를 얻을 수 있습니다. 또는 --quiet를 사용하면 식별자만(한 줄에 하나씩) 출력되어 다른 도구로 파이프할 수 있습니다. --sort를 사용하여 accessed, modified, name, size 기준으로 항목을 정렬할 수 있으며(순서를 제어하려면 :asc 또는 :desc를 추가), --limit을 사용하여 결과를 상위 N개 항목으로 제한할 수 있습니다. HF_HOME 밖에 저장된 캐시를 검사해야 하는 경우 이러한 옵션을 --cache-dir과 함께 사용하세요.
공통 셸 도구로 필터링
Tabular output는 이미 알고 있는 도구를 계속 사용할 수 있음을 의미합니다. 예를 들어, 아래 스니펫은 t5-small과 관련된 모든 캐시된 리비전을 찾습니다:
➜ eval "hf cache ls --revisions" | grep "t5-small" model/t5-small 1c610f6b3f5e7d8a 1.1G 3 months ago main model/t5-small 8f3ad1c90fed7a62 820.1M 2 weeks ago refs/pr/1
Python에서 캐시 검사
더 고급스러운 사용법을 원하시면, CLI 도구가 호출하는 Python 유틸리티인 scan_cache_dir()를 사용하세요.
4개의 dataclass를 중심으로 구성된 상세 보고서를 가져오기 위해 사용할 수 있습니다:
- HFCacheInfo: scan_cache_dir()가 반환하는 전체 리포트
- CachedRepoInfo: 캐시된 저장소에 대한 정보
- CachedRevisionInfo: 저장소 내부의 캐시된 리비전(예: “snapshot”)에 대한 정보
- CachedFileInfo: 스냅샷에 포함된 캐시된 파일에 대한 정보
…
>>> from huggingface_hub import scan_cache_dir
>>> hf_cache_info = scan_cache_dir()
HFCacheInfo(
size_on_disk=3398085269,
repos=frozenset({
CachedRepoInfo(
repo_id='t5-small',
repo_type='model',
repo_path=PosixPath(...),
size_on_disk=970726914,
nb_files=11,
last_accessed=1662971707.3567169,
last_modified=1662971107.3567169,
revisions=frozenset({
CachedRevisionInfo(
commit_hash='d78aea13fa7ecd06c29e3e46195d6341255065d5',
size_on_disk=970726339,
snapshot_path=PosixPath(...),
# No `last_accessed` as blobs are shared among revisions
last_modified=1662971107.3567169,
files=frozenset({
CachedFileInfo(
file_name='config.json',
size_on_disk=1197
file_path=PosixPath(...),
blob_path=PosixPath(...),
blob_last_accessed=1662971707.3567169,
blob_last_modified=1662971107.3567169,
),
CachedFileInfo(...),
...
}),
),
CachedRevisionInfo(...),
...
}),
),
CachedRepoInfo(...),
...
}),
warnings=[
CorruptedCacheException("Snapshots dir doesn't exist in cached repo: ..."),
CorruptedCacheException(...),
...
],
)캐시 확인
huggingface_hub는 캐시된 파일이 Hub의 체크섬과 일치하는지 확인할 수 있습니다. 특정 저장소의 특정 리비전 파일 일관성을 검증하려면 hf cache verify CLI를 사용하세요:
>>> hf cache verify meta-llama/Llama-3.2-1B-Instruct
✅ Verified 13 file(s) for 'meta-llama/Llama-3.2-1B-Instruct' (model) in ~/.cache/huggingface/hub/models--meta-llama--Llama-3.2-1B-Instruct/snapshots/9213176726f574b556790deb65791e0c5aa438b6
All checksums match.특정 캐시된 리비전을 확인합니다:
>>> hf cache verify meta-llama/Llama-3.1-8B-Instruct --revision 0e9e39f249a16976918f6564b8830bc894c89659사용법과 옵션의 전체 목록에 대한 자세한 내용은
hf cache verifyCLI 참고 문서를 확인하세요.
캐시 정리
캐시를 스캔하는 것도 유용하지만, 실제로 다음으로 하고 싶은 일은 보통 드라이브의 공간을 확보하기 위해 일부 데이터를 삭제하는 것입니다. 이를 위해 hf cache rm 및 hf cache prune CLI 명령을 사용할 수 있습니다. 또한 캐시를 스캔할 때 반환되는 HFCacheInfo 객체의 delete_revisions() 및 delete_files() 헬퍼를 프로그래매틱으로 사용할 수도 있습니다.
삭제 전략
일부 캐시를 삭제하려면 삭제할 리비전 목록을 전달해야 합니다. 이 도구는 해당 목록을 기반으로 공간을 확보하기 위한 전략을 정의합니다. 삭제될 파일과 폴더를 설명하는 DeleteCacheStrategy 객체를 반환합니다. DeleteCacheStrategy를 사용하면 예상되는 해제 공간의 양을 확인할 수 있습니다. 삭제를 승인한 후에는 삭제를 실행해야 삭제가 실제로 적용됩니다. 불일치를 방지하기 위해 전략 객체를 수동으로 편집할 수 없습니다.
리비전을 삭제하는 전략은 다음과 같습니다:
- 리비전 심볼릭 링크를 포함하는
snapshot폴더가 삭제됩니다. - 삭제 대상 리비전에만 대상이 되는 blobs 파일도 함께 삭제됩니다.
refs에 1개 이상의 리비전이 연결된 경우, 참조가 삭제됩니다.- 리포지토리의 모든 리비전이 삭제되면 캐시된 리포지토리 전체가 삭제됩니다.
delete_files()을 사용하여 개별 파일을 삭제하는 경우에도 동일한 로직이 적용됩니다. 스냅샷 항목이 제거되고, 다른 캐시된 파일이 해당 blob을 참조하지 않는 경우에만 blob이 삭제됩니다. Refs와 스냅샷 폴더는 유지됩니다.
Revision hash는 모든 저장소에서 고유합니다. 따라서
hf cache rm은 저장소 식별자(예:model/bert-base-uncased) 또는 revision hash만 전달할 수 있습니다. hash를 전달하는 경우 저장소를 별도로 지정할 필요가 없습니다.
캐시에서 리비전이 발견되지 않으면 조용히 무시됩니다. 또한, 파일 또는 폴더를 삭제하는 도중 해당 항목을 찾을 수 없으면 경고가 기록되지만 오류는 발생하지 않습니다. DeleteCacheStrategy 객체에 포함된 다른 경로에 대한 삭제는 계속 진행됩니다.
터미널에서 캐시 정리
hf cache rm를 사용하여 캐시에서 저장소 또는 리비전을 영구적으로 삭제합니다. 하나 이상의 저장소 식별자(예: model/bert-base-uncased) 또는 리비전 해시를 전달합니다:
➜ hf cache rm model/bert-base-cased About to delete 1 repo(s) totalling 1.9G. - model/bert-base-cased (entire repo) Proceed with deletion? [y/N]: y Deleted 1 repo(s) and 1 revision(s); freed 1.9G.
hf cache rm를 hf cache ls --quiet와 함께 사용하여 필터로 식별된 항목을 일괄 삭제할 수도 있습니다:
>>> hf cache rm $(hf cache ls --filter "accessed>1y" -q) -y
About to delete 2 repo(s) totalling 5.31G.
- model/meta-llama/Llama-3.2-1B-Instruct (entire repo)
- model/hexgrad/Kokoro-82M (entire repo)
Deleted 2 repo(s) and 2 revision(s); freed 5.31G.같은 호출에서 저장소와 리비전을 혼합할 수 있습니다. 영향을 미리 확인하려면 --dry-run을, 스크립팅 시 확인 프롬프트를 건너뛰려면 --yes를 추가하세요:
➜ hf cache rm model/t5-small 8f3ad1c --dry-run
About to delete 1 repo(s) and 1 revision(s) totalling 1.1G.
- model/t5-small:
8f3ad1c [main] 1.1G
Dry run: no files were deleted.전체 저장소가 아닌 단일 파일, 예를 들어 GGUF 양자화 파일을 삭제하려면 hf:// 파일 URI를 전달하세요. 해당 파일은 저장소의 모든 캐시된 리비전에서 제거되며, 다른 캐시된 파일이 여전히 참조하지 않는 경우에만 해당 blob이 삭제됩니다. 리비전은 계속 사용 가능하며, 삭제된 파일은 다음에 필요할 때 다시 다운로드됩니다. 경로는 정확히 일치해야 하며, 폴더와 glob 패턴은 지원되지 않습니다.
➜ hf cache rm hf://models/unsloth/gemma-3-27b-it-GGUF/gemma-3-27b-it-Q4_K_M.gguf --dry-run About to delete 1 file(s) totalling 16.5G. - model/unsloth/gemma-3-27b-it-GGUF@3f4b5c1d2e6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c/gemma-3-27b-it-Q4_K_M.gguf Dry run: no files were deleted.
기본 캐시 위치 이외의 위치에서 작업할 경우, 해당 명령에 --cache-dir PATH를 함께 사용하세요.
캐시 가비지를 일괄 정리하려면 hf cache prune를 실행하세요. 이 명령은 더 이상 브랜치나 태그에 의해 참조되지 않는 리비전, 중단된 다운로드에서 남은 .incomplete 파일, 그리고 더 이상 캐시된 저장소에서 참조하지 않는 공유 블롭을 자동으로 삭제합니다:
➜ hf cache prune
About to delete 3 unreferenced revision(s) and 2 incomplete download(s) (2.4G total).
- model/t5-small:
1c610f6b [refs/pr/1] 820.1M
d4ec9b72 [(detached)] 640.5M
- dataset/google/fleurs:
2b91c8dd [(detached)] 937.6M
Proceed? [y/N]: y
Deleted 3 unreferenced revision(s) and 2 incomplete download(s); freed 2.4G..incomplete 파일은 다운로드가 중단될 때 남아 있는 부분적인 blob입니다. 리비전 기반 스캔에서 추적되지 않으므로 hf cache ls는 힌트(Found X incomplete download(s) ...)로만 표시하고, 실제로 삭제하는 명령은 hf cache prune입니다. hf cache rm는 전체 리포지토리를 삭제하는 경우를 제외하고는 이 파일에 영향을 주지 않습니다.
두 명령어 모두 --dry-run, --yes, --cache-dir를 지원하므로 필요에 따라 미리보기, 자동화, 대안 캐시 디렉터리 지정이 가능합니다.
Python에서 캐시 정리
더 많은 유연성을 위해 delete_revisions() 메서드를 프로그래매틱으로 사용할 수도 있습니다. 간단한 예시는 다음과 같습니다. 자세한 내용은 참고 문서를 확인하세요.
>>> from huggingface_hub import scan_cache_dir
>>> delete_strategy = scan_cache_dir().delete_revisions(
... "81fd1d6e7847c99f5862c9fb81387956d99ec7aa"
... "e2983b237dccf3ab4937c97fa717319a9ca1a96d",
... "6c0e6080953db56375760c0471a8c5f2929baf11",
... )
>>> print("Will free " + delete_strategy.expected_freed_size_str)
Will free 8.6G
>>> delete_strategy.execute()