doropiza commited on
Commit
2d2eb5f
·
1 Parent(s): e1d42ff
Files changed (1) hide show
  1. app.py +164 -57
app.py CHANGED
@@ -10,7 +10,7 @@ Hugging Face Spaces (ZeroGPU) 対応版
10
 
11
  import gradio as gr
12
  import torch
13
- from transformers import AutoModelForCausalLM, AutoTokenizer
14
  import os
15
  from typing import List, Tuple
16
 
@@ -34,11 +34,16 @@ class ChatBot:
34
  def __init__(self):
35
  self.model = None
36
  self.tokenizer = None
 
37
  self.current_model = None
38
 
 
 
 
 
39
  def load_model(self, model_name: str):
40
  """モデルとトークナイザーをロード"""
41
- if self.current_model == model_name and self.model is not None:
42
  return
43
 
44
  try:
@@ -46,47 +51,121 @@ class ChatBot:
46
  if self.model is not None:
47
  del self.model
48
  del self.tokenizer
49
- if torch.cuda.is_available():
50
- torch.cuda.empty_cache()
51
- torch.cuda.synchronize()
52
-
53
- # トークナイザーロード
54
- self.tokenizer = AutoTokenizer.from_pretrained(
55
- model_name,
56
- token=HF_TOKEN,
57
- trust_remote_code=True,
58
- padding_side="left"
59
- )
60
-
61
- # パッドトークンの設定
62
- if self.tokenizer.pad_token is None:
63
- self.tokenizer.pad_token = self.tokenizer.eos_token
64
- self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
65
 
