一、概述
這次要解決的問題是輸入一張照片,輸出人物的顏值資料。
學習樣本來源於華南理工大學釋出的SCUT-FBP5500資料集,資料集包括 5500 人,每人按顏值魅力打分,分值在 1 到 5 分之間。其中包括男性、女性、中國人、外國人四個分類。
SCUT-FBP5500_full.csv檔案標記了每個圖片人物的顏值打分資料。(我把分值一項乘以了20,變成了滿分100分,不影響計算結果)
整個程式處理流程和前一篇圖片分類的基本一致,唯一的區別,分類用的是多元分類演算法,這次採用的是迴歸演算法。
二、原始碼
下面是全部程式碼:
namespace TensorFlow_ImageClassification { class Program { //Assets files download from:https://gitee.com/seabluescn/ML_Assets static readonly string AssetsFolder = @"D:\StepByStep\Blogs\ML_Assets"; static readonly string TrainDataFolder = Path.Combine(AssetsFolder, "FaceValueDetection", "SCUT-FBP5500"); static readonly string TrainTagsPath = Path.Combine(AssetsFolder, "FaceValueDetection", "SCUT-FBP5500_asia_full.csv"); static readonly string TestDataFolder = Path.Combine(AssetsFolder, "FaceValueDetection", "testimages"); static readonly string inceptionPb = Path.Combine(AssetsFolder, "TensorFlow", "tensorflow_inception_graph.pb"); static readonly string imageClassifierZip = Path.Combine(Environment.CurrentDirectory, "MLModel", "imageClassifier.zip"); //配置用常量 private struct ImageNetSettings { public const int imageHeight = 224; public const int imageWidth = 224; public const float mean = 117; public const float scale = 1; public const bool channelsLast = true; } static void Main(string[] args) { TrainAndSaveModel(); LoadAndPrediction(); Console.WriteLine("Hit any key to finish the app"); Console.ReadKey(); } public static void TrainAndSaveModel() { MLContext mlContext = new MLContext(seed: 1); // STEP 1: 準備資料 var fulldata = mlContext.Data.LoadFromTextFile<ImageNetData>(path: TrainTagsPath, separatorChar: ',', hasHeader: true); var trainTestData = mlContext.Data.TrainTestSplit(fulldata, testFraction: 0.2); var trainData = trainTestData.TrainSet; var testData = trainTestData.TestSet; // STEP 2:建立學習管道 var pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: TrainDataFolder, inputColumnName: nameof(ImageNetData.ImagePath)) .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: ImageNetSettings.imageWidth, imageHeight: ImageNetSettings.imageHeight, inputColumnName: "input")) .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: ImageNetSettings.channelsLast, offsetImage: ImageNetSettings.mean)) .Append(mlContext.Model.LoadTensorFlowModel(inceptionPb). ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput: true)) .Append(mlContext.Regression.Trainers.LbfgsPoissonRegression(labelColumnName: "Label", featureColumnName: "softmax2_pre_activation")); // STEP 3:通過訓練資料調整模型 ITransformer model = pipeline.Fit(trainData); // STEP 4:評估模型 var predictions = model.Transform(testData); var metrics = mlContext.Regression.Evaluate(predictions, labelColumnName: "Label", scoreColumnName: "Score"); PrintRegressionMetrics( metrics); //STEP 5:儲存模型 Console.WriteLine("====== Save model to local file ========="); mlContext.Model.Save(model, trainData.Schema, imageClassifierZip); } static void LoadAndPrediction() { MLContext mlContext = new MLContext(seed: 1); // Load the model ITransformer loadedModel = mlContext.Model.Load(imageClassifierZip, out var modelInputSchema); // Make prediction function (input = ImageNetData, output = ImageNetPrediction) var predictor = mlContext.Model.CreatePredictionEngine<ImageNetData, ImageNetPrediction>(loadedModel); DirectoryInfo testdir = new DirectoryInfo(TestDataFolder); foreach (var jpgfile in testdir.GetFiles("*.jpg")) { ImageNetData image = new ImageNetData(); image.ImagePath = jpgfile.FullName; var pred = predictor.Predict(image); Console.WriteLine($"Filename:{jpgfile.Name}:\tPredict Result:{pred.FaceValue}"); } } } public class ImageNetData { [LoadColumn(0)] public string ImagePath; [LoadColumn(1)] public float Label; } public class ImageNetPrediction { [ColumnName("Score")] public float FaceValue; } }
三、分析
1、資料處理通道
// STEP 2:建立學習管道 var pipeline = mlContext.Transforms.LoadImages(...) .Append(mlContext.Transforms.ResizeImages(...) .Append(mlContext.Transforms.ExtractPixels(...) .Append(mlContext.Model.LoadTensorFlowModel(inceptionPb) .ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput: true))
.Append(mlContext.Regression.Trainers.LbfgsPoissonRegression(labelColumnName: "Label", featureColumnName: "softmax2_pre_activation"));
LoadImages、ResizeImages、ExtractPixels:上篇文章都已經介紹過了;
ScoreTensorFlowModel方法把圖片畫素值轉換為圖片特徵資料,並儲存在softmax2_pre_activation列,Label列儲存的是顏值資料,通過迴歸演算法形成模型,當輸入新的特徵資料時就可以得出對應的顏值資料。
演算法採用的是:L-BFGS Poisson Regression (擬牛頓法泊松迴歸)
2、預測結果
在網上找了一些大頭照,通過程式進行預測,右側是預測結果:
預測結果雖然和我認為的不完全一致,但總體上可以接受,大方向沒什麼問題,存在偏差主要有以下幾個因素:
1、學習樣本的客觀性存疑,其打分資料可能是分配給多人打分後彙總的,每個人標準不一致;
2、被檢測圖片不是很規範,如尺寸、比例、背景、使用美顏軟體等;
3、顏值本身就不具備客觀性,不存在標準答案,如果我說林心如比如花漂亮,大家肯定都同意,但我如果說古力娜扎比迪麗熱巴漂亮,肯定有人不贊成。
四、資源獲取
原始碼下載地址:https://github.com/seabluescn/Study_ML.NET
工程名稱:TensorFlow_FaceValueDetection
資源獲取:https://gitee.com/seabluescn/ML_Assets (SCUT-FBP5500)