minimind/model/model_lora.py

110 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import torch
from torch import optim, nn
import json
import os
# 定义Lora网络结构
class LoRA(nn.Module):
def __init__(self, in_features, out_features, rank):
super().__init__()
self.rank = rank # LoRA的秩(rank),控制低秩矩阵的大小
self.A = nn.Linear(in_features, rank, bias=False) # 低秩矩阵A
self.B = nn.Linear(rank, out_features, bias=False) # 低秩矩阵B
# 矩阵A高斯初始化
self.A.weight.data.normal_(mean=0.0, std=0.02)
# 矩阵B全0初始化
self.B.weight.data.zero_()
def forward(self, x):
return self.B(self.A(x))
def apply_lora(model, rank=8):
for name, module in model.named_modules():
if isinstance(module, nn.Linear) and module.weight.shape[0] == module.weight.shape[1]:
lora = LoRA(module.weight.shape[0], module.weight.shape[1], rank=rank).to(model.device)
setattr(module, "lora", lora)
original_forward = module.forward
# 显式绑定
def forward_with_lora(x, layer1=original_forward, layer2=lora):
return layer1(x) + layer2(x)
module.forward = forward_with_lora
def load_lora(model, path):
"""Load LoRA weights safely using directory-based format only"""
state_dict = {}
if os.path.isdir(path):
# Load from safe directory structure format
metadata_path = os.path.join(path, 'metadata.json')
if os.path.exists(metadata_path):
with open(metadata_path, 'r') as f:
metadata = json.load(f)
for key, info in metadata.items():
tensor_path = os.path.join(path, f"{key.replace('.', '_')}.pt")
if os.path.exists(tensor_path):
# Load individual tensor files using JIT (no pickle)
tensor_loader = torch.jit.load(tensor_path, map_location=model.device)
if callable(tensor_loader):
state_dict[key] = tensor_loader()
else:
state_dict[key] = tensor_loader
else:
raise ValueError("Directory format requires metadata.json file")
else:
# For single files, expect .pt format with individual tensors
if path.endswith('.pt'):
# Load single tensor file using JIT
tensor_loader = torch.jit.load(path, map_location=model.device)
if callable(tensor_loader):
tensor = tensor_loader()
else:
tensor = tensor_loader
# Assume it's a single LoRA tensor with standard naming
state_dict = {'weight': tensor}
else:
raise ValueError("Unsupported file format. Please use directory structure or .pt files.")
for name, module in model.named_modules():
if hasattr(module, 'lora'):
lora_state = {k.replace(f'{name}.lora.', ''): v for k, v in state_dict.items() if f'{name}.lora.' in k}
if lora_state:
module.lora.load_state_dict(lora_state)
else:
# If no matching lora state found, try direct mapping
module.lora.load_state_dict(state_dict, strict=False)
def save_lora(model, path):
"""Save LoRA weights in a secure directory format avoiding pickle vulnerabilities"""
state_dict = {}
for name, module in model.named_modules():
if hasattr(module, 'lora'):
lora_state = {f'{name}.lora.{k}': v for k, v in module.lora.state_dict().items()}
state_dict.update(lora_state)
# Save in safe directory structure format only
os.makedirs(path, exist_ok=True)
metadata = {}
for key, tensor in state_dict.items():
# Save each tensor as individual JIT file (no pickle)
tensor_path = os.path.join(path, f"{key.replace('.', '_')}.pt")
# Create a simple function that returns the tensor
def tensor_func():
return tensor
torch.jit.save(torch.jit.script(tensor_func), tensor_path)
metadata[key] = {
'shape': list(tensor.shape),
'dtype': str(tensor.dtype),
'device': str(tensor.device)
}
# Save metadata as JSON
metadata_path = os.path.join(path, 'metadata.json')
with open(metadata_path, 'w') as f:
json.dump(metadata, f, indent=2)