【Bug已解决】Remove downloaded tensorflow and pytorch (Hugging face) models 解决方案

📅 发布时间:2026/8/28 23:02:51
【Bug已解决】Remove downloaded tensorflow and pytorch (Hugging face) models 解决方案 【Bug已解决】Remove downloaded tensorflow and pytorch (Hugging face) models 解决方案问题描述在使用 Hugging Face Transformers、TensorFlow Hub 或 PyTorch Hub 下载预训练模型时模型文件会被缓存到本地磁盘。随着使用时间的增长这些缓存文件会占用大量存储空间常常达到数十甚至上百 GB。常见的问题包括磁盘空间不足系统提示存储空间告警下载了多个版本的同一模型重复占用空间下载后不再使用的模型仍然残留在缓存中不清楚模型文件存储在哪个目录不确定哪些文件可以安全删除Hugging Face 缓存目录结构复杂难以手动清理Hugging Face 默认将模型缓存到~/.cache/huggingface/hub/目录下TensorFlow Hub 缓存到~/.cache/tfhub_modules/PyTorch Hub 缓存到~/.cache/torch/hub/。这些目录的结构和命名方式对用户不够友好手动清理容易出错。错误复现以下代码和命令演示了缓存堆积的问题# 下载多个 Hugging Face 模型 from transformers import AutoModel, AutoTokenizer # 下载多个模型每个可能数 GB models_to_download [ bert-base-uncased, gpt2, facebook/bart-large, google/t5-v1_1-large, microsoft/deberta-v3-large, ] for model_name in models_to_download: print(f下载 {model_name}...) model AutoModel.from_pretrained(model_name) tokenizer AutoTokenizer.from_pretrained(model_name) # 模型被缓存到本地但不会自动清理 # 检查缓存大小 import os import shutil cache_dir os.path.expanduser(~/.cache/huggingface/hub) if os.path.exists(cache_dir): total_size 0 for dirpath, dirnames, filenames in os.walk(cache_dir): for f in filenames: fp os.path.join(dirpath, f) total_size os.path.getsize(fp) print(fHugging Face 缓存大小: {total_size / 1024**3:.2f} GB) # 可能输出: Hugging Face 缓存大小: 25.50 GB在终端中检查# 检查 Hugging Face 缓存 du -sh ~/.cache/huggingface/ # 可能输出: 25G /home/user/.cache/huggingface/ # 检查 TensorFlow Hub 缓存 du -sh ~/.cache/tfhub_modules/ # 可能输出: 8G /home/user/.cache/tfhub_modules/ # 检查 PyTorch Hub 缓存 du -sh ~/.cache/torch/ # 可能输出: 5G /home/user/.cache/torch/ # 查看缓存目录结构 ls ~/.cache/huggingface/hub/ # 输出: # models--bert-base-uncased/ # models--gpt2/ # models--facebook--bart-large/ # models--google--t5-v1_1-large/ # models--microsoft--deberta-v3-large/根因分析1. Hugging Face 缓存机制Hugging Face 使用一个基于 Git LFS 的缓存系统。每个下载的模型存储在~/.cache/huggingface/hub/models--{org}--{model}/目录下包含以下子目录~/.cache/huggingface/hub/models--bert-base-uncased/ ├── blobs/ # 实际文件内容以哈希命名 ├── refs/ # 分支引用如 main └── snapshots/ # 符号链接到 blobs 的快照 └── commit_hash/ ├── config.json ├── pytorch_model.bin - ../../blobs/hash ├── tokenizer.json - ../../blobs/hash └── ...这种设计使用符号链接和内容寻址存储content-addressable storage可以避免重复下载相同文件。但缺点是目录结构复杂手动清理困难。2. 多版本缓存当你下载同一模型的不同版本时Hugging Face 会保留所有版本的快照。例如bert-base-uncased的main分支和v1.0分支会分别存储即使它们的大部分文件是共享的通过 blobs 去重。3. TensorFlow Hub 缓存TensorFlow Hub 将模型缓存为~/.cache/tfhub_modules/下的目录每个模型一个目录包含模型的 SavedModel 格式文件。这些文件没有去重机制每个模型版本独立存储。4. PyTorch Hub 缓存PyTorch Hub 将下载的模型代码和权重分别缓存模型代码~/.cache/torch/hub/下的 Git 仓库预训练权重通常由模型代码自行管理位置不固定5. 临时文件和下载中断下载中断会留下不完整的临时文件.incomplete后缀这些文件不会被自动清理持续占用磁盘空间。解决方案方案一使用 Hugging Face CLI 清理缓存Hugging Face 提供了huggingface-cli命令行工具来管理缓存# 查看 Hugging Face 缓存 huggingface-cli scan-cache # 输出示例: # REPO TYPE REVISION SIZE PATH # bert-base-uncased model abc123... 420MB ~/.cache/huggingface/hub/models--bert-base-uncased # gpt2 model def456... 550MB ~/.cache/huggingface/hub/models--gpt2 # facebook/bart-large model ghi789... 1.6GB ~/.cache/huggingface/hub/models--facebook--bart-large # 删除特定模型缓存 huggingface-cli delete-cache # 交互式选择要删除的模型 # 或者使用 Python API方案二使用 Python 脚本清理 Hugging Face 缓存import os import shutil from pathlib import Path from typing import List, Dict, Optional class HuggingFaceCacheManager: Hugging Face 模型缓存管理器。 提供缓存扫描、清理和统计功能。 def __init__(self, cache_dir: Optional[str] None): self.cache_dir Path(cache_dir or os.path.expanduser( ~/.cache/huggingface/hub )) def scan_cache(self) - List[Dict]: 扫描缓存返回所有已缓存的模型信息 models [] if not self.cache_dir.exists(): print(f缓存目录不存在: {self.cache_dir}) return models # 遍历 models--* 目录 for model_dir in self.cache_dir.iterdir(): if not model_dir.is_dir(): continue if not model_dir.name.startswith(models--): continue # 解析模型名称 parts model_dir.name.split(--) if len(parts) 3: org parts[1] model_name parts[2] full_name f{org}/{model_name} if org ! models else model_name else: full_name model_dir.name # 计算大小 size self._get_dir_size(model_dir) # 获取版本信息 snapshots_dir model_dir / snapshots revisions [] if snapshots_dir.exists(): revisions [d.name for d in snapshots_dir.iterdir() if d.is_dir()] models.append({ name: full_name, path: str(model_dir), size_mb: size / 1024**2, size_gb: size / 1024**3, revisions: revisions, num_revisions: len(revisions), }) # 按大小排序 models.sort(keylambda x: x[size_mb], reverseTrue) return models def _get_dir_size(self, path: Path) - int: 计算目录总大小 total 0 for file_path in path.rglob(*): if file_path.is_file(): # 对于符号链接获取实际文件大小 try: total file_path.stat().st_size except OSError: pass return total def print_cache_summary(self): 打印缓存摘要 models self.scan_cache() if not models: print(缓存为空) return print( * 80) print(fHugging Face 模型缓存摘要) print(f缓存目录: {self.cache_dir}) print( * 80) total_size 0 for i, model in enumerate(models, 1): print(f\n{i}. {model[name]}) print(f 大小: {model[size_gb]:.2f} GB ({model[size_mb]:.0f} MB)) print(f 版本数: {model[num_revisions]}) print(f 路径: {model[path]}) total_size model[size_mb] print(f\n{ * 80}) print(f总模型数: {len(models)}) print(f总缓存大小: {total_size / 1024**3:.2f} GB) print(f{ * 80}) def delete_model(self, model_name: str, confirm: bool True) - bool: 删除指定模型的缓存 # 构建目录名 if / in model_name: org, name model_name.split(/, 1) dir_name fmodels--{org}--{name} else: dir_name fmodels--{model_name} model_dir self.cache_dir / dir_name if not model_dir.exists(): print(f模型缓存不存在: {model_name}) return False size self._get_dir_size(model_dir) if confirm: response input( f确认删除 {model_name} ({size/1024**2:.0f} MB)? [y/N]: ) if response.lower() ! y: print(已取消) return False shutil.rmtree(model_dir) print(f已删除 {model_name} ({size/1024**2:.0f} MB)) return True def delete_all(self, confirm: bool True) - int: 删除所有缓存 if confirm: ![配图](https://i-blog.csdnimg.cn/img_convert/03e1f66457d3d2ae3e88e2b1c796e7a8.png) response input(f确认删除所有缓存? 此操作不可逆! [type DELETE]: ) if response ! DELETE: print(已取消) return 0 models self.scan_cache() deleted 0 total_freed 0 for model in models: model_dir Path(model[path]) if model_dir.exists(): total_freed model[size_mb] shutil.rmtree(model_dir) deleted 1 print(f 已删除: {model[name]} ({model[size_mb]:.0f} MB)) print(f\n共删除 {deleted} 个模型释放 {total_freed/1024**2:.2f} GB 空间) return deleted def delete_old_revisions(self, keep_latest: int 1) - int: 删除旧版本只保留最新的几个 models self.scan_cache() deleted 0 for model in models: model_dir Path(model[path]) snapshots_dir model_dir / snapshots if not snapshots_dir.exists(): continue revisions sorted( snapshots_dir.iterdir(), keylambda d: d.stat().st_mtime, reverseTrue ) if len(revisions) keep_latest: continue for old_rev in revisions[keep_latest:]: shutil.rmtree(old_rev) deleted 1 print(f 删除旧版本: {model[name]} {old_rev.name}) print(f\n共删除 {deleted} 个旧版本) return deleted def clean_incomplete_downloads(self) - int: 清理未完成的下载文件 deleted 0 freed 0 for file_path in self.cache_dir.rglob(*.incomplete): size file_path.stat().st_size file_path.unlink() deleted 1 freed size print(f 删除不完整文件: {file_path.name} ({size/1024**2:.0f} MB)) if deleted 0: print(无不完整下载文件) else: print(f\n共删除 {deleted} 个不完整文件释放 {freed/1024**2:.0f} MB) return deleted class TorchHubCacheManager: PyTorch Hub 缓存管理器 def __init__(self, cache_dir: Optional[str] None): self.cache_dir Path(cache_dir or os.path.expanduser( ~/.cache/torch/hub )) def scan_cache(self) - List[Dict]: 扫描 PyTorch Hub 缓存 items [] if not self.cache_dir.exists(): return items for item in self.cache_dir.iterdir(): if item.is_dir(): # 可能是 Git 仓库模型代码 if (item / .git).exists(): size self._get_dir_size(item) items.append({ name: item.name, type: git_repo, path: str(item), size_mb: size / 1024**2, }) else: size self._get_dir_size(item) items.append({ name: item.name, type: directory, path: str(item), size_mb: size / 1024**2, }) elif item.is_file(): items.append({ name: item.name, type: file, path: str(item), size_mb: item.stat().st_size / 1024**2, }) items.sort(keylambda x: x[size_mb], reverseTrue) return items def _get_dir_size(self, path: Path) - int: total 0 for fp in path.rglob(*): if fp.is_file(): try: total fp.stat().st_size except OSError: pass return total def print_summary(self): 打印缓存摘要 items self.scan_cache() print(\n * 60) print(PyTorch Hub 缓存摘要) print(f目录: {self.cache_dir}) print( * 60) total 0 for item in items: print(f {item[name]}: {item[size_mb]:.1f} MB ({item[type]})) total item[size_mb] print(f\n总大小: {total / 1024**2:.2f} GB) def clear_all(self, confirm: bool True): 清空所有缓存 if confirm: response input(确认清空 PyTorch Hub 缓存? [y/N]: ) if response.lower() ! y: return if self.cache_dir.exists(): shutil.rmtree(self.cache_dir) print(PyTorch Hub 缓存已清空) class TensorFlowCacheManager: TensorFlow Hub 缓存管理器 def __init__(self, cache_dir: Optional[str] None): self.cache_dir Path(cache_dir or os.path.expanduser( ~/.cache/tfhub_modules )) def scan_cache(self) - List[Dict]: 扫描 TF Hub 缓存 items [] if not self.cache_dir.exists(): return items for item in self.cache_dir.iterdir(): if item.is_dir(): size self._get_dir_size(item) items.append({ name: item.name, path: str(item), size_mb: size / 1024**2, }) items.sort(keylambda x: x[size_mb], reverseTrue) return items def _get_dir_size(self, path: Path) - int: total 0 for fp in path.rglob(*): if fp.is_file(): try: total fp.stat().st_size except OSError: pass return total def print_summary(self): 打印缓存摘要 items self.scan_cache() print(\n * 60) print(TensorFlow Hub 缓存摘要) print(f目录: {self.cache_dir}) print( * 60) total 0 for item in items: print(f {item[name]}: {item[size_mb]:.1f} MB) total item[size_mb] print(f\n总大小: {total / 1024**2:.2f} GB) def clear_all(self, confirm: bool True): 清空所有缓存 if confirm: response input(确认清空 TF Hub 缓存? [y/N]: ) if response.lower() ! y: return if self.cache_dir.exists(): shutil.rmtree(self.cache_dir) print(TF Hub 缓存已清空) # 统一管理器 class ModelCacheManager: 统一的模型缓存管理器。 管理 Hugging Face、PyTorch Hub 和 TensorFlow Hub 的缓存。 def __init__(self): self.hf_manager HuggingFaceCacheManager() self.torch_manager TorchHubCacheManager() self.tf_manager TensorFlowCacheManager() def full_scan(self): 扫描所有缓存 print( * 80) print(全平台模型缓存扫描) print( * 80) self.hf_manager.print_cache_summary() self.torch_manager.print_summary() self.tf_manager.print_summary() # 总计 hf_models self.hf_manager.scan_cache() torch_items self.torch_manager.scan_cache() tf_items self.tf_manager.scan_cache() hf_total sum(m[size_mb] for m in hf_models) torch_total sum(i[size_mb] for i in torch_items) tf_total sum(i[size_mb] for i in tf_items) grand_total hf_total torch_total tf_total print(f\n{ * 80}) print(f缓存总计:) print(f Hugging Face: {hf_total / 1024**3:.2f} GB ({len(hf_models)} 个模型)) print(f PyTorch Hub: {torch_total / 1024**3:.2f} GB ({len(torch_items)} 个项目)) print(f TensorFlow: {tf_total / 1024**3:.2f} GB ({len(tf_items)} 个模型)) print(f 总计: {grand_total / 1024**3:.2f} GB) print(f{ * 80}) def clean_all_incomplete(self): 清理所有不完整下载 print(\n--- 清理不完整下载 ---) self.hf_manager.clean_incomplete_downloads() def interactive_cleanup(self): 交互式清理 print(\n--- 交互式清理 ---) print(1. 清理 Hugging Face 缓存) print(2. 清理 PyTorch Hub 缓存) print(3. 清理 TensorFlow Hub 缓存) print(4. 清理所有不完整下载) print(5. 清理 Hugging Face 旧版本) print(6. 清理所有缓存危险) print(0. 退出) choice input(\n选择操作: ) if choice 1: models self.hf_manager.scan_cache() for i, m in enumerate(models, 1): print(f {i}. {m[name]} ({m[size_gb]:.2f} GB)) print(f 0. 删除所有) sel input(选择要删除的模型编号: ) if sel 0: self.hf_manager.delete_all() else: try: idx int(sel) - 1 self.hf_manager.delete_model(models[idx][name]) except (ValueError, IndexError): print(无效选择) elif choice 2: self.torch_manager.clear_all() elif choice 3: self.tf_manager.clear_all() elif choice 4: self.clean_all_incomplete() elif choice 5: self.hf_manager.delete_old_revisions(keep_latest1) elif choice 6: print(\n警告: 这将删除所有模型缓存) self.hf_manager.delete_all() self.torch_manager.clear_all() self.tf_manager.clear_all() elif choice 0: print(退出) # 使用示例 if __name__ __main__: manager ModelCacheManager() # 全量扫描 manager.full_scan() # 清理不完整下载 manager.clean_all_incomplete() # 交互式清理取消注释以使用 # manager.interactive_cleanup() # 编程式删除特定模型 # hf_mgr HuggingFaceCacheManager() # hf_mgr.delete_model(gpt2, confirmFalse) # 删除旧版本 # hf_mgr HuggingFaceCacheManager() # hf_mgr.delete_old_revisions(keep_latest1)方案三修改默认缓存路径如果磁盘空间有限可以将缓存目录设置到更大的磁盘import os # 方法1通过环境变量设置 Hugging Face 缓存目录 os.environ[HF_HOME] /data/huggingface_cache os.environ[TRANSFORMERS_CACHE] /data/huggingface_cache/models # 方法2在代码中指定 cache_dir from transformers import AutoModel model AutoModel.from_pretrained(bert-base-uncased, cache_dir/data/hf_models) # 方法3设置 PyTorch Hub 缓存目录 os.environ[TORCH_HOME] /data/torch_cache # 方法4设置 TensorFlow Hub 缓存目录 os.environ[TFHUB_CACHE_DIR] /data/tfhub_cache方案四使用符号链接迁移缓存# 1. 将缓存目录移动到更大的磁盘 mv ~/.cache/huggingface /data/huggingface_cache # 2. 创建符号链接 ln -s /data/huggingface_cache ~/.cache/huggingface # 3. 验证 ls -la ~/.cache/huggingface # 应显示: - /data/huggingface_cache常见陷阱与注意事项1. 符号链接与缓存清理Hugging Face 缓存使用符号链接snapshots 指向 blobs。在清理时删除 snapshot 目录下的符号链接不会释放 blobs 中的实际文件空间。必须删除整个模型目录包含 blobs才能释放空间。2. 正在使用的模型不要删除正在被其他进程使用的模型缓存。这会导致运行中的程序出错。在清理前确保没有程序正在加载这些模型。3. 环境变量的优先级HF_HOME环境变量影响所有 Hugging Face 工具的缓存路径。如果设置了此变量~/.cache/huggingface/下的缓存将不再被使用。确保清理正确的目录。4. 量化模型和原始模型同一模型的量化版本如TheBloke/Llama-2-7B-GPTQ和原始版本meta-llama/Llama-2-7b-hf是不同的缓存条目。清理时需要分别处理。5. Dataset 缓存Hugging Face Datasets 也有独立的缓存目录~/.cache/huggingface/datasets/与模型缓存分开管理。清理模型缓存不会影响数据集缓存。6. Docker 容器中的缓存在 Docker 中运行时缓存目录在容器内。使用 Docker volume 持久化缓存避免每次重建容器都重新下载docker run -v /host/cache:/root/.cache/huggingface my_image总结管理深度学习模型的本地缓存是保持磁盘空间健康的重要任务。核心要点如下了解缓存位置Hugging Face 缓存在~/.cache/huggingface/hub/PyTorch Hub 在~/.cache/torch/hub/TF Hub 在~/.cache/tfhub_modules/。使用管理工具HuggingFaceCacheManager类提供了扫描、统计、选择性删除和批量清理功能。定期清理不完整下载下载中断留下的.incomplete文件不会被自动清理。删除旧版本Hugging Face 会保留所有版本的快照定期清理旧版本可以释放空间。修改缓存路径通过环境变量将缓存设置到更大的磁盘或使用符号链接迁移。谨慎批量删除删除前确认没有程序正在使用这些模型避免运行时错误。通过系统性地管理模型缓存可以在有限的磁盘空间下高效地进行深度学习开发。