AI如何“慧眼識珠(zhū)”進行計數呢?

9Y 包裝機論壇AI如何“慧眼識珠”進行計數呢?已關閉評論137字數 4206閱讀14分1秒閱讀模式

在競爭日益激烈(liè)的製造業與電商領域,每(měi)一分成本都至關重要。您(nín)是否還在為產(chǎn)品計數環節而困擾?

  • 高價值小零件(如(rú)螺(luó)絲、珠寶、電子元件)的人(rén)工計數,效率低下且易出錯?

  • 藥品、保健品瓶裝前的計(jì)數(shù),對精度有嚴苛要求,容(róng)不得半點馬虎?

  • 海量零散物品的分裝與包裝,人工成本(běn)高昂,管理困難?

人(rén)工計數的時(shí)代,該落幕了。 是時候讓更智能、更可(kě)靠的夥伴——視覺計數包裝機,來接管這項繁瑣(suǒ)而關鍵的(de)任(rèn)務。

核心技術揭秘(mì):AI的“火眼金睛”是怎樣煉成的?

許多人好(hǎo)奇,這台機器是如何像人(rén)眼一樣,甚至比(bǐ)人眼更精準地識別並數(shù)出(chū)成千上(shàng)萬的物體的?其核心,在於融合了尖(jiān)端計算機視覺深(shēn)度學習AI的智能係統(tǒng)。整個過程,可以概括為以下四(sì)個精密的(de)步驟:

第(dì)一步:高清捕捉,“明察秋毫”
係統首先通過工業級高分辨率攝像頭,在均勻穩定的光源環(huán)境下,對傳送帶或振動(dòng)盤上的(de)待計數產品進行快速連續拍照。這確保了獲取的圖片清晰、無陰影、無畸變,為AI的精準分析打下堅(jiān)實基礎。

第二步:智(zhì)能識別,“去偽存真”
這是AI大顯身手的(de)環節。經過海量數據訓練的(de)深度學習模(mó)型,會對圖片進(jìn)行如下分析:

  • 特征提取(qǔ): AI模型能夠自動學習並識別目標物體的獨特特征,如形狀、大小、顏色、紋理、邊緣輪廓(kuò)等。無論是圓形的(de)藥片、方(fāng)形的芯片還是異形的螺絲,它都能精準捕捉其本質特征。

  • 目標(biāo)檢測與分割: AI會像一位經驗豐富的(de)老師傅,迅速在(zài)圖片中“圈出(chū)”每一個獨立的物體,哪怕它們有部(bù)分重疊或(huò)堆積。先進的算法能(néng)夠智能地(dì)將粘連的物體區(qū)分開來,極大降(jiàng)低了誤判率。

  • 分類過濾: 係(xì)統可以(yǐ)設定規則,自動忽略(luè)背景幹擾(rǎo)、灰塵或與目標物形態迥異的雜質,確保隻計數正確的產品,實現“去偽存真”。

第三步:精準計數,“分毫不錯”
在成(chéng)功識別出每一個物體後,AI會對其進行實時標記。係統會以驚人的速度對標記框(kuàng)進(jìn)行統計,無論是成千上萬的(de)零部件,還是細如發絲的元器件,都能在瞬間完成計數,速度遠超人工,且精度高達99.9%以上,徹底告(gào)別人工計(jì)數的誤差與爭議。

AI如何“慧眼識(shí)珠”進行計(jì)數呢?

從像素到數據:圖像識別計數AI的底層邏輯與算(suàn)法革新:

