arturevs commited on
Commit
526a74f
·
1 Parent(s): 378e40c
.gitignore ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ignorar ambiente virtual
2
+ .venv/
3
+ venv/
4
+ env/
5
+
6
+ # Ignorar arquivos de cache do Python
7
+ __pycache__/
8
+ *.pyc
9
+ *.pyo
10
+ *.pyd
11
+
12
+ # Ignorar arquivos de configuração do VS Code
13
+ .vscode/
14
+
15
+ # Ignorar arquivos de ambiente
16
+ .env
17
+ .env.local
18
+
19
+ # Ignorar arquivos de dados temporários (opcional, dependendo do fluxo de trabalho)
20
+ *.csv
21
+ *.joblib
22
+
23
+ # Ignorar arquivos de log
24
+ *.log
25
+
26
+ # Ignorar diretórios de build e distribuição
27
+ build/
28
+ dist/
29
+ *.egg-info/
30
+
31
+ # Ignorar arquivos do sistema operacional
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Ignorar arquivos específicos do Hugging Face (se houver)
36
+ *.huggingface/
37
+ *.hf_cache/
38
+
39
+ # Ignorar arquivos gerados pelo Streamlit (opcional)
40
+ .streamlit/
41
+
42
+ animals/
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch==2.1.0
2
+ torchvision==0.16.0
3
+ pandas==2.1.1
4
+ numpy==1.26.0
5
+ opencv-python==4.8.1.78
6
+ scikit-learn==1.3.2
7
+ seaborn==0.12.2
8
+ matplotlib==3.7.2
9
+ streamlit==1.27.0
10
+ joblib==1.3.2
11
+ huggingface_hub==0.19.4
12
+ Pillow==10.0.0
src/app.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import joblib
3
+ from PIL import Image
4
+ import torchvision.transforms as transforms
5
+ from huggingface_hub import hf_hub_download
6
+
7
+ # Função para carregar o modelo e o LabelEncoder do Hugging Face
8
+ @st.cache_resource
9
+ def load_model():
10
+ # Baixar o modelo e o LabelEncoder do Hugging Face
11
+ model_path = hf_hub_download(repo_id="arturevs/90AnimalClassification", filename="mlp_classifier.joblib")
12
+ label_encoder_path = hf_hub_download(repo_id="arturevs/90AnimalClassification", filename="label_encoder.joblib")
13
+
14
+ # Carregar o modelo e o LabelEncoder
15
+ model = joblib.load(model_path)
16
+ label_encoder = joblib.load(label_encoder_path)
17
+ return model, label_encoder
18
+
19
+ # Função para pré-processar a imagem
20
+ def preprocess_image(image):
21
+ transform = transforms.Compose([
22
+ transforms.Resize((256, 256)),
23
+ transforms.ToTensor(),
24
+ transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
25
+ ])
26
+ image = transform(image).flatten().numpy().reshape(1, -1)
27
+ return image
28
+
29
+ # Interface da aplicação
30
+ st.title("Classificador de Animais")
31
+ st.write("Envie uma imagem de um animal (gato, cachorro ou pássaro) para classificação.")
32
+
33
+ # Carregar o modelo e o LabelEncoder
34
+ model, label_encoder = load_model()
35
+
36
+ # Upload da imagem
37
+ uploaded_file = st.file_uploader("Escolha uma imagem...", type=["jpg", "jpeg", "png"])
38
+ if uploaded_file is not None:
39
+ image = Image.open(uploaded_file)
40
+ st.image(image, caption="Imagem enviada", use_column_width=True)
41
+
42
+ # Pré-processar a imagem
43
+ image_processed = preprocess_image(image)
44
+
45
+ # Fazer a previsão
46
+ prediction = model.predict(image_processed)
47
+ predicted_class = label_encoder.inverse_transform(prediction)[0]
48
+
49
+ # Exibir o resultado
50
+ st.write(f"Classificação: **{predicted_class}**")
src/csv_creation.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pandas as pd
3
+
4
+ # Caminho para a pasta csv_folder (usando caminho relativo)
5
+ CSV_FOLDER = "./csv_folder"
6
+
7
+ # Lista para armazenar todos os DataFrames
8
+ dataframes = []
9
+
10
+ # Percorrer todos os arquivos na pasta csv_folder
11
+ for csv_file in os.listdir(CSV_FOLDER):
12
+ if csv_file.endswith(".csv"): # Verificar se é um arquivo CSV
13
+ csv_path = os.path.join(CSV_FOLDER, csv_file)
14
+
15
+ try:
16
+ # Ler o CSV e adicionar à lista de DataFrames
17
+ df = pd.read_csv(csv_path)
18
+ dataframes.append(df)
19
+ print(f"Arquivo '{csv_file}' carregado com sucesso.")
20
+ except Exception as e:
21
+ print(f"Erro ao carregar o arquivo '{csv_file}': {e}")
22
+
23
+ # Verificar se há DataFrames para unificar
24
+ if not dataframes:
25
+ print("Nenhum arquivo CSV foi carregado. Verifique a pasta 'csv_folder'.")
26
+ else:
27
+ # Unificar todos os DataFrames em um único DataFrame
28
+ unified_df = pd.concat(dataframes, ignore_index=True)
29
+
30
+ # Salvar o DataFrame unificado em um novo arquivo CSV
31
+ output_csv_path = os.path.join("./", "unified_dataset.csv")
32
+ try:
33
+ unified_df.to_csv(output_csv_path, index=False)
34
+ print(f"Dataset unificado salvo como '{output_csv_path}'")
35
+ except Exception as e:
36
+ print(f"Erro ao salvar o dataset unificado: {e}")
src/img_processing.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import pandas as pd
4
+ import torch
5
+ import torch.nn as nn
6
+ import torchvision.transforms as transforms
7
+ from torchvision.models import resnet18 # Usaremos uma CNN pré-treinada (ResNet18)
8
+
9
+ # Configurações
10
+ DATASET_PATH = "./animals/" # Pasta principal com as subpastas de animais
11
+ CSV_FOLDER = "./csv_folder" # Pasta para salvar os CSVs
12
+ IMAGE_SIZE = (224, 224) # Tamanho das imagens para a CNN
13
+
14
+ # Criar a pasta csv_folder se não existir
15
+ if not os.path.exists(CSV_FOLDER):
16
+ os.makedirs(CSV_FOLDER)
17
+
18
+ # Carregar uma CNN pré-treinada (ResNet18) e remover a camada final (fully connected)
19
+ cnn_model = resnet18(pretrained=True)
20
+ cnn_model = nn.Sequential(*list(cnn_model.children())[:-1]) # Remove a última camada
21
+ cnn_model.eval() # Colocar o modelo em modo de avaliação
22
+
23
+ # Transformações para a imagem
24
+ transform = transforms.Compose([
25
+ transforms.ToPILImage(),
26
+ transforms.Resize(IMAGE_SIZE),
27
+ transforms.ToTensor(),
28
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # Normalização para o modelo pré-treinado
29
+ ])
30
+
31
+ # Função para extrair características de uma imagem usando a CNN
32
+ def extract_features(image):
33
+ with torch.no_grad(): # Desativar cálculo de gradientes
34
+ image_tensor = transform(image).unsqueeze(0) # Adicionar dimensão do batch
35
+ features = cnn_model(image_tensor) # Extrair características
36
+ return features.flatten().numpy() # Achatar e converter para numpy array
37
+
38
+ # Função para processar uma subpasta (espécie) e salvar em um DataFrame
39
+ def process_animal_folder(animal_class, class_path):
40
+ # Lista para armazenar os dados da subpasta
41
+ data = []
42
+
43
+ # Percorrer as imagens da subpasta
44
+ for image_name in os.listdir(class_path):
45
+ image_path = os.path.join(class_path, image_name)
46
+
47
+ try:
48
+ # Verificar se o arquivo é uma imagem válida
49
+ if not image_name.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
50
+ print(f"Ignorando arquivo não suportado: {image_path}")
51
+ continue
52
+
53
+ # Carregar a imagem
54
+ image = cv2.imread(image_path)
55
+ if image is None:
56
+ raise ValueError(f"Falha ao carregar a imagem: {image_path}")
57
+
58
+ # Extrair características usando a CNN
59
+ features = extract_features(image)
60
+
61
+ # Adicionar ao dataset com o label sendo o nome da subpasta
62
+ data.append([animal_class] + list(features))
63
+ except Exception as e:
64
+ print(f"Erro ao processar {image_path}: {e}")
65
+
66
+ # Verificar se há dados antes de criar o DataFrame
67
+ if not data:
68
+ print(f"Nenhuma imagem válida encontrada na pasta: {class_path}")
69
+ return None
70
+
71
+ # Criar DataFrame
72
+ columns = ["label"] + [f"feature_{i}" for i in range(len(data[0]) - 1)]
73
+ df = pd.DataFrame(data, columns=columns)
74
+
75
+ return df
76
+
77
+ # Percorrer as subpastas
78
+ for animal_class in os.listdir(DATASET_PATH):
79
+ class_path = os.path.join(DATASET_PATH, animal_class)
80
+
81
+ # Verificar se é uma pasta
82
+ if os.path.isdir(class_path):
83
+ print(f"Processando imagens da classe: {animal_class}")
84
+
85
+ # Processar a subpasta e obter o DataFrame
86
+ df = process_animal_folder(animal_class, class_path)
87
+
88
+ if df is not None:
89
+ # Salvar CSV com o nome do animal
90
+ csv_filename = os.path.join(CSV_FOLDER, f"{animal_class}_dataset.csv")
91
+ try:
92
+ df.to_csv(csv_filename, index=False)
93
+ print(f"Dataset salvo como '{csv_filename}'")
94
+ except Exception as e:
95
+ print(f"Erro ao salvar o dataset {csv_filename}: {e}")
96
+
97
+ print("Processamento concluído!")
src/model_training.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from sklearn.model_selection import train_test_split
4
+ from sklearn.preprocessing import StandardScaler, LabelEncoder
5
+ from sklearn.neural_network import MLPClassifier
6
+ from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
7
+ import seaborn as sns
8
+ import matplotlib.pyplot as plt
9
+ import joblib
10
+
11
+ # Carregar o dataset unificado
12
+ csv_path = "./unified_dataset.csv" # Caminho relativo a partir da pasta src
13
+ try:
14
+ df = pd.read_csv(csv_path)
15
+ print("Dataset carregado com sucesso!")
16
+ except Exception as e:
17
+ print(f"Erro ao carregar o dataset: {e}")
18
+ exit()
19
+
20
+ # Verificar o dataset
21
+ print(df.head())
22
+ print(f"Tamanho do dataset: {df.shape}")
23
+
24
+ # Separar features (X) e labels (y)
25
+ X = df.drop(columns=["label"]) # Todas as colunas exceto 'label'
26
+ y = df["label"] # Coluna 'label'
27
+
28
+ # Codificar as labels (transformar strings em números)
29
+ label_encoder = LabelEncoder()
30
+ y = label_encoder.fit_transform(y)
31
+
32
+ # Dividir o dataset em treino e teste
33
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
34
+
35
+ # Normalizar as features (importante para MLP)
36
+ scaler = StandardScaler()
37
+ X_train = scaler.fit_transform(X_train)
38
+ X_test = scaler.transform(X_test)
39
+
40
+ # Criar e treinar a MLP
41
+ mlp = MLPClassifier(
42
+ hidden_layer_sizes=(128, 64), # Duas camadas ocultas com 128 e 64 neurônios
43
+ activation="relu", # Função de ativação ReLU
44
+ solver="adam", # Otimizador Adam
45
+ max_iter=500, # Número máximo de iterações
46
+ random_state=42,
47
+ verbose=True # Mostrar progresso durante o treinamento
48
+ )
49
+
50
+ print("Treinando a MLP...")
51
+ mlp.fit(X_train, y_train)
52
+
53
+ # Fazer previsões no conjunto de teste
54
+ y_pred = mlp.predict(X_test)
55
+
56
+ # Avaliar o modelo
57
+ print("\nRelatório de classificação:")
58
+ print(classification_report(y_test, y_pred, target_names=label_encoder.classes_))
59
+
60
+ print(f"Acurácia: {accuracy_score(y_test, y_pred):.4f}")
61
+
62
+ # Gerar a matriz de confusão
63
+ conf_matrix = confusion_matrix(y_test, y_pred)
64
+
65
+ # Plotar a matriz de confusão
66
+ plt.figure(figsize=(10, 7))
67
+ sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Blues",
68
+ xticklabels=label_encoder.classes_,
69
+ yticklabels=label_encoder.classes_)
70
+ plt.xlabel("Predito")
71
+ plt.ylabel("Verdadeiro")
72
+ plt.title("Matriz de Confusão")
73
+ plt.show()
74
+
75
+ # Extrair valores relevantes da matriz de confusão
76
+ print("\nValores relevantes da matriz de confusão:")
77
+
78
+ # Acurácia por classe
79
+ class_accuracy = conf_matrix.diagonal() / conf_matrix.sum(axis=1)
80
+ for i, accuracy in enumerate(class_accuracy):
81
+ print(f"Acurácia da classe {label_encoder.classes_[i]}: {accuracy:.4f}")
82
+
83
+ # Erros mais comuns (maiores valores fora da diagonal)
84
+ conf_matrix_df = pd.DataFrame(conf_matrix, index=label_encoder.classes_, columns=label_encoder.classes_)
85
+ conf_matrix_df.values[np.diag_indices_from(conf_matrix_df)] = 0 # Zerar a diagonal para focar nos erros
86
+ most_common_errors = conf_matrix_df.stack().sort_values(ascending=False).head(5)
87
+
88
+ print("\nErros mais comuns:")
89
+ print(most_common_errors)
90
+
91
+ # Salvar o modelo
92
+ model_filename = "mlp_classifier.joblib"
93
+ joblib.dump(mlp, model_filename)
94
+ print(f"Modelo salvo como {model_filename}")
95
+
96
+ # Salvar o LabelEncoder (para decodificar as previsões)
97
+ label_encoder_filename = "label_encoder.joblib"
98
+ joblib.dump(label_encoder, label_encoder_filename)
99
+ print(f"LabelEncoder salvo como {label_encoder_filename}")
src/send_model.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub import HfApi
2
+
3
+ # Nome do repositório no Hugging Face
4
+ repo_id = "arturevs/90AnimalClassifier" # Substitua pelo seu usuário e nome do repositório
5
+
6
+ # Arquivos a serem enviados
7
+ files_to_upload = ["./mlp_classifier.joblib", "./label_encoder.joblib"]
8
+
9
+ # Enviar arquivos para o Hugging Face
10
+ api = HfApi()
11
+ for file in files_to_upload:
12
+ api.upload_file(
13
+ path_or_fileobj=file,
14
+ path_in_repo=file,
15
+ repo_id=repo_id,
16
+ repo_type="model"
17
+ )
18
+ print("Modelo enviado para o Hugging Face!")