🔬 DUIX/HeyGen 移动端数字人模型的逆向工程与 PyTorch 复现
本项目对 DUIX(硅基智能开源的移动端数字人方案)中使用的 NCNN 神经网络模型进行了深入分析和 PyTorch 复现。
| 成果 | 描述 |
|---|---|
| 模型解密 | 破解 AES-128-CBC 加密的模型文件 |
| 结构分析 | 完整解析 MobileNetV2 + U-Net 架构 |
| PyTorch 复现 | 基于解密 NCNN graph 的逐层 PyTorch 实现 |
| 推理复现 | 音频驱动人脸生成的完整流程 |
- 模型架构: MobileNetV2 编码器 + U-Net 解码器
- 双输入单输出: 音频特征 + 6通道人脸图像 → 生成图像
- 加密方式: AES-128-CBC,密钥硬编码在 JNI 代码中
- 参数量: ~7.54M 个 FP16 权重(
dh_model.b约 15.1MB)
duix-heygen-reverse/
├── README.md # 本文件
├── LICENSE # MIT 许可证
├── .gitignore # Git 忽略配置
│
├── models/ # 🧠 模型复现代码
│ ├── MobileNet_Fixed.py # ✅ PyTorch 复现模型
│ └── README.md # 模型使用说明
│
├── tools/ # 🔧 工具脚本
│ ├── decrypt_model.py # 模型解密工具
│ └── README.md # 工具使用说明
│
├── docs/ # 📚 技术文档
│ ├── encryption_analysis.md # 加密机制分析
│ ├── model_structure_analysis.md # 模型结构分析
│ ├── ncnn_inference_analysis.md # NCNN 推理流程分析
│ ├── config_parameters.md # 配置文件参数详解
│ └── android_data_flow.md # Android 数据流分析
│
└── examples/ # 💡 使用示例
├── inference.py # PyTorch 模型推理示例
└── audio_inference.py # 音频特征提取推理示例
165 层 NCNN 权重转换和多模型参数平均见
docs/ncnn_to_pytorch.md。
pip install torch torchvision numpy
pip install pycryptodome # 用于模型解密
pip install onnxruntime librosa soundfile # 用于音频特征提取from models.MobileNet_Fixed import MobileNetV2Unet
import torch
# 创建模型
model = MobileNetV2Unet(use_groupnorm=False)
model.eval()
# 准备输入
audio = torch.randn(1, 256, 20) # 音频特征 [B, 256, 20]
face = torch.randn(1, 6, 160, 160) # 6通道人脸 [B, 6, H, W]
# 推理
with torch.no_grad():
output = model(face, audio) # [B, 3, 160, 160]
print(f"输出形状: {output.shape}") # torch.Size([1, 3, 160, 160])
print(f"输出范围: [{output.min():.2f}, {output.max():.2f}]") # [-1, 1]# 解密 NCNN 模型文件
python tools/decrypt_model.py path/to/dh_model.p decrypted_model.param
python tools/decrypt_model.py path/to/dh_model.b decrypted_model.bin
# 解密 WeNet ONNX 模型文件
python tools/decrypt_wenet.py path/to/encrypted_wenet.onnx wenet.onnx# 使用解密后的 WeNet 模型提取音频特征
python examples/audio_inference.py wenet.onnx audio.wav output_bnf.npy输入
├── Audio: [B, 256, 20] # 音频特征(如 Wenet 提取)
└── Face: [B, 6, 160, 160] # 当前帧 + 参考帧(各3通道)
│
▼
┌─────────────────────────────────────────────┐
│ Audio Encoder (8层) │
│ [B, 256, 20] ──────► [B, 128, 5, 5] │
└─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ Image Encoder (MobileNetV2) │
│ │ │
│ │ [B, 6, 160, 160] │
│ │ │ │
│ │ ▼ │
│ │ ┌──► x1: [B, 16, 80, 80] ─────────┐ │
│ │ │ │ │
│ │ ├──► x2: [B, 24, 40, 40] ────────┐│ │
│ │ │ ││ │
│ │ ├──► x3: [B, 32, 20, 20] ───────┐││ │
│ │ │ │││ │
│ │ ├──► x4: [B, 96, 10, 10] ──────┐│││ │
│ │ │ ││││ │
│ │ └──► x5: [B, 320, 5, 5] ──────┐││││ │
│ │ │││││ │
│ └─────────────────────────────────┼┼┼┼┼──────┘
│ │││││
▼ ▼▼▼▼▼
┌─────────────────────────────────────────────────────────────────────┐
│ U-Net Decoder (跳跃连接) │
│ │
│ [audio + x5] ──► [96, 5, 5] │
│ │ │
│ ▼ + x4 │
│ [96, 10, 10] ──► [96, 10, 10] │
│ │ │
│ ▼ + x3 │
│ [32, 20, 20] ──► [32, 20, 20] │
│ │ │
│ ▼ + x2 │
│ [24, 40, 40] ──► [24, 40, 40] │
│ │ │
│ ▼ + x1 │
│ [16, 80, 80] ──► [16, 80, 80] │
│ │ │
│ ▼ │
│ [8, 160, 160] ──► Conv ──► TanH ──► [3, 160, 160] │
│ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
输出: [B, 3, 160, 160] # 生成的 RGB 图像,范围 [-1, 1]
| 组件 | 设计 | 参数量 |
|---|---|---|
| Audio Encoder | 8层 Conv + Residual | ~0.55M |
| Image Encoder | MobileNetV2 Backbone | ~1.92M |
| U-Net Decoder | 5级跳跃连接 + InvertedResidual | ~2.06M |
| 总计 | - | ~7.51M(PyTorch) |
| 项目 | 值 |
|---|---|
| 算法 | AES-128-CBC |
| 密钥 | yymrjzbwyrbjszrk (16字节) |
| IV | yymrjzbwyrbjszrk (16字节) |
| 文件头 | gjdigits (8字节魔数) |
| 文件 | 内容 |
|---|---|
dh_model.p |
网络结构 (.param) |
dh_model.b |
模型权重 (.bin) |
config.j |
配置文件 |
bbox.j |
边界框配置 |
wenet.onnx |
WeNet 音频特征提取模型(ONNX 格式) |
密钥直接硬编码在 Android JNI 代码中:
// duix-sdk/src/main/cpp/aes/aesmain.c
char* key = "yymrjzbwyrbjszrk";
char* aiv = "yymrjzbwyrbjszrk";详见 加密分析文档。
| 项目 | NCNN 原版 | PyTorch 复现 | 状态 |
|---|---|---|---|
| 输入形状 | audio(256×20) + face(6×H×W) | ✅ 相同 | ✅ |
| 输出形状 | 3×H×W | ✅ 相同 | ✅ |
| 输出激活 | TanH [-1, 1] | ✅ TanH [-1, 1] | ✅ |
| 参数量 | 约 7.54M FP16 权重 | 约 7.51M PyTorch 权重 | ✅ |
| 归一化 | GroupNorm | BatchNorm (可选) | |
| 结构一致性 | 100% | 以解密 dh_model.p 为基准 |
✅ |
dh_model.b 是 FP16 权重文件,因此文件大小除以 2 才是权重个数;此前按
float32 除 4 得出的 3.77M 是误读。当前 PyTorch 复现为约 7.51M 个参数,
与官方权重文件的约 7.54M 个 FP16 权重相符。
| 文档 | 描述 |
|---|---|
| 模型结构分析 | NCNN 模型逐层解析 |
| 加密机制分析 | AES 加密破解过程 |
| 推理流程分析 | NCNN 推理代码分析 |
| 音频特征提取 | WeNet ONNX 音频特征提取 |
本项目仅用于学习和研究目的。
- 📖 所有分析基于公开的开源代码
- 🔬 旨在理解移动端数字人技术的实现
- ⚖️ 请遵守相关法律法规和开源协议
- 🚫 请勿用于商业目的或侵犯他人权益
- DUIX / 硅基智能 - 开源的移动端数字人方案
- NCNN - 腾讯的高性能神经网络推理框架
- MobileNetV2 - 轻量级骨干网络
MIT License - 详见 LICENSE
欢迎提交 Issue 和 Pull Request!
- 完善权重转换脚本(NCNN → PyTorch)
- 添加音频特征提取推理示例
- 添加 WeNet ONNX 模型解密工具
- 优化参数量到与原版一致
- 添加训练代码
- 添加完整的端到端推理流程示例
⭐ 如果这个项目对你有帮助,请给个 Star!