/// <summary>
/// 暗區域檢測數據集 - 自(zì)動加載圖像和標(biāo)注文(wén)件進行訓練
/// 支持多種標注格式並(bìng)包含針(zhēn)對暗區域的專用數據(jù)增強
/// </summary>
public class DarkRegionDataset : IEnumerable<Dictionary<string, Tensor>>, IDisposable
{
private readonly string[] imageFilePaths; // 圖(tú)像文件路徑數組
private readonly string[] annotationFilePaths; // 標(biāo)注文件路(lù)徑數組
private readonly DarkRegionDetectorConfig config; // 訓練配置參數
private readonly Random randomGenerator; // 隨機數生成器,用於數據增強
private readonly int inputImageSize; // 輸入(rù)圖像尺寸
private bool isDisposed = false; // 資源釋放標誌

/// <summary>
/// 構造函數 - 初始(shǐ)化數據集並驗證數據完整性
/// </summary>
public DarkRegionDataset(string imagesDirectory, string annotationsDirectory, DarkRegionDetectorConfig config)
{
this.config = config; // 保(bǎo)存配置(zhì)參數
this.randomGenerator = new Random(DateTime.Now.Millisecond); // 初始化隨機數生成器(qì)
this.inputImageSize = 640; // 設置輸入圖像尺寸為640x640

// 加載圖像文件路徑
this.imageFilePaths = Directory.GetFiles(imagesDirectory, "*.jpg") // 獲取(qǔ)所有jpg文件
.Concat(Directory.GetFiles(imagesDirectory, "*.png")) // 獲取所有png文件
.Concat(Directory.GetFiles(imagesDirectory, "*.bmp")) // 獲取所(suǒ)有bmp文(wén)件(jiàn)
.OrderBy(path => path) // 按路徑排序確保一致性
.ToArray(); // 轉換為數組

// 加載標注文件路徑(jìng)
this.annotationFilePaths = Directory.GetFiles(annotationsDirectory, "*.txt") // 獲取所有txt標注文件(jiàn)
.OrderBy(path => path) // 按路徑(jìng)排序
.ToArray(); // 轉換為數組

// 驗證數(shù)據完(wán)整性
ValidateDatasetIntegrity(); // 檢查圖(tú)像和標注文件是否匹配
Console.WriteLine($"數據集加載完成: {imageFilePaths.Length} 張圖像, {annotationFilePaths.Length} 個標注文件"); // 輸出加載信息
}

/// <summary>
/// 驗證(zhèng)數據集完整性(xìng) - 檢查圖像和標注文件是否匹配
/// </summary>
private void ValidateDatasetIntegrity()
{
if (imageFilePaths.Length != annotationFilePaths.Length) // 檢查數量是否一致
{
throw new InvalidDataException($"圖像文件數量({imageFilePaths.Length})與(yǔ)標注文件(jiàn)數量({annotationFilePaths.Length})不匹配"); // 拋出異(yì)常
}

// 檢查文件名是(shì)否對應
for (int i = 0; i < imageFilePaths.Length; i++) // 遍曆(lì)所有文件
{
string imageName = Path.GetFileNameWithoutExtension(imageFilePaths[i]); // 獲取圖像文件(jiàn)名(不含擴展名(míng))
string annotationName = Path.GetFileNameWithoutExtension(annotationFilePaths[i]); // 獲取標注文件名(不含擴展名(míng))

if (imageName != annotationName) // 檢查文件名(míng)是否一致
{
throw new InvalidDataException($"文件不匹配: {imageName} 與 {annotationName}"); // 拋出異常
}
}
}

/// <summary>
/// 獲取數據集大小
/// </summary>
public int Count => imageFilePaths.Length; // 返回圖像文件數量

/// <summary>
/// 索引器 - 通過索引獲取單個數據樣本(běn)
/// </summary>
public Dictionary<string, Tensor> this[int index]
{
get
{
if (index < 0 || index >= imageFilePaths.Length) // 檢查索引有效性
throw new IndexOutOfRangeException($"索引 {index} 超出範圍 [0, {imageFilePaths.Length - 1}]");

return LoadSingleSample(index); // 加載單個樣本
}
}

/// <summary>
/// 加載(zǎi)單個樣本 - 讀取圖像(xiàng)和標注(zhù)並執行預處理
/// </summary>
private Dictionary<string, Tensor> LoadSingleSample(int index)
{
// 加載並預處(chù)理圖像
Tensor processedImage = LoadAndPreprocessImage(imageFilePaths[index]); // 加(jiā)載和預處理圖像

// 加載並解析標注
Tensor processedAnnotations = LoadAndParseAnnotations(annotationFilePaths[index]); // 加載和解(jiě)析標注(zhù)

// 應(yīng)用數據增強(訓練時)
if (config.EnableDarknessEnhancement) // 如果啟用數據增強
{
(processedImage, processedAnnotations) = ApplyTrainingAugmentations(processedImage, processedAnnotations); // 應用數據增強
}

// 返回樣本字典
return new Dictionary<string, Tensor>
{
{ "image", processedImage }, // 處理後的圖像張量
{ "target", processedAnnotations } // 處理(lǐ)後的標注張(zhāng)量
};
}

/// <summary>
/// 加載和(hé)預處理圖像 - 讀取圖像文件並轉換為模型輸入格式
/// </summary>
private Tensor LoadAndPreprocessImage(string imagePath)
{
// 使(shǐ)用System.Drawing加載圖像
using (var bitmap = new Bitmap(imagePath)) // 加載位圖文件
{
// 轉(zhuǎn)換(huàn)為RGB格式(確保3通道)
using (var rgbBitmap = new Bitmap(bitmap.Width, bitmap.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb)) // 創建RGB位圖
{
using (var graphics = Graphics.FromImage(rgbBitmap)) // 創建(jiàn)繪圖對象
{
graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); // 繪(huì)製原圖像
}

// 將Bitmap轉換為Tensor
Tensor imageTensor = BitmapToTensor(rgbBitmap); // 轉換位圖為張量

// 應用圖像預處理
imageTensor = PreprocessImageTensor(imageTensor); // 預處理圖像張量

return imageTensor; // 返回處理後的張量
}
}
}

