ArthurTBorges commited on
Commit
b561380
·
verified ·
1 Parent(s): ab9c559

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +292 -261
app.py CHANGED
@@ -26,7 +26,7 @@ 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
@@ -55,7 +55,7 @@ nomes_classes = ['airplane', 'automobile', 'bird', 'cat', 'deer',
55
  emojis_classes = {
56
  'airplane': '✈️',
57
  'automobile': '🚗',
58
- 'bird': '🐦',
59
  'cat': '🐱',
60
  'deer': '🦌',
61
  'dog': '🐕',
@@ -65,17 +65,29 @@ emojis_classes = {
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):
@@ -84,72 +96,89 @@ def visualiza_imagens(images, labels, num_imagens=25):
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):
@@ -163,7 +192,7 @@ def plotar_historico(history):
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')
@@ -172,9 +201,10 @@ def plotar_historico(history):
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
@@ -202,24 +232,27 @@ with tab1:
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):
@@ -231,6 +264,7 @@ with tab1:
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"):
@@ -266,10 +300,11 @@ with tab2:
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("---")
@@ -277,7 +312,6 @@ with tab2:
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'))
@@ -297,7 +331,7 @@ with tab2:
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
 
@@ -308,17 +342,14 @@ with tab2:
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
@@ -364,8 +395,8 @@ with tab3:
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
 
