doropiza commited on
Commit
d4bc91d
·
1 Parent(s): f05af65
Files changed (1) hide show
  1. app.py +120 -31
app.py CHANGED
@@ -27,13 +27,24 @@ class ChatBot:
27
  self.current_model_name = "gemma-2b-it"
28
  self.model_path = self.available_models[self.current_model_name]
29
 
30
- # 量子化設定
31
- self.quantization_config = BitsAndBytesConfig(
32
- load_in_4bit=True,
33
- bnb_4bit_quant_type="nf4",
34
- bnb_4bit_use_double_quant=True,
35
- bnb_4bit_compute_dtype=torch.float16
36
- )
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  # モデル初期化
39
  self.tokenizer = None
@@ -56,49 +67,101 @@ class ChatBot:
56
  "repetition_penalty": 1.2
57
  }
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  def load_model(self):
60
- """モデルの読み込み"""
61
  try:
62
  logger.info(f"モデル {self.model_path} を読み込み中...")
63
 
64
  # 既存モデルのメモリ解放
65
- if hasattr(self, 'model') and self.model is not None:
66
- del self.model
67
- if hasattr(self, 'tokenizer') and self.tokenizer is not None:
68
- del self.tokenizer
69
-
70
- # ガベージコレクション実行
71
- gc.collect()
72
- if torch.cuda.is_available():
73
- torch.cuda.empty_cache()
74
 
 
75
  self.tokenizer = AutoTokenizer.from_pretrained(
76
  self.model_path,
77
  token=HUGGINGFACE_TOKEN,
78
  trust_remote_code=True
79
  )
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  self.model = AutoModelForCausalLM.from_pretrained(
82
  self.model_path,
83
- token=HUGGINGFACE_TOKEN,
84
- quantization_config=self.quantization_config,
85
- device_map="auto",
86
- torch_dtype=torch.float16,
87
- trust_remote_code=True
88
  )
89
 
90
- # パディングトークン設定
91
- if self.tokenizer.pad_token is None:
92
- self.tokenizer.pad_token = self.tokenizer.eos_token
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  self.model_loaded = True
95
  logger.info(f"モデル {self.model_path} の読み込み完了")
96
 
 
 
 
 
 
97
  except Exception as e:
98
  logger.error(f"モデル読み込みエラー: {e}")
99
  self.model_loaded = False
100
  self.tokenizer = None
101
  self.model = None
 
 
102
 
103
  def switch_model(self, model_name):
104
  """モデルの切り替え"""
@@ -161,7 +224,7 @@ class ChatBot:
161
 
162
  @spaces.GPU(duration=45)
163
  def generate_response(self, message, conversation_history=None):
164
- """応答生成(GPU使用)"""
165
  if not self.model_loaded:
166
  return "申し訳ありませんが、現在AIモデルが利用できません。モデルを読み込み直してください。"
167
 
@@ -172,14 +235,21 @@ class ChatBot:
172
  # プロンプト作成
173
  prompt = self.create_prompt(message, conversation_history)
174
 
175
- # トークン化
176
  inputs = self.tokenizer.encode(
177
  prompt,
178
  return_tensors='pt',
179
  max_length=1024,
180
- truncation=True
 
181
  )
182
 
 
 
 
 
 
 
183
  # 生成パラメータを動的に調整
184
  generation_kwargs = {
185
  "inputs": inputs,
@@ -190,12 +260,24 @@ class ChatBot:
190
  "repetition_penalty": self.generation_config["repetition_penalty"],
191
  "pad_token_id": self.tokenizer.pad_token_id,
192
  "eos_token_id": self.tokenizer.eos_token_id,
193
- "use_cache": True
 
194
  }
195
 
196
- # 生成
197
  with torch.no_grad():
198
- outputs = self.model.generate(**generation_kwargs)
 
 
 
 
 
 
 
 
 
 
 
199
 
200
  # デコード