/// <summary>
/// 將Bitmap轉換(huàn)為Tensor - 圖像數據(jù)轉換為PyTorch張量(liàng)格式
/// </summary>
private Tensor BitmapToTensor(Bitmap bitmap)
{
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), // 鎖定位(wèi)圖數據
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); // 隻(zhī)讀模式

try
{
int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8; // 計算每像素字節數
byte[] pixelData = new byte[bitmapData.Stride * bitmap.Height]; // 創建像素數據(jù)數組(zǔ)
Marshal.Copy(bitmapData.Scan0, pixelData, 0, pixelData.Length); // 複製非托管數據到托管數組

// 將字節數據轉換為float張量
Tensor tensor = torch.zeros(new long[] { bitmap.Height, bitmap.Width, 3 }, torch.float32); // 創建空張量

for (int y = 0; y < bitmap.Height; y++) // 遍曆所有行
{
for (int x = 0; x < bitmap.Width; x++) // 遍曆所有列(liè)
{
int index = y * bitmapData.Stride + x * bytesPerPixel; // 計(jì)算像素索(suǒ)引

// 讀取BGR值並轉換為RGB
float b = pixelData[index] / 255.0f; // 藍色通道,歸一化到[0,1]
float g = pixelData[index + 1] / 255.0f; // 綠色通(tōng)道,歸一化到[0,1]
float r = pixelData[index + 2] / 255.0f; // 紅色通道,歸一化到[0,1]

tensor[y, x, 0] = r; // 紅色通道
tensor[y, x, 1] = g; // 綠(lǜ)色通道(dào)
tensor[y, x, 2] = b; // 藍色通道
}
}

return tensor; // 返回圖像張量
}
finally
{
bitmap.UnlockBits(bitmapData); // 解鎖位圖數據
}
}

/// <summary>
/// 圖(tú)像(xiàng)預處理 - 調整尺寸(cùn)、歸一化等操作
/// </summary>
private Tensor PreprocessImageTensor(Tensor image)
{
// 調整圖(tú)像(xiàng)尺寸到目標大小
image = functional.interpolate(image.unsqueeze(0), // 添加批(pī)次維(wéi)度並插值
new long[] { inputImageSize, inputImageSize }, // 目標尺寸
mode: InterpolationMode.Bilinear, // 雙線性插值
align_corners: false).squeeze(0); // 移除批次(cì)維度

// 如果配置為(wéi)單通道輸入(rù),轉換為灰度圖
if (config.InputChannels == 1) // 檢查是(shì)否需要單通道
{
image = ConvertToGrayscale(image); // 轉換為灰度圖
}

// 歸一化到(dào)[0,1]範圍(如果尚未歸一化)
if (image.max().item<float>() > 1.0f) // 檢查是否已經歸(guī)一化
{
image = image / 255.0f; // 歸一化到[0,1]
}

// 調(diào)整維度順序為 [C, H, W]
image = image.permute(new long[] { 2, 0, 1 }); // 從[H,W,C]變為[C,H,W]

return image; // 返回預處理後的圖像(xiàng)
}

/// <summary>
/// 轉(zhuǎn)換為(wéi)灰度圖 - 將RGB圖像轉換為單通道灰度圖
/// </summary>
private Tensor ConvertToGrayscale(Tensor rgbImage)
{
// 使用標準灰度轉換公式: Y = 0.299R + 0.587G + 0.114B
Tensor grayscale = 0.299f * rgbImage[":", ":", 0] + // 紅色分量
0.587f * rgbImage[":", ":", 1] + // 綠色分量
0.114f * rgbImage[":", ":", 2]; // 藍色分量

return grayscale.unsqueeze(2); // 添加通道維度 [H, W, 1]
}

