X commited on
Commit
465c7ad
·
verified ·
1 Parent(s): 489ff6f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -226
app.py CHANGED
@@ -1,241 +1,88 @@
1
- import numpy as np
 
 
 
2
  import torch
3
- import torch.nn as nn
4
- import torch.optim as optim
5
- from torch.distributions import Categorical
6
  import gradio as gr
7
- import matplotlib.pyplot as plt
8
- import io
9
- from PIL import Image
10
 
11
- # ==========================================
12
- # 1. БЕСКОНЕЧНЫЙ МИР
13
- # ==========================================
14
- class InfiniteWorld:
15
- CHUNK_SIZE = 16
16
- VIEW_RADIUS = 8
17
-
18
- def __init__(self, seed=42):
19
- self.seed = seed
20
- self.chunks = {}
21
- self.agent_pos = [0, 0]
22
- self.steps = 0
23
- self.max_steps = 1000
24
-
25
- def _get_chunk(self, cx, cy):
26
- if (cx, cy) not in self.chunks:
27
- rng = np.random.RandomState(hash((cx, cy, self.seed)) % (2**31))
28
- chunk = np.zeros((self.CHUNK_SIZE, self.CHUNK_SIZE), dtype=np.float32)
29
- noise = rng.rand(self.CHUNK_SIZE, self.CHUNK_SIZE)
30
- chunk[noise > 0.7] = 1.0
31
- self.chunks[(cx, cy)] = chunk
32
- return self.chunks[(cx, cy)]
33
-
34
- def _world_coords(self, x, y):
35
- cx, lx = divmod(x, self.CHUNK_SIZE)
36
- cy, ly = divmod(y, self.CHUNK_SIZE)
37
- return cx, cy, lx, ly
38
 
39
- def get_block(self, x, y):
40
- cx, cy, lx, ly = self._world_coords(x, y)
41
- return self._get_chunk(cx, cy)[lx, ly]
42
 
43
- def set_block(self, x, y, val):
44
- cx, cy, lx, ly = self._world_coords(x, y)
45
- self._get_chunk(cx, cy)[lx, ly] = val
 
 
 
 
 
 
 
 
46
 
47
- def reset(self):
48
- self.agent_pos = [0, 0]
49
- self.steps = 0
50
- return self._get_obs()
51
 
52
- def _get_obs(self):
53
- x, y = self.agent_pos
54
- patch = np.zeros((self.VIEW_RADIUS*2, self.VIEW_RADIUS*2, 3), dtype=np.float32)
55
- for dx in range(-self.VIEW_RADIUS, self.VIEW_RADIUS):
56
- for dy in range(-self.VIEW_RADIUS, self.VIEW_RADIUS):
57
- wx, wy = x + dx, y + dy
58
- block = self.get_block(wx, wy)
59
- px = dx + self.VIEW_RADIUS
60
- py = dy + self.VIEW_RADIUS
61
- patch[px, py, 0] = block
62
- patch[px, py, 1] = max(0, 1.0 - abs(dx)/self.VIEW_RADIUS)
63
- patch[px, py, 2] = max(0, 1.0 - abs(dy)/self.VIEW_RADIUS)
64
- patch[self.VIEW_RADIUS, self.VIEW_RADIUS, 1] = 1.0
65
- return patch
66
 
67
- def step(self, action):
68
- self.steps += 1
69
- reward = -0.005
70
- done = self.steps >= self.max_steps
71
-
72
- if action == 0: self.agent_pos[0] -= 1
73
- elif action == 1: self.agent_pos[0] += 1
74
- elif action == 2: self.agent_pos[1] -= 1
75
- elif action == 3: self.agent_pos[1] += 1
76
- elif action == 4:
77
- x, y = self.agent_pos
78
- if self.get_block(x, y) == 0:
79
- self.set_block(x, y, 1.0)
80
- reward = 1.0
81
- elif action == 5:
82
- x, y = self.agent_pos
83
- if self.get_block(x, y) == 1.0:
84
- self.set_block(x, y, 0.0)
85
- reward = 0.3
86
-
87
- return self._get_obs(), reward, done, {}
88
-
89
-
90
- # ==========================================
91
- # 2. PPO AGENT
92
- # ==========================================
93
- class PPOAgent(nn.Module):
94
- def __init__(self):
95
- super().__init__()
96
- self.encoder = nn.Sequential(
97
- nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(),
98
- nn.Conv2d(32, 64, 3, stride=2, padding=1), nn.ReLU(),
99
- nn.Conv2d(64, 64, 3, stride=2, padding=1), nn.ReLU(),
100
- nn.AdaptiveAvgPool2d((4, 4)),
101
- nn.Flatten()
102
- )
103
- self.gru = nn.GRUCell(64 * 4 * 4, 256)
104
- self.actor = nn.Linear(256, 6)
105
- self.critic = nn.Linear(256, 1)
106
 
