app.py
DELETED
@@ -1,157 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
import pandas as pd
|
3 |
-
import numpy as np
|
4 |
-
import torch
|
5 |
-
import joblib
|
6 |
-
import json
|
7 |
-
import os
|
8 |
-
import traceback
|
9 |
-
from transformers import AutoTokenizer, AutoModel
|
10 |
-
from torch import nn
|
11 |
-
|
12 |
-
# --- 1. DEFINIÇÃO DA ARQUITETURA DO MODELO ---
|
13 |
-
# Colocamos a classe aqui para que a aplicação seja autocontida.
|
14 |
-
class RegressionTransformer(nn.Module):
|
15 |
-
def __init__(self, model_name='neuralmind/bert-base-portuguese-cased'):
|
16 |
-
super(RegressionTransformer, self).__init__()
|
17 |
-
self.bert = AutoModel.from_pretrained(model_name)
|
18 |
-
self.regressor = nn.Sequential(
|
19 |
-
nn.Dropout(p=0.2),
|
20 |
-
nn.Linear(self.bert.config.hidden_size, 128),
|
21 |
-
nn.ReLU(),
|
22 |
-
nn.Dropout(p=0.2),
|
23 |
-
nn.Linear(128, 1)
|
24 |
-
)
|
25 |
-
def forward(self, input_ids, attention_mask):
|
26 |
-
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
|
27 |
-
pooled_output = outputs.pooler_output
|
28 |
-
return self.regressor(pooled_output)
|
29 |
-
|
30 |
-
# --- 2. CARREGAMENTO DOS ARTEFATOS GLOBAIS ---
|
31 |
-
# Esta seção é executada uma vez quando a aplicação inicia no Hugging Face Spaces.
|
32 |
-
print("Carregando artefatos do modelo...")
|
33 |
-
device = torch.device("cpu")
|
34 |
-
CACHE_DIR = "./huggingface_cache"
|
35 |
-
|
36 |
-
try:
|
37 |
-
# Carrega o tokenizador
|
38 |
-
TOKENIZER_NAME = 'neuralmind/bert-base-portuguese-cased'
|
39 |
-
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_NAME, cache_dir=CACHE_DIR)
|
40 |
-
print("Tokenizador carregado.")
|
41 |
-
|
42 |
-
# Carrega o modelo treinado
|
43 |
-
MODEL_PATH = './modelo/best_model_state.pth'
|
44 |
-
model = RegressionTransformer(model_name=TOKENIZER_NAME)
|
45 |
-
model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
|
46 |
-
model.to(device)
|
47 |
-
model.eval()
|
48 |
-
print("Modelo carregado.")
|
49 |
-
|
50 |
-
# Carrega a lista de features que o modelo espera
|
51 |
-
FEATURES_PATH = './modelo/model_features.json'
|
52 |
-
with open(FEATURES_PATH, 'r') as f:
|
53 |
-
features_dict = json.load(f)
|
54 |
-
model_features = features_dict['numeric'] + features_dict['categorical']
|
55 |
-
print("Features do modelo carregadas.")
|
56 |
-
|
57 |
-
except Exception as e:
|
58 |
-
print("!!!!!! ERRO FATAL AO CARREGAR ARTEFATOS !!!!!!")
|
59 |
-
traceback.print_exc()
|
60 |
-
raise e
|
61 |
-
|
62 |
-
# --- 3. FUNÇÃO DE LÓGICA / PREVISÃO ---
|
63 |
-
# Esta função contém a "inteligência" do back-end.
|
64 |
-
def predict_price(make, model_name, year, odometer, trim, body, transmission, color, interior):
|
65 |
-
try:
|
66 |
-
print(f"Recebida nova predição: {make}, {model_name}, {year}, {odometer}km")
|
67 |
-
|
68 |
-
# Converte os inputs da interface em um DataFrame de uma linha
|
69 |
-
input_data_dict = {
|
70 |
-
'make': make, 'model': model_name, 'year': int(year), 'odometer': float(odometer),
|
71 |
-
'trim': trim, 'body': body, 'transmission': transmission,
|
72 |
-
'color': color, 'interior': interior
|
73 |
-
}
|
74 |
-
input_df = pd.DataFrame([input_data_dict])
|
75 |
-
|
76 |
-
# Aplica a mesma engenharia de features do treinamento
|
77 |
-
ano_referencia = 2024
|
78 |
-
input_df['age'] = ano_referencia - input_df['year']
|
79 |
-
input_df['sale_month'] = 0 # Placeholder
|
80 |
-
input_df['sale_dayofweek'] = 0 # Placeholder
|
81 |
-
input_df['sale_dayofyear'] = 0
|
82 |
-
input_df['make_popularity'] = 0
|
83 |
-
input_df['model_popularity'] = 0
|
84 |
-
input_df['km_per_year'] = 0
|
85 |
-
|
86 |
-
# Cria a "frase" textual
|
87 |
-
def criar_representacao_textual(row):
|
88 |
-
partes = [f"{coluna}[{str(row[coluna])}]" for coluna in model_features if coluna in row]
|
89 |
-
return " | ".join(partes)
|
90 |
-
text_input = input_df.apply(criar_representacao_textual, axis=1).iloc[0]
|
91 |
-
|
92 |
-
# Tokeniza e prepara os tensores
|
93 |
-
encoded_text = tokenizer.encode_plus(
|
94 |
-
text_input, max_length=128, return_tensors='pt',
|
95 |
-
padding='max_length', truncation=True
|
96 |
-
)
|
97 |
-
input_ids = encoded_text['input_ids'].to(device)
|
98 |
-
attention_mask = encoded_text['attention_mask'].to(device)
|
99 |
-
|
100 |
-
# Faz a inferência
|
101 |
-
with torch.no_grad():
|
102 |
-
prediction_log = model(input_ids, attention_mask)
|
103 |
-
|
104 |
-
# Pós-processa o resultado
|
105 |
-
predicted_price = np.expm1(prediction_log.cpu().numpy()[0][0])
|
106 |
-
|
107 |
-
# Formata a string de saída para o usuário
|
108 |
-
return f"R$ {predicted_price:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
109 |
-
|
110 |
-
except Exception as e:
|
111 |
-
print("!!!!!! ERRO DURANTE A PREVISÃO !!!!!!")
|
112 |
-
traceback.print_exc()
|
113 |
-
return "Ocorreu um erro. Verifique os logs."
|
114 |
-
|
115 |
-
# --- 4. DEFINIÇÃO DA INTERFACE COM GRADIO ---
|
116 |
-
# Esta seção define a aparência do "front-end".
|
117 |
-
with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}") as demo:
|
118 |
-
gr.Markdown("# 🚗 FipeFinder AI: Previsão de Preços de Carros Usados")
|
119 |
-
gr.Markdown("Preencha as características do veículo para receber uma estimativa de preço de mercado baseada em nosso modelo de IA. Este projeto demonstra um pipeline completo de Machine Learning, desde a análise de dados e treinamento de um modelo Transformer até o deploy de uma aplicação interativa.")
|
120 |
-
|
121 |
-
with gr.Row():
|
122 |
-
with gr.Column(scale=1):
|
123 |
-
make_input = gr.Dropdown(label="Marca", choices=["Ford", "Chevrolet", "Honda", "Toyota", "Nissan", "Hyundai", "Kia", "BMW", "Mercedes-Benz", "Volkswagen"])
|
124 |
-
model_input = gr.Textbox(label="Modelo", placeholder="Ex: Ka, Onix, Civic, Corolla...")
|
125 |
-
year_input = gr.Slider(label="Ano do Modelo", minimum=2000, maximum=2024, step=1, value=2015)
|
126 |
-
|
127 |
-
with gr.Column(scale=1):
|
128 |
-
odo_input = gr.Number(label="Quilometragem (km)", value=80000)
|
129 |
-
trim_input = gr.Textbox(label="Versão", placeholder="Ex: SE 1.0, LTZ, EXL...")
|
130 |
-
body_input = gr.Dropdown(label="Carroceria", choices=["Sedan", "SUV", "Hatchback", "Pickup", "Minivan", "Coupe", "Wagon"])
|
131 |
-
|
132 |
-
with gr.Column(scale=1):
|
133 |
-
trans_input = gr.Radio(label="Transmissão", choices=["automatic", "manual"], value="automatic")
|
134 |
-
color_input = gr.Textbox(label="Cor Externa", placeholder="Ex: preto, branco, prata...")
|
135 |
-
interior_input = gr.Textbox(label="Cor Interior", placeholder="Ex: preto, cinza, bege...")
|
136 |
-
|
137 |
-
predict_btn = gr.Button("Estimar Preço", variant="primary")
|
138 |
-
output_price = gr.Label(label="Preço Estimado")
|
139 |
-
|
140 |
-
predict_btn.click(
|
141 |
-
fn=predict_price,
|
142 |
-
inputs=[make_input, model_input, year_input, odo_input, trim_input, body_input, trans_input, color_input, interior_input],
|
143 |
-
outputs=output_price
|
144 |
-
)
|
145 |
-
|
146 |
-
gr.Examples(
|
147 |
-
examples=[
|
148 |
-
["Ford", "Focus", 2013, 75000.0, "SE 2.0", "Hatchback", "automatic", "prata", "preto"],
|
149 |
-
["Chevrolet", "Onix", 2018, 90000.0, "LT 1.0", "Hatchback", "manual", "branco", "cinza"],
|
150 |
-
["Toyota", "Corolla", 2020, 40000.0, "XEi", "Sedan", "automatic", "preto", "preto"],
|
151 |
-
],
|
152 |
-
inputs=[make_input, model_input, year_input, odo_input, trim_input, body_input, trans_input, color_input, interior_input]
|
153 |
-
)
|
154 |
-
|
155 |
-
# --- Lançamento da Interface ---
|
156 |
-
if __name__ == "__main__":
|
157 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|