/// <summary>
/// 加載和解(jiě)析(xī)標(biāo)注(zhù) - 讀取標注文件並轉換為模型目標格式
/// </summary>
private Tensor LoadAndParseAnnotations(string annotationPath)
{
var annotations = new List<float[]>(); // 創建標注列表

if (File.Exists(annotationPath)) // 檢查標注文件(jiàn)是否存在
{
string[] lines = File.ReadAllLines(annotationPath); // 讀取所有行
foreach (string line in lines) // 遍曆每一行
{
if (string.IsNullOrWhiteSpace(line)) // 跳過空(kōng)行
continue;

string[] parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); // 分割字(zì)符串
if (parts.Length >= 5) // 檢查格式是否正(zhèng)確(class x_center y_center width height)
{
float classId = float.Parse(parts[0]); // 類別ID
float xCenter = float.Parse(parts[1]); // 中心點x坐標(歸一(yī)化)
float yCenter = float.Parse(parts[2]); // 中心點y坐標(歸一化)
float width = float.Parse(parts[3]); // 寬度(歸一化)
float height = float.Parse(parts[4]); // 高度(dù)(歸(guī)一化)

annotations.Add(new float[] { classId, xCenter, yCenter, width, height }); // 添加到列表(biǎo)
}
}
}

// 轉換為Tensor格式
if (annotations.Count > 0) // 如(rú)果有標注
{
Tensor annotationTensor = torch.zeros(new long[] { annotations.Count, 5 }, torch.float32); // 創建標注(zhù)張(zhāng)量
for (int i = 0; i < annotations.Count; i++) // 遍曆所有標注(zhù)
{
annotationTensor[i] = torch.tensor(annotations[i]); // 設置每一行數據
}
return annotationTensor; // 返回標注張量
}
else // 如果沒有標注(zhù)(負樣本(běn))
{
return torch.zeros(new long[] { 0, 5 }, torch.float32); // 返回空標注
}
}

/// <summary>
/// 應用訓練時數據增強 - 提高(gāo)模(mó)型泛化能力
/// </summary>
private (Tensor image, Tensor annotations) ApplyTrainingAugmentations(Tensor image, Tensor annotations)
{
Tensor augmentedImage = image.clone(); // 克隆圖像,避(bì)免修改原始數據
Tensor augmentedAnnotations = annotations.clone(); // 克隆標注

// 隨機水平翻轉(50%概率)
if (config.EnableHorizontalFlip && randomGenerator.NextDouble() > 0.5) // 檢(jiǎn)查是(shì)否(fǒu)啟用並隨機決定
{
(augmentedImage, augmentedAnnotations) = ApplyHorizontalFlip(augmentedImage, augmentedAnnotations); // 應用水平翻轉
}

// 隨機亮度調整
if (randomGenerator.NextDouble() > 0.5) // 50%概率應(yīng)用亮度調整
{
augmentedImage = AdjustBrightness(augmentedImage, config.LuminanceAdjustment); // 調整亮度
}

// 隨機對比度調整
if (randomGenerator.NextDouble() > 0.5) // 50%概率應用對比度調整
{
augmentedImage = AdjustContrast(augmentedImage, config.ContrastVariation); // 調整(zhěng)對比度(dù)
}

// 針對暗區域的特殊增強
if (config.EnableDarknessEnhancement) // 如果(guǒ)啟(qǐ)用暗區域增強
{
augmentedImage = EnhanceDarkRegions(augmentedImage); // 增強暗區域
}

return (augmentedImage, augmentedAnnotations); // 返回增強後的數(shù)據和標注
}

/// <summary>
/// 應用水平翻轉 - 同時翻轉圖像和調整標注(zhù)坐(zuò)標
/// </summary>
private (Tensor image, Tensor annotations) ApplyHorizontalFlip(Tensor image, Tensor annotations)
{
// 翻轉圖像(在寬度維度)
Tensor flippedImage = functional.pad(image, new long[] { 0, 0, 0, 0 }, mode: PaddingModes.Reflect); // 填充
flippedImage = torch.flip(flippedImage, new long[] { 2 }); // 沿寬(kuān)度維度翻轉

// 調整標注坐標
if (annotations.shape[0] > 0) // 如果有標注
{
Tensor flippedAnnotations = annotations.clone(); // 克隆標注
flippedAnnotations[":", 1] = 1.0f - flippedAnnotations[":", 1]; // 翻轉x中心坐標
annotations = flippedAnnotations; // 更新標(biāo)注
}

return (flippedImage, annotations); // 返回翻轉後的圖像和(hé)標注
}

