diff --git a/api/DataProcesser.py b/api/DataProcesser.py index 2ba3f577..f23902e7 100644 --- a/api/DataProcesser.py +++ b/api/DataProcesser.py @@ -3,20 +3,21 @@ from NbEmotionsModel import make_prediction from NbLinRegressionModel import make_prediction_nblin from available_classifiers import get_available_classifiers -from tensorflow.python.keras.models import load_model -import scipy as sp -import pandas as pd -import numpy as np -import tensorflow as tf +import Neural_Network2 import pickle import re import joblib +import numpy as np import string import os -from sklearn.feature_extraction.text import TfidfVectorizer +import pandas as pd +import torch +from collections import Counter +from functools import partial import nltk +from nltk.tokenize import wordpunct_tokenize from nltk.corpus import stopwords nltk.download('stopwords') # mais imports @@ -31,7 +32,7 @@ def handle_classify(self, df, classifier): model_name = classifier_switcher[classifier] if model_name.endswith('.pkl'): return self.pretrained_predict(df, model_name) - elif model_name.endswith('.h5'): + else: return self.trained_predict(df, model_name) #classifier_switcher = { # 0: self.classify_emotions, @@ -54,16 +55,13 @@ def generate_statistics(self, df): def preprocess_text(self, text): - text = str(text).lower() - text = re.sub('\[.*?\]', '', text) - text = re.sub("\\W", " ", text) - text = re.sub('https?://\S+|www\.\S+', '', text) - text = re.sub('<.*?>+', '', text) - text = re.sub('[%s]' % re.escape(string.punctuation), '', text) - text = re.sub('\n', '', text) - text = re.sub('\w*\d\w*', '', text) - - return text + stop_words = set(stopwords.words('english')) + text = str(text) + text = re.sub(r'[^\w\s]', '', text) + text = text.lower() + tokens = wordpunct_tokenize(text) + tokens = [token for token in tokens if token not in stop_words] + return tokens def classify_emotions(self, df): @@ -90,44 +88,80 @@ def pretrained_predict(self, df, model_name): return df def load_weights_and_model(self, name): - model_filename = f"api/models/{name}" - num_classes = model_filename[model_filename.index("s") + 2 : model_filename.index("-")] - model = tf.keras.Sequential([ - tf.keras.layers.Embedding(input_dim=20000, output_dim=128), - tf.keras.layers.LSTM(64), - tf.keras.layers.Dense(int(num_classes), activation='softmax') - ]) - model.load_weights(model_filename) - return model - - def trained_predict(self, df, model_name): - model = self.load_weights_and_model(model_name) - - - encoder_name = model_name[model_name.index('l') + 2 : model_name.index('.')] + model_filename = os.path.join("api", "models", name) + if os.path.exists(model_filename): + model = torch.load(model_filename) + return model + else: + raise FileNotFoundError(f"Model file '{model_filename}' not found.") + + def predict_with_model(self, data, model): + # Convert data to PyTorch tensor if needed + data_tensor = torch.tensor(data, dtype=torch.float32) + # Perform prediction + predictions = model(data_tensor) + return predictions - label_map_filename = f"api\encoders/LabelMapping-{encoder_name}.joblib" + def trained_predict(self, df, model_name): + label_map_filename = f"api/encoders/LabelMapping-{model_name}.joblib" label_encoder = joblib.load(label_map_filename) - raw_text = df['input_column'].tolist() - - # prediction (nao sei como fazer agora) - # vectorizer = TfidfVectorizer(max_features=20000) - # raw_text = [self.preprocess_text(text).encode("utf-8") for text in raw_text] - # vectorizer.fit_transform(raw_text) - # vectorized_data = vectorizer.transform(raw_text) - - # vectorized_data = np.asarray(vectorized_data.todense()) - - # # Make predictions using the model - - # predictions = model.predict(vectorized_data) - - # predicted_labels_encoded = tf.argmax(predictions, axis=1).numpy() - - # predicted_labels = [label_encoder.classes_[label] for label in predicted_labels_encoded] + model = self.load_weights_and_model(model_name) + model.eval() - # df['output_column'] = predicted_labels + stop_words = set(stopwords.words('english')) + + df['tokens'] = df.input_column.progress_apply( + partial(Neural_Network2.tokenize, stop_words=stop_words), + ) + + # Replace rare words with + all_tokens = [sublst for lst in df.tokens.tolist() for sublst in lst] + common_tokens = set(list(zip( + *Counter(all_tokens).most_common(20000)))[0]) + df.loc[:, 'tokens'] = df.tokens.progress_apply( + partial( + Neural_Network2.remove_rare_words, + common_tokens=common_tokens, + max_len=200, + ), + ) + + # Remove sequences with only + df = df[df.tokens.progress_apply( + lambda tokens: any(token != '' for token in tokens), + )] + + vocab = sorted({ + sublst for lst in df.tokens.tolist() for sublst in lst + }) + self.token2idx = {token: idx for idx, token in enumerate(vocab)} + + # Add a padding idx + self.token2idx[''] = max(self.token2idx.values()) + 1 + + self.idx2token = {idx: token for token, idx in self.token2idx.items()} + + df['indexed_tokens'] = df.tokens.apply( + lambda tokens: [self.token2idx[token] for token in tokens], + ) + + # Decode predictions using label_encoder + + + predictions = [] + for input_column_row in df['indexed_tokens']: + with torch.no_grad(): + _, logits = model([input_column_row], return_activations=True) + logits = logits.detach().cpu().numpy() + prediction = np.argmax(logits, axis=1)[0] + predictions.append(prediction) + + # Decode predictions using label encoder + decoded_predictions = label_encoder.inverse_transform(predictions) + + # Assign decoded predictions back to the DataFrame + df['output_column'] = decoded_predictions return df diff --git a/api/Neural_Network2.py b/api/Neural_Network2.py index ba1b3106..62abf20f 100644 --- a/api/Neural_Network2.py +++ b/api/Neural_Network2.py @@ -1,107 +1,385 @@ -import json +import random import re -import string -import pandas as pd -import numpy as np -import tensorflow as tf import os +from collections import Counter +from functools import partial +from pathlib import Path import joblib -import keras -from sklearn.feature_extraction.text import TfidfVectorizer - -from tensorflow.python.keras import layers -from tensorflow.python.keras.models import Sequential +import json +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from nltk.corpus import stopwords +from nltk import wordpunct_tokenize +from tqdm import tqdm +from sklearn.metrics import classification_report from sklearn.preprocessing import LabelEncoder -from tensorflow.python.keras.callbacks import Callback - -dirname = os.path.dirname(__file__) - -def preprocess_text(text): - text = str(text).lower() - text = re.sub('\[.*?\]', '', text) - text = re.sub("\\W", " ", text) - text = re.sub('https?://\S+|www\.\S+', '', text) - text = re.sub('<.*?>+', '', text) - text = re.sub('[%s]' % re.escape(string.punctuation), '', text) - text = re.sub('\n', '', text) - text = re.sub('\w*\d\w*', '', text) - return text - -class TrainingProgressCallback(Callback): - def __init__(self, epochs): - super(TrainingProgressCallback, self).__init__() - self.epochs = epochs - self.epoch_num = 0 - - def on_epoch_begin(self, epoch, logs=None): - self.epoch_step = 0 - - def on_epoch_end(self, epoch, logs=None): - self.epoch_num += 1 - - def on_batch_end(self, batch, logs=None): - self.epoch_step += 1 - progress = self.epoch_step / self.params["steps"] / self.epochs * 100 + self.epoch_num / self.epochs * 100 - - training_progress = { - 'training_progress': progress, - 'training_in_progress': True - } - with open('training_progress.json', 'w') as file: - json.dump(training_progress, file) - # print(progress) - # with open('training_progress.json', 'w') as file: - # json.dump(training_progress, file) - - -def create_and_train_model(train_texts, train_labels, name, epochs=5, batch_size=32, learning_rate=0.001): - label_encoder = LabelEncoder() - train_labels_encoded = label_encoder.fit_transform(train_labels) - - num_classes = len(label_encoder.classes_) - train_labels_one_hot = tf.keras.utils.to_categorical(train_labels_encoded, num_classes=num_classes) - - label_mapping_file = f"api/encoders/LabelMapping-{name}.joblib" - joblib.dump(label_encoder, label_mapping_file) - - tfidf_vectorizer = TfidfVectorizer(max_features=20000) - - train_texts = [preprocess_text(text) for text in train_texts] - train_texts_tfidf = tfidf_vectorizer.fit_transform(train_texts) - train_dataset = tf.data.Dataset.from_tensor_slices((train_texts_tfidf.toarray(), train_labels_one_hot)) - train_dataset = train_dataset.shuffle(len(train_texts)).batch(32) +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch import optim +from torch.optim.lr_scheduler import CosineAnnealingLR +from torch.utils.data import Dataset, DataLoader +from torch.utils.data.dataset import random_split +from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence + +tqdm.pandas() + +import nltk +nltk.download('stopwords') - # Parâmetros do modelo - num_features = train_texts_tfidf.shape[1] +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - # Define a arquitetura do modelo - model = tf.keras.Sequential([ - tf.keras.layers.Dense(64, activation='relu', input_shape=(num_features,)), - tf.keras.layers.Dense(num_classes, activation='softmax') - ]) +def remove_rare_words(tokens, common_tokens, max_len): + return [token if token in common_tokens + else '' for token in tokens][-max_len:] +def tokenize(text, stop_words): + text = str(text) + text = re.sub(r'[^\w\s]', '', text) + text = text.lower() + tokens = wordpunct_tokenize(text) + tokens = [token for token in tokens if token not in stop_words] + return tokens - model.compile(loss="categorical_crossentropy", optimizer=keras.optimizers.Adam(learning_rate=learning_rate), metrics=["accuracy"]) - try: - progress_callback = TrainingProgressCallback(epochs=epochs) +class CustomDataset(Dataset): + def __init__(self, df, max_vocab, max_len, name): + # Clean and tokenize + stop_words = set(stopwords.words('english')) - # history = model.fit(train_dataset, epochs=epochs, batch_size=batch_size, verbose=2) + df['tokens'] = df.input_text.progress_apply( + partial(tokenize, stop_words=stop_words), + ) + + # Replace rare words with + all_tokens = [sublst for lst in df.tokens.tolist() for sublst in lst] + common_tokens = set(list(zip( + *Counter(all_tokens).most_common(max_vocab)))[0]) + df.loc[:, 'tokens'] = df.tokens.progress_apply( + partial( + remove_rare_words, + common_tokens=common_tokens, + max_len=max_len, + ), + ) + + # Remove sequences with only + df = df[df.tokens.progress_apply( + lambda tokens: any(token != '' for token in tokens), + )] + + vocab = sorted({ + sublst for lst in df.tokens.tolist() for sublst in lst + }) + self.token2idx = {token: idx for idx, token in enumerate(vocab)} + + # Add a padding idx + self.token2idx[''] = max(self.token2idx.values()) + 1 + + self.idx2token = {idx: token for token, idx in self.token2idx.items()} + + df['indexed_tokens'] = df.tokens.apply( + lambda tokens: [self.token2idx[token] for token in tokens], + ) + + label_encoder = LabelEncoder() + df['encoded_labels'] = label_encoder.fit_transform(df['labels']) + encoder_name = f"LabelMapping-{name}" + encoder_filename = os.path.join("api", "encoders", f"{encoder_name}.joblib") + os.makedirs(os.path.dirname(encoder_filename), exist_ok=True) + joblib.dump(label_encoder, encoder_filename) + + self.text = df.input_text.tolist() + self.sequences = df.indexed_tokens.tolist() + self.targets = df.encoded_labels.tolist() + + def __getitem__(self, i): + return self.sequences[i], self.targets[i], self.text[i] + + def __len__(self): + return len(self.sequences) + + +def split_train_valid_test(corpus, valid_ratio=0.1, test_ratio=0.1): + """Split dataset into train, validation, and test.""" + test_length = int(len(corpus) * test_ratio) + valid_length = int(len(corpus) * valid_ratio) + train_length = len(corpus) - valid_length - test_length + return random_split( + corpus, lengths=[train_length, valid_length, test_length], + ) + + +def collate(batch): + inputs = [item[0] for item in batch] + target = torch.LongTensor([item[1] for item in batch]) + text = [item[2] for item in batch] + return inputs, target, text + +def pad_sequences(sequences, padding_val=0, pad_left=False): + """Pad a list of sequences to the same length with a padding_val.""" + sequence_length = max(len(sequence) for sequence in sequences) + if not pad_left: + return [ + sequence + (sequence_length - len(sequence)) * [padding_val] + for sequence in sequences + ] + return [ + (sequence_length - len(sequence)) * [padding_val] + sequence + for sequence in sequences + ] + + +class RNNClassifier(nn.Module): + def __init__(self, output_size, hidden_size, vocab_size, padding_idx, + device, dropout_probability=0.3, bidirectional=False, n_layers=1, + embedding_dimension=50, batch_size=32): + super(RNNClassifier, self).__init__() + self.hidden_size = hidden_size + self.output_size = output_size + self.batch_size = batch_size + self.n_layers = n_layers + self.dropout_probability = dropout_probability + self.device = device + self.padding_idx = padding_idx + + # We need to multiply some layers by two if the model is bidirectional + self.input_size_factor = 2 if bidirectional else 1 + + self.embedding = nn.Embedding(vocab_size, embedding_dimension) + + self.rnn = nn.LSTM( + embedding_dimension, + self.hidden_size, + self.n_layers, + bidirectional=bidirectional, + ) + + self.fc1 = nn.Linear( + self.hidden_size * self.input_size_factor, + 16, + ) + self.fc2 = nn.Linear( + 16, + self.output_size, + ) + + + def init_hidden(self): + """Set initial hidden states.""" + h0 = torch.randn( + self.n_layers * self.input_size_factor, + self.batch_size, + self.hidden_size, + ) + c0 = torch.randn( + self.n_layers * self.input_size_factor, + self.batch_size, + self.hidden_size, + ) + + h0 = h0.to(self.device) + c0 = c0.to(self.device) + + return h0, c0 + + def apply_rnn(self, embedding_out, lengths): + packed = pack_padded_sequence( + embedding_out, + lengths, + batch_first=True, + ) + activations, _ = self.rnn(packed, self.init_hidden()) + activations, _ = pad_packed_sequence(activations, batch_first=True) + + indices = (lengths - 1).view(-1, 1).expand( + activations.size(0), activations.size(2), + ).unsqueeze(1) + indices = indices.to(self.device) - history = model.fit(train_dataset, epochs=epochs, batch_size=batch_size, verbose=2, callbacks=[progress_callback]) + activations = activations.gather(1, indices).squeeze(1) + return activations - model_filename = f"api/models/{str(num_classes)}-Trained-Model-{name}.weights.h5" - model.save_weights(model_filename) + def forward(self, inputs, return_activations=False): + batch_size = len(inputs) - training_stats = { - "loss": history.history['loss'], - "accuracy": history.history['accuracy'] - } + # This makes the model not break for the last batch that might be less + # than batch_size in size + if batch_size != self.batch_size: + self.batch_size = batch_size - return json.dumps(training_stats) + lengths = torch.LongTensor([len(x) for x in inputs]) + lengths, permutation_indices = lengths.sort(0, descending=True) - except Exception as e: - return f"Error during model creation/training: {str(e)}" + # Pad sequences so that they are all the same length + padded_inputs = pad_sequences(inputs, padding_val=self.padding_idx) + inputs = torch.LongTensor(padded_inputs) + + # Sort inputs + inputs = inputs[permutation_indices].to(self.device) + + # Get embeddings + embedding_out = self.embedding(inputs) + + activations = self.apply_rnn(embedding_out, lengths) + + x = F.dropout(torch.relu(self.fc1(activations)), 0.05) + x = self.fc2(x) + out = torch.sigmoid(x) + + # Put the output back in correct order + permutation_index_pairs = list(zip( + permutation_indices.tolist(), + list(range(len(permutation_indices))), + )) + reordered_indices = [ + pair[1] for pair + in sorted(permutation_index_pairs, key=lambda pair: pair[0]) + ] + + if return_activations: + return out[reordered_indices], x[reordered_indices] + + return out[reordered_indices] + + + +def train_epoch(model, optimizer, scheduler, train_loader, criterion, curr_epoch, num_total_epochs): + model.train() + total_loss = total = 0 + progress_bar = tqdm(train_loader, desc='Training', leave=False) + num_iters = 0 + for inputs, target, text in progress_bar: + target = target.to(device) + + # Clean old gradients + optimizer.zero_grad() + + # Forwards pass + output = model(inputs) + + # Calculate how wrong the model is + loss = criterion(output, target) + + # Perform gradient descent, backwards pass + loss.backward() + + # Take a step in the right direction + optimizer.step() + scheduler.step() + + # Record metrics + total_loss += loss.item() + total += len(target) + num_iters += 1 + if num_iters % 20 == 0: + with open('training_progress.json', 'w') as f: + progress = 100 * (curr_epoch + num_iters/len(train_loader)) / num_total_epochs + training_progress = { + 'training_progress': progress, + 'training_in_progress': True + } + json.dump(training_progress, f) + + return total_loss / total + +def validate_epoch(model, valid_loader, criterion): + model.eval() + total_loss = total = 0 + with torch.no_grad(): + progress_bar = tqdm(valid_loader, desc='Validating', leave=False) + for inputs, target, text in progress_bar: + target = target.to(device) + + # Forwards pass + output = model(inputs) + + # Calculate how wrong the model is + loss = criterion(output, target) + + # Record metrics + total_loss += loss.item() + total += len(target) + + return total_loss / total + +def create_and_train_model(df, name, epochs = 10, batch_size = 32, learning_rate = 0.001): + + dropout_probability = 0.2 + n_rnn_layers = 1 + embedding_dimension = 128 + hidden_size = 64 + is_bidirectional = True + max_vocab = 20000 + max_len = 200 + valid_ratio = 0.05 + test_ratio = 0.05 + + dataset = CustomDataset(df, max_vocab, max_len, name) + + train_dataset, valid_dataset, test_dataset = split_train_valid_test( + dataset, valid_ratio=valid_ratio, test_ratio=test_ratio) + len(train_dataset), len(valid_dataset), len(test_dataset) + + + train_loader = DataLoader(train_dataset, batch_size=batch_size, collate_fn=collate) + valid_loader = DataLoader(valid_dataset, batch_size=batch_size, collate_fn=collate) + test_loader = DataLoader(test_dataset, batch_size=batch_size, collate_fn=collate) + + + model = RNNClassifier( + output_size=len(df.labels), + hidden_size=hidden_size, + embedding_dimension=embedding_dimension, + vocab_size=len(dataset.token2idx), + padding_idx=dataset.token2idx[''], + dropout_probability=dropout_probability, + bidirectional=is_bidirectional, + n_layers=n_rnn_layers, + device=device, + batch_size=batch_size, + ) + model = model.to(device) + + criterion = nn.CrossEntropyLoss() + optimizer = optim.Adam( + filter(lambda p: p.requires_grad, model.parameters()), + lr=learning_rate, + ) + scheduler = CosineAnnealingLR(optimizer, 1) + + n_epochs = 0 + train_losses, valid_losses = [], [] + for curr_epoch in range(epochs): + train_loss = train_epoch(model, optimizer, scheduler, train_loader, criterion, curr_epoch, epochs) + valid_loss = validate_epoch(model, valid_loader, criterion) + + tqdm.write( + f'epoch #{n_epochs + 1:3d}\ttrain_loss: {train_loss:.2e}' + f'\tvalid_loss: {valid_loss:.2e}\n', + ) + + # Early stopping if the current valid_loss is greater than the last three valid losses + if len(valid_losses) > 2 and all(valid_loss >= loss + for loss in valid_losses[-3:]): + print('Stopping early') + break + + train_losses.append(train_loss) + valid_losses.append(valid_loss) + + n_epochs += 1 + + model_path = os.path.join('api', 'models', name) + os.makedirs(os.path.dirname(model_path), exist_ok=True) + torch.save(model, model_path) + training_progress = { + 'training_progress': 0, + 'training_in_progress': True + } + with open('training_progress.json', 'w') as file: + json.dump(training_progress, file) diff --git a/api/app.py b/api/app.py index c8489eaa..e2cbcb1d 100644 --- a/api/app.py +++ b/api/app.py @@ -10,6 +10,7 @@ import nltk import json import asyncio +import logging nltk.download('wordnet') @@ -17,6 +18,10 @@ server_thread = None CORS(app) # Permite todas as origens por padrão (não recomendado para produção) +log = logging.getLogger('werkzeug') +log.disabled = True + + app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True data_processer = DataProcesser() @@ -43,9 +48,10 @@ def upload_file(): df = pd.DataFrame(selected_data, columns=['input_column']) result = data_processer.handle_classify(df, selected_classifier) - stats = data_processer.generate_statistics(result) + # stats = data_processer.generate_statistics(result) - return jsonify({'result': result.to_json(), 'stats': stats}) + return jsonify({'result': result.to_json()}) + # return jsonify({'result': result.to_json(), 'stats': stats}) @app.route('/get-classifiers', methods=["GET"]) def get_classifiers(): @@ -57,7 +63,7 @@ def shutdown(): shutdown_server() return 'Server shutting down...' -@app.route('/neural-network',methods=["POST"]) +@app.route('/neural-network', methods=["POST"]) def train_model(): received_data = request.json @@ -89,7 +95,9 @@ def train_model(): with open('training_progress.json', 'w') as file: json.dump(training_progress, file) - create_and_train_model(selected_data, selected_label, name, epochs, batch_size, learning_rate) + df = pd.DataFrame({'input_text': selected_data, 'labels': selected_label}) + + create_and_train_model(df, name, epochs, batch_size, learning_rate) return jsonify({"message": "Model train started successfully."}), 200 @@ -118,4 +126,10 @@ def get_training_status(): if __name__ == '__main__': - app.run(host='127.0.0.1', port=5000, debug=True) \ No newline at end of file + training_progress = { + 'training_progress': 0, + 'training_in_progress': True + } + with open('training_progress.json', 'w') as file: + json.dump(training_progress, file) + app.run(host='0.0.0.0', port=5000, debug=False) \ No newline at end of file diff --git a/api/available_classifiers.py b/api/available_classifiers.py index 3694a77e..8a3c47bd 100644 --- a/api/available_classifiers.py +++ b/api/available_classifiers.py @@ -1,5 +1,4 @@ import os -import pickle def get_available_classifiers(): model_folder = 'api/models' @@ -15,7 +14,6 @@ def get_available_classifiers(): classifiers = {} for file in model_files: - if file.endswith('.pkl') or file.endswith('.keras') or file.endswith('.h5'): - classifiers[len(classifiers)] = file + classifiers[len(classifiers)] = file return classifiers \ No newline at end of file diff --git a/api/models/IHIJI b/api/models/IHIJI new file mode 100644 index 00000000..9eab30d8 Binary files /dev/null and b/api/models/IHIJI differ diff --git a/api/models/Trained-Model-Testing.keras b/api/models/Trained-Model-Testing.keras deleted file mode 100644 index eaa14a43..00000000 Binary files a/api/models/Trained-Model-Testing.keras and /dev/null differ diff --git a/api/models/Tweet_emotions b/api/models/Tweet_emotions new file mode 100644 index 00000000..357ed44f Binary files /dev/null and b/api/models/Tweet_emotions differ diff --git a/api/models_code/test.py b/api/models_code/test.py new file mode 100644 index 00000000..6f8734c1 --- /dev/null +++ b/api/models_code/test.py @@ -0,0 +1,14 @@ +Emotions_found = {} +with open(r'C:\Users\camer\Downloads\output (1).csv', 'r') as f: + next(f) + for line in f: + if line[line.rfind(',')+1:-1] == '': + continue + #line[line.rfind(',')+1:] != "neutral\n" and line[line.rfind(',')+1:] != "empty\n": + if line[line.rfind(',')+1:-1] not in Emotions_found: + Emotions_found[line[line.rfind(',')+1:-1]] = 0 + else: + Emotions_found[line[line.rfind(',')+1:-1]] += 1 + +for emotion, times in Emotions_found.items(): + print(f'{emotion}: {times}') \ No newline at end of file diff --git a/src/pages/train.tsx b/src/pages/train.tsx index 58ab3a6d..91be7f58 100644 --- a/src/pages/train.tsx +++ b/src/pages/train.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import SelectFileCard from "../components/selectFileCard/selectFileCard"; import axios from "axios"; import ResultTable from "../components/resultTable/resultTable"; @@ -47,10 +47,6 @@ export default function Train() { console.log(sendData); - const maxRetries = 3; - let retryCount = 0; - - const url = "http://localhost:5000/neural-network"; await axios @@ -65,7 +61,7 @@ export default function Train() { await axios .post(url, sendData) .catch((error) => { - console.error(error.response.data); + throw new Error(error); }) }) }) @@ -114,25 +110,42 @@ export default function Train() { // carregamento const [loadingProgress, setLoadingProgress] = useState(0); + const prevLoadingProgressRef = useRef(0); // Explicitly type prevLoadingProgressRef useEffect(() => { const fetchData = async () => { try { - const response = await axios.get( - "http://localhost:5000/training-status" - ); + const response = await axios.get("http://localhost:5000/training-status"); const { training_progress, training_in_progress } = response.data; - setLoadingProgress( - training_in_progress || training_progress === 100 - ? training_progress - : 0 - ); + const newProgress: number = training_in_progress || training_progress === 100 ? training_progress : 0; // Explicitly type newProgress + updateLoadingProgress(newProgress); } catch (error) { - console.error("Erro ao buscar progresso:", error); + console.error("Error fetching progress:", error); } }; - const interval = setInterval(fetchData, 3000); + const updateLoadingProgress = (newProgress: number) => { // Explicitly type newProgress parameter + const duration = 1000; // Duration in milliseconds for the transition + const startTime = Date.now(); + const startProgress = prevLoadingProgressRef.current; + + const updateProgress = () => { + const elapsedTime = Date.now() - startTime; + const progress = Math.min(1, elapsedTime / duration); // Ensure progress doesn't exceed 1 + const interpolatedProgress = startProgress + (newProgress - startProgress) * progress; + setLoadingProgress(interpolatedProgress); + + if (progress < 1) { + requestAnimationFrame(updateProgress); + } else { + prevLoadingProgressRef.current = newProgress; + } + }; + + updateProgress(); + }; + + const interval = setInterval(fetchData, 1050); return () => clearInterval(interval); }, []); @@ -301,10 +314,10 @@ export default function Train() {
- {`Treinamento em ${loadingProgress}%`} + {`Treinamento em ${loadingProgress.toFixed(2)}%`}
)}