201
  response = self.tokenizer.decode(
@@ -215,6 +297,13 @@ class ChatBot:
215
 
216
  except Exception as e:
217
  logger.error(f"応答生成エラー: {e}")
 
 
 
 
 
 
 
218
  return f"エラーが発生しました: {str(e)}"
219
 
220
  def get_conversation(self, session_id="default"):
 
27
  self.current_model_name = "gemma-2b-it"
28
  self.model_path = self.available_models[self.current_model_name]
29
 
30
+ # デバイス設定
31
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
32
+ logger.info(f"使用デバイス: {self.device}")
33
+
34
+ # 量子化設定(改良版)
35
+ if torch.cuda.is_available():
36
+ self.quantization_config = BitsAndBytesConfig(
37
+ load_in_4bit=True,
38
+ bnb_4bit_quant_type="nf4",
39
+ bnb_4bit_use_double_quant=True,
40
+ bnb_4bit_compute_dtype=torch.float16,
41
+ bnb_4bit_quant_storage=torch.uint8, # 追加
42
+ llm_int8_enable_fp32_cpu_offload=False # 追加
43
+ )
44
+ else:
45
+ # CPU使用時は量子化を無効化
46
+ self.quantization_config = None
47
+ logger.info("CPUモードのため量子化を無効化")
48
 
49
  # モデル初期化
50
  self.tokenizer = None
 
67
  "repetition_penalty": 1.2
68
  }
69
 
70
+ def cleanup_memory(self):
71
+ """メモリクリーンアップ"""
72
+ if hasattr(self, 'model') and self.model is not None:
73
+ del self.model
74
+ if hasattr(self, 'tokenizer') and self.tokenizer is not None:
75
+ del self.tokenizer
76
+
77
+ # ガベージコレクション実行
78
+ gc.collect()
79
+ if torch.cuda.is_available():
80
+ torch.cuda.empty_cache()
81
+ # 追加: CUDA同期
82
+ torch.cuda.synchronize()
83
+
84
  def load_model(self):
85
+ """モデルの読み込み(改良版)"""
86
  try:
87
  logger.info(f"モデル {self.model_path} を読み込み中...")
88
 
89
  # 既存モデルのメモリ解放
90
+ self.cleanup_memory()
 
 
 
 
 
 
 
 
91
 
92
+ # トークナイザー読み込み
93
  self.tokenizer = AutoTokenizer.from_pretrained(
94
  self.model_path,
95
  token=HUGGINGFACE_TOKEN,
96
  trust_remote_code=True
97
  )
98
 
99
+ # パディングトークン設定(事前に)
100
+ if self.tokenizer.pad_token is None:
101
+ self.tokenizer.pad_token = self.tokenizer.eos_token
102
+
103
+ # モデル読み込み設定
104
+ model_kwargs = {
105
+ "token": HUGGINGFACE_TOKEN,
106
+ "trust_remote_code": True,
107
+ "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32,
108
+ "low_cpu_mem_usage": True, # 追加
109
+ "use_flash_attention_2": False # 安定性のため無効化
110
+ }
111
+
112
+ # 量子化設定の適用
113
+ if self.quantization_config is not None:
114
+ model_kwargs["quantization_config"] = self.quantization_config
115
+ model_kwargs["device_map"] = "auto"
116
+ else:
117
+ # CPU使用時
118
+ model_kwargs["torch_dtype"] = torch.float32
119
+
120
  self.model = AutoModelForCausalLM.from_pretrained(
121
  self.model_path,
122
+ **model_kwargs
 
 
 
 
123
  )
124
 