107
- def forward(self, obs, hidden=None):
108
- features = self.encoder(obs.permute(0, 3, 1, 2))
109
- h = self.gru(features, hidden)
110
- return self.actor(h), self.critic(h), h
111
 
112
- def act(self, obs, hidden=None):
113
- with torch.no_grad():
114
- logits, value, new_hidden = self.forward(obs.unsqueeze(0), hidden)
115
- dist = Categorical(logits=logits)
116
- action = dist.sample()
117
- return action.item(), dist.log_prob(action), value.squeeze(), new_hidden
118
 
119
-
120
- # ==========================================
121
- # 3. ОБУЧЕНИЕ
122
- # ==========================================
123
- def train_ppo(episodes=100):
124
- env = InfiniteWorld()
125
- agent = PPOAgent()
126
- optimizer = optim.Adam(agent.parameters(), lr=3e-4)
127
 
128
- for ep in range(episodes):
129
- obs = env.reset()
130
- hidden = None
131
- buffers = {'obs': [], 'actions': [], 'log_probs': [], 'rewards': [], 'values': []}
132
-
133
- for _ in range(256):
134
- obs_t = torch.FloatTensor(obs)
135
- action, log_prob, value, hidden = agent.act(obs_t, hidden)
136
- next_obs, reward, done, _ = env.step(action)
137
-
138
- buffers['obs'].append(obs_t)
139
- buffers['actions'].append(action)
140
- buffers['log_probs'].append(log_prob)
141
- buffers['rewards'].append(reward)
142
- buffers['values'].append(value)
143
-
144
- obs = next_obs
145
- if done:
146
- obs = env.reset()
147
- hidden = None
148
-
149
- # GAE
150
- returns, advantages = [], []
151
- R, A = 0, 0
152
- for i in reversed(range(len(buffers['rewards']))):
153
- R = buffers['rewards'][i] + 0.99 * R
154
- next_val = buffers['values'][i+1].item() if i < len(buffers['values'])-1 else 0
155
- delta = buffers['rewards'][i] + 0.99 * next_val - buffers['values'][i].item()
156
- A = delta + 0.99 * 0.95 * A
157
- returns.insert(0, R)
158
- advantages.insert(0, A)
159
 
160
- returns = torch.FloatTensor(returns)
161
- advantages = torch.FloatTensor(advantages)
162
- advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)
163
-
164
- obs_batch = torch.stack(buffers['obs'])
165
- actions_batch = torch.LongTensor(buffers['actions'])
166
- old_log_probs = torch.stack(buffers['log_probs']).detach()
167
-
168
- for _ in range(4):
169
- logits, values, _ = agent.forward(obs_batch)
170
- dist = Categorical(logits=logits)
171
- new_log_probs = dist.log_prob(actions_batch)
172
- ratio = (new_log_probs - old_log_probs).exp()
173
- surr = torch.min(ratio * advantages, torch.clamp(ratio, 0.8, 1.2) * advantages)
174
- loss = -surr.mean() + 0.5 * (returns - values.squeeze()).pow(2).mean() - 0.01 * dist.entropy().mean()
175
-
176
- optimizer.zero_grad()
177
- loss.backward()
178
- nn.utils.clip_grad_norm_(agent.parameters(), 0.5)
179
- optimizer.step()
180
-
181
- if ep % 20 == 0:
182
- print(f"Ep {ep} | Chunks: {len(env.chunks)}")
183
 
