ArthurTBorges commited on
Commit
7856a14
·
verified ·
1 Parent(s): 19df22d

Crie o arquivo app.py

Browse files
Files changed (1) hide show
  1. app.py +661 -0
app.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import tensorflow as tf
3
+ from tensorflow.keras import datasets, layers, models
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ from PIL import Image
7
+ import time
8
+
9
+ # Configuração da página
10
+ st.set_page_config(
11
+ page_title="Classificador de Imagens CIFAR-10",
12
+ page_icon="🖼️",
13
+ layout="wide"
14
+ )
15
+
16
+ # Título principal
17
+ st.title("🖼️ Classificador de Imagens com Deep Learning")
18
+ st.markdown("---")
19
+
20
+ # Sidebar com informações
21
+ st.sidebar.title("📊 Informações do Projeto")
22
+ st.sidebar.info("""
23
+ **Problema de Negócio:**
24
+
25
+ Construir um modelo de Inteligência Artificial capaz de classificar imagens
26
+ considerando 10 categorias:
27
+ - ✈️ Airplane
28
+ - 🚗 Automobile
29
+ - 🐦 Bird
30
+ - 🐱 Cat
31
+ - 🦌 Deer
32
+ - 🐕 Dog
33
+ - 🐸 Frog
34
+ - 🐴 Horse
35
+ - 🚢 Ship
36
+ - 🚛 Truck
37
+ """)
38
+
39
+ st.sidebar.markdown("---")
40
+ st.sidebar.markdown("### 🔧 Tecnologias Utilizadas")
41
+ st.sidebar.markdown("""
42
+ - Python
43
+ - TensorFlow/Keras
44
+ - Streamlit
45
+ - NumPy
46
+ - Matplotlib
47
+ - PIL
48
+ """)
49
+
50
+ # Classes das imagens
51
+ nomes_classes = ['airplane', 'automobile', 'bird', 'cat', 'deer',
52
+ 'dog', 'frog', 'horse', 'ship', 'truck']
53
+
54
+ # Emojis para cada classe
55
+ emojis_classes = {
56
+ 'airplane': '✈️',
57
+ 'automobile': '🚗',
58
+ 'bird': '🐦',
59
+ 'cat': '🐱',
60
+ 'deer': '🦌',
61
+ 'dog': '🐕',
62
+ 'frog': '🐸',
63
+ 'horse': '🐴',
64
+ 'ship': '🚢',
65
+ 'truck': '🚛'
66
+ }
67
+
68
+ # Função para carregar dados
69
+ @st.cache_data
70
+ def carregar_dados():
71
+ """Carrega e pré-processa o dataset CIFAR-10"""
72
+ (imagens_treino, labels_treino), (imagens_teste, labels_teste) = datasets.cifar10.load_data()
73
+
74
+ # Normaliza os valores dos pixels
75
+ imagens_treino = imagens_treino / 255.0
76
+ imagens_teste = imagens_teste / 255.0
77
+
78
+ return (imagens_treino, labels_treino), (imagens_teste, labels_teste)
79
+
80
+ # Função para visualizar imagens
81
+ def visualiza_imagens(images, labels, num_imagens=25):
82
+ """Cria uma figura com múltiplas imagens do dataset"""
83
+ fig, axes = plt.subplots(5, 5, figsize=(10, 10))
84
+ fig.suptitle('Exemplos do Dataset CIFAR-10', fontsize=16)
85
+
86
+ for i, ax in enumerate(axes.flat):
87
+ if i < num_imagens:
88
+ ax.imshow(images[i])
89
+ ax.set_title(nomes_classes[labels[i][0]], fontsize=8)
90
+ ax.axis('off')
91
+
92
+ plt.tight_layout()
93
+ return fig
94
+
95
+ # Função para criar o modelo
96
+ @st.cache_resource
97
+ def criar_modelo():
98
+ """Cria a arquitetura da rede neural convolucional"""
99
+ modelo = models.Sequential([
100
+ # Primeiro bloco convolucional
101
+ layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
102
+ layers.MaxPooling2D((2, 2)),
103
+
104
+ # Segundo bloco convolucional
105
+ layers.Conv2D(128, (3, 3), activation='relu'),
106
+ layers.MaxPooling2D((2, 2)),
107
+
108
+ # Terceiro bloco convolucional
109
+ layers.Conv2D(64, (3, 3), activation='relu'),
110
+ layers.MaxPooling2D((2, 2)),
111
+
112
+ # Camadas de classificação
113
+ layers.Flatten(),
114
+ layers.Dense(64, activation='relu'),
115
+ layers.Dense(10, activation='softmax')
116
+ ])
117
+
118
+ modelo.compile(
119
+ optimizer='adam',
120
+ loss='sparse_categorical_crossentropy',
121
+ metrics=['accuracy']
122
+ )
123
+
124
+ return modelo
125
+
126
+ # Função para treinar o modelo
127
+ def treinar_modelo(modelo, imagens_treino, labels_treino, imagens_teste, labels_teste, epochs=10):
128
+ """Treina o modelo com os dados fornecidos"""
129
+ history = modelo.fit(
130
+ imagens_treino,
131
+ labels_treino,
132
+ epochs=epochs,
133
+ validation_data=(imagens_teste, labels_teste),
134
+ verbose=0
135
+ )
136
+ return history
137
+
138
+ # Função para processar imagem do usuário
139
+ def processar_imagem_usuario(imagem_file):
140
+ """Processa a imagem enviada pelo usuário"""
141
+ imagem = Image.open(imagem_file)
142
+
143
+ # Redimensiona para 32x32
144
+ imagem_redimensionada = imagem.resize((32, 32))
145
+
146
+ # Converte para array e normaliza
147
+ imagem_array = np.array(imagem_redimensionada) / 255.0
148
+
149
+ # Expande dimensão
150
+ imagem_array = np.expand_dims(imagem_array, axis=0)
151
+
152
+ return imagem_redimensionada, imagem_array
153
+
154
+ # Função para criar gráficos de treinamento
155
+ def plotar_historico(history):
156
+ """Cria gráficos de acurácia e perda durante o treinamento"""
157
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
158
+
159
+ # Gráfico de acurácia
160
+ ax1.plot(history.history['accuracy'], label='Treino', marker='o')
161
+ ax1.plot(history.history['val_accuracy'], label='Validação', marker='s')
162
+ ax1.set_title('Acurácia do Modelo')
163
+ ax1.set_xlabel('Época')
164
+ ax1.set_ylabel('Acurácia')
165
+ ax1.legend()
166
+ ax1.grid(True)
167
+
168
+ # Gráfico de perda
169
+ ax2.plot(history.history['loss'], label='Treino', marker='o')
170
+ ax2.plot(history.history['val_loss'], label='Validação', marker='s')
171
+ ax2.set_title('Perda do Modelo')
172
+ ax2.set_xlabel('Época')
173
+ ax2.set_ylabel('Perda')
174
+ ax2.legend()
175
+ ax2.grid(True)
176
+
177
+ plt.tight_layout()
178
+ return fig
179
+
180
+ # Abas principais
181
+ tab1, tab2, tab3, tab4 = st.tabs([
182
+ "📚 Dados",
183
+ "🏗️ Modelo",
184
+ "📈 Treinamento",
185
+ "🎯 Predição"
186
+ ])
187
+
188
+ # ==================== ABA 1: DADOS ====================
189
+ with tab1:
190
+ st.header("📚 Exploração dos Dados")
191
+
192
+ st.markdown("""
193
+ ### Dataset CIFAR-10
194
+
195
+ O **CIFAR-10** é um dataset clássico de visão computacional que contém 60.000 imagens
196
+ coloridas de 32x32 pixels em 10 classes diferentes, com 6.000 imagens por classe.
197
+
198
+ - **50.000 imagens de treino**
199
+ - **10.000 imagens de teste**
200
+ - **10 classes balanceadas**
201
+ """)
202
+
203
+ if st.button("🔄 Carregar Dataset CIFAR-10", key="carregar_dados"):
204
+ with st.spinner("Carregando dados..."):
205
+ (imagens_treino, labels_treino), (imagens_teste, labels_teste) = carregar_dados()
206
+ st.session_state.dados_carregados = True
207
+ st.session_state.imagens_treino = imagens_treino
208
+ st.session_state.labels_treino = labels_treino
209
+ st.session_state.imagens_teste = imagens_teste
210
+ st.session_state.labels_teste = labels_teste
211
+
212
+ st.success("✅ Dados carregados com sucesso!")
213
+
214
+ # Mostrar estatísticas
215
+ col1, col2, col3 = st.columns(3)
216
+
217
+ with col1:
218
+ st.metric("Imagens de Treino", f"{len(imagens_treino):,}")
219
+ with col2:
220
+ st.metric("Imagens de Teste", f"{len(imagens_teste):,}")
221
+ with col3:
222
+ st.metric("Classes", len(nomes_classes))
223
+
224
+ # Visualização das imagens
225
+ if st.session_state.get('dados_carregados', False):
226
+ st.markdown("---")
227
+ st.subheader("🖼️ Exemplos de Imagens do Dataset")
228
+
229
+ fig = visualiza_imagens(
230
+ st.session_state.imagens_treino,
231
+ st.session_state.labels_treino
232
+ )
233
+ st.pyplot(fig)
234
+
235
+ # Informações detalhadas
236
+ with st.expander("📊 Informações Detalhadas dos Dados"):
237
+ col1, col2 = st.columns(2)
238
+
239
+ with col1:
240
+ st.markdown("**Dados de Treino:**")
241
+ st.write(f"- Shape: {st.session_state.imagens_treino.shape}")
242
+ st.write(f"- Tipo: {st.session_state.imagens_treino.dtype}")
243
+ st.write(f"- Valores min/max: {st.session_state.imagens_treino.min():.2f} / {st.session_state.imagens_treino.max():.2f}")
244
+
245
+ with col2:
246
+ st.markdown("**Dados de Teste:**")
247
+ st.write(f"- Shape: {st.session_state.imagens_teste.shape}")
248
+ st.write(f"- Tipo: {st.session_state.imagens_teste.dtype}")
249
+ st.write(f"- Valores min/max: {st.session_state.imagens_teste.min():.2f} / {st.session_state.imagens_teste.max():.2f}")
250
+
251
+ # ==================== ABA 2: MODELO ====================
252
+ with tab2:
253
+ st.header("🏗️ Arquitetura do Modelo")
254
+
255
+ st.markdown("""
256
+ ### Rede Neural Convolucional (CNN)
257
+
258
+ O modelo utiliza uma arquitetura de **Rede Neural Convolucional**,
259
+ ideal para processamento de imagens. A arquitetura consiste em:
260
+
261
+ 1. **Camadas Convolucionais**: Extraem características das imagens
262
+ 2. **Camadas de Pooling**: Reduzem a dimensionalidade
263
+ 3. **Camadas Densas**: Realizam a classificação final
264
+ """)
265
+
266
+ if st.button("🔨 Criar Modelo", key="criar_modelo"):
267
+ with st.spinner("Criando modelo..."):
268
+ modelo = criar_modelo()
269
+ st.session_state.modelo = modelo
270
+ st.session_state.modelo_criado = True
271
+
272
+ st.success("✅ Modelo criado com sucesso!")
273
+
274
+ if st.session_state.get('modelo_criado', False):
275
+ st.markdown("---")
276
+ st.subheader("📋 Sumário da Arquitetura")
277
+
278
+ # Captura o sumário do modelo
279
+ from io import StringIO
280
+ import sys
281
+
282
+ buffer = StringIO()
283
+ st.session_state.modelo.summary(print_fn=lambda x: buffer.write(x + '\n'))
284
+ summary_string = buffer.getvalue()
285
+
286
+ st.code(summary_string, language='text')
287
+
288
+ # Visualização da arquitetura
289
+ with st.expander("🔍 Detalhes das Camadas"):
290
+ st.markdown("""
291
+ **Camada 1 - Conv2D (32 filtros, 3x3):**
292
+ - Primeira camada convolucional
293
+ - Extrai 32 características diferentes
294
+ - Ativação ReLU
295
+
296
+ **Camada 2 - MaxPooling2D (2x2):**
297
+ - Reduz dimensionalidade pela metade
298
+ - Mantém características mais importantes
299
+
300
+ **Camada 3 - Conv2D (128 filtros, 3x3):**
301
+ - Segunda camada convolucional
302
+ - Extrai características mais complexas
303
+
304
+ **Camada 4 - MaxPooling2D (2x2):**
305
+ - Segunda redução de dimensionalidade
306
+
307
+ **Camada 5 - Conv2D (64 filtros, 3x3):**
308
+ - Terceira camada convolucional
309
+ - Refinamento de características
310
+
311
+ **Camada 6 - MaxPooling2D (2x2):**
312
+ - Terceira redução de dimensionalidade
313
+
314
+ **Camada 7 - Flatten:**
315
+ - Converte matriz em vetor
316
+
317
+ **Camada 8 - Dense (64 neurônios):**
318
+ - Camada totalmente conectada
319
+ - Ativação ReLU
320
+
321
+ **Camada 9 - Dense (10 neurônios):**
322
+ - Camada de saída
323
+ - Ativação Softmax (probabilidades)
324
+ - Uma saída para cada classe
325
+ """)
326
+
327
+ # Informações sobre parâmetros
328
+ trainable_params = np.sum([np.prod(v.shape) for v in st.session_state.modelo.trainable_weights])
329
+
330
+ col1, col2, col3 = st.columns(3)
331
+ with col1:
332
+ st.metric("Parâmetros Treináveis", f"{trainable_params:,}")
333
+ with col2:
334
+ st.metric("Camadas", len(st.session_state.modelo.layers))
335
+ with col3:
336
+ st.metric("Tamanho Input", "32x32x3")
337
+
338
+ # ==================== ABA 3: TREINAMENTO ====================
339
+ with tab3:
340
+ st.header("📈 Treinamento do Modelo")
341
+
342
+ st.markdown("""
343
+ ### Processo de Treinamento
344
+
345
+ Durante o treinamento, o modelo aprende a identificar padrões nas imagens
346
+ através de múltiplas iterações (épocas) sobre os dados de treino.
347
+
348
+ **Métricas monitoradas:**
349
+ - **Acurácia**: Percentual de predições corretas
350
+ - **Perda (Loss)**: Medida de erro do modelo
351
+ """)
352
+
353
+ # Verificar pré-requisitos
354
+ if not st.session_state.get('dados_carregados', False):
355
+ st.warning("⚠️ Por favor, carregue os dados primeiro na aba 'Dados'")
356
+ elif not st.session_state.get('modelo_criado', False):
357
+ st.warning("⚠️ Por favor, crie o modelo primeiro na aba 'Modelo'")
358
+ else:
359
+ st.markdown("---")
360
+
361
+ col1, col2 = st.columns(2)
362
+
363
+ with col1:
364
+ epochs = st.slider(
365
+ "Número de Épocas",
366
+ min_value=1,
367
+ max_value=20,
368
+ value=10,
369
+ help="Número de vezes que o modelo verá todos os dados de treino"
370
+ )
371
+
372
+ with col2:
373
+ st.markdown("### ⚙️ Configurações")
374
+ st.write(f"- **Otimizador**: Adam")
375
+ st.write(f"- **Função de Perda**: Sparse Categorical Crossentropy")
376
+ st.write(f"- **Métrica**: Accuracy")
377
+
378
+ if st.button("🚀 Iniciar Treinamento", key="treinar"):
379
+
380
+ progress_bar = st.progress(0)
381
+ status_text = st.empty()
382
+
383
+ # Container para métricas em tempo real
384
+ metrics_container = st.empty()
385
+
386
+ start_time = time.time()
387
+
388
+ # Treinar o modelo
389
+ with st.spinner("Treinando modelo..."):
390
+ history = treinar_modelo(
391
+ st.session_state.modelo,
392
+ st.session_state.imagens_treino,
393
+ st.session_state.labels_treino,
394
+ st.session_state.imagens_teste,
395
+ st.session_state.labels_teste,
396
+ epochs=epochs
397
+ )
398
+
399
+ st.session_state.history = history
400
+ st.session_state.modelo_treinado = True
401
+
402
+ end_time = time.time()
403
+ training_time = end_time - start_time
404
+
405
+ progress_bar.progress(100)
406
+ status_text.success(f"✅ Treinamento concluído em {training_time:.2f} segundos!")
407
+
408
+ # Mostrar métricas finais
409
+ final_train_acc = history.history['accuracy'][-1]
410
+ final_val_acc = history.history['val_accuracy'][-1]
411
+ final_train_loss = history.history['loss'][-1]
412
+ final_val_loss = history.history['val_loss'][-1]
413
+
414
+ col1, col2, col3, col4 = st.columns(4)
415
+
416
+ with col1:
417
+ st.metric("Acurácia Treino", f"{final_train_acc:.2%}")
418
+ with col2:
419
+ st.metric("Acurácia Validação", f"{final_val_acc:.2%}")
420
+ with col3:
421
+ st.metric("Perda Treino", f"{final_train_loss:.4f}")
422
+ with col4:
423
+ st.metric("Perda Validação", f"{final_val_loss:.4f}")
424
+
425
+ # Mostrar gráficos se já treinado
426
+ if st.session_state.get('modelo_treinado', False):
427
+ st.markdown("---")
428
+ st.subheader("📊 Evolução do Treinamento")
429
+
430
+ fig = plotar_historico(st.session_state.history)
431
+ st.pyplot(fig)
432
+
433
+ with st.expander("💡 Interpretação dos Gráficos"):
434
+ st.markdown("""
435
+ **Gráfico de Acurácia:**
436
+ - Mostra como a precisão do modelo melhora ao longo das épocas
437
+ - Idealmente, ambas as curvas devem crescer juntas
438
+ - Se a curva de validação estagna ou cai, pode indicar overfitting
439
+
440
+ **Gráfico de Perda:**
441
+ - Mostra como o erro do modelo diminui ao longo das épocas
442
+ - Valores menores indicam melhor desempenho
443
+ - A diferença entre treino e validação indica generalização
444
+ """)
445
+
446
+ # Avaliação final
447
+ st.markdown("---")
448
+ st.subheader("🎯 Avaliação Final")
449
+
450
+ if st.button("📊 Avaliar Modelo no Conjunto de Teste"):
451
+ with st.spinner("Avaliando modelo..."):
452
+ erro_teste, acc_teste = st.session_state.modelo.evaluate(
453
+ st.session_state.imagens_teste,
454
+ st.session_state.labels_teste,
455
+ verbose=0
456
+ )
457
+
458
+ col1, col2 = st.columns(2)
459
+
460
+ with col1:
461
+ st.metric(
462
+ "Acurácia no Teste",
463
+ f"{acc_teste:.2%}",
464
+ help="Percentual de imagens classificadas corretamente"
465
+ )
466
+
467
+ with col2:
468
+ st.metric(
469
+ "Erro no Teste",
470
+ f"{erro_teste:.4f}",
471
+ help="Medida de erro do modelo"
472
+ )
473
+
474
+ # Interpretação do resultado
475
+ if acc_teste >= 0.70:
476
+ st.success(f"✅ Excelente! O modelo alcançou {acc_teste:.1%} de acurácia!")
477
+ elif acc_teste >= 0.60:
478
+ st.info(f"ℹ️ Bom resultado! O modelo alcançou {acc_teste:.1%} de acurácia.")
479
+ else:
480
+ st.warning(f"⚠️ O modelo pode melhorar. Acurácia atual: {acc_teste:.1%}")
481
+
482
+ # ==================== ABA 4: PREDIÇÃO ====================
483
+ with tab4:
484
+ st.header("🎯 Fazer Predições")
485
+
486
+ st.markdown("""
487
+ ### Classificação de Novas Imagens
488
+
489
+ Envie uma imagem para que o modelo treinado faça a classificação!
490
+
491
+ **Dicas para melhores resultados:**
492
+ - Use imagens claras das categorias suportadas
493
+ - Evite imagens muito diferentes do dataset de treino
494
+ - Quanto mais simples a imagem, melhor
495
+ """)
496
+
497
+ if not st.session_state.get('modelo_treinado', False):
498
+ st.warning("⚠️ Por favor, treine o modelo primeiro na aba 'Treinamento'")
499
+ else:
500
+ st.markdown("---")
501
+
502
+ # Upload de imagem
503
+ uploaded_file = st.file_uploader(
504
+ "📤 Escolha uma imagem",
505
+ type=['png', 'jpg', 'jpeg'],
506
+ help="Formatos aceitos: PNG, JPG, JPEG"
507
+ )
508
+
509
+ if uploaded_file is not None:
510
+ col1, col2 = st.columns(2)
511
+
512
+ with col1:
513
+ st.subheader("🖼️ Imagem Original")
514
+ imagem_original = Image.open(uploaded_file)
515
+ st.image(imagem_original, use_container_width=True)
516
+
517
+ # Informações da imagem
518
+ st.markdown(f"""
519
+ **Informações:**
520
+ - Tamanho: {imagem_original.size[0]} x {imagem_original.size[1]} pixels
521
+ - Formato: {imagem_original.format}
522
+ - Modo: {imagem_original.mode}
523
+ """)
524
+
525
+ with col2:
526
+ st.subheader("🔍 Imagem Processada (32x32)")
527
+
528
+ # Processar imagem
529
+ imagem_processada, imagem_array = processar_imagem_usuario(uploaded_file)
530
+ st.image(imagem_processada, use_container_width=True)
531
+
532
+ st.info("A imagem foi redimensionada para 32x32 pixels para corresponder ao formato de entrada do modelo.")
533
+
534
+ # Botão de predição
535
+ st.markdown("---")
536
+
537
+ if st.button("🎲 Classificar Imagem", key="classificar", use_container_width=True):
538
+ with st.spinner("Analisando imagem..."):
539
+ # Fazer predição
540
+ previsoes = st.session_state.modelo.predict(imagem_array, verbose=0)
541
+ classe_prevista = np.argmax(previsoes[0])
542
+ confianca = previsoes[0][classe_prevista]
543
+ nome_classe = nomes_classes[classe_prevista]
544
+ emoji_classe = emojis_classes[nome_classe]
545
+
546
+ # Resultado principal
547
+ st.markdown("---")
548
+ st.subheader("🎉 Resultado da Classificação")
549
+
550
+ # Card de resultado
551
+ st.markdown(f"""
552
+ <div style="
553
+ padding: 30px;
554
+ border-radius: 10px;
555
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
556
+ color: white;
557
+ text-align: center;
558
+ margin: 20px 0;
559
+ ">
560
+ <h1 style="font-size: 4em; margin: 0;">{emoji_classe}</h1>
561
+ <h2 style="margin: 10px 0;">{nome_classe.upper()}</h2>
562
+ <p style="font-size: 1.5em; margin: 0;">Confiança: {confianca:.1%}</p>
563
+ </div>
564
+ """, unsafe_allow_html=True)
565
+
566
+ # Barra de progresso da confiança
567
+ st.progress(float(confianca))
568
+
569
+ # Mostrar todas as probabilidades
570
+ st.markdown("---")
571
+ st.subheader("📊 Probabilidades para Todas as Classes")
572
+
573
+ # Criar DataFrame com resultados
574
+ import pandas as pd
575
+
576
+ resultados = pd.DataFrame({
577
+ 'Classe': [f"{emojis_classes[nome]} {nome}" for nome in nomes_classes],
578
+ 'Probabilidade': previsoes[0],
579
+ 'Confiança (%)': previsoes[0] * 100
580
+ })
581
+ resultados = resultados.sort_values('Probabilidade', ascending=False)
582
+
583
+ # Gráfico de barras
584
+ fig, ax = plt.subplots(figsize=(10, 6))
585
+ colors = ['#667eea' if i == classe_prevista else '#cccccc'
586
+ for i in range(len(nomes_classes))]
587
+
588
+ bars = ax.barh(
589
+ resultados['Classe'],
590
+ resultados['Probabilidade'],
591
+ color=[colors[nomes_classes.index(nome.split()[-1])] for nome in resultados['Classe']]
592
+ )
593
+
594
+ ax.set_xlabel('Probabilidade', fontsize=12)
595
+ ax.set_title('Distribuição de Probabilidades', fontsize=14, fontweight='bold')
596
+ ax.set_xlim(0, 1)
597
+
598
+ # Adicionar valores nas barras
599
+ for bar in bars:
600
+ width = bar.get_width()
601
+ ax.text(width, bar.get_y() + bar.get_height()/2,
602
+ f'{width:.1%}',
603
+ ha='left', va='center', fontsize=9,
604
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
605
+
606
+ plt.tight_layout()
607
+ st.pyplot(fig)
608
+
609
+ # Tabela detalhada
610
+ with st.expander("📋 Ver Tabela Detalhada"):
611
+ st.dataframe(
612
+ resultados.style.format({
613
+ 'Probabilidade': '{:.4f}',
614
+ 'Confiança (%)': '{:.2f}%'
615
+ }).background_gradient(subset=['Probabilidade'], cmap='Blues'),
616
+ use_container_width=True
617
+ )
618
+
619
+ # Interpretação
620
+ with st.expander("💡 Como Interpretar o Resultado"):
621
+ st.markdown(f"""
622
+ ### Análise da Predição:
623
+
624
+ - **Classe Prevista**: {emoji_classe} **{nome_classe.upper()}**
625
+ - **Confiança**: {confianca:.1%}
626
+
627
+ #### O que significa a confiança?
628
+
629
+ A confiança indica o quão "certo" o modelo está sobre sua predição:
630
+
631
+ - **> 90%**: 🟢 Muito confiante - O modelo está muito seguro da classificação
632
+ - **70-90%**: 🟡 Confiante - Boa certeza, mas com alguma margem de dúvida
633
+ - **50-70%**: 🟠 Moderado - O modelo tem dúvidas significativas
634
+ - **< 50%**: 🔴 Baixa confiança - O modelo está muito incerto
635
+
636
+ #### Por que o modelo pode errar?
637
+
638
+ - Imagem muito diferente das do dataset de treino
639
+ - Objeto muito pequeno ou distante
640
+ - Ângulo ou iluminação incomum
641
+ - Múltiplos objetos na imagem
642
+ - Qualidade da imagem (blur, pixelização)
643
+ """)
644
+
645
+ # Footer
646
+ st.markdown("---")
647
+ st.markdown("""
648
+ <div style="text-align: center; color: #666; padding: 20px;">
649
+ <p><strong>🎓 Projeto Educacional - Deep Learning com TensorFlow</strong></p>
650
+ <p>Desenvolvido para fins de aprendizado e demonstração</p>
651
+ <p style="font-size: 0.8em;">Dataset: CIFAR-10 | Framework: TensorFlow/Keras | Interface: Streamlit</p>
652
+ </div>
653
+ """, unsafe_allow_html=True)
654
+
655
+ # Inicializar session state
656
+ if 'dados_carregados' not in st.session_state:
657
+ st.session_state.dados_carregados = False
658
+ if 'modelo_criado' not in st.session_state:
659
+ st.session_state.modelo_criado = False
660
+ if 'modelo_treinado' not in st.session_state:
661
+ st.session_state.modelo_treinado = False