gbrabbit commited on
Commit
e3c60d5
·
1 Parent(s): f480fa2

Auto commit at 22-2025-08 21:22:44

Browse files
lily_llm_api/app_v2_250822_1812.py DELETED
The diff for this file is too large to render. See raw diff
 
lily_llm_api/models/kanana_1_5_v_3b_instruct.py CHANGED
@@ -127,6 +127,7 @@ class Kanana15V3bInstructProfile:
127
 
128
  processor = AutoProcessor.from_pretrained(
129
  model_path,
 
130
  trust_remote_code=True,
131
  local_files_only=use_local,
132
  use_fast=True # 🔄 빠른 이미지 프로세서 사용 (경고 제거)
@@ -134,16 +135,17 @@ class Kanana15V3bInstructProfile:
134
 
135
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
136
 
137
- # dtype 설정 최적화 - CPU에서는 float32 사용
138
  if device == 'cuda':
139
- selected_dtype = torch.bfloat16 # GPU에서는 float16으로 메모리 절약
140
  else:
141
- selected_dtype = torch.bfloat16 # CPU에서는 float32로 안정성 확보
142
 
143
  logger.info(f"🔧 선택된 dtype: {selected_dtype} (device: {device})")
144
 
145
  model = AutoModelForVision2Seq.from_pretrained(
146
  model_path,
 
147
  trust_remote_code=True,
148
  torch_dtype=selected_dtype,
149
  local_files_only=use_local,
@@ -168,9 +170,9 @@ class Kanana15V3bInstructProfile:
168
  return {
169
  "max_new_tokens": 128,
170
  "do_sample": True,
171
- "temperature": 0.9,
172
  "top_k": 50,
173
- "top_p": 0.95,
174
  "repetition_penalty": 1.1,
175
  "no_repeat_ngram_size": 3,
176
  "pad_token_id": 128001,
 
127
 
128
  processor = AutoProcessor.from_pretrained(
129
  model_path,
130
+ token=HF_TOKEN,
131
  trust_remote_code=True,
132
  local_files_only=use_local,
133
  use_fast=True # 🔄 빠른 이미지 프로세서 사용 (경고 제거)
 
135
 
136
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
137
 
138
+ # 공식 설정 파일 bfloat16 사용, float32 사용시 메모리 에러 발생
139
  if device == 'cuda':
140
+ selected_dtype = torch.bfloat16
141
  else:
142
+ selected_dtype = torch.bfloat16
143
 
144
  logger.info(f"🔧 선택된 dtype: {selected_dtype} (device: {device})")
145
 
146
  model = AutoModelForVision2Seq.from_pretrained(
147
  model_path,
148
+ token=HF_TOKEN,
149
  trust_remote_code=True,
150
  torch_dtype=selected_dtype,
151
  local_files_only=use_local,
 
170
  return {
171
  "max_new_tokens": 128,
172
  "do_sample": True,
173
+ "temperature": 0.7,
174
  "top_k": 50,
175
+ "top_p": 0.9,
176
  "repetition_penalty": 1.1,
177
  "no_repeat_ngram_size": 3,
178
  "pad_token_id": 128001,
lily_llm_api/models/polyglot_ko_1_3b_chat.py CHANGED
@@ -267,6 +267,7 @@ class PolyglotKo13bChatProfile:
267
  "no_repeat_ngram_size": 3, # 2 → 3으로 조정
268
  "pad_token_id": 2, # 공식 설정 사용
269
  "eos_token_id": 2, # 공식 설정 사용
 
270
  "use_cache": True, # 캐시 활성화 (속도 향상)
271
  "early_stopping": False, # EOS 토큰까지 생성하도록 설정
272
  }
 
267
  "no_repeat_ngram_size": 3, # 2 → 3으로 조정
268
  "pad_token_id": 2, # 공식 설정 사용
269
  "eos_token_id": 2, # 공식 설정 사용
270
+ "bos_token_id": 0, # 공식 설정 사용
271
  "use_cache": True, # 캐시 활성화 (속도 향상)
272
  "early_stopping": False, # EOS 토큰까지 생성하도록 설정
273
  }
lily_llm_core/lora_manager.py CHANGED
@@ -148,6 +148,20 @@ class LoRAManager:
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),
@@ -157,7 +171,7 @@ class LoRAManager:
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),
@@ -288,17 +302,23 @@ class LoRAManager:
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 사용)
 
148
  self.tokenizer.pad_token = self.tokenizer.eos_token
149
 
150
  # 모델 로드
151
+ # 모델 타입에 따른 dtype 결정
152
+ # if model_type in ['kanana-1.5-v-3b-instruct', 'vision2seq']:
153
+ # selected_dtype = torch.bfloat16 if self.device == "cuda" else torch.bfloat16
154
+ # logger.info(f"🔍 [DEBUG] Kanana 모델 감지: {model_type} -> bfloat16 사용")
155
+ # elif model_type in ['polyglot-ko-1.3b-chat', 'causal_lm']:
156
+ # selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
157
+ # logger.info(f"🔍 [DEBUG] 일반 모델 감지: {model_type} -> {selected_dtype} 사용")
158
+ # elif model_type in ['polyglot-ko-5.8b-chat', 'causal_lm']:
159
+ # selected_dtype = torch.bfloat16 if self.device == "cuda" else torch.bfloat16
160
+ # logger.info(f"🔍 [DEBUG] 일반 모델 감지: {model_type} -> {selected_dtype} 사용")
161
+ # else:
162
+ # # 🔄 기타 모델: 기존 로직 사용
163
+ # selected_dtype = torch.float16 if self.device == "cuda" else torch.float16
164
+
165
  if model_type == "causal_lm":
166
  self.base_model = AutoModelForCausalLM.from_pretrained(
167
  str(model_path),
 
171
  device_map="auto" if self.device == "cuda" else None
172
  )
173
  elif model_type == "vision2seq":
174
+ # 🔄 Vision2Seq 모델 지원 추가 (kanana 등, bfloat16 사용)
175
  from transformers import AutoModelForVision2Seq
176
  self.base_model = AutoModelForVision2Seq.from_pretrained(
177
  str(model_path),
 
302
  # 🔄 모델 타입에 따른 dtype 결정
303
  if hasattr(self.base_model, 'config') and hasattr(self.base_model.config, 'model_type'):
304
  model_type = self.base_model.config.model_type
305
+ if model_type in ['kanana-1.5-v-3b-instruct', 'vision2seq']:
306
  # 🔄 Kanana 모델: bfloat16 사용
307
+ selected_dtype = torch.bfloat16 if self.device == "cuda" else torch.bfloat16
308
  logger.info(f"🔍 [DEBUG] Kanana 모델 감지: {model_type} -> bfloat16 사용")
309
+ elif model_type in ['polyglot-ko-1.3b-chat', 'causal_lm']:
310
+ selected_dtype = torch.float16 if self.device == "cuda" else torch.float32
311
+ logger.info(f"🔍 [DEBUG] 일반 모델 감지: {model_type} -> {selected_dtype} 사용")
312
+ elif model_type in ['polyglot-ko-5.8b-chat', 'causal_lm']:
313
+ selected_dtype = torch.bfloat16 if self.device == "cuda" else torch.bfloat16
314
+ logger.info(f"🔍 [DEBUG] 일반 모델 감지: {model_type} -> {selected_dtype} 사용")
315
  else:
316
  # 🔄 기타 모델: 기존 로직 사용
317
+ selected_dtype = torch.float16 if self.device == "cuda" else torch.float16
318
  logger.info(f"🔍 [DEBUG] 일반 모델 감지: {model_type} -> {selected_dtype} 사용")
319
  else:
320
  # 🔄 모델 타입을 알 수 없는 경우: 기존 로직 사용
321
+ selected_dtype = torch.float16 if self.device == "cuda" else torch.float16
322
  logger.info(f"🔍 [DEBUG] 모델 타입 미확인 -> {selected_dtype} 사용")
323
 
324
  # LoRA 어댑터 로드 (모델별 최적 dtype 사용)
lily_llm_core/lora_manager_250822_1812.py DELETED
@@ -1,589 +0,0 @@
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_cos.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:65aaa9057a45c5c3d63d425ac56f2eb23bb0688420548179a2ea951afdc0b9d7
3
+ size 212382
test_design.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ab9559d67982f525582ced9415fe63c13ce7084401ac02d3459000f44bd7e4ef
3
+ size 424879
test_math.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b74be60ccf89d7174365abb6ce2ade4c04b197fa77c1d32c74e5cdbb782ccf0f
3
+ size 174502