@@ -380,9 +411,6 @@ with tab3:
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
@@ -396,31 +424,32 @@ with tab3:
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):
@@ -429,6 +458,7 @@ with tab3:
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("""
@@ -449,35 +479,38 @@ with tab3:
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:
@@ -512,7 +545,7 @@ with tab4:
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"""
@@ -527,120 +560,126 @@ with tab4:
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("---")
@@ -650,12 +689,4 @@ st.markdown("""
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
 
26
  considerando 10 categorias:
27
  - ✈️ Airplane
28
  - 🚗 Automobile
29
+ - 🦅 Bird
30
  - 🐱 Cat
31
  - 🦌 Deer
32
  - 🐕 Dog
 
55
  emojis_classes = {
56
  'airplane': '✈️',
57
  'automobile': '🚗',
58
+ 'bird': '🦅',
59
  'cat': '🐱',
60
  'deer': '🦌',
61
  'dog': '🐕',
 
65
  'truck': '🚛'
66
  }
67
 
68
+ # Inicializar session state no início
69
+ if 'dados_carregados' not in st.session_state:
70
+ st.session_state.dados_carregados = False
71
+ if 'modelo_criado' not in st.session_state:
72
+ st.session_state.modelo_criado = False
73
+ if 'modelo_treinado' not in st.session_state:
74
+ st.session_state.modelo_treinado = False
75
+
76
  # Função para carregar dados
77
  @st.cache_data
78
  def carregar_dados():
79
  """Carrega e pré-processa o dataset CIFAR-10"""
80
+ try:
81
+ (imagens_treino, labels_treino), (imagens_teste, labels_teste) = datasets.cifar10.load_data()
82
+
83
+ # Normaliza os valores dos pixels
84
+ imagens_treino = imagens_treino / 255.0
85
+ imagens_teste = imagens_teste / 255.0
86
+
87
+ return (imagens_treino, labels_treino), (imagens_teste, labels_teste)
88
+ except Exception as e:
89
+ st.error(f"Erro ao carregar dados: {str(e)}")
90
+ return None
91
 
92
  # Função para visualizar imagens
93
  def visualiza_imagens(images, labels, num_imagens=25):
 
96
  fig.suptitle('Exemplos do Dataset CIFAR-10', fontsize=16)
97
 
98
  for i, ax in enumerate(axes.flat):
99
+ if i < num_imagens and i < len(images):
100
  ax.imshow(images[i])
101
  ax.set_title(nomes_classes[labels[i][0]], fontsize=8)
102
  ax.axis('off')
103
 
104
  plt.tight_layout()
105
+ plt.close('all') # Prevenir memory leak
106
  return fig
107
 
108
  # Função para criar o modelo
109
  @st.cache_resource
110
  def criar_modelo():
111
  """Cria a arquitetura da rede neural convolucional"""
112
+ try:
113
+ modelo = models.Sequential([
114
+ # Primeiro bloco convolucional
115
+ layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
116
+ layers.MaxPooling2D((2, 2)),
117
+
118
+ # Segundo bloco convolucional
119
+ layers.Conv2D(64, (3, 3), activation='relu'),
120
+ layers.MaxPooling2D((2, 2)),
121
+
122
+ # Terceiro bloco convolucional
123
+ layers.Conv2D(64, (3, 3), activation='relu'),
124
+
125
+ # Camadas de classificação
126
+ layers.Flatten(),
127
+ layers.Dense(64, activation='relu'),
128
+ layers.Dense(10, activation='softmax')
129
+ ])
130
 
131
+ modelo.compile(
132
+ optimizer='adam',
133
+ loss='sparse_categorical_crossentropy',
134
+ metrics=['accuracy']
135
+ )
136
 
137
+ return modelo
138
+ except Exception as e:
139
+ st.error(f"Erro ao criar modelo: {str(e)}")
140
+ return None
 
 
 
 
 
 
 
 
 
141
 
142
  # Função para treinar o modelo
143
  def treinar_modelo(modelo, imagens_treino, labels_treino, imagens_teste, labels_teste, epochs=10):
144
  """Treina o modelo com os dados fornecidos"""
145
+ try:
146
+ history = modelo.fit(
147
+ imagens_treino,
148
+ labels_treino,
149
+ epochs=epochs,
150
+ validation_data=(imagens_teste, labels_teste),
151
+ verbose=0,
152
+ batch_size=64
153
+ )
154
+ return history
155
+ except Exception as e:
156
+ st.error(f"Erro durante o treinamento: {str(e)}")
157
+ return None
158
 
159
  # Função para processar imagem do usuário
160
  def processar_imagem_usuario(imagem_file):
161
  """Processa a imagem enviada pelo usuário"""
162
+ try:
163
+ imagem = Image.open(imagem_file)
164
+
165
+ # Converte para RGB se necessário
166
+ if imagem.mode != 'RGB':
167
+ imagem = imagem.convert('RGB')
168
+
169
+ # Redimensiona para 32x32
170
+ imagem_redimensionada = imagem.resize((32, 32))
171
+
172
+ # Converte para array e normaliza
173
+ imagem_array = np.array(imagem_redimensionada) / 255.0
174
+
175
+ # Expande dimensão
176
+ imagem_array = np.expand_dims(imagem_array, axis=0)
177
+
178
+ return imagem_redimensionada, imagem_array
179
+ except Exception as e:
180
+ st.error(f"Erro ao processar imagem: {str(e)}")
181
+ return None, None
182
 
183
  # Função para criar gráficos de treinamento
184
  def plotar_historico(history):
 
192
  ax1.set_xlabel('Época')
193
  ax1.set_ylabel('Acurácia')
194
  ax1.legend()
195
+ ax1.grid(True, alpha=0.3)
196
 
197
  # Gráfico de perda
198
  ax2.plot(history.history['loss'], label='Treino', marker='o')
 
201
  ax2.set_xlabel('Época')
202
  ax2.set_ylabel('Perda')
203
  ax2.legend()
204
+ ax2.grid(True, alpha=0.3)
205
 
206
  plt.tight_layout()
207
+ plt.close('all') # Prevenir memory leak
208
  return fig
209
 
210
  # Abas principais
 
232
 
233
  if st.button("🔄 Carregar Dataset CIFAR-10", key="carregar_dados"):
234
  with st.spinner("Carregando dados..."):
235
+ resultado = carregar_dados()
 
 
 
 
 
236
 
237
+ if resultado is not None:
238
+ (imagens_treino, labels_treino), (imagens_teste, labels_teste) = resultado
239
+ st.session_state.dados_carregados = True
240
+ st.session_state.imagens_treino = imagens_treino
241
+ st.session_state.labels_treino = labels_treino
242
+ st.session_state.imagens_teste = imagens_teste
243
+ st.session_state.labels_teste = labels_teste
244
+
245
+ st.success(" Dados carregados com sucesso!")
246
+
247
+ # Mostrar estatísticas
248
+ col1, col2, col3 = st.columns(3)
249
+
250
+ with col1:
251
+ st.metric("Imagens de Treino", f"{len(imagens_treino):,}")
252
+ with col2:
253
+ st.metric("Imagens de Teste", f"{len(imagens_teste):,}")
254
+ with col3:
255
+ st.metric("Classes", len(nomes_classes))
256
 
257
  # Visualização das imagens
258
  if st.session_state.get('dados_carregados', False):
 
264
  st.session_state.labels_treino
265
  )
266
  st.pyplot(fig)
267
+ plt.clf()
268
 
269
  # Informações detalhadas
270
  with st.expander("📊 Informações Detalhadas dos Dados"):
 
300
  if st.button("🔨 Criar Modelo", key="criar_modelo"):
301
  with st.spinner("Criando modelo..."):
302
  modelo = criar_modelo()
 
 
303
 
304
+ if modelo is not None:
305
+ st.session_state.modelo = modelo
306
+ st.session_state.modelo_criado = True
307
+ st.success("✅ Modelo criado com sucesso!")
308
 
309
  if st.session_state.get('modelo_criado', False):
310
  st.markdown("---")
 
312
 
313
  # Captura o sumário do modelo
314
  from io import StringIO
 
315
 
316
  buffer = StringIO()
317
  st.session_state.modelo.summary(print_fn=lambda x: buffer.write(x + '\n'))
 
331
  - Reduz dimensionalidade pela metade
332
  - Mantém características mais importantes
333
 
334
+ **Camada 3 - Conv2D (64 filtros, 3x3):**
335
  - Segunda camada convolucional
336
  - Extrai características mais complexas
337
 
 
342
  - Terceira camada convolucional
343
  - Refinamento de características
344
 
345
+ **Camada 6 - Flatten:**
 
 
 
346
  - Converte matriz em vetor
347
 
348
+ **Camada 7 - Dense (64 neurônios):**
349
  - Camada totalmente conectada
350
  - Ativação ReLU
351
 
352
+ **Camada 8 - Dense (10 neurônios):**
353
  - Camada de saída
354
  - Ativação Softmax (probabilidades)
355
  - Uma saída para cada classe
 
395
  epochs = st.slider(
396
  "Número de Épocas",
397
  min_value=1,
398
+ max_value=15,
399
+ value=5,
400
  help="Número de vezes que o modelo verá todos os dados de treino"
401
  )
402
 
 
411
  progress_bar = st.progress(0)
412
  status_text = st.empty()
413
 
 
 
 
414
  start_time = time.time()
415
 
416
  # Treinar o modelo
 
424
  epochs=epochs
425
  )
426
 
427
+ if history is not None:
428
+ st.session_state.history = history
429
+ st.session_state.modelo_treinado = True
430
+
431
+ end_time = time.time()
432
+ training_time = end_time - start_time
433
+
434
+ progress_bar.progress(100)
435
+ status_text.success(f"✅ Treinamento concluído em {training_time:.2f} segundos!")
436
+
437
+ # Mostrar métricas finais
438
+ final_train_acc = history.history['accuracy'][-1]
439
+ final_val_acc = history.history['val_accuracy'][-1]
440
+ final_train_loss = history.history['loss'][-1]
441
+ final_val_loss = history.history['val_loss'][-1]
442
+
443
+ col1, col2, col3, col4 = st.columns(4)
444
+
445
+ with col1:
446
+ st.metric("Acurácia Treino", f"{final_train_acc:.2%}")
447
+ with col2:
448
+ st.metric("Acurácia Validação", f"{final_val_acc:.2%}")
449
+ with col3:
450
+ st.metric("Perda Treino", f"{final_train_loss:.4f}")
451
+ with col4:
452
+ st.metric("Perda Validação", f"{final_val_loss:.4f}")
453
 
454
  # Mostrar gráficos se já treinado
455
  if st.session_state.get('modelo_treinado', False):
 
458
 
459
  fig = plotar_historico(st.session_state.history)
460
  st.pyplot(fig)
461
+ plt.clf()
462
 
463
  with st.expander("💡 Interpretação dos Gráficos"):
464
  st.markdown("""
 
479
 
480
  if st.button("📊 Avaliar Modelo no Conjunto de Teste"):
481
  with st.spinner("Avaliando modelo..."):
482
+ try:
483
+ erro_teste, acc_teste = st.session_state.modelo.evaluate(
484
+ st.session_state.imagens_teste,
485
+ st.session_state.labels_teste,
486
+ verbose=0
487
+ )
488
+
489
+ col1, col2 = st.columns(2)
490
+
491
+ with col1:
492
+ st.metric(
493
+ "Acurácia no Teste",
494
+ f"{acc_teste:.2%}",
495
+ help="Percentual de imagens classificadas corretamente"
496
+ )
497
+
498
+ with col2:
499
+ st.metric(
500
+ "Erro no Teste",
501
+ f"{erro_teste:.4f}",
502
+ help="Medida de erro do modelo"
503
+ )
504
+
505
+ # Interpretação do resultado
506
+ if acc_teste >= 0.70:
507
+ st.success(f"✅ Excelente! O modelo alcançou {acc_teste:.1%} de acurácia!")
508
+ elif acc_teste >= 0.60:
509
+ st.info(f"ℹ️ Bom resultado! O modelo alcançou {acc_teste:.1%} de acurácia.")
510
+ else:
511
+ st.warning(f"⚠️ O modelo pode melhorar. Acurácia atual: {acc_teste:.1%}")
512
+ except Exception as e:
513
+ st.error(f"Erro ao avaliar modelo: {str(e)}")
514
 
515
  # ==================== ABA 4: PREDIÇÃO ====================
516
  with tab4:
 
545
  with col1:
546
  st.subheader("🖼️ Imagem Original")
547
  imagem_original = Image.open(uploaded_file)
548
+ st.image(imagem_original, use_column_width=True)
549
 
550
  # Informações da imagem
551
  st.markdown(f"""
 
560
 
561
  # Processar imagem
562
  imagem_processada, imagem_array = processar_imagem_usuario(uploaded_file)
 
563
 
564
+ if imagem_processada is not None:
565
+ st.image(imagem_processada, use_column_width=True)
566
+ st.info("A imagem foi redimensionada para 32x32 pixels para corresponder ao formato de entrada do modelo.")
567
 
568
  # Botão de predição
569
  st.markdown("---")
570
 
571
  if st.button("🎲 Classificar Imagem", key="classificar", use_container_width=True):
572
+ if imagem_array is not None:
573
+ with st.spinner("Analisando imagem..."):
574
+ try:
575
+ # Fazer predição
576
+ previsoes = st.session_state.modelo.predict(imagem_array, verbose=0)
577
+ classe_prevista = np.argmax(previsoes[0])
578
+ confianca = previsoes[0][classe_prevista]
579
+ nome_classe = nomes_classes[classe_prevista]
580
+ emoji_classe = emojis_classes[nome_classe]
581
+
582
+ # Resultado principal
583
+ st.markdown("---")
584
+ st.subheader("🎉 Resultado da Classificação")
585
+
586
+ # Card de resultado
587
+ st.markdown(f"""
588
+ <div style="
589
+ padding: 30px;
590
+ border-radius: 10px;
591
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
592
+ color: white;
593
+ text-align: center;
594
+ margin: 20px 0;
595
+ ">
596
+ <h1 style="font-size: 4em; margin: 0;">{emoji_classe}</h1>
597
+ <h2 style="margin: 10px 0;">{nome_classe.upper()}</h2>
598
+ <p style="font-size: 1.5em; margin: 0;">Confiança: {confianca:.1%}</p>
599
+ </div>
600
+ """, unsafe_allow_html=True)
601
+
602
+ # Barra de progresso da confiança
603
+ st.progress(float(confianca))
604
+
605
+ # Mostrar todas as probabilidades
606
+ st.markdown("---")
607
+ st.subheader("📊 Probabilidades para Todas as Classes")
608
+
609
+ # Criar DataFrame com resultados
610
+ import pandas as pd
611
+
612
+ resultados = pd.DataFrame({
613
+ 'Classe': [f"{emojis_classes[nome]} {nome}" for nome in nomes_classes],
614
+ 'Probabilidade': previsoes[0],
615
+ 'Confiança (%)': previsoes[0] * 100
616
+ })
617
+ resultados = resultados.sort_values('Probabilidade', ascending=False)
618
+
619
+ # Gráfico de barras
620
+ fig, ax = plt.subplots(figsize=(10, 6))
621
+ colors = ['#667eea' if i == classe_prevista else '#cccccc'
622
+ for i in range(len(nomes_classes))]
623
+
624
+ bars = ax.barh(
625
+ resultados['Classe'],
626
+ resultados['Probabilidade'],
627
+ color=[colors[nomes_classes.index(nome.split()[-1])] for nome in resultados['Classe']]
628
+ )
629
+
630
+ ax.set_xlabel('Probabilidade', fontsize=12)
631
+ ax.set_title('Distribuição de Probabilidades', fontsize=14, fontweight='bold')
632
+ ax.set_xlim(0, 1)
633
+
634
+ # Adicionar valores nas barras
635
+ for bar in bars:
636
+ width = bar.get_width()
637
+ ax.text(width, bar.get_y() + bar.get_height()/2,
638
+ f'{width:.1%}',
639
+ ha='left', va='center', fontsize=9,
640
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
641
+
642
+ plt.tight_layout()
643
+ st.pyplot(fig)
644
+ plt.clf()
645
+
646
+ # Tabela detalhada
647
+ with st.expander("📋 Ver Tabela Detalhada"):
648
+ st.dataframe(
649
+ resultados.style.format({
650
+ 'Probabilidade': '{:.4f}',
651
+ 'Confiança (%)': '{:.2f}%'
652
+ }).background_gradient(subset=['Probabilidade'], cmap='Blues'),
653
+ use_container_width=True
654
+ )
655
+
656
+ # Interpretação
657
+ with st.expander("💡 Como Interpretar o Resultado"):
658
+ st.markdown(f"""
659
+ ### Análise da Predição:
660
+
661
+ - **Classe Prevista**: {emoji_classe} **{nome_classe.upper()}**
662
+ - **Confiança**: {confianca:.1%}
663
+
664
+ #### O que significa a confiança?
665
+
666
+ A confiança indica o quão "certo" o modelo está sobre sua predição:
667
+
668
+ - **> 90%**: 🟢 Muito confiante - O modelo está muito seguro da classificação
669
+ - **70-90%**: 🟡 Confiante - Boa certeza, mas com alguma margem de dúvida
670
+ - **50-70%**: 🟠 Moderado - O modelo tem dúvidas significativas
671
+ - **< 50%**: 🔴 Baixa confiança - O modelo está muito incerto
672
+
673
+ #### Por que o modelo pode errar?
674
+
675
+ - Imagem muito diferente das do dataset de treino
676
+ - Objeto muito pequeno ou distante
677
+ - Ângulo ou iluminação incomum
678
+ - Múltiplos objetos na imagem
679
+ - Qualidade da imagem (blur, pixelização)
680
+ """)
681
+ except Exception as e:
682
+ st.error(f"Erro ao fazer predição: {str(e)}")
683
 
684
  # Footer
685
  st.markdown("---")
 
689
  <p>Desenvolvido para fins de aprendizado e demonstração</p>
690
  <p style="font-size: 0.8em;">Dataset: CIFAR-10 | Framework: TensorFlow/Keras | Interface: Streamlit</p>
691
  </div>
692
+ """, unsafe_allow_html=True)