Yuchan5386 commited on
Commit
009dbf5
·
verified ·
1 Parent(s): 4a29dd6

Update api.py

Browse files
Files changed (1) hide show
  1. api.py +217 -9
api.py CHANGED
@@ -1,14 +1,222 @@
1
- from fastapi import FastAPI
2
  from fastapi.responses import StreamingResponse
3
- import time
 
 
 
 
 
 
4
 
5
  app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- def stream_text():
8
- for word in ["안녕", "세상아", "나는", "스트리밍", "API야", "!"]:
9
- yield f"{word} "
10
- time.sleep(0.5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- @app.get("/stream")
13
- def stream():
14
- return StreamingResponse(stream_text(), media_type="text/plain")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
  from fastapi.responses import StreamingResponse
3
+ import asyncio
4
+ import json
5
+ import numpy as np
6
+ import tensorflow as tf
7
+ from tensorflow.keras import layers
8
+ import sentencepiece as spm
9
+ import requests
10
 
11
  app = FastAPI()
12
+
13
+ sp = spm.SentencePieceProcessor()
14
+ sp.load("kolig_unigram.model")
15
+
16
+ pad_id = sp.piece_to_id("<pad>")
17
+ if pad_id == -1: pad_id = 0
18
+ start_id = sp.piece_to_id("<start>")
19
+ if start_id == -1: start_id = 1
20
+ end_id = sp.piece_to_id("< end >")
21
+ if end_id == -1: end_id = 2
22
+ unk_id = sp.piece_to_id("<unk>")
23
+ if unk_id == -1: unk_id = 3
24
+
25
+ vocab_size = sp.get_piece_size()
26
+ max_len = 100
27
+
28
+ def text_to_ids(text):
29
+ return sp.encode(text, out_type=int)
30
+
31
+ def ids_to_text(ids):
32
+ return sp.decode(ids)
33
 
34
+ class RotaryPositionalEmbedding(layers.Layer):
35
+ def __init__(self, dim):
36
+ super().__init__()
37
+ inv_freq = 1.0 / (10000 ** (np.arange(0, dim, 2) / dim))
38
+ self.inv_freq = tf.constant(inv_freq, dtype=tf.float32)
39
+
40
+ def call(self, x):
41
+ batch, heads, seq_len, depth = tf.unstack(tf.shape(x))
42
+ t = tf.range(seq_len, dtype=tf.float32)
43
+ freqs = tf.einsum('i,j->ij', t, self.inv_freq)
44
+ emb_sin = tf.sin(freqs)
45
+ emb_cos = tf.cos(freqs)
46
+ emb_cos = tf.reshape(emb_cos, [1, 1, seq_len, -1])
47
+ emb_sin = tf.reshape(emb_sin, [1, 1, seq_len, -1])
48
+ x1 = x[..., ::2]
49
+ x2 = x[..., 1::2]
50
+ x_rotated = tf.stack([
51
+ x1 * emb_cos - x2 * emb_sin,
52
+ x1 * emb_sin + x2 * emb_cos
53
+ ], axis=-1)
54
+ x_rotated = tf.reshape(x_rotated, tf.shape(x))
55
+ return x_rotated
56
 
57
+ class SwiGLU(tf.keras.layers.Layer):
58
+ def __init__(self, d_model, d_ff):
59
+ super().__init__()
60
+ self.proj = tf.keras.layers.Dense(d_ff * 2)
61
+ self.out = tf.keras.layers.Dense(d_model)
62
+
63
+ def call(self, x):
64
+ x_proj = self.proj(x)
65
+ x_val, x_gate = tf.split(x_proj, 2, axis=-1)
66
+ return self.out(x_val * tf.nn.silu(x_gate))
67
+
68
+ class GPTBlock(tf.keras.layers.Layer):
69
+ def __init__(self, d_model, d_ff, num_heads=8, dropout_rate=0.1, adapter_dim=64):
70
+ super().__init__()
71
+ self.ln1 = tf.keras.layers.LayerNormalization(epsilon=1e-5)
72
+ self.mha = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=d_model // num_heads)
73
+ self.dropout1 = tf.keras.layers.Dropout(dropout_rate)
74
+ self.adapter_down = tf.keras.layers.Dense(adapter_dim, activation='gelu')
75
+ self.adapter_up = tf.keras.layers.Dense(d_model)
76
+
77
+ self.ln2 = tf.keras.layers.LayerNormalization(epsilon=1e-5)
78
+ self.ffn = SwiGLU(d_model, d_ff)
79
+ self.dropout2 = tf.keras.layers.Dropout(dropout_rate)
80
+ self.rope = RotaryPositionalEmbedding(d_model // num_heads)
81
+
82
+ def call(self, x, training=False):
83
+ x_norm = self.ln1(x)
84
+ b, s, _ = tf.shape(x_norm)[0], tf.shape(x_norm)[1], tf.shape(x_norm)[2]
85
+ h = self.mha.num_heads
86
+ d = x_norm.shape[-1] // h
87
+
88
+ qkv = tf.reshape(x_norm, [b, s, h, d])
89
+ qkv = tf.transpose(qkv, [0, 2, 1, 3])
90
+ q = self.rope(qkv)
91
+ k = self.rope(qkv)
92
+ q = tf.reshape(tf.transpose(q, [0, 2, 1, 3]), [b, s, h * d])
93
+ k = tf.reshape(tf.transpose(k, [0, 2, 1, 3]), [b, s, h * d])
94
+
95
+ attn_out = self.mha(query=q, value=x_norm, key=k, use_causal_mask=True, training=training)
96
+ attn_out = self.dropout1(attn_out, training=training)
97
+
98
+ adapter_out = self.adapter_up(self.adapter_down(attn_out))
99
+ attn_out = attn_out + adapter_out
100
+
101
+ x = x + attn_out
102
+ ffn_out = self.ffn(self.ln2(x))
103
+ x = x + self.dropout2(ffn_out, training=training)
104
+ return x
105
+
106
+ class InteractGPT(tf.keras.Model):
107
+ def __init__(self, vocab_size, seq_len, d_model, d_ff, n_layers, num_heads=8, dropout_rate=0.1):
108
+ super().__init__()
109
+ self.token_embedding = tf.keras.layers.Embedding(vocab_size, d_model)
110
+ self.blocks = [GPTBlock(d_model, d_ff, num_heads, dropout_rate) for _ in range(n_layers)]
111
+ self.ln_f = tf.keras.layers.LayerNormalization(epsilon=1e-5)
112
+
113
+ def call(self, x, training=False):
114
+ x = self.token_embedding(x)
115
+ for block in self.blocks:
116
+ x = block(x, training=training)
117
+ x = self.ln_f(x)
118
+ logits = tf.matmul(x, self.token_embedding.embeddings, transpose_b=True)
119
+ return logits
120
+
121
+ model = InteractGPT(vocab_size=vocab_size, seq_len=max_len, d_model=256, d_ff=1024, n_layers=6)
122
+
123
+ dummy_input = tf.zeros((1, max_len), dtype=tf.int32) # 배치1, 시퀀스길이 max_len
124
+ _ = model(dummy_input) # 모델이 빌드됨
125
+ model.load_weights("InteractGPT.weights.h5")
126
+ print("모델 가중치 로드 완료!")
127
+
128
+ def decode_sp_tokens(tokens):
129
+ text = ''.join(tokens).replace('▁', ' ').strip()
130
+ return text
131
+
132
+ def generate_text_mirostat_top_p(model, prompt, max_len=100, max_gen=98,
133
+ temperature=1.0, min_len=20,
134
+ repetition_penalty=1.2, eta=0.1, m=100, p=0.9):
135
+ model_input = text_to_ids(f"<start> {prompt} <sep>")
136
+ model_input = model_input[:max_len]
137
+ generated = list(model_input)
138
+ text_so_far = []
139
+
140
+ tau = 5.0 # 초기 목표 surprise
141
+
142
+ for step in range(max_gen):
143
+ pad_length = max(0, max_len - len(generated))
144
+ input_padded = np.pad(generated, (0, pad_length), constant_values=pad_id)
145
+ input_tensor = tf.convert_to_tensor([input_padded])
146
+ logits = model(input_tensor, training=False)
147
+ next_token_logits = logits[0, len(generated) - 1].numpy()
148
+
149
+ # 반복 페널티 적용
150
+ token_counts = {}
151
+ for t in generated:
152
+ token_counts[t] = token_counts.get(t, 0) + 1
153
+ for token_id, count in token_counts.items():
154
+ next_token_logits[token_id] /= (repetition_penalty ** count)
155
+
156
+ # 최소 길이 넘으면 종료 토큰 확률 낮추기
157
+ if len(generated) >= min_len:
158
+ next_token_logits[end_id] -= 5.0
159
+ next_token_logits[pad_id] -= 10.0
160
+
161
+ # 온도 조절
162
+ next_token_logits = next_token_logits / temperature
163
+
164
+ # --- 미로스타트 + Top-p 샘플링 시작 ---
165
+ logits_stable = next_token_logits - np.max(next_token_logits)
166
+ probs = np.exp(logits_stable)
167
+ probs /= probs.sum()
168
+
169
+ # 1. mirostat top-m 후보 추리기
170
+ sorted_indices = np.argsort(-probs)
171
+ top_indices = sorted_indices[:m]
172
+ top_probs = probs[top_indices]
173
+ top_probs /= top_probs.sum()
174
+
175
+ # 2. mirostat 샘플링
176
+ sampled_index = np.random.choice(top_indices, p=top_probs)
177
+ sampled_prob = probs[sampled_index]
178
+ observed_surprise = -np.log(sampled_prob + 1e-9)
179
+ tau += eta * (observed_surprise - tau)
180
+
181
+ # 3. top-p 필터링
182
+ sorted_top_indices = top_indices[np.argsort(-top_probs)]
183
+ sorted_top_probs = np.sort(top_probs)[::-1]
184
+ cumulative_probs = np.cumsum(sorted_top_probs)
185
+ cutoff = np.searchsorted(cumulative_probs, p, side='left') + 1
186
+ filtered_indices = sorted_top_indices[:cutoff]
187
+ filtered_probs = sorted_top_probs[:cutoff]
188
+ filtered_probs /= filtered_probs.sum()
189
+
190
+ # 4. 최종 토큰은 filtered 집합에서 다시 샘플링
191
+ final_token = np.random.choice(filtered_indices, p=filtered_probs)
192
+
193
+ generated.append(int(final_token))
194
+
195
+ next_word = sp.id_to_piece(int(final_token))
196
+ text_so_far.append(next_word)
197
+ decoded_text = decode_sp_tokens(text_so_far)
198
+
199
+ if len(generated) >= min_len and final_token == end_id:
200
+ break
201
+ if len(generated) >= min_len and decoded_text.endswith(('.', '!', '?', '<end>')):
202
+ break
203
+
204
+ yield decoded_text
205
+
206
+ async def async_generator_wrapper(prompt: str):
207
+ # 동기 제너레이터를 비동기로 감싸기
208
+ loop = asyncio.get_event_loop()
209
+ gen = generate_text_mirostat_top_p(model, prompt)
210
+
211
+ for text_piece in gen:
212
+ yield text_piece
213
+ # 토큰 생성 속도 조절 (0.1초 딜레이)
214
+ await asyncio.sleep(0.1)
215
+
216
+ @app.get("/generate")
217
+ async def generate(request: Request):
218
+ # 쿼리 파라미터로 prompt 받음, 없으면 기본값
219
+ prompt = request.query_params.get("prompt", "안녕하세요")
220
+
221
+ # 스트리밍 응답으로 보냄
222
+ return StreamingResponse(async_generator_wrapper(prompt), media_type="text/plain")