66
- # モデルロード(ZeroGPU対応)
67
- self.model = AutoModelForCausalLM.from_pretrained(
68
- model_name,
69
- token=HF_TOKEN,
70
- torch_dtype=torch.float16,
71
- low_cpu_mem_usage=True,
72
- trust_remote_code=True,
73
- load_in_8bit=False, # ZeroGPU環境では8bit量子化は使わない
74
- device_map=None # ZeroGPU環境では自動マッピングしない
75
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  self.current_model = model_name
78
  print(f"モデル {model_name} のロードが完了しました。")
79
 
80
  except Exception as e:
81
  print(f"モデルのロード中にエラーが発生しました: {str(e)}")
82
- raise
 
 
 
 
83
 
84
  def _generate_response_gpu(self, message: str, history: List[Tuple[str, str]], model_name: str,
85
  temperature: float = 0.7, max_tokens: int = 512) -> str:
86
  """GPU上で応答を生成する実際の処理"""
87
- # モデルロード
88
- self.load_model(model_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  # GPUに移動
91
  self.model.to('cuda')
92
 
@@ -128,33 +207,61 @@ class ChatBot:
128
  return self._generate_response_gpu(message, history, model_name, temperature, max_tokens)
129
  else:
130
  # 通常環境の場合
131
- self.load_model(model_name)
132
- device = 'cuda' if torch.cuda.is_available() else 'cpu'
133
-
134
- if device == 'cuda':
135
- self.model.to(device)
136
-
137
- prompt = self._build_prompt(message, history)
138
- inputs = self.tokenizer.encode(prompt, return_tensors="pt").to(device)
139
-
140
- with torch.no_grad():
141
- outputs = self.model.generate(
142
- inputs,
143
- max_new_tokens=max_tokens,
144
- temperature=temperature,
145
- do_sample=True,
146
- top_p=0.95,
147
- top_k=50,
148
- repetition_penalty=1.1,
149
- pad_token_id=self.tokenizer.pad_token_id,
150
- eos_token_id=self.tokenizer.eos_token_id
151
- )
152
-
153
- response = self.tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True)
154
- return response.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  def _build_prompt(self, message: str, history: List[Tuple[str, str]]) -> str:
157
- """会話履歴からプロンプトを構築"""
158
  prompt = ""
159
 
160
  # 履歴を追加(最新3件のみ使用 - メモリ効率のため)
@@ -278,7 +385,7 @@ with gr.Blocks(title="ChatGPT Clone", theme=gr.themes.Soft()) as app:
278
  - ZeroGPU使用により高速推論が可能
279
  - 1回の生成は120秒以内に完了します
280
  - 大きなモデル使用時は、短めの応答になる場合があります
281
- - gpt-oss-20bは推論専用モデルです
282
  """)
283
 
284
  # イベントハンドラ
 
10
 
11
  import gradio as gr
12
  import torch
13
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
14
  import os
15
  from typing import List, Tuple
16
 
 
34
  def __init__(self):
35
  self.model = None
36
  self.tokenizer = None
37
+ self.pipeline = None
38
  self.current_model = None
39
 
40
+ def is_gpt_oss_model(self, model_name: str) -> bool:
41
+ """gpt-ossモデルかどうかを判定"""
42
+ return "gpt-oss" in model_name.lower()
43
+
44
  def load_model(self, model_name: str):
45
  """モデルとトークナイザーをロード"""
46
+ if self.current_model == model_name and (self.model is not None or self.pipeline is not None):
47
  return
48
 
49
  try:
 
51
  if self.model is not None:
52
  del self.model
53
  del self.tokenizer
54
+ if self.pipeline is not None:
55
+ del self.pipeline
56
+
57
+ if torch.cuda.is_available():
58
+ torch.cuda.empty_cache()
59
+ torch.cuda.synchronize()
 
 
 
 
 
 
 
 
 
 
60
 
61
+ if self.is_gpt_oss_model(model_name):
62
+ # gpt-ossモデルの場合はpipelineを使用
63
+ print(f"gpt-ossモデル {model_name} をpipelineでロードします...")
64
+ self.pipeline = pipeline(
65
+ "text-generation",
66
+ model=model_name,
67
+ torch_dtype=torch.float16,
68
+ trust_remote_code=True,
69
+ token=HF_TOKEN,
70
+ device_map=None # ZeroGPU対応のため手動制御
71
+ )
72
+ self.model = None
73
+ self.tokenizer = None
74
+ else:
75
+ # 通常のモデルの場合
76
+ print(f"通常のモデル {model_name} をロードします...")
77
+ # トークナイザーロード
78
+ self.tokenizer = AutoTokenizer.from_pretrained(
79
+ model_name,
80
+ token=HF_TOKEN,
81
+ trust_remote_code=True,
82
+ padding_side="left"
83
+ )
84
+
85
+ # パッドトークンの設定
86
+ if self.tokenizer.pad_token is None:
87
+ self.tokenizer.pad_token = self.tokenizer.eos_token
88
+ self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
89
+
90
+ # モデルロード(ZeroGPU対応)
91
+ self.model = AutoModelForCausalLM.from_pretrained(
92
+ model_name,
93
+ token=HF_TOKEN,
94
+ torch_dtype=torch.float16,
95
+ low_cpu_mem_usage=True,
96
+ trust_remote_code=True,
97
+ load_in_8bit=False, # ZeroGPU環境では8bit量子化は使わない
98
+ device_map=None # ZeroGPU環境では自動マッピングしない
99
+ )
100
+ self.pipeline = None
101
 
102
  self.current_model = model_name
103
  print(f"モデル {model_name} のロードが完了しました。")
104
 
105
  except Exception as e:
106
  print(f"モデルのロード中にエラーが発生しました: {str(e)}")
107
+ # gpt-ossモデルでエラーが出た場合、使用不可と表示
108
+ if self.is_gpt_oss_model(model_name):
109
+ raise Exception(f"gpt-ossモデルのロードに失敗しました。このモデルは現在の環境では使用できません: {str(e)}")
110
+ else:
111
+ raise
112
 
113
  def _generate_response_gpu(self, message: str, history: List[Tuple[str, str]], model_name: str,
114
  temperature: float = 0.7, max_tokens: int = 512) -> str:
115
  """GPU上で応答を生成する実際の処理"""
116
+ try:
117
+ # モデルロード
118
+ self.load_model(model_name)
119
+
120
+ if self.is_gpt_oss_model(model_name):
121
+ # gpt-ossモデルの場合
122
+ return self._generate_with_pipeline(message, history, temperature, max_tokens)
123
+ else:
124
+ # 通常のモデルの場合
125
+ return self._generate_with_model(message, history, temperature, max_tokens)
126
+
127
+ except Exception as e:
128
+ return f"エラー: {str(e)}"
129
+
130
+ def _generate_with_pipeline(self, message: str, history: List[Tuple[str, str]],
131
+ temperature: float, max_tokens: int) -> str:
132
+ """pipelineを使用した生成(gpt-oss用)"""
133
+ # GPUに移動
134
+ if hasattr(self.pipeline.model, 'to'):
135
+ self.pipeline.model.to('cuda')
136
 
137
+ # gpt-ossはchat format用のmessages形式を使用
138
+ messages = []
139
+
140
+ # 履歴を追加(最新3件のみ)
141
+ for user_msg, assistant_msg in history[-3:]:
142
+ messages.append({"role": "user", "content": user_msg})
143
+ messages.append({"role": "assistant", "content": assistant_msg})
144
+
145
+ # 現在のメッセージを追加
146
+ messages.append({"role": "user", "content": message})
147
+
148
+ # pipeline経由で生成
149
+ outputs = self.pipeline(
150
+ messages,
151
+ max_new_tokens=max_tokens,
152
+ temperature=temperature,
153
+ do_sample=True,
154
+ top_p=0.95,
155
+ return_full_text=False
156
+ )
157
+
158
+ # CPUに戻す(メモリ節約)
159
+ if hasattr(self.pipeline.model, 'to'):
160
+ self.pipeline.model.to('cpu')
161
+ torch.cuda.empty_cache()
162
+ torch.cuda.synchronize()
163
+
164
+ return outputs[0]["generated_text"].strip()
165
+
166
+ def _generate_with_model(self, message: str, history: List[Tuple[str, str]],
167
+ temperature: float, max_tokens: int) -> str:
168
+ """通常のモデルを使用した生成"""
169
  # GPUに移動
170
  self.model.to('cuda')
171
 
 
207
  return self._generate_response_gpu(message, history, model_name, temperature, max_tokens)
208
  else:
209
  # 通常環境の場合
210
+ try:
211
+ self.load_model(model_name)
212
+
213
+ if self.is_gpt_oss_model(model_name):
214
+ # gpt-ossモデルの場合
215
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
216
+ if hasattr(self.pipeline.model, 'to') and device == 'cuda':
217
+ self.pipeline.model.to(device)
218
+
219
+ messages = []
220
+ for user_msg, assistant_msg in history[-3:]:
221
+ messages.append({"role": "user", "content": user_msg})
222
+ messages.append({"role": "assistant", "content": assistant_msg})
223
+ messages.append({"role": "user", "content": message})
224
+
225
+ outputs = self.pipeline(
226
+ messages,
227
+ max_new_tokens=max_tokens,
228
+ temperature=temperature,
229
+ do_sample=True,
230
+ top_p=0.95,
231
+ return_full_text=False
232
+ )
233
+ return outputs[0]["generated_text"].strip()
234
+
235
+ else:
236
+ # 通常のモデルの場合
237
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
238
+ if device == 'cuda':
239
+ self.model.to(device)
240
+
241
+ prompt = self._build_prompt(message, history)
242
+ inputs = self.tokenizer.encode(prompt, return_tensors="pt").to(device)
243
+
244
+ with torch.no_grad():
245
+ outputs = self.model.generate(
246
+ inputs,
247
+ max_new_tokens=max_tokens,
248
+ temperature=temperature,
249
+ do_sample=True,
250
+ top_p=0.95,
251
+ top_k=50,
252
+ repetition_penalty=1.1,
253
+ pad_token_id=self.tokenizer.pad_token_id,
254
+ eos_token_id=self.tokenizer.eos_token_id
255
+ )
256
+
257
+ response = self.tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True)
258
+ return response.strip()
259
+
260
+ except Exception as e:
261
+ return f"エラー: {str(e)}"
262
 
263
  def _build_prompt(self, message: str, history: List[Tuple[str, str]]) -> str:
264
+ """会話履歴からプロンプトを構築(通常のモデル用)"""
265
  prompt = ""
266
 
267
  # 履歴を追加(最新3件のみ使用 - メモリ効率のため)
 
385
  - ZeroGPU使用により高速推論が可能
386
  - 1回の生成は120秒以内に完了します
387
  - 大きなモデル使用時は、短めの応答になる場合があります
388
+ - gpt-oss-20bは推論専用モデルで、harmony formatを使用します
389
  """)
390
 
391
  # イベントハンドラ