184
- return agent
185
-
186
-
187
- # ==========================================
188
- # 4. ГРАФИЧЕСКИЙ ИНТЕРФЕЙС (ИСПРАВЛЕНО)
189
- # ==========================================
190
- def fig_to_pil(fig):
191
- """Конвертирует matplotlib figure в PIL Image без schema-багов"""
192
- buf = io.BytesIO()
193
- fig.savefig(buf, format='png', bbox_inches='tight')
194
- buf.seek(0)
195
- img = Image.open(buf)
196
- plt.close(fig)
197
- return img
198
-
199
-
200
- def run_simulation(n_steps):
201
- n_steps = int(n_steps)
202
- agent = run_simulation.agent
203
- env = InfiniteWorld(seed=np.random.randint(0, 99999))
204
- obs = env.reset()
205
- hidden = None
206
-
207
- images = []
208
- with torch.no_grad():
209
- for _ in range(min(n_steps, 300)):
210
- fig, ax = plt.subplots(figsize=(4, 4))
211
- ax.imshow(obs)
212
- ax.set_title(f"Pos: {env.agent_pos}")
213
- ax.axis('off')
214
- images.append(fig_to_pil(fig))
215
-
216
- obs_t = torch.FloatTensor(obs)
217
- action, _, _, hidden = agent.act(obs_t, hidden)
218
- obs, _, done, _ = env.step(action)
219
- if done:
220
- break
221
-
222
- return images
223
-
224
-
225
- # Предобучаем модель один раз при загрузке
226
- print("🏗️ Обучение агента...")
227
- run_simulation.agent = train_ppo(episodes=80)
228
- run_simulation.agent.eval()
229
- print("✅ Обучение завершено!")
230
-
231
-
232
- # Интерфейс БЕЗ типизации возврата, БЕЗ Gallery
233
- with gr.Blocks(title="Infinite Builder") as demo:
234
- gr.Markdown("# 🌍 Бесконечный мир: PPO-агент")
235
- slider = gr.Slider(50, 300, value=100, step=50, label="Шагов")
236
- btn = gr.Button("▶️ Запустить")
237
- output = gr.Gallery(label="Результат", columns=4)
238
- btn.click(fn=run_simulation, inputs=[slider], outputs=[output])
239
 
240
- if __name__ == "__main__":
241
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # Шаг 1. Установка
2
+ !pip install diffusers transformers accelerate safetensors gradio --quiet
3
+
4
+ # Шаг 2. Импорт
5
  import torch
6
+ from diffusers import StableDiffusionPipeline
7
+ from PIL import Image, ImageFilter, ImageEnhance
8
+ import numpy as np
9
  import gradio as gr
 
 
 
10
 
11
+ # Шаг 3. Загрузка модели
12
+ print("Загрузка модели...")
13
+ model_id = "runwayml/stable-diffusion-v1-5"
14
+ pipe = StableDiffusionPipeline.from_pretrained(
15
+ model_id,
16
+ torch_dtype=torch.float16,
17
+ safety_checker=None,
18
+ requires_safety_checker=False
19
+ )
20
+ pipe = pipe.to("cuda")
21
+ pipe.enable_attention_slicing()
22
+ pipe.enable_vae_slicing()
23
+ print("Модель загружена!")
24
+
25
+ # Шаг 4. Функция генерации
26
+ def generate_image(prompt, negative_prompt):
27
+ # УСИЛЕНИЕ ПРОМПТА (если он короткий)
28
+ if len(prompt.split()) < 10:
29
+ prompt = prompt + ", detailed, sharp focus, masterpiece"
 
 
 
 
 
 
 
 
30
 
31
+ generator = torch.Generator(device="cuda").manual_seed(2021)
 
 
32
 
33
+ with torch.autocast("cuda"):
34
+ image = pipe(
35
+ prompt=prompt,
36
+ negative_prompt=negative_prompt if negative_prompt else "",
37
+ width=512,
38
+ height=768,
39
+ num_inference_steps=10,
40
+ guidance_scale=11.0,
41
+ generator=generator,
42
+ eta=0.8
43
+ ).images[0]
44
 
45
+ # Постобработка
46
+ img_array = np.array(image)
47
+ noise = np.random.randint(-25, 25, img_array.shape, dtype=np.int16)
48
+ img_noisy = np.clip(img_array.astype(np.int16) + noise, 0, 255).astype(np.uint8)
49
 
50
+ img_final = Image.fromarray(img_noisy).filter(ImageFilter.GaussianBlur(radius=0.6))
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ enhancer = ImageEnhance.Contrast(img_final)
53
+ img_final = enhancer.enhance(1.3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ enhancer = ImageEnhance.Color(img_final)
56
+ img_final = enhancer.enhance(1.3)
 
 
57
 
58
+ return img_final
 
 
 
 
 
59
 
60
+ # Шаг 5. Интерфейс
61
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
62
+ gr.Markdown("#")
 
 
 
 
 
63
 
64
+ with gr.Row():
65
+ with gr.Column(scale=1):
66
+ prompt_input = gr.Textbox(
67
+ label="",
68
+ placeholder="Ваш промпт...",
69
+ lines=10
70
+ )
71
+ negative_input = gr.Textbox(
72
+ label="",
73
+ placeholder="Ваш негативный промпт...",
74
+ lines=5
75
+ )
76
+ generate_btn = gr.Button("Создать", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ with gr.Column(scale=2):
79
+ output = gr.Image(label="", type="pil", height=600)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ generate_btn.click(
82
+ fn=generate_image,
83
+ inputs=[prompt_input, negative_input],
84
+ outputs=output
85
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ # Шаг 6. Запуск
88
+ demo.launch(share=True, debug=False)