Spaces:
Sleeping
Sleeping
Auto commit at 22-2025-08 19:14:51
Browse files- lily_llm_api/app_v2.py +92 -24
- lily_llm_api/app_v2_250822_1812.py +0 -0
- lily_llm_core/lora_manager.py +18 -2
- lily_llm_core/lora_manager_250822_1812.py +589 -0
lily_llm_api/app_v2.py
CHANGED
|
@@ -20,6 +20,12 @@ import io
|
|
| 20 |
import os
|
| 21 |
import json
|
| 22 |
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# logging μ€μ μ λ¨Όμ ꡬμ±
|
| 25 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
@@ -854,11 +860,25 @@ def generate_sync(prompt: str, image_data_list: Optional[List[bytes]], max_lengt
|
|
| 854 |
print(f"π [DEBUG] κ²°ν©λ μ΄λ―Έμ§ ν
μ shape: {pixel_values.shape}")
|
| 855 |
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μ dtype: {pixel_values.dtype}")
|
| 856 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 857 |
if pixel_values.device != model.device:
|
| 858 |
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μλ₯Ό λͺ¨λΈ λλ°μ΄μ€λ‘ μ΄λ: {pixel_values.device} -> {model.device}")
|
| 859 |
pixel_values = pixel_values.to(model.device)
|
| 860 |
|
| 861 |
print(f"π [DEBUG] μ΅μ’
μ΄λ―Έμ§ ν
μ λλ°μ΄μ€: {pixel_values.device}")
|
|
|
|
| 862 |
print(f"π [DEBUG] λͺ¨λΈ μμ± μμ - λ©ν°λͺ¨λ¬")
|
| 863 |
|
| 864 |
# LoRA μ΄λν°κ° μ μ©λ λͺ¨λΈμΈμ§ νμΈ
|
|
@@ -869,18 +889,31 @@ def generate_sync(prompt: str, image_data_list: Optional[List[bytes]], max_lengt
|
|
| 869 |
if lora_model:
|
| 870 |
print(f"π [DEBUG] LoRA λͺ¨λΈλ‘ λ©ν°λͺ¨λ¬ μμ± μ€ν")
|
| 871 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 872 |
-
# π λ©νλ°μ΄ν°λ₯Ό
|
| 873 |
import torch
|
| 874 |
processed_image_metas = {}
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 881 |
else:
|
| 882 |
-
|
|
|
|
|
|
|
| 883 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 884 |
processed_image_metas[key] = value
|
| 885 |
|
| 886 |
generate_kwargs = {
|
|
@@ -892,21 +925,43 @@ def generate_sync(prompt: str, image_data_list: Optional[List[bytes]], max_lengt
|
|
| 892 |
}
|
| 893 |
print(f"π [DEBUG] LoRA λͺ¨λΈ μμ± νλΌλ―Έν°: {list(generate_kwargs.keys())}")
|
| 894 |
print(f"π [DEBUG] μ²λ¦¬λ image_metas: {list(processed_image_metas.keys())}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 895 |
generated_ids = lora_model.generate(**generate_kwargs)
|
| 896 |
else:
|
| 897 |
print(f"β οΈ [DEBUG] LoRA λͺ¨λΈμ κ°μ Έμ¬ μ μμ, κΈ°λ³Έ λͺ¨λΈ μ¬μ©")
|
| 898 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 899 |
-
# π λ©νλ°μ΄ν°λ₯Ό
|
| 900 |
processed_image_metas = {}
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 907 |
else:
|
| 908 |
-
|
|
|
|
|
|
|
| 909 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 |
processed_image_metas[key] = value
|
| 911 |
|
| 912 |
generate_kwargs = {
|
|
@@ -922,17 +977,30 @@ def generate_sync(prompt: str, image_data_list: Optional[List[bytes]], max_lengt
|
|
| 922 |
else:
|
| 923 |
print(f"π [DEBUG] LoRA μ΄λν° μμ (λ©ν°λͺ¨λ¬), κΈ°λ³Έ οΏ½οΏ½οΏ½λΈ μ¬μ©")
|
| 924 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 925 |
-
# π λ©νλ°μ΄ν°λ₯Ό
|
| 926 |
processed_image_metas = {}
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 933 |
else:
|
| 934 |
-
|
|
|
|
|
|
|
| 935 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 936 |
processed_image_metas[key] = value
|
| 937 |
|
| 938 |
generate_kwargs = {
|
|
|
|
| 20 |
import os
|
| 21 |
import json
|
| 22 |
from pathlib import Path
|
| 23 |
+
import warnings
|
| 24 |
+
|
| 25 |
+
# π RoPE κ²½κ³ μ¨κΈ°κΈ° (Kanana λͺ¨λΈ λ΄λΆ ꡬν κ΄λ ¨)
|
| 26 |
+
warnings.filterwarnings("ignore", message="The attention layers in this model are transitioning")
|
| 27 |
+
warnings.filterwarnings("ignore", message="rotary_pos_emb will be removed")
|
| 28 |
+
warnings.filterwarnings("ignore", message="position_embeddings will be mandatory")
|
| 29 |
|
| 30 |
# logging μ€μ μ λ¨Όμ ꡬμ±
|
| 31 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
| 860 |
print(f"π [DEBUG] κ²°ν©λ μ΄λ―Έμ§ ν
μ shape: {pixel_values.shape}")
|
| 861 |
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μ dtype: {pixel_values.dtype}")
|
| 862 |
|
| 863 |
+
# π λͺ¨λΈκ³Ό λμΌν dtypeμΌλ‘ λ³ν (μ±λ₯ μ΅μ ν)
|
| 864 |
+
if hasattr(model, 'dtype'):
|
| 865 |
+
target_dtype = model.dtype
|
| 866 |
+
if pixel_values.dtype != target_dtype:
|
| 867 |
+
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μ dtype λ³ν: {pixel_values.dtype} -> {target_dtype}")
|
| 868 |
+
pixel_values = pixel_values.to(dtype=target_dtype)
|
| 869 |
+
else:
|
| 870 |
+
# π λͺ¨λΈ dtypeμ μ μ μλ κ²½μ° bfloat16 μ¬μ© (Kanana λͺ¨λΈ κΈ°λ³Έκ°)
|
| 871 |
+
target_dtype = torch.bfloat16
|
| 872 |
+
if pixel_values.dtype != target_dtype:
|
| 873 |
+
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μ dtype λ³ν: {pixel_values.dtype} -> {target_dtype}")
|
| 874 |
+
pixel_values = pixel_values.to(dtype=target_dtype)
|
| 875 |
+
|
| 876 |
if pixel_values.device != model.device:
|
| 877 |
print(f"π [DEBUG] μ΄λ―Έμ§ ν
μλ₯Ό λͺ¨λΈ λλ°μ΄μ€λ‘ μ΄λ: {pixel_values.device} -> {model.device}")
|
| 878 |
pixel_values = pixel_values.to(model.device)
|
| 879 |
|
| 880 |
print(f"π [DEBUG] μ΅μ’
μ΄λ―Έμ§ ν
μ λλ°μ΄μ€: {pixel_values.device}")
|
| 881 |
+
print(f"π [DEBUG] μ΅μ’
μ΄λ―Έμ§ ν
μ dtype: {pixel_values.dtype}")
|
| 882 |
print(f"π [DEBUG] λͺ¨λΈ μμ± μμ - λ©ν°λͺ¨λ¬")
|
| 883 |
|
| 884 |
# LoRA μ΄λν°κ° μ μ©λ λͺ¨λΈμΈμ§ νμΈ
|
|
|
|
| 889 |
if lora_model:
|
| 890 |
print(f"π [DEBUG] LoRA λͺ¨λΈλ‘ λ©ν°λͺ¨λ¬ μμ± μ€ν")
|
| 891 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 892 |
+
# π λ©νλ°μ΄ν°λ₯Ό 곡μ κ΅¬μ‘°λ‘ λ³ν (λͺ¨λΈ μꡬμ¬ν)
|
| 893 |
import torch
|
| 894 |
processed_image_metas = {}
|
| 895 |
+
|
| 896 |
+
# π 곡μ λ°©μ: vision_grid_thwλ₯Ό ν
μλ‘ λ³ν
|
| 897 |
+
if 'vision_grid_thw' in combined_image_metas:
|
| 898 |
+
vision_grid = combined_image_metas['vision_grid_thw']
|
| 899 |
+
if isinstance(vision_grid, list):
|
| 900 |
+
# π Kanana λͺ¨λΈ μꡬμ¬ν: (T, H, W) ννμ 3μ°¨μ ν
μ
|
| 901 |
+
if len(vision_grid) == 1 and len(vision_grid[0]) == 3:
|
| 902 |
+
# [(1, 34, 52)] -> (1, 34, 52) ν
μλ‘ λ³ν
|
| 903 |
+
t, h, w = vision_grid[0]
|
| 904 |
+
# π 3μ°¨μ ν
μλ‘ λ³ν: (1, H, W) νν
|
| 905 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor([[t, h, w]], dtype=torch.long)
|
| 906 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν: {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 907 |
else:
|
| 908 |
+
# π λ€λ₯Έ ννμ κ²½μ° μλ³Έ μ μ§
|
| 909 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor(vision_grid, dtype=torch.long)
|
| 910 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν (κΈ°οΏ½οΏ½): {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 911 |
else:
|
| 912 |
+
processed_image_metas['vision_grid_thw'] = vision_grid
|
| 913 |
+
|
| 914 |
+
# π λ€λ₯Έ λ©νλ°μ΄ν°λ κ·Έλλ‘ μ μ§
|
| 915 |
+
for key, value in combined_image_metas.items():
|
| 916 |
+
if key != 'vision_grid_thw':
|
| 917 |
processed_image_metas[key] = value
|
| 918 |
|
| 919 |
generate_kwargs = {
|
|
|
|
| 925 |
}
|
| 926 |
print(f"π [DEBUG] LoRA λͺ¨λΈ μμ± νλΌλ―Έν°: {list(generate_kwargs.keys())}")
|
| 927 |
print(f"π [DEBUG] μ²λ¦¬λ image_metas: {list(processed_image_metas.keys())}")
|
| 928 |
+
print(f"π [DEBUG] λͺ¨λΈ μμ± μμ... (νμμμ μμ)")
|
| 929 |
+
|
| 930 |
+
# π μμ± μ μ΅μ’
κ²μ¦
|
| 931 |
+
print(f"π [DEBUG] μ΅μ’
νλΌλ―Έν° κ²μ¦:")
|
| 932 |
+
print(f" - input_ids: {input_ids.shape}, dtype: {input_ids.dtype}")
|
| 933 |
+
print(f" - attention_mask: {attention_mask.shape}, dtype: {attention_mask.dtype}")
|
| 934 |
+
print(f" - pixel_values: {pixel_values.shape}, dtype: {pixel_values.dtype}")
|
| 935 |
+
print(f" - vision_grid_thw: {processed_image_metas.get('vision_grid_thw', 'None')}")
|
| 936 |
+
|
| 937 |
generated_ids = lora_model.generate(**generate_kwargs)
|
| 938 |
else:
|
| 939 |
print(f"β οΈ [DEBUG] LoRA λͺ¨λΈμ κ°μ Έμ¬ μ μμ, κΈ°λ³Έ λͺ¨λΈ μ¬μ©")
|
| 940 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 941 |
+
# π λ©νλ°μ΄ν°λ₯Ό 곡μ κ΅¬μ‘°λ‘ λ³ν (λͺ¨λΈ μꡬμ¬ν)
|
| 942 |
processed_image_metas = {}
|
| 943 |
+
|
| 944 |
+
# π 곡μ λ°©μ: vision_grid_thwλ₯Ό ν
μλ‘ λ³ν
|
| 945 |
+
if 'vision_grid_thw' in combined_image_metas:
|
| 946 |
+
vision_grid = combined_image_metas['vision_grid_thw']
|
| 947 |
+
if isinstance(vision_grid, list):
|
| 948 |
+
# π Kanana λͺ¨λΈ μꡬμ¬ν: (T, H, W) ννμ 3μ°¨μ ν
μ
|
| 949 |
+
if len(vision_grid) == 1 and len(vision_grid[0]) == 3:
|
| 950 |
+
# [(1, 34, 52)] -> (1, 34, 52) ν
μλ‘ λ³ν
|
| 951 |
+
t, h, w = vision_grid[0]
|
| 952 |
+
# π 3μ°¨μ ν
μλ‘ λ³ν: (1, H, W) νν
|
| 953 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor([[t, h, w]], dtype=torch.long)
|
| 954 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν: {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 955 |
else:
|
| 956 |
+
# π λ€λ₯Έ ννμ κ²½μ° μλ³Έ μ μ§
|
| 957 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor(vision_grid, dtype=torch.long)
|
| 958 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν (κΈ°λ³Έ): {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 959 |
else:
|
| 960 |
+
processed_image_metas['vision_grid_thw'] = vision_grid
|
| 961 |
+
|
| 962 |
+
# π λ€λ₯Έ λ©νλ°μ΄ν°λ κ·Έλλ‘ μ μ§
|
| 963 |
+
for key, value in combined_image_metas.items():
|
| 964 |
+
if key != 'vision_grid_thw':
|
| 965 |
processed_image_metas[key] = value
|
| 966 |
|
| 967 |
generate_kwargs = {
|
|
|
|
| 977 |
else:
|
| 978 |
print(f"π [DEBUG] LoRA μ΄λν° μμ (λ©ν°λͺ¨λ¬), κΈ°λ³Έ οΏ½οΏ½οΏ½λΈ μ¬μ©")
|
| 979 |
# π image_metas νλΌλ―Έν° μΆκ° (곡μ λ°©μ)
|
| 980 |
+
# π λ©νλ°μ΄ν°λ₯Ό 곡μ κ΅¬μ‘°λ‘ λ³ν (λͺ¨λΈ μꡬμ¬ν)
|
| 981 |
processed_image_metas = {}
|
| 982 |
+
|
| 983 |
+
# π 곡μ λ°©μ: vision_grid_thwλ₯Ό ν
μλ‘ λ³ν
|
| 984 |
+
if 'vision_grid_thw' in combined_image_metas:
|
| 985 |
+
vision_grid = combined_image_metas['vision_grid_thw']
|
| 986 |
+
if isinstance(vision_grid, list):
|
| 987 |
+
# π Kanana λͺ¨λΈ μꡬμ¬ν: (T, H, W) ννμ 3μ°¨μ ν
μ
|
| 988 |
+
if len(vision_grid) == 1 and len(vision_grid[0]) == 3:
|
| 989 |
+
# [(1, 34, 52)] -> (1, 34, 52) ν
μλ‘ λ³ν
|
| 990 |
+
t, h, w = vision_grid[0]
|
| 991 |
+
# π 3μ°¨μ ν
μλ‘ λ³ν: (1, H, W) νν
|
| 992 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor([[t, h, w]], dtype=torch.long)
|
| 993 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν: {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 994 |
else:
|
| 995 |
+
# π λ€λ₯Έ ννμ κ²½μ° μλ³Έ μ μ§
|
| 996 |
+
processed_image_metas['vision_grid_thw'] = torch.tensor(vision_grid, dtype=torch.long)
|
| 997 |
+
print(f"π [DEBUG] vision_grid_thw ν
μ λ³ν (κΈ°λ³Έ): {vision_grid} -> {processed_image_metas['vision_grid_thw'].shape}")
|
| 998 |
else:
|
| 999 |
+
processed_image_metas['vision_grid_thw'] = vision_grid
|
| 1000 |
+
|
| 1001 |
+
# π λ€λ₯Έ λ©νλ°μ΄ν°λ κ·Έλλ‘ μ μ§
|
| 1002 |
+
for key, value in combined_image_metas.items():
|
| 1003 |
+
if key != 'vision_grid_thw':
|
| 1004 |
processed_image_metas[key] = value
|
| 1005 |
|
| 1006 |
generate_kwargs = {
|
lily_llm_api/app_v2_250822_1812.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lily_llm_core/lora_manager.py
CHANGED
|
@@ -285,11 +285,27 @@ class LoRAManager:
|
|
| 285 |
if base_model_path:
|
| 286 |
self.load_base_model(base_model_path)
|
| 287 |
|
| 288 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
self.lora_model = PeftModel.from_pretrained(
|
| 290 |
self.base_model,
|
| 291 |
adapter_path,
|
| 292 |
-
torch_dtype=
|
| 293 |
)
|
| 294 |
|
| 295 |
# λλ°μ΄μ€λ‘ μ΄λ
|
|
|
|
| 285 |
if base_model_path:
|
| 286 |
self.load_base_model(base_model_path)
|
| 287 |
|
| 288 |
+
# π λͺ¨λΈ νμ
μ λ°λ₯Έ dtype κ²°μ
|
| 289 |
+
if hasattr(self.base_model, 'config') and hasattr(self.base_model.config, 'model_type'):
|
| 290 |
+
model_type = self.base_model.config.model_type
|
| 291 |
+
if model_type in ['kanana-1.5-v', 'vision2seq']:
|
| 292 |
+
# π Kanana λͺ¨λΈ: bfloat16 μ¬μ©
|
| 293 |
+
selected_dtype = torch.bfloat16
|
| 294 |
+
logger.info(f"π [DEBUG] Kanana λͺ¨λΈ κ°μ§: {model_type} -> bfloat16 μ¬μ©")
|
| 295 |
+
else:
|
| 296 |
+
# π κΈ°ν λͺ¨λΈ: κΈ°μ‘΄ λ‘μ§ μ¬μ©
|
| 297 |
+
selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
|
| 298 |
+
logger.info(f"π [DEBUG] μΌλ° λͺ¨λΈ κ°μ§: {model_type} -> {selected_dtype} μ¬μ©")
|
| 299 |
+
else:
|
| 300 |
+
# π λͺ¨λΈ νμ
μ μ μ μλ κ²½μ°: κΈ°μ‘΄ λ‘μ§ μ¬μ©
|
| 301 |
+
selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
|
| 302 |
+
logger.info(f"π [DEBUG] λͺ¨λΈ νμ
λ―ΈνμΈ -> {selected_dtype} μ¬μ©")
|
| 303 |
+
|
| 304 |
+
# LoRA μ΄λν° λ‘λ (λͺ¨λΈλ³ μ΅μ dtype μ¬μ©)
|
| 305 |
self.lora_model = PeftModel.from_pretrained(
|
| 306 |
self.base_model,
|
| 307 |
adapter_path,
|
| 308 |
+
torch_dtype=selected_dtype
|
| 309 |
)
|
| 310 |
|
| 311 |
# λλ°μ΄μ€λ‘ μ΄λ
|
lily_llm_core/lora_manager_250822_1812.py
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
LoRA/QLoRA κ΄λ¦¬μ (LoRA Manager)
|
| 4 |
+
LoRA μ΄λν°λ₯Ό λ‘λνκ³ κ΄λ¦¬νλ μμ€ν
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import logging
|
| 8 |
+
import os
|
| 9 |
+
import json
|
| 10 |
+
import torch
|
| 11 |
+
from typing import Dict, Any, Optional, List, Union
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
import warnings
|
| 14 |
+
import time
|
| 15 |
+
|
| 16 |
+
# loggerλ₯Ό λ¨Όμ μ μ
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
# PEFT κ΄λ ¨ import (μ€μΉλμ§ μμ κ²½μ° κ²½κ³ )
|
| 20 |
+
try:
|
| 21 |
+
logger.info("π PEFT λΌμ΄λΈλ¬λ¦¬ import μλ μ€...")
|
| 22 |
+
from peft import (
|
| 23 |
+
LoraConfig,
|
| 24 |
+
get_peft_model,
|
| 25 |
+
PeftModel,
|
| 26 |
+
TaskType,
|
| 27 |
+
prepare_model_for_kbit_training
|
| 28 |
+
)
|
| 29 |
+
from peft.utils import get_peft_model_state_dict
|
| 30 |
+
PEFT_AVAILABLE = True
|
| 31 |
+
logger.info("β
PEFT λΌμ΄λΈλ¬λ¦¬ import μ±κ³΅")
|
| 32 |
+
except ImportError as e:
|
| 33 |
+
PEFT_AVAILABLE = False
|
| 34 |
+
logger.error(f"β PEFT λΌμ΄λΈλ¬λ¦¬ import μ€ν¨: {e}")
|
| 35 |
+
logger.error(f"β Python κ²½λ‘: {os.environ.get('PYTHONPATH', 'Not set')}")
|
| 36 |
+
logger.error(f"β νμ¬ μμ
λλ ν 리: {os.getcwd()}")
|
| 37 |
+
warnings.warn(f"PEFT λΌμ΄λΈλ¬λ¦¬κ° μ€μΉλμ§ μμμ΅λλ€. LoRA κΈ°λ₯μ μ¬μ©ν μ μμ΅λλ€. μ€λ₯: {e}")
|
| 38 |
+
|
| 39 |
+
# Transformers κ΄λ ¨ import
|
| 40 |
+
try:
|
| 41 |
+
logger.info("π Transformers λΌμ΄λΈλ¬λ¦¬ import μλ μ€...")
|
| 42 |
+
from transformers import (
|
| 43 |
+
AutoModelForCausalLM,
|
| 44 |
+
AutoTokenizer,
|
| 45 |
+
BitsAndBytesConfig,
|
| 46 |
+
TrainingArguments,
|
| 47 |
+
Trainer,
|
| 48 |
+
DataCollatorForLanguageModeling
|
| 49 |
+
)
|
| 50 |
+
TRANSFORMERS_AVAILABLE = True
|
| 51 |
+
logger.info("β
Transformers λΌμ΄λΈλ¬λ¦¬ import μ±κ³΅")
|
| 52 |
+
except ImportError as e:
|
| 53 |
+
TRANSFORMERS_AVAILABLE = False
|
| 54 |
+
logger.error(f"β Transformers λΌμ΄λΈλ¬λ¦¬ import μ€ν¨: {e}")
|
| 55 |
+
warnings.warn(f"Transformers λΌμ΄λΈλ¬λ¦¬κ° μ€μΉλμ§ μμμ΅λλ€. μ€λ₯: {e}")
|
| 56 |
+
|
| 57 |
+
class LoRAManager:
|
| 58 |
+
"""LoRA/QLoRA λͺ¨λΈ κ΄λ¦¬ ν΄λμ€"""
|
| 59 |
+
|
| 60 |
+
def __init__(self, base_model_path: str = None, device: str = "auto"):
|
| 61 |
+
"""
|
| 62 |
+
Args:
|
| 63 |
+
base_model_path: κΈ°λ³Έ λͺ¨λΈ κ²½λ‘
|
| 64 |
+
device: μ¬μ©ν λλ°μ΄μ€ ('auto', 'cpu', 'cuda', 'mps')
|
| 65 |
+
"""
|
| 66 |
+
logger.info(f"π§ LoRA κ΄λ¦¬μ μ΄κΈ°ν μμ: PEFT_AVAILABLE={PEFT_AVAILABLE}, TRANSFORMERS_AVAILABLE={TRANSFORMERS_AVAILABLE}")
|
| 67 |
+
|
| 68 |
+
if not PEFT_AVAILABLE:
|
| 69 |
+
logger.error("β PEFT λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©ν μ μμ΅λλ€.")
|
| 70 |
+
logger.error("β pip install peftλ₯Ό μ€ννλμ§ νμΈνμΈμ.")
|
| 71 |
+
logger.error("β κ°μνκ²½μ΄ νμ±νλμ΄ μλμ§ νμΈνμΈμ.")
|
| 72 |
+
raise ImportError("PEFT λΌμ΄λΈλ¬λ¦¬κ° νμν©λλ€. pip install peftλ₯Ό μ€ννμΈμ.")
|
| 73 |
+
|
| 74 |
+
if not TRANSFORMERS_AVAILABLE:
|
| 75 |
+
logger.error("β Transformers λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©ν μ μμ΅λλ€.")
|
| 76 |
+
logger.error("β pip install transformersλ₯Ό μ€ννλμ§ νμΈνμΈμ.")
|
| 77 |
+
raise ImportError("Transformers λΌμ΄λΈλ¬λ¦¬κ° νμν©λλ€. pip install transformersλ₯Ό μ€ννμΈμ.")
|
| 78 |
+
|
| 79 |
+
self.base_model_path = base_model_path
|
| 80 |
+
self.device = self._get_device(device)
|
| 81 |
+
|
| 82 |
+
# λͺ¨λΈ λ° ν ν¬λμ΄μ
|
| 83 |
+
self.base_model = None
|
| 84 |
+
self.tokenizer = None
|
| 85 |
+
self.lora_model = None
|
| 86 |
+
|
| 87 |
+
# LoRA μ€μ
|
| 88 |
+
self.lora_config = None
|
| 89 |
+
self.current_adapter_name = None
|
| 90 |
+
|
| 91 |
+
# μ΄λν° μ μ₯ κ²½λ‘
|
| 92 |
+
self.adapters_dir = Path("lora_adapters")
|
| 93 |
+
self.adapters_dir.mkdir(exist_ok=True)
|
| 94 |
+
|
| 95 |
+
# λ‘λλ μ΄λν° λͺ©λ‘
|
| 96 |
+
self.loaded_adapters = {}
|
| 97 |
+
|
| 98 |
+
logger.info(f"π§ LoRA κ΄λ¦¬μ μ΄κΈ°ν: device={self.device}")
|
| 99 |
+
|
| 100 |
+
def get_model(self):
|
| 101 |
+
"""νμ¬ LoRA λͺ¨λΈ λ°ν"""
|
| 102 |
+
if self.lora_model is not None:
|
| 103 |
+
return self.lora_model
|
| 104 |
+
elif self.base_model is not None:
|
| 105 |
+
return self.base_model
|
| 106 |
+
else:
|
| 107 |
+
logger.warning("β οΈ λ‘λλ λͺ¨λΈμ΄ μμ΅λλ€.")
|
| 108 |
+
return None
|
| 109 |
+
|
| 110 |
+
def _get_device(self, device: str) -> str:
|
| 111 |
+
"""μ¬μ© κ°λ₯ν λλ°μ΄μ€ νμΈ"""
|
| 112 |
+
if device == "auto":
|
| 113 |
+
if torch.cuda.is_available():
|
| 114 |
+
return "cuda"
|
| 115 |
+
elif torch.backends.mps.is_available():
|
| 116 |
+
return "mps"
|
| 117 |
+
else:
|
| 118 |
+
return "cpu"
|
| 119 |
+
return device
|
| 120 |
+
|
| 121 |
+
def load_base_model(self, model_path: str = None, model_type: str = "causal_lm") -> bool:
|
| 122 |
+
"""κΈ°λ³Έ λͺ¨λΈ λ‘λ"""
|
| 123 |
+
try:
|
| 124 |
+
model_path = model_path or self.base_model_path
|
| 125 |
+
if not model_path:
|
| 126 |
+
raise ValueError("λͺ¨λΈ κ²½λ‘κ° μ§μ λμ§ μμμ΅λλ€.")
|
| 127 |
+
|
| 128 |
+
logger.info(f"π₯ κΈ°λ³Έ λͺ¨λΈ λ‘λ© μμ: {model_path}")
|
| 129 |
+
|
| 130 |
+
# κ²½λ‘ μ κ·ν λ° μ‘΄μ¬ νμΈ
|
| 131 |
+
model_path = Path(model_path).resolve()
|
| 132 |
+
if not model_path.exists():
|
| 133 |
+
raise FileNotFoundError(f"λͺ¨λΈ κ²½οΏ½οΏ½οΏ½κ° μ‘΄μ¬νμ§ μμ΅λλ€: {model_path}")
|
| 134 |
+
|
| 135 |
+
logger.info(f"π λͺ¨λΈ κ²½λ‘ νμΈ: {model_path}")
|
| 136 |
+
logger.info(f"π κ²½λ‘ μ‘΄μ¬: {model_path.exists()}")
|
| 137 |
+
logger.info(f"π μ λ κ²½λ‘: {model_path.absolute()}")
|
| 138 |
+
|
| 139 |
+
# ν ν¬λμ΄μ λ‘λ
|
| 140 |
+
self.tokenizer = AutoTokenizer.from_pretrained(
|
| 141 |
+
str(model_path),
|
| 142 |
+
trust_remote_code=True,
|
| 143 |
+
local_files_only=True
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
# ν¨λ© ν ν° μ€μ
|
| 147 |
+
if self.tokenizer.pad_token is None:
|
| 148 |
+
self.tokenizer.pad_token = self.tokenizer.eos_token
|
| 149 |
+
|
| 150 |
+
# λͺ¨λΈ λ‘λ
|
| 151 |
+
if model_type == "causal_lm":
|
| 152 |
+
self.base_model = AutoModelForCausalLM.from_pretrained(
|
| 153 |
+
str(model_path),
|
| 154 |
+
trust_remote_code=True,
|
| 155 |
+
local_files_only=True,
|
| 156 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
|
| 157 |
+
device_map="auto" if self.device == "cuda" else None
|
| 158 |
+
)
|
| 159 |
+
elif model_type == "vision2seq":
|
| 160 |
+
# π Vision2Seq λͺ¨λΈ μ§μ μΆκ° (kanana λ±)
|
| 161 |
+
from transformers import AutoModelForVision2Seq
|
| 162 |
+
self.base_model = AutoModelForVision2Seq.from_pretrained(
|
| 163 |
+
str(model_path),
|
| 164 |
+
trust_remote_code=True,
|
| 165 |
+
local_files_only=True,
|
| 166 |
+
torch_dtype=torch.bfloat16 if self.device == "cuda" else torch.bfloat16,
|
| 167 |
+
device_map="auto" if self.device == "cuda" else None
|
| 168 |
+
)
|
| 169 |
+
else:
|
| 170 |
+
raise ValueError(f"μ§μνμ§ μλ λͺ¨λΈ νμ
: {model_type}")
|
| 171 |
+
|
| 172 |
+
# λλ°μ΄μ€λ‘ μ΄λ
|
| 173 |
+
if self.device != "cuda": # cudaλ device_map="auto" μ¬μ©
|
| 174 |
+
self.base_model = self.base_model.to(self.device)
|
| 175 |
+
|
| 176 |
+
self.base_model_path = model_path
|
| 177 |
+
logger.info(f"β
κΈ°λ³Έ λͺ¨λΈ λ‘λ© μλ£: {model_path}")
|
| 178 |
+
return True
|
| 179 |
+
|
| 180 |
+
except Exception as e:
|
| 181 |
+
logger.error(f"β κΈ°λ³Έ λͺ¨λΈ λ‘λ© μ€ν¨: {e}")
|
| 182 |
+
return False
|
| 183 |
+
|
| 184 |
+
def create_lora_config(self,
|
| 185 |
+
r: int = 16,
|
| 186 |
+
lora_alpha: int = 32,
|
| 187 |
+
target_modules: List[str] = None,
|
| 188 |
+
lora_dropout: float = 0.1,
|
| 189 |
+
bias: str = "none",
|
| 190 |
+
task_type: str = "CAUSAL_LM") -> LoraConfig:
|
| 191 |
+
"""LoRA μ€μ μμ±"""
|
| 192 |
+
if target_modules is None:
|
| 193 |
+
# μΌλ°μ μΈ λͺ¨λΈ μν€ν
μ²μ λν κΈ°λ³Έκ°
|
| 194 |
+
target_modules = ["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
|
| 195 |
+
|
| 196 |
+
# TaskType λ³ν (μμ ν λ°©μ)
|
| 197 |
+
logger.info(f"π [DEBUG] μ
λ ₯λ task_type: {task_type}")
|
| 198 |
+
|
| 199 |
+
try:
|
| 200 |
+
# μ§μ TaskType μ¬μ© (λ¬Έμμ΄ λ³ν μ κ±°)
|
| 201 |
+
if task_type == "CAUSAL_LM":
|
| 202 |
+
task_type_enum = TaskType.CAUSAL_LM
|
| 203 |
+
elif task_type == "VISION_2_SEQ":
|
| 204 |
+
# π Vision2Seq λͺ¨λΈ μ§μ μΆκ°
|
| 205 |
+
task_type_enum = TaskType.SEQ_2_SEQ_LM # Vision2Seqλ SEQ_2_SEQ_LMκ³Ό μ μ¬
|
| 206 |
+
elif task_type == "SEQ_2_SEQ_LM":
|
| 207 |
+
task_type_enum = TaskType.SEQ_2_SEQ_LM
|
| 208 |
+
elif task_type == "SEQUENCE_CLASSIFICATION":
|
| 209 |
+
task_type_enum = TaskType.SEQUENCE_CLASSIFICATION
|
| 210 |
+
elif task_type == "TOKEN_CLASSIFICATION":
|
| 211 |
+
task_type_enum = TaskType.TOKEN_CLASSIFICATION
|
| 212 |
+
elif task_type == "QUESTION_ANSWERING":
|
| 213 |
+
task_type_enum = TaskType.QUESTION_ANSWERING
|
| 214 |
+
else:
|
| 215 |
+
# κΈ°λ³Έκ°μΌλ‘ CAUSAL_LM μ¬μ©
|
| 216 |
+
task_type_enum = TaskType.CAUSAL_LM
|
| 217 |
+
logger.warning(f"β οΈ μ μ μλ task_type: {task_type}, κΈ°λ³Έκ° CAUSAL_LM μ¬μ©")
|
| 218 |
+
except Exception as e:
|
| 219 |
+
logger.error(f"β TaskType λ³ν μ€ν¨: {e}, κΈ°λ³Έκ° CAUSAL_LM μ¬μ©")
|
| 220 |
+
task_type_enum = TaskType.CAUSAL_LM
|
| 221 |
+
|
| 222 |
+
logger.info(f"π [DEBUG] μ΅μ’
μ νλ TaskType: {task_type_enum}")
|
| 223 |
+
|
| 224 |
+
self.lora_config = LoraConfig(
|
| 225 |
+
r=r,
|
| 226 |
+
lora_alpha=lora_alpha,
|
| 227 |
+
target_modules=target_modules,
|
| 228 |
+
lora_dropout=lora_dropout,
|
| 229 |
+
bias=bias,
|
| 230 |
+
task_type=task_type_enum
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
logger.info(f"π§ LoRA μ€μ μμ±: r={r}, alpha={lora_alpha}, target_modules={target_modules}")
|
| 234 |
+
return self.lora_config
|
| 235 |
+
|
| 236 |
+
def apply_lora_to_model(self, adapter_name: str = "default") -> bool:
|
| 237 |
+
"""LoRAλ₯Ό κΈ°λ³Έ λͺ¨λΈμ μ μ©"""
|
| 238 |
+
try:
|
| 239 |
+
if self.base_model is None:
|
| 240 |
+
raise ValueError("κΈ°λ³Έ λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€.")
|
| 241 |
+
|
| 242 |
+
if self.lora_config is None:
|
| 243 |
+
raise ValueError("LoRA μ€μ μ΄ μμ±λμ§ μμμ΅λλ€.")
|
| 244 |
+
|
| 245 |
+
logger.info(f"π LoRA μ΄λν° μ μ© μμ: {adapter_name}")
|
| 246 |
+
|
| 247 |
+
# LoRA λͺ¨λΈ μμ±
|
| 248 |
+
self.lora_model = get_peft_model(self.base_model, self.lora_config)
|
| 249 |
+
|
| 250 |
+
# μ΄λν° μ΄λ¦ μ€μ
|
| 251 |
+
self.current_adapter_name = adapter_name
|
| 252 |
+
|
| 253 |
+
# νλ ¨ λͺ¨λλ‘ μ€μ
|
| 254 |
+
self.lora_model.train()
|
| 255 |
+
|
| 256 |
+
# λͺ¨λΈ μ 보 μΆλ ₯
|
| 257 |
+
self.lora_model.print_trainable_parameters()
|
| 258 |
+
|
| 259 |
+
logger.info(f"β
LoRA μ΄λν° μ μ© μλ£: {adapter_name}")
|
| 260 |
+
return True
|
| 261 |
+
|
| 262 |
+
except Exception as e:
|
| 263 |
+
logger.error(f"β LoRA μ΄λν° μ μ© μ€ν¨: {e}")
|
| 264 |
+
return False
|
| 265 |
+
|
| 266 |
+
def load_lora_adapter(self, adapter_path: str, adapter_name: str = None) -> bool:
|
| 267 |
+
"""μ μ₯λ LoRA μ΄λν° λ‘λ"""
|
| 268 |
+
try:
|
| 269 |
+
if not os.path.exists(adapter_path):
|
| 270 |
+
raise FileNotFoundError(f"μ΄λν° κ²½λ‘λ₯Ό μ°Ύμ μ μμ΅λλ€: {adapter_path}")
|
| 271 |
+
|
| 272 |
+
if adapter_name is None:
|
| 273 |
+
adapter_name = Path(adapter_path).stem
|
| 274 |
+
|
| 275 |
+
logger.info(f"π₯ LoRA μ΄λν° λ‘λ© μμ: {adapter_path}")
|
| 276 |
+
|
| 277 |
+
# κΈ°λ³Έ λͺ¨λΈμ΄ λ‘λλμ§ μμ κ²½μ° λ‘λ
|
| 278 |
+
if self.base_model is None:
|
| 279 |
+
# μ΄λν° μ€μ νμΌμμ κΈ°λ³Έ λͺ¨λΈ κ²½λ‘ νμΈ
|
| 280 |
+
config_path = os.path.join(adapter_path, "adapter_config.json")
|
| 281 |
+
if os.path.exists(config_path):
|
| 282 |
+
with open(config_path, 'r') as f:
|
| 283 |
+
config = json.load(f)
|
| 284 |
+
base_model_path = config.get("base_model_name_or_path")
|
| 285 |
+
if base_model_path:
|
| 286 |
+
self.load_base_model(base_model_path)
|
| 287 |
+
|
| 288 |
+
# π λͺ¨λΈ νμ
μ λ°λ₯Έ dtype κ²°μ
|
| 289 |
+
if hasattr(self.base_model, 'config') and hasattr(self.base_model.config, 'model_type'):
|
| 290 |
+
model_type = self.base_model.config.model_type
|
| 291 |
+
if model_type in ['kanana-1.5-v', 'vision2seq']:
|
| 292 |
+
# π Kanana λͺ¨λΈ: bfloat16 μ¬μ©
|
| 293 |
+
selected_dtype = torch.bfloat16
|
| 294 |
+
logger.info(f"π [DEBUG] Kanana λͺ¨λΈ κ°μ§: {model_type} -> bfloat16 μ¬μ©")
|
| 295 |
+
else:
|
| 296 |
+
# π κΈ°ν λͺ¨λΈ: κΈ°μ‘΄ λ‘μ§ μ¬μ©
|
| 297 |
+
selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
|
| 298 |
+
logger.info(f"π [DEBUG] μΌλ° λͺ¨λΈ κ°μ§: {model_type} -> {selected_dtype} μ¬μ©")
|
| 299 |
+
else:
|
| 300 |
+
# π λͺ¨λΈ νμ
μ μ μ μλ κ²½μ°: κΈ°μ‘΄ λ‘μ§ μ¬μ©
|
| 301 |
+
selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
|
| 302 |
+
logger.info(f"π [DEBUG] λͺ¨λΈ νμ
λ―ΈνμΈ -> {selected_dtype} μ¬μ©")
|
| 303 |
+
|
| 304 |
+
# LoRA μ΄λν° λ‘λ (λͺ¨λΈλ³ μ΅μ dtype μ¬μ©)
|
| 305 |
+
self.lora_model = PeftModel.from_pretrained(
|
| 306 |
+
self.base_model,
|
| 307 |
+
adapter_path,
|
| 308 |
+
torch_dtype=selected_dtype
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
# λλ°μ΄μ€λ‘ μ΄λ
|
| 312 |
+
if self.device != "cuda":
|
| 313 |
+
self.lora_model = self.lora_model.to(self.device)
|
| 314 |
+
|
| 315 |
+
self.current_adapter_name = adapter_name
|
| 316 |
+
self.loaded_adapters[adapter_name] = adapter_path
|
| 317 |
+
|
| 318 |
+
logger.info(f"β
LoRA μ΄λν° λ‘λ© μλ£: {adapter_name}")
|
| 319 |
+
return True
|
| 320 |
+
|
| 321 |
+
except Exception as e:
|
| 322 |
+
logger.error(f"β LoRA μ΄λν° λ‘λ© μ€ν¨: {e}")
|
| 323 |
+
return False
|
| 324 |
+
|
| 325 |
+
def save_lora_adapter(self, adapter_name: str = None, output_dir: str = None) -> bool:
|
| 326 |
+
"""LoRA μ΄λν° μ μ₯"""
|
| 327 |
+
try:
|
| 328 |
+
if self.lora_model is None:
|
| 329 |
+
raise ValueError("LoRA λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€.")
|
| 330 |
+
|
| 331 |
+
adapter_name = adapter_name or self.current_adapter_name or "default"
|
| 332 |
+
output_dir = output_dir or str(self.adapters_dir / adapter_name)
|
| 333 |
+
|
| 334 |
+
logger.info(f"πΎ LoRA μ΄λν° μ μ₯ μμ: {adapter_name} -> {output_dir}")
|
| 335 |
+
|
| 336 |
+
# μ΄λν° μ μ₯
|
| 337 |
+
self.lora_model.save_pretrained(output_dir)
|
| 338 |
+
|
| 339 |
+
# ν ν¬λμ΄μ λ μ μ₯
|
| 340 |
+
if self.tokenizer:
|
| 341 |
+
self.tokenizer.save_pretrained(output_dir)
|
| 342 |
+
|
| 343 |
+
# μ΄λν° μ 보 μ μ₯
|
| 344 |
+
adapter_info = {
|
| 345 |
+
"adapter_name": adapter_name,
|
| 346 |
+
"base_model": self.base_model_path,
|
| 347 |
+
"lora_config": self.lora_config.to_dict() if self.lora_config else None,
|
| 348 |
+
"created_at": str(torch.tensor(time.time())),
|
| 349 |
+
"device": self.device
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
with open(os.path.join(output_dir, "adapter_info.json"), 'w') as f:
|
| 353 |
+
json.dump(adapter_info, f, indent=2)
|
| 354 |
+
|
| 355 |
+
logger.info(f"β
LoRA μ΄λν° μ μ₯ μλ£: {output_dir}")
|
| 356 |
+
return True
|
| 357 |
+
|
| 358 |
+
except Exception as e:
|
| 359 |
+
logger.error(f"β LoRA μ΄λν° μ μ₯ μ€ν¨: {e}")
|
| 360 |
+
return False
|
| 361 |
+
|
| 362 |
+
def merge_lora_with_base(self, output_path: str = None) -> bool:
|
| 363 |
+
"""LoRA μ΄λν°λ₯Ό κΈ°λ³Έ λͺ¨λΈκ³Ό λ³ν©"""
|
| 364 |
+
try:
|
| 365 |
+
if self.lora_model is None:
|
| 366 |
+
raise ValueError("LoRA λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€.")
|
| 367 |
+
|
| 368 |
+
output_path = output_path or f"{self.base_model_path}_merged"
|
| 369 |
+
|
| 370 |
+
logger.info(f"π LoRA μ΄λν° λ³ν© μμ: {output_path}")
|
| 371 |
+
|
| 372 |
+
# λ³ν©λ λͺ¨λΈ μμ±
|
| 373 |
+
merged_model = self.lora_model.merge_and_unload()
|
| 374 |
+
|
| 375 |
+
# λ³ν©λ λͺ¨λΈ μ μ₯
|
| 376 |
+
merged_model.save_pretrained(output_path)
|
| 377 |
+
|
| 378 |
+
# ν ν¬λμ΄μ λ μ μ₯
|
| 379 |
+
if self.tokenizer:
|
| 380 |
+
self.tokenizer.save_pretrained(output_path)
|
| 381 |
+
|
| 382 |
+
logger.info(f"β
LoRA μ΄λν° λ³ν© μλ£: {output_path}")
|
| 383 |
+
return True
|
| 384 |
+
|
| 385 |
+
except Exception as e:
|
| 386 |
+
logger.error(f"β LoRA μ΄λν° λ³ν© μ€ν¨: {e}")
|
| 387 |
+
return False
|
| 388 |
+
|
| 389 |
+
def list_available_adapters(self) -> List[Dict[str, Any]]:
|
| 390 |
+
"""μ¬μ© κ°λ₯ν μ΄λν° λͺ©λ‘ λ°ν"""
|
| 391 |
+
adapters = []
|
| 392 |
+
|
| 393 |
+
for adapter_dir in self.adapters_dir.iterdir():
|
| 394 |
+
if adapter_dir.is_dir():
|
| 395 |
+
config_path = adapter_dir / "adapter_config.json"
|
| 396 |
+
info_path = adapter_dir / "adapter_info.json"
|
| 397 |
+
|
| 398 |
+
adapter_info = {
|
| 399 |
+
"name": adapter_dir.name,
|
| 400 |
+
"path": str(adapter_dir),
|
| 401 |
+
"config_exists": config_path.exists(),
|
| 402 |
+
"info_exists": info_path.exists()
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
# μ΄λν° μ 보 λ‘λ
|
| 406 |
+
if info_path.exists():
|
| 407 |
+
try:
|
| 408 |
+
with open(info_path, 'r') as f:
|
| 409 |
+
info = json.load(f)
|
| 410 |
+
adapter_info.update(info)
|
| 411 |
+
except Exception as e:
|
| 412 |
+
logger.warning(f"μ΄λν° μ 보 λ‘λ μ€ν¨: {e}")
|
| 413 |
+
|
| 414 |
+
adapters.append(adapter_info)
|
| 415 |
+
|
| 416 |
+
return adapters
|
| 417 |
+
|
| 418 |
+
def get_adapter_stats(self) -> Dict[str, Any]:
|
| 419 |
+
"""μ΄λν° ν΅κ³ μ 보 λ°ν"""
|
| 420 |
+
if self.lora_model is None:
|
| 421 |
+
return {"error": "LoRA λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€."}
|
| 422 |
+
|
| 423 |
+
try:
|
| 424 |
+
# νλ ¨ κ°λ₯ν νλΌλ―Έν° μ
|
| 425 |
+
trainable_params = 0
|
| 426 |
+
all_param = 0
|
| 427 |
+
|
| 428 |
+
for param in self.lora_model.parameters():
|
| 429 |
+
all_param += param.numel()
|
| 430 |
+
if param.requires_grad:
|
| 431 |
+
trainable_params += param.numel()
|
| 432 |
+
|
| 433 |
+
return {
|
| 434 |
+
"adapter_name": self.current_adapter_name,
|
| 435 |
+
"trainable_params": trainable_params,
|
| 436 |
+
"all_params": all_param,
|
| 437 |
+
"trainable_ratio": trainable_params / all_param if all_param > 0 else 0,
|
| 438 |
+
"device": self.device,
|
| 439 |
+
"model_type": type(self.lora_model).__name__
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
except Exception as e:
|
| 443 |
+
logger.error(f"μ΄λν° ν΅κ³ μμ§ μ€ν¨: {e}")
|
| 444 |
+
return {"error": str(e)}
|
| 445 |
+
|
| 446 |
+
def switch_adapter(self, adapter_name: str) -> bool:
|
| 447 |
+
"""λ€λ₯Έ μ΄λν°λ‘ μ ν"""
|
| 448 |
+
try:
|
| 449 |
+
if adapter_name not in self.loaded_adapters:
|
| 450 |
+
# μ΄λν° λ‘λ
|
| 451 |
+
adapter_path = self.adapters_dir / adapter_name
|
| 452 |
+
if not adapter_path.exists():
|
| 453 |
+
raise FileNotFoundError(f"μ΄λν°λ₯Ό μ°Ύμ μ μμ΅λλ€: {adapter_name}")
|
| 454 |
+
|
| 455 |
+
return self.load_lora_adapter(str(adapter_path), adapter_name)
|
| 456 |
+
else:
|
| 457 |
+
# μ΄λ―Έ λ‘λλ μ΄λν° μ¬μ©
|
| 458 |
+
self.current_adapter_name = adapter_name
|
| 459 |
+
logger.info(f"π μ΄λν° μ ν: {adapter_name}")
|
| 460 |
+
return True
|
| 461 |
+
|
| 462 |
+
except Exception as e:
|
| 463 |
+
logger.error(f"β μ΄λν° μ ν μ€ν¨: {e}")
|
| 464 |
+
return False
|
| 465 |
+
|
| 466 |
+
def unload_adapter(self) -> bool:
|
| 467 |
+
"""LoRA μ΄λν° μΈλ‘λ"""
|
| 468 |
+
try:
|
| 469 |
+
if self.lora_model is None:
|
| 470 |
+
return True
|
| 471 |
+
|
| 472 |
+
logger.info("ποΈ LoRA μ΄λν° μΈλ‘λ μμ")
|
| 473 |
+
|
| 474 |
+
# μ΄λν° μ κ±°
|
| 475 |
+
self.lora_model = None
|
| 476 |
+
self.current_adapter_name = None
|
| 477 |
+
self.lora_config = None
|
| 478 |
+
|
| 479 |
+
logger.info("οΏ½οΏ½ LoRA μ΄λν° μΈλ‘λ μλ£")
|
| 480 |
+
return True
|
| 481 |
+
|
| 482 |
+
except Exception as e:
|
| 483 |
+
logger.error(f"β LoRA μ΄λν° μΈλ‘λ μ€ν¨: {e}")
|
| 484 |
+
return False
|
| 485 |
+
|
| 486 |
+
def generate_text(self, prompt: str, max_length: int = 100, temperature: float = 0.7) -> str:
|
| 487 |
+
"""LoRA λͺ¨λΈμ μ¬μ©ν ν
μ€νΈ μμ±"""
|
| 488 |
+
try:
|
| 489 |
+
if self.lora_model is None:
|
| 490 |
+
raise ValueError("LoRA λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€.")
|
| 491 |
+
|
| 492 |
+
if self.tokenizer is None:
|
| 493 |
+
raise ValueError("ν ν¬λμ΄μ κ° λ‘λλμ§ μμμ΅λλ€.")
|
| 494 |
+
|
| 495 |
+
# μ
λ ₯ ν ν¬λμ΄μ§
|
| 496 |
+
inputs = self.tokenizer(prompt, return_tensors="pt")
|
| 497 |
+
|
| 498 |
+
# token_type_ids μ κ±° (PEFT λͺ¨λΈμμ μ§μνμ§ μμ)
|
| 499 |
+
if 'token_type_ids' in inputs:
|
| 500 |
+
del inputs['token_type_ids']
|
| 501 |
+
logger.info("π token_type_ids μ κ±°λ¨ (PEFT λͺ¨λΈ νΈνμ±)")
|
| 502 |
+
|
| 503 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 504 |
+
|
| 505 |
+
# μΆλ‘ λͺ¨λλ‘ μ€μ
|
| 506 |
+
self.lora_model.eval()
|
| 507 |
+
|
| 508 |
+
with torch.no_grad():
|
| 509 |
+
outputs = self.lora_model.generate(
|
| 510 |
+
**inputs,
|
| 511 |
+
max_new_tokens=max_length,
|
| 512 |
+
temperature=temperature,
|
| 513 |
+
do_sample=True,
|
| 514 |
+
pad_token_id=self.tokenizer.eos_token_id
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
# μλ΅ λμ½λ©
|
| 518 |
+
response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 519 |
+
|
| 520 |
+
# ν둬ννΈ μ κ±°
|
| 521 |
+
if response.startswith(prompt):
|
| 522 |
+
response = response[len(prompt):].strip()
|
| 523 |
+
|
| 524 |
+
return response
|
| 525 |
+
|
| 526 |
+
except Exception as e:
|
| 527 |
+
logger.error(f"β ν
μ€νΈ μμ± μ€ν¨: {e}")
|
| 528 |
+
return f"ν
μ€νΈ μμ± μ€ μ€λ₯κ° λ°μνμ΅λλ€: {str(e)}"
|
| 529 |
+
|
| 530 |
+
def prepare_for_training(self, training_args: TrainingArguments = None) -> bool:
|
| 531 |
+
"""νλ ¨μ μν λͺ¨λΈ μ€λΉ"""
|
| 532 |
+
try:
|
| 533 |
+
if self.lora_model is None:
|
| 534 |
+
raise ValueError("LoRA λͺ¨λΈμ΄ λ‘λλμ§ μμμ΅λλ€.")
|
| 535 |
+
|
| 536 |
+
logger.info("π§ νλ ¨μ μν λͺ¨λΈ μ€λΉ μμ")
|
| 537 |
+
|
| 538 |
+
# κΈ°λ³Έ νλ ¨ μΈμ
|
| 539 |
+
if training_args is None:
|
| 540 |
+
training_args = TrainingArguments(
|
| 541 |
+
output_dir="./lora_training_output",
|
| 542 |
+
num_train_epochs=3,
|
| 543 |
+
per_device_train_batch_size=4,
|
| 544 |
+
gradient_accumulation_steps=4,
|
| 545 |
+
learning_rate=2e-4,
|
| 546 |
+
warmup_steps=100,
|
| 547 |
+
logging_steps=10,
|
| 548 |
+
save_steps=500,
|
| 549 |
+
eval_steps=500,
|
| 550 |
+
evaluation_strategy="steps",
|
| 551 |
+
save_strategy="steps",
|
| 552 |
+
load_best_model_at_end=True,
|
| 553 |
+
metric_for_best_model="eval_loss",
|
| 554 |
+
greater_is_better=False,
|
| 555 |
+
fp16=torch.cuda.is_available(),
|
| 556 |
+
dataloader_pin_memory=False,
|
| 557 |
+
)
|
| 558 |
+
|
| 559 |
+
# νλ ¨ λͺ¨λλ‘ μ€μ
|
| 560 |
+
self.lora_model.train()
|
| 561 |
+
|
| 562 |
+
# κ·ΈλλμΈνΈ 체ν¬ν¬μΈν
νμ±ν (λ©λͺ¨λ¦¬ μ μ½)
|
| 563 |
+
self.lora_model.gradient_checkpointing_enable()
|
| 564 |
+
|
| 565 |
+
# κ·ΈλλμΈνΈ ν΄λ¦¬ν μ€μ
|
| 566 |
+
self.lora_model.enable_input_require_grads()
|
| 567 |
+
|
| 568 |
+
logger.info("β
νλ ¨μ μν λͺ¨λΈ μ€λΉ μλ£")
|
| 569 |
+
return True
|
| 570 |
+
|
| 571 |
+
except Exception as e:
|
| 572 |
+
logger.error(f"β νλ ¨ μ€λΉ μ€ν¨: {e}")
|
| 573 |
+
return False
|
| 574 |
+
|
| 575 |
+
# μ μ LoRA κ΄λ¦¬μ μΈμ€ν΄μ€ (μμ ν μμ±)
|
| 576 |
+
try:
|
| 577 |
+
if PEFT_AVAILABLE and TRANSFORMERS_AVAILABLE:
|
| 578 |
+
lora_manager = LoRAManager()
|
| 579 |
+
logger.info("β
μ μ LoRA κ΄λ¦¬μ μΈμ€ν΄μ€ μμ± μλ£")
|
| 580 |
+
else:
|
| 581 |
+
lora_manager = None
|
| 582 |
+
logger.warning("β οΈ LoRA λΌμ΄λΈλ¬λ¦¬κ° μ¬μ© λΆκ°λ₯νμ¬ LoRA κ΄λ¦¬μλ₯Ό μμ±νμ§ μμμ΅λλ€.")
|
| 583 |
+
except Exception as e:
|
| 584 |
+
lora_manager = None
|
| 585 |
+
logger.error(f"β LoRA κ΄λ¦¬μ μΈμ€ν΄μ€ μμ± μ€ν¨: {e}")
|
| 586 |
+
|
| 587 |
+
def get_lora_manager() -> Optional[LoRAManager]:
|
| 588 |
+
"""μ μ LoRA κ΄λ¦¬μ λ°ν (NoneμΌ μ μμ)"""
|
| 589 |
+
return lora_manager
|