125
+ # CPU使用時の明示的なデバイス移動
126
+ if not torch.cuda.is_available():
127
+ self.model = self.model.to(self.device)
128
+
129
+ # 量子化モデルの場合、明示的にCUDAに移動
130
+ elif self.quantization_config is not None:
131
+ try:
132
+ # 量子化レイヤーの初期化
133
+ if hasattr(self.model, 'cuda'):
134
+ self.model.cuda()
135
+
136
+ # 各レイヤーを確実にGPUに移動
137
+ for name, module in self.model.named_modules():
138
+ if hasattr(module, 'cuda') and not next(module.parameters(), torch.tensor(0)).is_cuda:
139
+ try:
140
+ module.cuda()
141
+ except Exception as layer_e:
142
+ logger.warning(f"レイヤー {name} のCUDA移動に失敗: {layer_e}")
143
+
144
+ except Exception as cuda_e:
145
+ logger.warning(f"CUDA移動エラー: {cuda_e}")
146
+
147
+ # モデルを評価モードに設定
148
+ self.model.eval()
149
 
150
  self.model_loaded = True
151
  logger.info(f"モデル {self.model_path} の読み込み完了")
152
 
153
+ # メモリ使用量ログ
154
+ if torch.cuda.is_available():
155
+ memory_allocated = torch.cuda.memory_allocated() / 1024**3
156
+ logger.info(f"GPU メモリ使用量: {memory_allocated:.2f} GB")
157
+
158
  except Exception as e:
159
  logger.error(f"モデル読み込みエラー: {e}")
160
  self.model_loaded = False
161
  self.tokenizer = None
162
  self.model = None
163
+ # エラー時のメモリクリーンアップ
164
+ self.cleanup_memory()
165
 
166
  def switch_model(self, model_name):
167
  """モデルの切り替え"""
 
224
 
225
  @spaces.GPU(duration=45)
226
  def generate_response(self, message, conversation_history=None):
227
+ """応答生成(GPU使用・改良版)"""
228
  if not self.model_loaded:
229
  return "申し訳ありませんが、現在AIモデルが利用できません。モデルを読み込み直してください。"
230
 
 
235
  # プロンプト作成
236
  prompt = self.create_prompt(message, conversation_history)
237
 
238
+ # トークン化(デバイス指定を明示)
239
  inputs = self.tokenizer.encode(
240
  prompt,
241
  return_tensors='pt',
242
  max_length=1024,
243
+ truncation=True,
244
+ padding=True # 追加
245
  )
246
 
247
+ # 入力をモデルと同じデバイスに移動
248
+ if torch.cuda.is_available() and self.model.device.type == 'cuda':
249
+ inputs = inputs.to(self.model.device)
250
+ elif not torch.cuda.is_available():
251
+ inputs = inputs.to(self.device)
252
+
253
  # 生成パラメータを動的に調整
254
  generation_kwargs = {
255
  "inputs": inputs,
 
260
  "repetition_penalty": self.generation_config["repetition_penalty"],
261
  "pad_token_id": self.tokenizer.pad_token_id,
262
  "eos_token_id": self.tokenizer.eos_token_id,
263
+ "use_cache": True,
264
+ "attention_mask": torch.ones_like(inputs) # 追加
265
  }
266
 
267
+ # 生成実行
268
  with torch.no_grad():
269
+ try:
270
+ outputs = self.model.generate(**generation_kwargs)
271
+ except RuntimeError as runtime_error:
272
+ if "FP4 quantization state not initialized" in str(runtime_error):
273
+ logger.warning("量子化エラーを検出、モデルを再初期化します...")
274
+ self.load_model()
275
+ if self.model_loaded:
276
+ outputs = self.model.generate(**generation_kwargs)
277
+ else:
278
+ raise runtime_error
279
+ else:
280
+ raise runtime_error
281
 
282
  # デコード
283
  response = self.tokenizer.decode(
 
297
 
298
  except Exception as e:
299
  logger.error(f"応答生成エラー: {e}")
300
+ # 特定のエラーに対するリトライ機構
301
+ if "FP4 quantization" in str(e) or "not initialized" in str(e):
302
+ logger.info("量子化エラーによるモデル再読み込みを実行...")
303
+ self.load_model()
304
+ if self.model_loaded:
305
+ return "モデルを再読み込みしました。もう一度お試しください。"
306
+
307
  return f"エラーが発生しました: {str(e)}"
308
 
309
  def get_conversation(self, session_id="default"):