using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Vision; namespace VehicleModelTraining { class Program { static void Main(string[] args) { // Caminho para os diretórios de treinamento (organizados por categoria) string datasetFolder = "C:\\Users\\USER\\Pictures\\Veiculos"; string workspaceFolder = "C:\\Users\\USER\\Pictures\\WorkSpace"; string outputModelPath = "C:\\Users\\USER\\Pictures\\neMmodel.zip"; // Inicializar MLContext MLContext mlContext = new MLContext(seed: 1); var preprocessingPipeline = mlContext .Transforms .LoadRawImageBytes( outputColumnName: "ImageBytes", imageFolder: datasetFolder, inputColumnName: "ImagePath") .Append( mlContext .Transforms .Conversion .MapValueToKey( inputColumnName: "Label", outputColumnName: "LabelAsKey" ) ); // Carregar dados - usamos um método mais direto aqui var images = LoadImagesFromDirectory(datasetFolder); Console.WriteLine($"Número total de imagens carregadas: {images.Count}"); // Verificar se há imagens if (images.Count == 0) { Console.WriteLine("Nenhuma imagem foi carregada. Verifique o caminho e as extensões suportadas."); return; } // Criar o DataFrame IDataView imageData = mlContext.Data.LoadFromEnumerable(images); IDataView shuffledImageDataView = mlContext .Data .ShuffleRows(imageData, 0); Console.WriteLine("Pre processing images...."); var timestamp = DateTime.Now; // Pre Process images and split into train/test/validation IDataView preProcessedImageDataView = preprocessingPipeline .Fit(shuffledImageDataView) .Transform(shuffledImageDataView); Console.WriteLine($"Image preprocessing done in {(DateTime.Now - timestamp).TotalSeconds} seconds"); Console.WriteLine(); var firstSplit = mlContext .Data .TrainTestSplit(data: preProcessedImageDataView, testFraction: 0.3, seed: 0); var trainSet = firstSplit.TrainSet; var secondSplit = mlContext .Data .TrainTestSplit(data: firstSplit.TestSet, testFraction: 0.5, seed: 0); var validationSet = secondSplit.TrainSet; var testSet = secondSplit.TestSet; var classifierOptions = new ImageClassificationTrainer.Options() { FeatureColumnName = "ImageBytes", LabelColumnName = "LabelAsKey", Arch = ImageClassificationTrainer.Architecture.InceptionV3, //Arch = ImageClassificationTrainer.Architecture.MobilenetV2, //Arch = ImageClassificationTrainer.Architecture.ResnetV250, TestOnTrainSet = false, ValidationSet = validationSet, ReuseTrainSetBottleneckCachedValues = true, ReuseValidationSetBottleneckCachedValues = true, WorkspacePath = workspaceFolder, MetricsCallback = Console.WriteLine }; var trainingPipeline = mlContext .MulticlassClassification .Trainers .ImageClassification(classifierOptions) .Append(mlContext .Transforms .Conversion .MapKeyToValue("PredictedLabel")); Console.WriteLine("Training model...."); timestamp = DateTime.Now; var trainedModel = trainingPipeline.Fit(trainSet); Console.WriteLine($"Model training done in {(DateTime.Now - timestamp).TotalSeconds} seconds"); Console.WriteLine(); Console.WriteLine("Calculating metrics..."); IDataView evaluationData = trainedModel.Transform(testSet); var metrics = mlContext .MulticlassClassification .Evaluate(evaluationData, "LabelAsKey"); Console.WriteLine($"LogLoss: {metrics.LogLoss}"); Console.WriteLine($"LogLossReduction: {metrics.LogLossReduction}"); Console.WriteLine($"MicroAccuracy: {metrics.MicroAccuracy}"); Console.WriteLine($"MacroAccuracy: {metrics.MacroAccuracy}"); Console.WriteLine(); Console.WriteLine($"{metrics.ConfusionMatrix.GetFormattedConfusionTable()}"); Console.WriteLine(); Console.WriteLine("Saving model"); Directory.CreateDirectory("Model"); mlContext.Model.Save(trainedModel, preProcessedImageDataView.Schema, outputModelPath); //Console.WriteLine(); //// Exibir informações das categorias //var categoryCount = images.GroupBy(x => x.Label).Select(g => new { Category = g.Key, Count = g.Count() }).ToList(); //foreach (var category in categoryCount) //{ // Console.WriteLine($"Categoria: {category.Category}, Contagem: {category.Count}"); //} //// Dividir em dados de treinamento e teste //var dataSplit = mlContext.Data.TrainTestSplit(imageData, testFraction: 0.2); //var trainSet = dataSplit.TrainSet; //var testSet = dataSplit.TestSet; //// IMPORTANTE: Para o ImageClassificationTrainer, vamos usar a abordagem recomendada //var classifierOptions = new ImageClassificationTrainer.Options() //{ // FeatureColumnName = "Image", // Alterado para 'Image' em vez de 'Input' // LabelColumnName = "LabelKey", // ValidationSet = testSet, // Arch = ImageClassificationTrainer.Architecture.ResnetV2101, // Epoch = 10, // BatchSize = 5, // LearningRate = 0.01f, // MetricsCallback = (metrics) => Console.WriteLine(metrics), // WorkspacePath = Path.Combine(Environment.CurrentDirectory, "workspace"), // //TrainDatasetPath = Path.Combine(Environment.CurrentDirectory, "trainFolder"), // //TestDatasetPath = Path.Combine(Environment.CurrentDirectory, "testFolder"), // //OutputTensorName = "softmax2_pre_activation", // Nome da camada de saída do Resnet // ReuseValidationSetBottleneckCachedValues = true, // ReuseTrainSetBottleneckCachedValues = true //}; //try //{ // // Criar diretórios necessários // EnsureDirectoryExists(classifierOptions.WorkspacePath); // //EnsureDirectoryExists(classifierOptions.TrainDatasetPath); // //EnsureDirectoryExists(classifierOptions.TestDatasetPath); // // Construir pipeline com passos específicos para o ImageClassificationTrainer // var pipeline = mlContext.Transforms.LoadImages( // outputColumnName: "Image", // imageFolder: "", // inputColumnName: nameof(ImageData.ImagePath)) // .Append(mlContext.Transforms.Conversion.MapValueToKey( // outputColumnName: "LabelKey", // inputColumnName: nameof(ImageData.Label))) // .Append(mlContext.MulticlassClassification.Trainers.ImageClassification(classifierOptions)) // .Append(mlContext.Transforms.Conversion.MapKeyToValue( // outputColumnName: "PredictedLabel", // inputColumnName: "PredictedLabel")); // Console.WriteLine("\nIniciando o treinamento do modelo..."); // // Treinar o modelo // ITransformer trainedModel = pipeline.Fit(trainSet); // Console.WriteLine("Treinamento concluído com sucesso!"); // // Salvar o modelo // mlContext.Model.Save(trainedModel, trainSet.Schema, outputModelPath); // Console.WriteLine($"Modelo salvo em: {outputModelPath}"); // // Avaliar o modelo // EvaluateModel(mlContext, testSet, trainedModel); // // Opcional: Remover diretórios temporários após o uso // //CleanupDirectory(classifierOptions.WorkspacePath); // //CleanupDirectory(classifierOptions.TrainDatasetPath); // //CleanupDirectory(classifierOptions.TestDatasetPath); //} //catch (Exception ex) //{ // Console.WriteLine($"Erro durante o treinamento: {ex.Message}"); // Console.WriteLine($"Detalhes: {ex.StackTrace}"); // if (ex.InnerException != null) // { // Console.WriteLine($"Inner Exception: {ex.InnerException.Message}"); // Console.WriteLine($"Inner Stack Trace: {ex.InnerException.StackTrace}"); // } //} } private static void EnsureDirectoryExists(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); Console.WriteLine($"Diretório criado: {path}"); } } private static void CleanupDirectory(string path) { if (Directory.Exists(path)) { try { Directory.Delete(path, recursive: true); Console.WriteLine($"Diretório removido: {path}"); } catch (Exception ex) { Console.WriteLine($"Erro ao remover diretório {path}: {ex.Message}"); } } } // Avaliar o desempenho do modelo nos dados de teste private static void EvaluateModel(MLContext mlContext, IDataView testData, ITransformer trainedModel) { IDataView predictions = trainedModel.Transform(testData); var metrics = mlContext.MulticlassClassification.Evaluate(predictions, labelColumnName: "LabelKey", predictedLabelColumnName: "PredictedLabel"); Console.WriteLine($"Acurácia micro: {metrics.MicroAccuracy:0.###}"); Console.WriteLine($"Acurácia macro: {metrics.MacroAccuracy:0.###}"); Console.WriteLine($"Pontuação de perda de log: {metrics.LogLoss:#.###}"); // Exibir matriz de confusão Console.WriteLine("Matriz de confusão:"); Console.WriteLine(metrics.ConfusionMatrix.GetFormattedConfusionTable()); } // Carregar imagens do diretório private static List LoadImagesFromDirectory(string folder) { List images = new List(); try { var files = Directory.GetFiles(folder, "*", searchOption: SearchOption.AllDirectories); var imageFiles = files.Where(file => Path.GetExtension(file).ToLower() == ".jpg" || Path.GetExtension(file).ToLower() == ".jpeg" || Path.GetExtension(file).ToLower() == ".png" || Path.GetExtension(file).ToLower() == ".webp").ToList(); Console.WriteLine($"Encontrados {imageFiles.Count} arquivos de imagem em {folder}"); foreach (var file in imageFiles) { // Verificar se o arquivo existe if (!File.Exists(file)) { Console.WriteLine($"Arquivo não encontrado: {file}"); continue; } // Determinar a categoria com base no nome da pasta pai var label = Directory.GetParent(file).Name; Console.WriteLine($"Processando imagem: {file}, Label: {label}"); images.Add(new ImageData() { ImagePath = file, Label = label }); } } catch (Exception ex) { Console.WriteLine($"Erro ao carregar imagens: {ex.Message}"); } return images; } // Classe para representar dados de imagem public class ImageData { public string ImagePath { get; set; } public string Label { get; set; } public byte[] ImageBytes { get; set; } } } }