/// <summary>
/// 調整亮度 - 隨機改(gǎi)變圖像亮度
/// </summary>
private Tensor AdjustBrightness(Tensor image, float maxAdjustment)
{
float adjustment = (float)(randomGenerator.NextDouble() * maxAdjustment * 2 - maxAdjustment); // 隨(suí)機亮度調整量
return torch.clamp(image + adjustment, 0.0f, 1.0f); // 應(yīng)用調整並限製範圍
}

/// <summary>
/// 調整對比度 - 隨(suí)機改變圖像(xiàng)對比度
/// </summary>
private Tensor AdjustContrast(Tensor image, float maxFactor)
{
float factor = (float)(1.0 + randomGenerator.NextDouble() * maxFactor * 2 - maxFactor); // 隨機對比度因子
Tensor mean = image.mean(); // 計算圖像(xiàng)均值
return torch.clamp((image - mean) * factor + mean, 0.0f, 1.0f); // 應用對比度調整(zhěng)
}

/// <summary>
/// 增強(qiáng)暗區域 - 專門針對暗區域的對比度增強
/// </summary>
private Tensor EnhanceDarkRegions(Tensor image)
{
// 創建暗區域掩碼(像素值低於閾值)
Tensor darkMask = image < config.DarknessThreshold; // 暗區域掩碼

if (darkMask.any().item<bool>()) // 如果存在暗區域(yù)
{
// 增強暗區域對比度
Tensor enhancedDark = image * 1.5f; // 增強暗區域
enhancedDark = torch.clamp(enhancedDark, 0.0f, 1.0f); // 限製範圍

// 應用掩碼:隻增強暗區域
image = torch.where(darkMask, enhancedDark, image); // 條件替換
}

return image; // 返回增強(qiáng)後的圖像
}

/// <summary>
/// 實現迭代器接口 - 支持foreach遍曆
/// </summary>
public IEnumerator<Dictionary<string, Tensor>> GetEnumerator()
{
for (int i = 0; i < imageFilePaths.Length; i++) // 遍曆所有樣本
{
yield return this[i]; // 返回當前樣本
}
}

/// <summary>
/// 顯式接口實現 - 返回(huí)非泛型迭代器
/// </summary>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator(); // 返回泛型迭(dié)代器
}

