Add FP8 quantized model
Browse files- LICENSE.txt +77 -0
- README.md +75 -0
- convert_pth_to_safetensors.py +22 -0
- convert_safetensors_to_fp8.py +192 -0
- hunyuanvideo_foley.safetensors +3 -0
- hunyuanvideo_foley_fp8_e4m3fn.safetensors +3 -0
- hunyuanvideo_foley_fp8_e5m2.safetensors +3 -0
- pth_info.py +227 -0
- safetensors_info.py +165 -0
LICENSE.txt
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT
|
| 2 |
+
Tencent HunyuanVideo-Foley Release Date: August 28, 2025
|
| 3 |
+
THIS LICENSE AGREEMENT DOES NOT APPLY IN THE EUROPEAN UNION, UNITED KINGDOM AND SOUTH KOREA AND IS EXPRESSLY LIMITED TO THE TERRITORY, AS DEFINED BELOW.
|
| 4 |
+
By clicking to agree or by using, reproducing, modifying, distributing, performing or displaying any portion or element of the Tencent Hunyuan Works, including via any Hosted Service, You will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately.
|
| 5 |
+
1. DEFINITIONS.
|
| 6 |
+
a. “Acceptable Use Policy” shall mean the policy made available by Tencent as set forth in the Exhibit A.
|
| 7 |
+
b. “Agreement” shall mean the terms and conditions for use, reproduction, distribution, modification, performance and displaying of Tencent Hunyuan Works or any portion or element thereof set forth herein.
|
| 8 |
+
c. “Documentation” shall mean the specifications, manuals and documentation for Tencent Hunyuan made publicly available by Tencent.
|
| 9 |
+
d. “Hosted Service” shall mean a hosted service offered via an application programming interface (API), web access, or any other electronic or remote means.
|
| 10 |
+
e. “Licensee,” “You” or “Your” shall mean a natural person or legal entity exercising the rights granted by this Agreement and/or using the Tencent Hunyuan Works for any purpose and in any field of use.
|
| 11 |
+
f. “Materials” shall mean, collectively, Tencent’s proprietary Tencent Hunyuan and Documentation (and any portion thereof) as made available by Tencent under this Agreement.
|
| 12 |
+
g. “Model Derivatives” shall mean all: (i) modifications to Tencent Hunyuan or any Model Derivative of Tencent Hunyuan; (ii) works based on Tencent Hunyuan or any Model Derivative of Tencent Hunyuan; or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Tencent Hunyuan or any Model Derivative of Tencent Hunyuan, to that model in order to cause that model to perform similarly to Tencent Hunyuan or a Model Derivative of Tencent Hunyuan, including distillation methods, methods that use intermediate data representations, or methods based on the generation of synthetic data Outputs by Tencent Hunyuan or a Model Derivative of Tencent Hunyuan for training that model. For clarity, Outputs by themselves are not deemed Model Derivatives.
|
| 13 |
+
h. “Output” shall mean the information and/or content output of Tencent Hunyuan or a Model Derivative that results from operating or otherwise using Tencent Hunyuan or a Model Derivative, including via a Hosted Service.
|
| 14 |
+
i. “Tencent,” “We” or “Us” shall mean the applicable entity or entities in the Tencent corporate family that own(s) intellectual property or other rights embodied in or utilized by the Materials.
|
| 15 |
+
j. “Tencent Hunyuan” shall mean the large language models, text/image/video/audio/3D generation models, and multimodal large language models and their software and algorithms, including trained model weights, parameters (including optimizer states), machine-learning model code, inference-enabling code, training-enabling code, fine-tuning enabling code and other elements of the foregoing made publicly available by Us, including, without limitation to, Tencent HunyuanVideo-Foley released at [https://github.com/Tencent-Hunyuan/HunyuanVideo-Foley].
|
| 16 |
+
k. “Tencent Hunyuan Works” shall mean: (i) the Materials; (ii) Model Derivatives; and (iii) all derivative works thereof.
|
| 17 |
+
l. “Territory” shall mean the worldwide territory, excluding the territory of the European Union, United Kingdom and South Korea.
|
| 18 |
+
m. “Third Party” or “Third Parties” shall mean individuals or legal entities that are not under common control with Us or You.
|
| 19 |
+
n. “including” shall mean including but not limited to.
|
| 20 |
+
2. GRANT OF RIGHTS.
|
| 21 |
+
We grant You, for the Territory only, a non-exclusive, non-transferable and royalty-free limited license under Tencent’s intellectual property or other rights owned by Us embodied in or utilized by the Materials to use, reproduce, distribute, create derivative works of (including Model Derivatives), and make modifications to the Materials, only in accordance with the terms of this Agreement and the Acceptable Use Policy, and You must not violate (or encourage or permit anyone else to violate) any term of this Agreement or the Acceptable Use Policy.
|
| 22 |
+
3. DISTRIBUTION.
|
| 23 |
+
You may, subject to Your compliance with this Agreement, distribute or make available to Third Parties the Tencent Hunyuan Works, exclusively in the Territory, provided that You meet all of the following conditions:
|
| 24 |
+
a. You must provide all such Third Party recipients of the Tencent Hunyuan Works or products or services using them a copy of this Agreement;
|
| 25 |
+
b. You must cause any modified files to carry prominent notices stating that You changed the files;
|
| 26 |
+
c. You are encouraged to: (i) publish at least one technology introduction blogpost or one public statement expressing Your experience of using the Tencent Hunyuan Works; and (ii) mark the products or services developed by using the Tencent Hunyuan Works to indicate that the product/service is “Powered by Tencent Hunyuan”; and
|
| 27 |
+
d. All distributions to Third Parties (other than through a Hosted Service) must be accompanied by a “Notice” text file that contains the following notice: “Tencent Hunyuan is licensed under the Tencent Hunyuan Community License Agreement, Copyright © 2025 Tencent. All Rights Reserved. The trademark rights of “Tencent Hunyuan” are owned by Tencent or its affiliate.”
|
| 28 |
+
You may add Your own copyright statement to Your modifications and, except as set forth in this Section and in Section 5, may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Model Derivatives as a whole, provided Your use, reproduction, modification, distribution, performance and display of the work otherwise complies with the terms and conditions of this Agreement (including as regards the Territory). If You receive Tencent Hunyuan Works from a Licensee as part of an integrated end user product, then this Section 3 of this Agreement will not apply to You.
|
| 29 |
+
4. ADDITIONAL COMMERCIAL TERMS.
|
| 30 |
+
If, on the Tencent Hunyuan version release date, the monthly active users of all products or services made available by or for Licensee is greater than 100 million monthly active users in the preceding calendar month, You must request a license from Tencent, which Tencent may grant to You in its sole discretion, and You are not authorized to exercise any of the rights under this Agreement unless or until Tencent otherwise expressly grants You such rights.
|
| 31 |
+
5. RULES OF USE.
|
| 32 |
+
a. Your use of the Tencent Hunyuan Works must comply with applicable laws and regulations (including trade compliance laws and regulations) and adhere to the Acceptable Use Policy for the Tencent Hunyuan Works, which is hereby incorporated by reference into this Agreement. You must include the use restrictions referenced in these Sections 5(a) and 5(b) as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Tencent Hunyuan Works and You must provide notice to subsequent users to whom You distribute that Tencent Hunyuan Works are subject to the use restrictions in these Sections 5(a) and 5(b).
|
| 33 |
+
b. You must not use the Tencent Hunyuan Works or any Output or results of the Tencent Hunyuan Works to improve any other AI model (other than Tencent Hunyuan or Model Derivatives thereof).
|
| 34 |
+
c. You must not use, reproduce, modify, distribute, or display the Tencent Hunyuan Works, Output or results of the Tencent Hunyuan Works outside the Territory. Any such use outside the Territory is unlicensed and unauthorized under this Agreement.
|
| 35 |
+
6. INTELLECTUAL PROPERTY.
|
| 36 |
+
a. Subject to Tencent’s ownership of Tencent Hunyuan Works made by or for Tencent and intellectual property rights therein, conditioned upon Your compliance with the terms and conditions of this Agreement, as between You and Tencent, You will be the owner of any derivative works and modifications of the Materials and any Model Derivatives that are made by or for You.
|
| 37 |
+
b. No trademark licenses are granted under this Agreement, and in connection with the Tencent Hunyuan Works, Licensee may not use any name or mark owned by or associated with Tencent or any of its affiliates, except as required for reasonable and customary use in describing and distributing the Tencent Hunyuan Works. Tencent hereby grants You a license to use “Tencent Hunyuan” (the “Mark”) in the Territory solely as required to comply with the provisions of Section 3(c), provided that You comply with any applicable laws related to trademark protection. All goodwill arising out of Your use of the Mark will inure to the benefit of Tencent.
|
| 38 |
+
c. If You commence a lawsuit or other proceedings (including a cross-claim or counterclaim in a lawsuit) against Us or any person or entity alleging that the Materials or any Output, or any portion of any of the foregoing, infringe any intellectual property or other right owned or licensable by You, then all licenses granted to You under this Agreement shall terminate as of the date such lawsuit or other proceeding is filed. You will defend, indemnify and hold harmless Us from and against any claim by any Third Party arising out of or related to Your or the Third Party’s use or distribution of the Tencent Hunyuan Works.
|
| 39 |
+
d. Tencent claims no rights in Outputs You generate. You and Your users are solely responsible for Outputs and their subsequent uses.
|
| 40 |
+
7. DISCLAIMERS OF WARRANTY AND LIMITATIONS OF LIABILITY.
|
| 41 |
+
a. We are not obligated to support, update, provide training for, or develop any further version of the Tencent Hunyuan Works or to grant any license thereto.
|
| 42 |
+
b. UNLESS AND ONLY TO THE EXTENT REQUIRED BY APPLICABLE LAW, THE TENCENT HUNYUAN WORKS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED “AS IS” WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND INCLUDING ANY WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, COURSE OF DEALING, USAGE OF TRADE, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR DISTRIBUTING ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR OR A THIRD PARTY’S USE OR DISTRIBUTION OF ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
|
| 43 |
+
c. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL TENCENT OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS, EVEN IF TENCENT OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
|
| 44 |
+
8. SURVIVAL AND TERMINATION.
|
| 45 |
+
a. The term of this Agreement shall commence upon Your acceptance of this Agreement or access to the Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein.
|
| 46 |
+
b. We may terminate this Agreement if You breach any of the terms or conditions of this Agreement. Upon termination of this Agreement, You must promptly delete and cease use of the Tencent Hunyuan Works. Sections 6(a), 6(c), 7 and 9 shall survive the termination of this Agreement.
|
| 47 |
+
9. GOVERNING LAW AND JURISDICTION.
|
| 48 |
+
a. This Agreement and any dispute arising out of or relating to it will be governed by the laws of the Hong Kong Special Administrative Region of the People’s Republic of China, without regard to conflict of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
|
| 49 |
+
b. Exclusive jurisdiction and venue for any dispute arising out of or relating to this Agreement will be a court of competent jurisdiction in the Hong Kong Special Administrative Region of the People’s Republic of China, and Tencent and Licensee consent to the exclusive jurisdiction of such court with respect to any such dispute.
|
| 50 |
+
|
| 51 |
+
EXHIBIT A
|
| 52 |
+
ACCEPTABLE USE POLICY
|
| 53 |
+
|
| 54 |
+
Tencent reserves the right to update this Acceptable Use Policy from time to time.
|
| 55 |
+
Last modified: November 5, 2024
|
| 56 |
+
|
| 57 |
+
Tencent endeavors to promote safe and fair use of its tools and features, including Tencent Hunyuan. You agree not to use Tencent Hunyuan or Model Derivatives:
|
| 58 |
+
1. Outside the Territory;
|
| 59 |
+
2. In any way that violates any applicable national, federal, state, local, international or any other law or regulation;
|
| 60 |
+
3. To harm Yourself or others;
|
| 61 |
+
4. To repurpose or distribute output from Tencent Hunyuan or any Model Derivatives to harm Yourself or others;
|
| 62 |
+
5. To override or circumvent the safety guardrails and safeguards We have put in place;
|
| 63 |
+
6. For the purpose of exploiting, harming or attempting to exploit or harm minors in any way;
|
| 64 |
+
7. To generate or disseminate verifiably false information and/or content with the purpose of harming others or influencing elections;
|
| 65 |
+
8. To generate or facilitate false online engagement, including fake reviews and other means of fake online engagement;
|
| 66 |
+
9. To intentionally defame, disparage or otherwise harass others;
|
| 67 |
+
10. To generate and/or disseminate malware (including ransomware) or any other content to be used for the purpose of harming electronic systems;
|
| 68 |
+
11. To generate or disseminate personal identifiable information with the purpose of harming others;
|
| 69 |
+
12. To generate or disseminate information (including images, code, posts, articles), and place the information in any public context (including –through the use of bot generated tweets), without expressly and conspicuously identifying that the information and/or content is machine generated;
|
| 70 |
+
13. To impersonate another individual without consent, authorization, or legal right;
|
| 71 |
+
14. To make high-stakes automated decisions in domains that affect an individual’s safety, rights or wellbeing (e.g., law enforcement, migration, medicine/health, management of critical infrastructure, safety components of products, essential services, credit, employment, housing, education, social scoring, or insurance);
|
| 72 |
+
15. In a manner that violates or disrespects the social ethics and moral standards of other countries or regions;
|
| 73 |
+
16. To perform, facilitate, threaten, incite, plan, promote or encourage violent extremism or terrorism;
|
| 74 |
+
17. For any use intended to discriminate against or harm individuals or groups based on protected characteristics or categories, online or offline social behavior or known or predicted personal or personality characteristics;
|
| 75 |
+
18. To intentionally exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm;
|
| 76 |
+
19. For military purposes;
|
| 77 |
+
20. To engage in the unauthorized or unlicensed practice of any profession including, but not limited to, financial, legal, medical/health, or other professional practices.
|
README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: tencent/HunyuanVideo-Foley
|
| 3 |
+
tags:
|
| 4 |
+
- quantized
|
| 5 |
+
- fp8
|
| 6 |
+
- audio-generation
|
| 7 |
+
- video-to-audio
|
| 8 |
+
- comfyui
|
| 9 |
+
library_name: transformers
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# HunyuanVideo-Foley FP8 Quantized
|
| 13 |
+
|
| 14 |
+
This is an FP8 quantized version of [tencent/HunyuanVideo-Foley](https://huggingface.co/tencent/HunyuanVideo-Foley) optimized for reduced VRAM usage while maintaining audio generation quality.
|
| 15 |
+
|
| 16 |
+
## Quantization Details
|
| 17 |
+
|
| 18 |
+
- **Quantization Method**: FP8 E5M2 & E4M3FN weight-only quantization
|
| 19 |
+
- **Layers Quantized**: Transformer block weights only (attention and FFN layers)
|
| 20 |
+
- **Preserved Precision**: Normalization layers, embeddings, and biases remain in original precision
|
| 21 |
+
- **Expected VRAM Savings**: ~30-40% reduction compared to BF16 original
|
| 22 |
+
- **Memory Usage**: Enables running on <12GB GPUs when combined with other optimizations
|
| 23 |
+
|
| 24 |
+
## Usage
|
| 25 |
+
|
| 26 |
+
### ComfyUI (Recommended)
|
| 27 |
+
|
| 28 |
+
This model is specifically optimized for use with the [ComfyUI-HunyuanVideo-Foley](https://github.com/phazei/ComfyUI-HunyuanVideo-Foley) custom node, which provides:
|
| 29 |
+
|
| 30 |
+
- **VRAM-friendly loading** with ping-pong memory management
|
| 31 |
+
- **Built-in FP8 support** that automatically handles the quantized weights
|
| 32 |
+
- **Torch compile integration** for ~30% speed improvements after first run
|
| 33 |
+
- **Text-to-Audio and Video-to-Audio** modes
|
| 34 |
+
- **Batch generation** with audio selection tools
|
| 35 |
+
|
| 36 |
+
**Installation:**
|
| 37 |
+
1. Install the ComfyUI node: [ComfyUI-HunyuanVideo-Foley](https://github.com/phazei/ComfyUI-HunyuanVideo-Foley)
|
| 38 |
+
2. Download this quantized model to `ComfyUI/models/foley/`
|
| 39 |
+
3. Enjoy <8GB VRAM usage with high-quality audio generation
|
| 40 |
+
|
| 41 |
+
**Typical VRAM Usage (5s audio, 50 steps):**
|
| 42 |
+
- Baseline (BF16): ~10-12 GB
|
| 43 |
+
- With FP8 quantization: ~8-10 GB
|
| 44 |
+
- Perfect for RTX 3080/4070 Ti and similar GPUs
|
| 45 |
+
|
| 46 |
+
### Other Frameworks
|
| 47 |
+
|
| 48 |
+
The FP8 weights can be used with any framework that supports automatic upcasting of FP8 to FP16/BF16 during computation. The quantized weights maintain compatibility with the original model architecture.
|
| 49 |
+
|
| 50 |
+
## Files
|
| 51 |
+
|
| 52 |
+
- `hunyuanvideo_foley_fp8_e4m3fn.safetensors` - Main model weights in FP8 format
|
| 53 |
+
|
| 54 |
+
## Performance Notes
|
| 55 |
+
|
| 56 |
+
- **Quality**: Maintains comparable audio generation quality to the original model
|
| 57 |
+
- **Speed**: Conversion overhead is minimal; actual generation speed depends on compute precision
|
| 58 |
+
- **Memory**: Significant VRAM reduction makes the model accessible on consumer GPUs
|
| 59 |
+
- **Compatibility**: Drop-in replacement for the original model weights
|
| 60 |
+
|
| 61 |
+
## Original Model
|
| 62 |
+
|
| 63 |
+
This quantization is based on [tencent/HunyuanVideo-Foley](https://huggingface.co/tencent/HunyuanVideo-Foley). Please refer to the original repository for:
|
| 64 |
+
- Model architecture details
|
| 65 |
+
- Training information
|
| 66 |
+
- License terms
|
| 67 |
+
- Citation information
|
| 68 |
+
|
| 69 |
+
## Technical Details
|
| 70 |
+
|
| 71 |
+
The quantization uses a conservative approach that only converts transformer block weights while preserving precision-sensitive components:
|
| 72 |
+
- ✅ **Converted**: Attention and FFN layer weights in transformer blocks
|
| 73 |
+
- ❌ **Preserved**: Normalization layers, embeddings, projections, bias terms
|
| 74 |
+
|
| 75 |
+
This selective quantization strategy maintains model quality while maximizing memory savings.
|
convert_pth_to_safetensors.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# convert_pth_to_safetensors.py
|
| 2 |
+
import sys, torch
|
| 3 |
+
from safetensors.torch import save_file
|
| 4 |
+
|
| 5 |
+
src = sys.argv[1]
|
| 6 |
+
dst = sys.argv[2] if len(sys.argv) > 2 else src.rsplit(".", 1)[0] + ".safetensors"
|
| 7 |
+
|
| 8 |
+
obj = torch.load(src, map_location="cpu") # weights_only=True if your torch>=2.0 supports it
|
| 9 |
+
sd = obj.get("state_dict", obj) # many .pth have nested {"state_dict": ...}
|
| 10 |
+
|
| 11 |
+
# If someone saved a whole nn.Module:
|
| 12 |
+
if hasattr(sd, "state_dict"):
|
| 13 |
+
sd = sd.state_dict()
|
| 14 |
+
|
| 15 |
+
# Ensure tensors are contiguous (defensive; usually not required)
|
| 16 |
+
sd = {k: (v.contiguous() if isinstance(v, torch.Tensor) else v) for k, v in sd.items()}
|
| 17 |
+
|
| 18 |
+
# Optional: add a tiny breadcrumb
|
| 19 |
+
meta = {"converted_from": src, "format": "pt->safetensors"}
|
| 20 |
+
|
| 21 |
+
save_file(sd, dst, metadata=meta)
|
| 22 |
+
print(f"Wrote: {dst}")
|
convert_safetensors_to_fp8.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import os
|
| 3 |
+
import argparse
|
| 4 |
+
from safetensors.torch import save_file
|
| 5 |
+
from safetensors import safe_open
|
| 6 |
+
from collections import OrderedDict
|
| 7 |
+
from tqdm import tqdm
|
| 8 |
+
import gc
|
| 9 |
+
|
| 10 |
+
def should_convert_to_fp8(tensor_name: str) -> bool:
|
| 11 |
+
"""
|
| 12 |
+
Conservative FP8 conversion policy:
|
| 13 |
+
- Only convert .weight tensors (not biases)
|
| 14 |
+
- Only convert transformer block layers
|
| 15 |
+
- Skip normalization layers (precision sensitive)
|
| 16 |
+
"""
|
| 17 |
+
if not tensor_name.endswith(".weight"):
|
| 18 |
+
return False
|
| 19 |
+
if not "blocks." in tensor_name:
|
| 20 |
+
return False
|
| 21 |
+
if "cross_attn" in tensor_name or \
|
| 22 |
+
"ffn" in tensor_name or \
|
| 23 |
+
"self_attn" in tensor_name or \
|
| 24 |
+
"linear" in tensor_name: # Added "linear" for broader coverage
|
| 25 |
+
if ".norm_k.weight" in tensor_name or \
|
| 26 |
+
".norm_q.weight" in tensor_name or \
|
| 27 |
+
".norm.weight" in tensor_name:
|
| 28 |
+
return False
|
| 29 |
+
return True
|
| 30 |
+
return False
|
| 31 |
+
|
| 32 |
+
def convert_safetensors_to_fp8(input_path: str, fp8_variant: str = "e4m3fn", device: str = "cuda"):
|
| 33 |
+
"""
|
| 34 |
+
Convert a single SafeTensors file to FP8, saving in the same directory
|
| 35 |
+
with fp8_e*** appended to the filename.
|
| 36 |
+
|
| 37 |
+
Args:
|
| 38 |
+
input_path: Path to input .safetensors file
|
| 39 |
+
fp8_variant: "e4m3fn" or "e5m2"
|
| 40 |
+
device: Device to use for conversion ("cuda" or "cpu")
|
| 41 |
+
"""
|
| 42 |
+
if not os.path.exists(input_path):
|
| 43 |
+
raise FileNotFoundError(f"Input file not found: {input_path}")
|
| 44 |
+
|
| 45 |
+
if not input_path.endswith('.safetensors'):
|
| 46 |
+
raise ValueError("Input file must be a .safetensors file")
|
| 47 |
+
|
| 48 |
+
# Determine target dtype
|
| 49 |
+
if fp8_variant == "e5m2":
|
| 50 |
+
target_dtype = torch.float8_e5m2
|
| 51 |
+
elif fp8_variant == "e4m3fn":
|
| 52 |
+
target_dtype = torch.float8_e4m3fn
|
| 53 |
+
else:
|
| 54 |
+
raise ValueError(f"Unsupported FP8 variant: {fp8_variant}. Use 'e4m3fn' or 'e5m2'")
|
| 55 |
+
|
| 56 |
+
# Generate output path
|
| 57 |
+
input_dir = os.path.dirname(input_path)
|
| 58 |
+
input_filename = os.path.basename(input_path)
|
| 59 |
+
name_without_ext = os.path.splitext(input_filename)[0]
|
| 60 |
+
output_filename = f"{name_without_ext}_fp8_{fp8_variant}.safetensors"
|
| 61 |
+
output_path = os.path.join(input_dir, output_filename)
|
| 62 |
+
|
| 63 |
+
print(f"Converting: {input_path}")
|
| 64 |
+
print(f"Output: {output_path}")
|
| 65 |
+
print(f"Target dtype: {target_dtype}")
|
| 66 |
+
print(f"Device: {device}")
|
| 67 |
+
|
| 68 |
+
# Check if output already exists
|
| 69 |
+
if os.path.exists(output_path):
|
| 70 |
+
response = input(f"Output file {output_path} already exists. Overwrite? (y/N): ")
|
| 71 |
+
if response.lower() != 'y':
|
| 72 |
+
print("Conversion cancelled.")
|
| 73 |
+
return
|
| 74 |
+
|
| 75 |
+
converted_state_dict = OrderedDict()
|
| 76 |
+
conversion_stats = {"converted": 0, "skipped": 0, "total": 0}
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
# Load and process tensors
|
| 80 |
+
with safe_open(input_path, framework="pt", device="cpu") as f:
|
| 81 |
+
tensor_names = list(f.keys())
|
| 82 |
+
conversion_stats["total"] = len(tensor_names)
|
| 83 |
+
|
| 84 |
+
print(f"Processing {len(tensor_names)} tensors...")
|
| 85 |
+
|
| 86 |
+
for tensor_name in tqdm(tensor_names, desc="Converting tensors"):
|
| 87 |
+
original_tensor = f.get_tensor(tensor_name)
|
| 88 |
+
|
| 89 |
+
if should_convert_to_fp8(tensor_name):
|
| 90 |
+
# Convert to FP8
|
| 91 |
+
converted_tensor = original_tensor.to(device).to(target_dtype).to("cpu")
|
| 92 |
+
converted_state_dict[tensor_name] = converted_tensor
|
| 93 |
+
conversion_stats["converted"] += 1
|
| 94 |
+
|
| 95 |
+
# Clean up GPU memory if using CUDA
|
| 96 |
+
if device == "cuda" and torch.cuda.is_available():
|
| 97 |
+
del converted_tensor
|
| 98 |
+
else:
|
| 99 |
+
# Keep original precision
|
| 100 |
+
converted_state_dict[tensor_name] = original_tensor.to("cpu")
|
| 101 |
+
conversion_stats["skipped"] += 1
|
| 102 |
+
|
| 103 |
+
# Save converted model
|
| 104 |
+
print(f"Saving converted model to: {output_path}")
|
| 105 |
+
save_file(converted_state_dict, output_path)
|
| 106 |
+
|
| 107 |
+
# Print conversion statistics
|
| 108 |
+
print(f"\nConversion complete!")
|
| 109 |
+
print(f"Total tensors: {conversion_stats['total']}")
|
| 110 |
+
print(f"Converted to FP8: {conversion_stats['converted']}")
|
| 111 |
+
print(f"Kept original precision: {conversion_stats['skipped']}")
|
| 112 |
+
print(f"Conversion rate: {conversion_stats['converted']/conversion_stats['total']*100:.1f}%")
|
| 113 |
+
|
| 114 |
+
# Calculate file sizes
|
| 115 |
+
input_size = os.path.getsize(input_path) / (1024**3) # GB
|
| 116 |
+
output_size = os.path.getsize(output_path) / (1024**3) # GB
|
| 117 |
+
size_reduction = (1 - output_size/input_size) * 100
|
| 118 |
+
|
| 119 |
+
print(f"\nFile size comparison:")
|
| 120 |
+
print(f"Original: {input_size:.2f} GB")
|
| 121 |
+
print(f"Converted: {output_size:.2f} GB")
|
| 122 |
+
print(f"Size reduction: {size_reduction:.1f}%")
|
| 123 |
+
|
| 124 |
+
except Exception as e:
|
| 125 |
+
print(f"Error during conversion: {e}")
|
| 126 |
+
if os.path.exists(output_path):
|
| 127 |
+
print(f"Removing incomplete output file: {output_path}")
|
| 128 |
+
os.remove(output_path)
|
| 129 |
+
raise
|
| 130 |
+
|
| 131 |
+
finally:
|
| 132 |
+
# Clean up memory
|
| 133 |
+
if 'converted_state_dict' in locals():
|
| 134 |
+
del converted_state_dict
|
| 135 |
+
if 'original_tensor' in locals():
|
| 136 |
+
del original_tensor
|
| 137 |
+
gc.collect()
|
| 138 |
+
if torch.cuda.is_available():
|
| 139 |
+
torch.cuda.empty_cache()
|
| 140 |
+
|
| 141 |
+
def main():
|
| 142 |
+
parser = argparse.ArgumentParser(
|
| 143 |
+
description="Convert SafeTensors model to FP8 precision",
|
| 144 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 145 |
+
epilog="""
|
| 146 |
+
Examples:
|
| 147 |
+
python convert_safetensors_to_fp8.py model.safetensors
|
| 148 |
+
python convert_safetensors_to_fp8.py model.safetensors --variant e5m2
|
| 149 |
+
python convert_safetensors_to_fp8.py model.safetensors --device cpu
|
| 150 |
+
"""
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
parser.add_argument(
|
| 154 |
+
"input_file",
|
| 155 |
+
help="Path to input .safetensors file"
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
parser.add_argument(
|
| 159 |
+
"--variant",
|
| 160 |
+
choices=["e4m3fn", "e5m2"],
|
| 161 |
+
default="e4m3fn",
|
| 162 |
+
help="FP8 variant to use (default: e4m3fn)"
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
parser.add_argument(
|
| 166 |
+
"--device",
|
| 167 |
+
choices=["cuda", "cpu"],
|
| 168 |
+
default="cuda" if torch.cuda.is_available() else "cpu",
|
| 169 |
+
help="Device to use for conversion"
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
args = parser.parse_args()
|
| 173 |
+
|
| 174 |
+
print(f"=== SafeTensors to FP8 Converter ===")
|
| 175 |
+
print(f"PyTorch version: {torch.__version__}")
|
| 176 |
+
print(f"CUDA available: {torch.cuda.is_available()}")
|
| 177 |
+
if torch.cuda.is_available():
|
| 178 |
+
print(f"CUDA device: {torch.cuda.get_device_name()}")
|
| 179 |
+
print()
|
| 180 |
+
|
| 181 |
+
try:
|
| 182 |
+
convert_safetensors_to_fp8(
|
| 183 |
+
input_path=args.input_file,
|
| 184 |
+
fp8_variant=args.variant,
|
| 185 |
+
device=args.device
|
| 186 |
+
)
|
| 187 |
+
except Exception as e:
|
| 188 |
+
print(f"Conversion failed: {e}")
|
| 189 |
+
exit(1)
|
| 190 |
+
|
| 191 |
+
if __name__ == "__main__":
|
| 192 |
+
main()
|
hunyuanvideo_foley.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fd58013006359cd243b3bfebc90e2ff51a303c074f10a7c79d24b7963cd06dba
|
| 3 |
+
size 10268151840
|
hunyuanvideo_foley_fp8_e4m3fn.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ce2af6afbe910197e48020261d5ce2af30267b3d15e2f8f385570cc4049e3934
|
| 3 |
+
size 6318689840
|
hunyuanvideo_foley_fp8_e5m2.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:43983270051d1b0bb3078f97040fd7bf84ffbee607d79ea1559c21a64741e5fc
|
| 3 |
+
size 6318689840
|
pth_info.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import torch
|
| 3 |
+
from collections import Counter
|
| 4 |
+
import os
|
| 5 |
+
import math
|
| 6 |
+
|
| 7 |
+
# --- PyTorch Dtype to Bytes Mapping ---
|
| 8 |
+
TORCH_DTYPE_TO_BYTES = {
|
| 9 |
+
# Boolean
|
| 10 |
+
torch.bool: 1,
|
| 11 |
+
# Floating point
|
| 12 |
+
torch.float16: 2,
|
| 13 |
+
torch.half: 2, # alias for float16
|
| 14 |
+
torch.bfloat16: 2,
|
| 15 |
+
torch.float32: 4,
|
| 16 |
+
torch.float: 4, # alias for float32
|
| 17 |
+
torch.float64: 8,
|
| 18 |
+
torch.double: 8, # alias for float64
|
| 19 |
+
# Complex
|
| 20 |
+
torch.complex64: 8, # 2 * float32
|
| 21 |
+
torch.complex128: 16, # 2 * float64
|
| 22 |
+
torch.cfloat: 8, # alias for complex64
|
| 23 |
+
torch.cdouble: 16, # alias for complex128
|
| 24 |
+
# Signed integers
|
| 25 |
+
torch.int8: 1,
|
| 26 |
+
torch.int16: 2,
|
| 27 |
+
torch.short: 2, # alias for int16
|
| 28 |
+
torch.int32: 4,
|
| 29 |
+
torch.int: 4, # alias for int32
|
| 30 |
+
torch.int64: 8,
|
| 31 |
+
torch.long: 8, # alias for int64
|
| 32 |
+
# Unsigned integers
|
| 33 |
+
torch.uint8: 1,
|
| 34 |
+
torch.uint16: 2,
|
| 35 |
+
torch.uint32: 4,
|
| 36 |
+
torch.uint64: 8,
|
| 37 |
+
# Quantized types (approximate sizes)
|
| 38 |
+
torch.qint8: 1,
|
| 39 |
+
torch.quint8: 1,
|
| 40 |
+
torch.qint32: 4,
|
| 41 |
+
torch.quint4x2: 1, # 4-bit packed
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
def get_bytes_per_element(dtype):
|
| 45 |
+
"""Returns the number of bytes for a given PyTorch dtype."""
|
| 46 |
+
return TORCH_DTYPE_TO_BYTES.get(dtype, None)
|
| 47 |
+
|
| 48 |
+
def get_dtype_name(dtype):
|
| 49 |
+
"""Returns a readable string for a PyTorch dtype."""
|
| 50 |
+
return str(dtype).replace('torch.', '')
|
| 51 |
+
|
| 52 |
+
def calculate_num_elements(shape):
|
| 53 |
+
"""Calculates the total number of elements from a tensor shape tuple."""
|
| 54 |
+
if not shape: # Scalar tensor (shape is ())
|
| 55 |
+
return 1
|
| 56 |
+
if 0 in shape: # If any dimension is 0, total elements is 0
|
| 57 |
+
return 0
|
| 58 |
+
num_elements = 1
|
| 59 |
+
for dim_size in shape:
|
| 60 |
+
num_elements *= dim_size
|
| 61 |
+
return num_elements
|
| 62 |
+
|
| 63 |
+
def extract_tensors_from_obj(obj, prefix=""):
|
| 64 |
+
"""
|
| 65 |
+
Recursively extracts tensors from nested dictionaries/objects.
|
| 66 |
+
Returns a dictionary of {key: tensor} pairs.
|
| 67 |
+
"""
|
| 68 |
+
tensors = {}
|
| 69 |
+
|
| 70 |
+
if isinstance(obj, torch.Tensor):
|
| 71 |
+
return {prefix or "tensor": obj}
|
| 72 |
+
|
| 73 |
+
elif isinstance(obj, dict):
|
| 74 |
+
for key, value in obj.items():
|
| 75 |
+
new_prefix = f"{prefix}.{key}" if prefix else key
|
| 76 |
+
tensors.update(extract_tensors_from_obj(value, new_prefix))
|
| 77 |
+
|
| 78 |
+
elif hasattr(obj, 'state_dict') and callable(getattr(obj, 'state_dict')):
|
| 79 |
+
# Handle nn.Module objects
|
| 80 |
+
state_dict = obj.state_dict()
|
| 81 |
+
new_prefix = f"{prefix}.state_dict" if prefix else "state_dict"
|
| 82 |
+
tensors.update(extract_tensors_from_obj(state_dict, new_prefix))
|
| 83 |
+
|
| 84 |
+
elif hasattr(obj, '__dict__'):
|
| 85 |
+
# Handle other objects with attributes
|
| 86 |
+
for key, value in obj.__dict__.items():
|
| 87 |
+
if isinstance(value, torch.Tensor):
|
| 88 |
+
new_prefix = f"{prefix}.{key}" if prefix else key
|
| 89 |
+
tensors[new_prefix] = value
|
| 90 |
+
|
| 91 |
+
return tensors
|
| 92 |
+
|
| 93 |
+
def inspect_pth_precision_and_size(filepath):
|
| 94 |
+
"""
|
| 95 |
+
Reads a .pth file, extracts tensors from it,
|
| 96 |
+
and reports the precision (dtype), actual size, and theoretical FP32 size.
|
| 97 |
+
"""
|
| 98 |
+
if not os.path.exists(filepath):
|
| 99 |
+
print(f"Error: File not found at '{filepath}'")
|
| 100 |
+
return
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
print(f"Loading PyTorch file: {filepath}")
|
| 104 |
+
|
| 105 |
+
# Load with weights_only=True for security if PyTorch >= 2.0
|
| 106 |
+
try:
|
| 107 |
+
obj = torch.load(filepath, map_location="cpu", weights_only=True)
|
| 108 |
+
print("(Loaded with weights_only=True for security)\n")
|
| 109 |
+
except TypeError:
|
| 110 |
+
# Fallback for older PyTorch versions
|
| 111 |
+
obj = torch.load(filepath, map_location="cpu")
|
| 112 |
+
print("(Warning: Loaded without weights_only=True - older PyTorch version)\n")
|
| 113 |
+
|
| 114 |
+
# Extract all tensors from the loaded object
|
| 115 |
+
tensors = extract_tensors_from_obj(obj)
|
| 116 |
+
|
| 117 |
+
if not tensors:
|
| 118 |
+
print("No tensors found in the file.")
|
| 119 |
+
return
|
| 120 |
+
|
| 121 |
+
tensor_info_list = []
|
| 122 |
+
dtype_counts = Counter()
|
| 123 |
+
total_actual_mb = 0.0
|
| 124 |
+
total_fp32_equiv_mb = 0.0
|
| 125 |
+
|
| 126 |
+
max_key_len = max(len("Tensor Name"), max(len(k) for k in tensors.keys()))
|
| 127 |
+
|
| 128 |
+
header = (
|
| 129 |
+
f"{'Tensor Name':<{max_key_len}} | "
|
| 130 |
+
f"{'Precision (dtype)':<17} | "
|
| 131 |
+
f"{'Shape':<20} | "
|
| 132 |
+
f"{'Actual Size (MB)':>16} | "
|
| 133 |
+
f"{'FP32 Equiv. (MB)':>18}"
|
| 134 |
+
)
|
| 135 |
+
print(header)
|
| 136 |
+
print(
|
| 137 |
+
f"{'-' * max_key_len}-|-------------------|{'-' * 20}|------------------|-------------------"
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
for key, tensor in tensors.items():
|
| 141 |
+
dtype = tensor.dtype
|
| 142 |
+
dtype_name = get_dtype_name(dtype)
|
| 143 |
+
shape = tuple(tensor.shape)
|
| 144 |
+
shape_str = str(shape)
|
| 145 |
+
|
| 146 |
+
num_elements = tensor.numel()
|
| 147 |
+
bytes_per_el_actual = get_bytes_per_element(dtype)
|
| 148 |
+
|
| 149 |
+
actual_size_mb_str = "N/A"
|
| 150 |
+
fp32_equiv_size_mb_str = "N/A"
|
| 151 |
+
actual_size_mb_val = 0.0
|
| 152 |
+
|
| 153 |
+
if bytes_per_el_actual is not None:
|
| 154 |
+
actual_bytes = num_elements * bytes_per_el_actual
|
| 155 |
+
actual_size_mb_val = actual_bytes / (1024 * 1024)
|
| 156 |
+
total_actual_mb += actual_size_mb_val
|
| 157 |
+
actual_size_mb_str = f"{actual_size_mb_val:.3f}"
|
| 158 |
+
|
| 159 |
+
# Theoretical FP32 size (FP32 is 4 bytes per element)
|
| 160 |
+
fp32_equiv_bytes = num_elements * 4
|
| 161 |
+
fp32_equiv_size_mb_val = fp32_equiv_bytes / (1024 * 1024)
|
| 162 |
+
total_fp32_equiv_mb += fp32_equiv_size_mb_val
|
| 163 |
+
fp32_equiv_size_mb_str = f"{fp32_equiv_size_mb_val:.3f}"
|
| 164 |
+
else:
|
| 165 |
+
print(f"Warning: Unknown dtype '{dtype}' for tensor '{key}'. Cannot calculate size.")
|
| 166 |
+
|
| 167 |
+
# Truncate shape string if too long
|
| 168 |
+
if len(shape_str) > 18:
|
| 169 |
+
shape_str = shape_str[:15] + "..."
|
| 170 |
+
|
| 171 |
+
print(
|
| 172 |
+
f"{key:<{max_key_len}} | "
|
| 173 |
+
f"{dtype_name:<17} | "
|
| 174 |
+
f"{shape_str:<20} | "
|
| 175 |
+
f"{actual_size_mb_str:>16} | "
|
| 176 |
+
f"{fp32_equiv_size_mb_str:>18}"
|
| 177 |
+
)
|
| 178 |
+
dtype_counts[dtype_name] += 1
|
| 179 |
+
|
| 180 |
+
print("\n--- Summary ---")
|
| 181 |
+
print(f"Total tensors found: {len(tensors)}")
|
| 182 |
+
if dtype_counts:
|
| 183 |
+
print("Precision distribution:")
|
| 184 |
+
for dtype, count in dtype_counts.most_common():
|
| 185 |
+
print(f" - {dtype:<12}: {count} tensor(s)")
|
| 186 |
+
else:
|
| 187 |
+
print("No dtypes to summarize.")
|
| 188 |
+
|
| 189 |
+
print(f"\nTotal actual size of all tensors: {total_actual_mb:.3f} MB")
|
| 190 |
+
print(f"Total theoretical FP32 size of all tensors: {total_fp32_equiv_mb:.3f} MB")
|
| 191 |
+
|
| 192 |
+
if total_fp32_equiv_mb > 0.00001: # Avoid division by zero
|
| 193 |
+
savings_percentage = (1 - (total_actual_mb / total_fp32_equiv_mb)) * 100
|
| 194 |
+
print(f"Overall size reduction compared to full FP32: {savings_percentage:.2f}%")
|
| 195 |
+
else:
|
| 196 |
+
print("Overall size reduction cannot be calculated (no FP32 equivalent data or zero size).")
|
| 197 |
+
|
| 198 |
+
# Additional info about non-tensor content
|
| 199 |
+
non_tensor_keys = []
|
| 200 |
+
if isinstance(obj, dict):
|
| 201 |
+
for key, value in obj.items():
|
| 202 |
+
if key not in [k.split('.')[0] for k in tensors.keys()]: # Simplified check
|
| 203 |
+
non_tensor_keys.append(f"{key}: {type(value).__name__}")
|
| 204 |
+
|
| 205 |
+
if non_tensor_keys:
|
| 206 |
+
print(f"\nNon-tensor content found:")
|
| 207 |
+
for item in non_tensor_keys[:5]: # Show first 5
|
| 208 |
+
print(f" - {item}")
|
| 209 |
+
if len(non_tensor_keys) > 5:
|
| 210 |
+
print(f" ... and {len(non_tensor_keys) - 5} more items")
|
| 211 |
+
|
| 212 |
+
except Exception as e:
|
| 213 |
+
print(f"An error occurred while processing '{filepath}':")
|
| 214 |
+
print(f" {e}")
|
| 215 |
+
print("Please ensure it's a valid PyTorch .pth file and PyTorch is installed correctly.")
|
| 216 |
+
|
| 217 |
+
if __name__ == "__main__":
|
| 218 |
+
parser = argparse.ArgumentParser(
|
| 219 |
+
description="Inspect tensor precision (dtype) and size in a PyTorch .pth file."
|
| 220 |
+
)
|
| 221 |
+
parser.add_argument(
|
| 222 |
+
"filepath",
|
| 223 |
+
help="Path to the .pth file to inspect."
|
| 224 |
+
)
|
| 225 |
+
args = parser.parse_args()
|
| 226 |
+
|
| 227 |
+
inspect_pth_precision_and_size(args.filepath)
|
safetensors_info.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from safetensors import safe_open
|
| 3 |
+
from collections import Counter
|
| 4 |
+
import os
|
| 5 |
+
import math # math.prod is Python 3.8+
|
| 6 |
+
|
| 7 |
+
# --- Dtype to Bytes Mapping ---
|
| 8 |
+
# Safetensors Dtype strings:
|
| 9 |
+
# BOOL, F8_E5M2, F8_E4M3FN, F16, BF16, F32, F64,
|
| 10 |
+
# I8, I16, I32, I64, U8, U16, U32, U64,
|
| 11 |
+
# F8_E5M2FNUZ, F8_E4M3FNUZ
|
| 12 |
+
DTYPE_TO_BYTES = {
|
| 13 |
+
"BOOL": 1,
|
| 14 |
+
# Float8 variants
|
| 15 |
+
"F8_E5M2": 1,
|
| 16 |
+
"F8E5M2": 1, # Common alternative naming
|
| 17 |
+
"F8_E4M3FN": 1,
|
| 18 |
+
"F8E4M3FN": 1, # Common alternative naming
|
| 19 |
+
"F8_E4M3": 1, # As seen in user example, likely E4M3FN
|
| 20 |
+
"F8_E5M2FNUZ": 1,
|
| 21 |
+
"F8E5M2FNUZ": 1, # Common alternative naming
|
| 22 |
+
"F8_E4M3FNUZ": 1,
|
| 23 |
+
"F8E4M3FNUZ": 1, # Common alternative naming
|
| 24 |
+
# Standard floats
|
| 25 |
+
"F16": 2,
|
| 26 |
+
"BF16": 2,
|
| 27 |
+
"F32": 4,
|
| 28 |
+
"F64": 8,
|
| 29 |
+
# Integers
|
| 30 |
+
"I8": 1,
|
| 31 |
+
"I16": 2,
|
| 32 |
+
"I32": 4,
|
| 33 |
+
"I64": 8,
|
| 34 |
+
# Unsigned Integers
|
| 35 |
+
"U8": 1,
|
| 36 |
+
"U16": 2,
|
| 37 |
+
"U32": 4,
|
| 38 |
+
"U64": 8,
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
def get_bytes_per_element(dtype_str):
|
| 42 |
+
"""Returns the number of bytes for a given safetensors dtype string."""
|
| 43 |
+
return DTYPE_TO_BYTES.get(dtype_str.upper(), None)
|
| 44 |
+
|
| 45 |
+
def calculate_num_elements(shape):
|
| 46 |
+
"""Calculates the total number of elements from a tensor shape tuple."""
|
| 47 |
+
if not shape: # Scalar tensor (shape is ())
|
| 48 |
+
return 1
|
| 49 |
+
if 0 in shape: # If any dimension is 0, total elements is 0
|
| 50 |
+
return 0
|
| 51 |
+
# Using math.prod for conciseness if Python 3.8+
|
| 52 |
+
# For broader compatibility, a loop can be used:
|
| 53 |
+
num_elements = 1
|
| 54 |
+
for dim_size in shape:
|
| 55 |
+
num_elements *= dim_size
|
| 56 |
+
return num_elements
|
| 57 |
+
|
| 58 |
+
def inspect_safetensors_precision_and_size(filepath):
|
| 59 |
+
"""
|
| 60 |
+
Reads a .safetensors file, iterates through its tensors,
|
| 61 |
+
and reports the precision (dtype), actual size, and theoretical FP32 size.
|
| 62 |
+
"""
|
| 63 |
+
if not os.path.exists(filepath):
|
| 64 |
+
print(f"Error: File not found at '{filepath}'")
|
| 65 |
+
return
|
| 66 |
+
|
| 67 |
+
if not filepath.lower().endswith(".safetensors"):
|
| 68 |
+
print(f"Warning: File '{filepath}' does not have a .safetensors extension. Attempting to read anyway.")
|
| 69 |
+
|
| 70 |
+
tensor_info_list = []
|
| 71 |
+
dtype_counts = Counter()
|
| 72 |
+
total_actual_mb = 0.0
|
| 73 |
+
total_fp32_equiv_mb = 0.0
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
print(f"Inspecting tensors in: {filepath}\n")
|
| 77 |
+
with safe_open(filepath, framework="pt", device="cpu") as f:
|
| 78 |
+
tensor_keys = list(f.keys())
|
| 79 |
+
if not tensor_keys:
|
| 80 |
+
print("No tensors found in the file.")
|
| 81 |
+
return
|
| 82 |
+
|
| 83 |
+
max_key_len = len("Tensor Name") # Default/minimum
|
| 84 |
+
if tensor_keys:
|
| 85 |
+
max_key_len = max(max_key_len, max(len(k) for k in tensor_keys))
|
| 86 |
+
|
| 87 |
+
header = (
|
| 88 |
+
f"{'Tensor Name':<{max_key_len}} | "
|
| 89 |
+
f"{'Precision (dtype)':<17} | "
|
| 90 |
+
f"{'Actual Size (MB)':>16} | "
|
| 91 |
+
f"{'FP32 Equiv. (MB)':>18}"
|
| 92 |
+
)
|
| 93 |
+
print(header)
|
| 94 |
+
print(
|
| 95 |
+
f"{'-' * max_key_len}-|-------------------|------------------|-------------------"
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
for key in tensor_keys:
|
| 99 |
+
tensor_slice = f.get_slice(key)
|
| 100 |
+
dtype_str = tensor_slice.get_dtype()
|
| 101 |
+
shape = tensor_slice.get_shape()
|
| 102 |
+
|
| 103 |
+
num_elements = calculate_num_elements(shape)
|
| 104 |
+
bytes_per_el_actual = get_bytes_per_element(dtype_str)
|
| 105 |
+
|
| 106 |
+
actual_size_mb_str = "N/A"
|
| 107 |
+
fp32_equiv_size_mb_str = "N/A"
|
| 108 |
+
actual_size_mb_val = 0.0
|
| 109 |
+
|
| 110 |
+
if bytes_per_el_actual is not None:
|
| 111 |
+
actual_bytes = num_elements * bytes_per_el_actual
|
| 112 |
+
actual_size_mb_val = actual_bytes / (1024 * 1024)
|
| 113 |
+
total_actual_mb += actual_size_mb_val
|
| 114 |
+
actual_size_mb_str = f"{actual_size_mb_val:.3f}"
|
| 115 |
+
|
| 116 |
+
# Theoretical FP32 size (FP32 is 4 bytes per element)
|
| 117 |
+
fp32_equiv_bytes = num_elements * 4
|
| 118 |
+
fp32_equiv_size_mb_val = fp32_equiv_bytes / (1024 * 1024)
|
| 119 |
+
total_fp32_equiv_mb += fp32_equiv_size_mb_val
|
| 120 |
+
fp32_equiv_size_mb_str = f"{fp32_equiv_size_mb_val:.3f}"
|
| 121 |
+
else:
|
| 122 |
+
print(f"Warning: Unknown dtype '{dtype_str}' for tensor '{key}'. Cannot calculate size.")
|
| 123 |
+
|
| 124 |
+
print(
|
| 125 |
+
f"{key:<{max_key_len}} | "
|
| 126 |
+
f"{dtype_str:<17} | "
|
| 127 |
+
f"{actual_size_mb_str:>16} | "
|
| 128 |
+
f"{fp32_equiv_size_mb_str:>18}"
|
| 129 |
+
)
|
| 130 |
+
dtype_counts[dtype_str] += 1
|
| 131 |
+
|
| 132 |
+
print("\n--- Summary ---")
|
| 133 |
+
print(f"Total tensors found: {len(tensor_keys)}")
|
| 134 |
+
if dtype_counts:
|
| 135 |
+
print("Precision distribution:")
|
| 136 |
+
for dtype, count in dtype_counts.most_common():
|
| 137 |
+
print(f" - {dtype:<12}: {count} tensor(s)")
|
| 138 |
+
else:
|
| 139 |
+
print("No dtypes to summarize.")
|
| 140 |
+
|
| 141 |
+
print(f"\nTotal actual size of all tensors: {total_actual_mb:.3f} MB")
|
| 142 |
+
print(f"Total theoretical FP32 size of all tensors: {total_fp32_equiv_mb:.3f} MB")
|
| 143 |
+
|
| 144 |
+
if total_fp32_equiv_mb > 0.00001: # Avoid division by zero or near-zero
|
| 145 |
+
savings_percentage = (1 - (total_actual_mb / total_fp32_equiv_mb)) * 100
|
| 146 |
+
print(f"Overall size reduction compared to full FP32: {savings_percentage:.2f}%")
|
| 147 |
+
else:
|
| 148 |
+
print("Overall size reduction cannot be calculated (no FP32 equivalent data or zero size).")
|
| 149 |
+
|
| 150 |
+
except Exception as e:
|
| 151 |
+
print(f"An error occurred while processing '{filepath}':")
|
| 152 |
+
print(f" {e}")
|
| 153 |
+
print("Please ensure it's a valid .safetensors file and the 'safetensors' (and 'torch') libraries are installed correctly.")
|
| 154 |
+
|
| 155 |
+
if __name__ == "__main__":
|
| 156 |
+
parser = argparse.ArgumentParser(
|
| 157 |
+
description="Inspect tensor precision (dtype) and size in a .safetensors file."
|
| 158 |
+
)
|
| 159 |
+
parser.add_argument(
|
| 160 |
+
"filepath",
|
| 161 |
+
help="Path to the .safetensors file to inspect."
|
| 162 |
+
)
|
| 163 |
+
args = parser.parse_args()
|
| 164 |
+
|
| 165 |
+
inspect_safetensors_precision_and_size(args.filepath)
|