/// <summary>
/// 釋放資源 - 實現(xiàn)IDisposable接口
/// </summary>
public void Dispose()
{
if (!isDisposed) // 如果尚未釋放
{
// 這裏可以釋放(fàng)任何非托管資源
isDisposed = true; // 標(biāo)記為已釋(shì)放
GC.SuppressFinalize(this); // 阻止終結(jié)器調用
}
}
}
2. 暗區域訓練器(完整訓練流程)
csharp
/// <summary>
/// 暗區域檢測訓練器 - 管理完(wán)整的模型(xíng)訓練流程
/// 包含訓練循環、驗證(zhèng)、模型保存和進度監控
/// </summary>
public class DarkRegionTrainer : IDisposable
{
private DarkRegionDetector model; // 暗區域檢(jiǎn)測模型
private optim.Optimizer modelOptimizer; // 模型優化(huà)器
private DarkRegionDetectionLoss lossFunction; // 損失(shī)函數(shù)
private DarkRegionDetectorConfig trainingConfig; // 訓練配置
private Device trainingDevice; // 訓練設備(CPU/GPU)
private LearningRateScheduler learningRateScheduler; // 學(xué)習率調度器
private bool isDisposed = false; // 資源釋放標誌

/// <summary>
/// 訓練進度事件 - 用於報告訓練進(jìn)度和指標
/// </summary>
public event Action<TrainingProgress> TrainingProgressUpdated;

/// <summary>
/// 構造函數 - 初始化訓練器的所有(yǒu)組件
/// </summary>
public DarkRegionTrainer(DarkRegionDetectorConfig config)
{
this.trainingConfig = config; // 保存(cún)訓練配置(zhì)
InitializeTrainingDevice(); // 初始化訓練設備
InitializeModelComponents(); // 初始化模型(xíng)和優化器
InitializeLearningRateScheduler(); // 初(chū)始化學習率調(diào)度器

Console.WriteLine($"訓練器初始化完成,使用設備: {trainingDevice}"); // 輸出初始化信息
}

/// <summary>
/// 初始化訓練(liàn)設備(bèi) - 自動選擇CPU或GPU
/// </summary>
private void InitializeTrainingDevice()
{
if (torch.cuda.is_available()) // 檢查(chá)CUDA是(shì)否可用
{
trainingDevice = Device.CUDA; // 使用GPU
Console.WriteLine("檢測到CUDA設備,使用GPU進(jìn)行訓練"); // 輸出GPU信息
}
else // 如果沒有GPU
{
trainingDevice = Device.CPU; // 使(shǐ)用CPU
Console.WriteLine("未檢測到CUDA設備,使用CPU進行訓練"); // 輸出CPU信息
}
}

/// <summary>
/// 初始化模(mó)型組件 - 創建模型、損失函數和優化器
/// </summary>
private void InitializeModelComponents()
{
// 初始化暗區(qū)域檢測模型
this.model = new DarkRegionDetector(trainingConfig, trainingDevice, ScalarType.Float32); // 創建模型

// 初始化損失函數,針對暗區域檢測優化參數
this.lossFunction = new DarkRegionDetectionLoss(
darkRegionWeight: 2.0f, // 暗區域權重較高
positiveSampleWeight: 1.0f, // 正樣本標準權(quán)重
negativeSampleWeight: 0.5f // 負樣本權重較低
);

// 將模型和損失函數移動到(dào)訓練設(shè)備
model.to(trainingDevice); // 移動模型到設備
lossFunction.to(trainingDevice); // 移動損失函數到(dào)設備

// 初始化優化器,使用Adam優化器
var trainableParameters = model.parameters().Where(param => param.requires_grad).ToList(); // 獲取(qǔ)可訓練參數
this.modelOptimizer = optim.Adam(
trainableParameters, // 可(kě)訓練參(cān)數列表(biǎo)
trainingConfig.InitialLearningRate, // 初始學習率
weight_decay: trainingConfig.RegularizationStrength // 權(quán)重(chóng)衰減
);

Console.WriteLine($"模(mó)型初始化(huà)完成,可訓練(liàn)參數: {trainableParameters.Count}"); // 輸出模型信息
}

/// <summary>
/// 初(chū)始化學習率(lǜ)調度器 - 動態調整學習率
/// </summary>
private void InitializeLearningRateScheduler()
{
// 使用餘弦退火學習率調度
this.learningRateScheduler = optim.lr_scheduler.CosineAnnealingLR(
modelOptimizer, // 優化器
T_max: trainingConfig.TotalEpochs, // 總周期數
eta_min: trainingConfig.InitialLearningRate * 0.01f // 最(zuì)小學習率
);
}

/// <summary>
/// 執行完整訓(xùn)練流(liú)程 - 包含訓練和驗證
/// </summary>
public void ExecuteTraining(string trainingImagesPath, string trainingAnnotationsPath,
string validationImagesPath = null, string validationAnnotationsPath = null)
{
// 加載訓練(liàn)數(shù)據集
using (var trainingDataset = new DarkRegionDataset(trainingImagesPath, trainingAnnotationsPath, trainingConfig)) // 創建訓練數據集
{
DarkRegionDataset validationDataset = null; // 驗證數據集

// 如果有(yǒu)驗證數據,加載驗證集
if (!string.IsNullOrEmpty(validationImagesPath) && !string.IsNullOrEmpty(validationAnnotationsPath)) // 檢查驗證路徑
{
validationDataset = new DarkRegionDataset(validationImagesPath, validationAnnotationsPath, trainingConfig); // 創建驗證數據集
Console.WriteLine($"驗證集加(jiā)載完成: {validationDataset.Count} 個樣本(běn)"); // 輸(shū)出(chū)驗證集信息
}

// 創建數(shù)據加載器
using (var trainingDataLoader = new DataLoader(trainingDataset, trainingConfig.BatchSize, shuffle: true)) // 訓(xùn)練數據加載器
{
// 執行訓(xùn)練循環
for (int currentEpoch = 0; currentEpoch < trainingConfig.TotalEpochs; currentEpoch++) // 遍曆所有訓練周期
{
// 執行單個訓練周期
float epochLoss = ExecuteSingleTrainingEpoch(trainingDataLoader, currentEpoch); // 訓(xùn)練一(yī)個周期

// 如果有驗證集,執行驗證
float validationLoss = 0f;
if (validationDataset != null) // 如果有驗證集
{
using (var validationDataLoader = new DataLoader(validationDataset, trainingConfig.BatchSize, shuffle: false)) // 驗證數據加載器
{
validationLoss = ExecutevalsidationEpoch(validationDataLoader, currentEpoch); // 執行驗證
}
}

// 更新學(xué)習率
learningRateScheduler.step(); // 調整學習率

// 報告訓練進(jìn)度
ReportTrainingProgress(currentEpoch, epochLoss, validationLoss); // 報告進度

// 定期保存模型檢(jiǎn)查點
if ((currentEpoch + 1) % 10 == 0 || currentEpoch == trainingConfig.TotalEpochs - 1) // 每10個周期或最後周期
{
SaveModelCheckpoint(currentEpoch, epochLoss, validationLoss); // 保存檢查點(diǎn)
}
}
}

// 釋(shì)放驗證數據集
validationDataset?.Dispose(); // 如果(guǒ)存在驗證集,釋放資源
}

Console.WriteLine("訓練完成!"); // 輸出完成信息
}

/// <summary>
/// 執行單(dān)個(gè)訓練周期 - 遍曆整個訓練集並更新模型參數
/// </summary>
private float ExecuteSingleTrainingEpoch(DataLoader trainingLoader, int epochNumber)
{
model.train(); // 設置模型為訓練模式
float totalEpochLoss = 0f; // 累計損失
int processedBatches = 0; // 已處理(lǐ)批次計數

Console.WriteLine($"開始訓練周(zhōu)期 {epochNumber + 1}/{trainingConfig.TotalEpochs}"); // 輸出周期開(kāi)始(shǐ)信息

foreach (var batchData in trainingLoader) // 遍(biàn)曆所(suǒ)有訓練批次
{
// 清空梯度
modelOptimizer.zero_grad(); // 清零梯度

繼續閱讀
我的微信
這是我的微信掃一掃(sǎo)
weinxin
我的微信
微信號已複製
我的微(wēi)信公眾號
我的微信(xìn)公眾號掃一(yī)掃
weinxin
我的公眾號
公眾號已複製
 
如何(hé)通過(guò)參(cān)數化配方,一鍵切換螺絲與糖果的包裝模式? 包裝(zhuāng)機論(lùn)壇

如何(hé)通過(guò)參數化(huà)配方,一鍵切(qiē)換螺絲與糖果的包裝模(mó)式?

如何通過參數化配方,一鍵切換螺(luó)絲與(yǔ)糖果的包裝(zhuāng)模式? 在競爭日益激烈的製造業中,柔(róu)性生產能力已成為企業的核心優勢(shì)。今天您的產線還(hái)在包裝M4x10的金屬(shǔ)螺絲,明(míng)天可能就需要切換至塑料件。傳統設備麵對這種跨...
視覺計數包裝機終極指南:原理、優勢與(yǔ)選型全解析 | 氿億智能裝備 包裝機(jī)論壇

視覺計數包裝機終極指南:原理、優勢與(yǔ)選型全解析 | 氿億智能裝備

視覺計數包裝機:重塑工(gōng)業計數(shù)包裝的智能核心,選對是關鍵! 在工業(yè)製造與物流配送領域,螺絲、螺母、藥片、膠(jiāo)囊、電子元(yuán)件等(děng)小型物料的計(jì)數包(bāo)裝是一項高(gāo)頻且關鍵的工序。傳統人工計數方式速度慢、易出錯,已成為製...
視覺計數包裝機的“大腦”:AI深度學習算法如(rú)何工作? 包裝機論壇

視覺(jiào)計數包裝機的“大腦(nǎo)”:AI深度學習算法如(rú)何(hé)工作?

視覺計數包(bāo)裝機的“大腦”:AI深度學習算法如何工作? 當人們驚歎於視覺計數包裝機99.99% 的計數精度時(shí),其背後真正(zhèng)的英雄並非冰冷的鋼鐵機械,而是一個(gè)無形的智能中樞——AI深度學習算法。它如同設備的...