MATLAB深度学习之数据集-数据库构建方法详解

发布于:2025-08-07 ⋅ 阅读:(25) ⋅ 点赞:(0)

前言

在MATLAB中,训练深度学习模型时,数据库的构建与输入是关键十分关键的一环,真对不同的数据类型和训练样本,正确的数据构建是训练代码跑通的基本前提。

本文章主要基于matlab官方文档内容和实际应用问题、技巧进行的总结。

基础概念-张量维度的类型:

        首先是模型输入的数据类型,MATLAB的深度学习构建框架支持"single", "double", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "logical", "char",12种类型的数据类型的输入。但通常会被输入层进行转化和归一化,在显卡中以single的数据类型进行推理和传播。

        而需要注意的是,在MATLAB中深度学习传递的张量是具有严格定义的,其常见的张量维度的定义如下:

数据简写 全称 规则特性
S Spatial 空间维度
C Channel 通道维度  对应深度学习模型中的特征维度,因此是大多数模型必带的维度,即使维度长度为1。需要注意的是,通道维度卷积过程中的计算规则与空间维度不同。
B Batch 批次维度 样本维度,可变的,数值可以设置成“NaN”
T Time 时相维度    循环神经网络中递推维度,无法直接参与卷积和自注意力机质的计算。
U Unspecified 未定义 自定义或需自动识别的维度。

所以即使是两组一模一样的4-D 张量,定义不同,区别也天差地别,以下是常见数据类型的张量维度定义参考。

  • 1024波段的波谱曲线:S(1024)-C(1)
  • 1024*1024分辨率的RGB图像:S(1024)*S(1024)*C(3)
  • 1024波段的1024*1024分辨率的高光谱图像:S(1024)*S(1024)*C(1024)
  • 1000个时间记录点的声波(含2000个频率点的强度数据)信息:C(2000)-T(1000)
  • 一共600帧的一个1000*1000分辨率的RGB视频:S(1000)*S(1000)*C(3)*T(600)

而批次维度“B”则主要用于适应训练和视频预测的显存瓶颈,如对于小显存的显卡,图像训练的批次量为32。

S(1024)*S(1024)*C(3)*B(32)

大显存,批次量为128时:

S(1024)*S(1024)*C(3)*B(128)

注意:对于拥有时相的数据:

32个批次,1000个时间记录点的声波(含2000个频率点的强度数据)信息:C(2000)-B(32)-T(1000),B不是最后一个维度,而T是。这是考虑到计算的方便性设计的。

MATLAB的DataStore对象

DataStore对象是Matlab深度学习训练的基石。其包含如下10种常见的对象,其中ArrayDatastore、ImageDatastore、CombinedDatastore以及FileDatastore是常用的数据储存对象。本文主要根据常用的类型进行介绍和讲解。

ArraryDatastore 矩阵数据存储-内存直读
TabularTextDatastore 表格文本文件数据存储
ImageDatastore 图像数据存储
SpreadsheetDatastore 用于电子表格文件的数据存储
KeyValueDatastore 用于 mapreduce 的键-值对组数据的数据存储
TallDatastore 用于存放 tall 数组的检查点的数据存储
DatabaseDatastore (Database Toolbox) 数据库中数据的数据存储,支持SQL
CombinedDatastore 组合数据库
ParquetDatastore         用于 Parquet 文件集合的数据存储
fileDatastore        

具有自定义文件读取器的数据存储

直接从内存中引入数据-arrayDatastore:

arrayDatastore-使用介绍

ArrayDatastore 是利用小型数据训练深度学习的关键函数,其基于内存中数据创建的数据存储。使用 arrayDatastore 函数创建 ArrayDatastore 对象。

arrds = arrayDatastore(A,Name,Value)

输入参数

A — 输入数组、张量
输入数组,指定为矩阵。ArrayDatastore 属性描述数据存储对象中内存数据的格式,并控制如何从数据存储中读取数据。

--------------------------------------------参数介绍 Name-Value---------------------------------------------
ReadSize — 要读取的数据量
1 (默认) | 正整数
在调用 read 函数时要读取的数据量,指定为由 'ReadSize' 和正整数组成的以逗号分隔的对组。每次调用 read 最多读取 ReadSize 行。如果为 'ReadSize' 指定的值超过输入数据的行数,则 read 将读取数据存储对象中的所有行。

'ReadSize' 的默认值是 1。

数据类型: double


IterationDimension — 要在其中读取数据的维度
1 (默认) | 正整数
在调用 read 函数时要在其中读取数据的维度,指定为由 'IterationDimension' 和正整数组成的以逗号分隔的对组。例如,'IterationDimension',2 使 read 从数据存储对象返回列向数据。'IterationDimension' 的默认值为 1,这使得 read 返回行向数据。

如果将 'OutputType' 属性的值指定为 'same',则 'IterationDimension' 必须设置为值 1。

如果在创建 ArrayDatastore 对象后修改 'IterationDimension' 的值,则数据存储会重置为未读状态。

数据类型: double


OutputType — 输出数据类型
'cell' (默认) | 'same'
输出数据类型,指定为以逗号分隔的对组,该对组由 'OutputType' 和下列值之一组成:

'cell' - 以 n×1 元胞数组形式返回数据。例如,如果 A 是数值数组,ReadSize 是 3,则 read 返回由数值数据组成的 3×1 元胞数组。Cell是较为通用的输出格式,支持字符、数值、布尔类型的输出。

'same' - 返回与输入数组 A 相同的数据类型。例如,如果 A 是数值数组,则 read 返回数值数组。
OutputType 的值决定 preview、read 和 readall 函数返回的数据类型。

如果在创建 ArrayDatastore 对象后修改 'OutputType' 的值,数据会存储重置为未读状态。

数据类型: char | string

arrayData对象

利用arrayDatastore函数建立ArrayData对象,可直接输入至配合的深度学习网络进行训练,程序会将维度按S-C-B-T的先后顺序自动排序。如将1个4-D矩阵转化为arrayData中(其中第4个维度是样本维度-也就是批次维度),再arryData 输入深度学习网络A(S-S-C-B)和深度学习网络B(S-C-B-T)是完全不同的,在深度学习网络B中,第四个维度是时序维度,不是样本维度,会导致数据匹配错误。这一问题在RNN的训练中需要重点注意,如需进行匹配,要将数据的第三维度与第四维度进行翻转,才能对样本维度进行匹配。读者可以参考以下两个实战案例进行深入理解:

RNN之:LSTM 长短期记忆模型-结构-理论详解-及实战(Matlab向)-CSDN博客

利用Bi-LSTM实现基于光谱数据对数值进行预测-实战示例(Matlab)_bi-lstm预测-CSDN博客

ArrayDatastore是典型的MATLAB的Datastore对象,支持的各类Datastore函数如下所示,利用对象函数,可以实现训练过程中对数据库的细节控制,从而实现较为复杂的模型训练方法。典型的如生成对抗网络,读者可以参考下列文章进行深入理解:

MATLAB代码解析:利用DCGAN实现图像数据的生成 全网最细&DCGAN设计-训练入门(源码分享)_projectandreshapelayer-CSDN博客

MATLAB实战 利用1D-DCGAN生成光谱或信号数据-CSDN博客

对象函数
hasdata 确定是否有数据可读取
numpartitions 数据存储分区数
partition 划分数据存储
preview 预览数据存储中的数据子集
read 读取数据存储中的数据
readall 读取数据存储中的所有数据
reset 将数据存储重置为初始状态
transform 变换数据存储
combine 合并来自多个数据存储的数据
shuffle 对数据存储中的所有数据进行乱序处理
subset 创建数据存储或 FileSet 的子集

常规图像数据-imageDatastore:

imageDatastore的使用介绍

imds = imageDatastore(location,Name,Value)

location-包含数据的文件夹

总文件夹路径,其可以包含多级子文件,子文件夹中再包含图像数据。
数据类型: char | cell | string

--------------------------------------------参数介绍 Name-Value---------------------------------------------
IncludeSubfolders — 子文件夹包含标记
false (默认) | true
子文件夹包含标志,指定为由 "IncludeSubfolders" 和 true 或 false 组成的名称-值参量。指定 true 可包含每个文件夹中的所有文件和子文件夹,指定 false 则仅包含每个文件夹中的文件。

如果不指定 "IncludeSubfolders",则默认值为 false。

示例: "IncludeSubfolders",true

数据类型: logical | double


FileExtensions — 图像文件扩展名
字符向量 | 字符向量元胞数组 | 字符串标量 | 字符串数组
图像文件扩展名,指定为以逗号分隔的对组,其中包含 "FileExtensions" 和一个字符向量、字符向量元胞数组、字符串标量或字符串数组。指定的扩展名不要求采用 imformats 格式,您可以使用空引号 "" 来表示不带扩展名的文件。如果未指定 "FileExtensions",imageDatastore 将自动包含指定路径中具有 imformats(imformats - 管理图像文件格式注册表 - MATLAB) 扩展名的所有图像。如果要包含 imformats 无法识别的扩展名,请指定所有扩展名。

数据类型: char | cell | string


AlternateFileSystemRoots — 备用文件系统根路径
字符串向量 | 元胞数组
备用文件系统根路径,以名称-值参量形式指定,其中包含 "AlternateFileSystemRoots" 和一个字符串向量或元胞数组。用于利用云盘的数据进行训练,普通项目一般不用。

数据类型: string | cell


LabelSource — 提供标签数据的源
"none" (默认) | "foldernames"
提供标签数据的源,指定为由 "LabelSource" 和 "none" 或 "foldernames" 组成的名称-值参量。如果指定了 "none",则 Labels 属性为空。如果指定了 "foldernames",将根据文件夹名称分配标签并存储在 Labels 属性中。。

数据类型: char | string

ImageDatastore对象

Matlab的 ImageDatastore 对象是一种不需要一次性引入全部图像数据的训练数据管理。使用 imageDatastore 函数创建 ImageDatastore 对象,指定其属性,然后使用对象函数导入和处理数据。其引入的数据格式为S-S-C,一般服务于图像数据或是高光谱数据。其支持的函数如下所示:

countEachLabel 对 ImageDatastore 标签中的文件进行计数
hasdata 确定是否有数据可读取
numpartitions 数据存储分区数
partition 划分数据存储
preview 预览数据存储中的数据子集
read 读取数据存储中的数据
readall 读取数据存储中的所有数据
readimage 从数据存储读取指定的图像
writeall 将数据存储写入文件
reset 将数据存储重置为初始状态
shuffle 对数据存储中的所有数据进行乱序处理
splitEachLabel 按比例拆分 ImageDatastore 标签
subset 创建数据存储或 FileSet 的子集
transform 变换数据存储
combine 合并来自多个数据存储的数据
isPartitionable 确定数据存储是否可分区
isSubsettable 确定数据存储是否可子集
isShuffleable 确定数据存储是否可乱序

利用imageDatastore引入一维张量数据

可以取巧地利用imageDatastore来引入一维波谱数据S-C,首先将一维波谱数据另存为图像,一般以映射后的uint16或是uint32格式来保存,避免数据精度的损失;如1000长度的波谱数据保存后的图像为S(1000)-S(1)-C(1)。再应用imageDatastore进行引入,结合深度学习算法进行训练,但一些模型组件也要进行替换,如用2D卷积核代替1D卷积核。

但总体而言,属于下下策。1是会有数据精度损失。2是这种取巧行为不会使模型性能有显著提升,浪费精力。

通用大型数据引入-datastore

datastore函数可以引入matlab支持的大多数数据。但其需要提供更多的输入参数来规范输入内容,具体可以参考官方文档。

ds = datastore(location,Name,Value)

多源输入的数据引入-CombinedDatastore

combine函数的应用

CombinedDatastore是构建多源异构输入模型训练使用数据的最直接方法。对于不同类型的Datastore数据,combinedDatastore具有极强兼容性。

想要创建CombinedDatastore,使用combine函数按输入次序衔接各类数据库即可:
dsnew = combine(ds1,ds2,...,dsN,ReadOrder=order)
 

输出参数
ds1、ds2...
合并的数据储存,为DataStore类型

order-读取数据的顺序

associated(默认)|sequential

combine函数也可创建次序读取的组合储存库,设置成sequential时,类似于这一次读取的是一张图像,下一次读取就是一条曲线(1维张量)。

而设置成associated则是平行输出,也就是用于多源输入的模型。

其中,是可以将验证数据一并打包进combinedDatastore中,训练函数会自动识别。比如将categorical()对象或是数值对象用arrayDatastore打包,放在combine()函数的最后。

以下是部分参考案例:
多源图像融合训练的脚本示例 (Matlab训练多个输入的CNN模型)_怎么将多源数据进行训练-CSDN博客

combineDatastore对象

combineDatastore对象支持函数如下所示,最骚的是,其继续支持combine函数,也就是可以进行多次合并。

combine 合并来自多个数据存储的数据
hasdata 确定是否有数据可读取
preview 预览数据存储中的数据子集
read 读取数据存储中的数据
readall 读取数据存储中的所有数据
writeall 将数据存储写入文件
reset 将数据存储重置为初始状态
transform 变换数据存储
numpartitions 数据存储分区数
partition 划分数据存储
shuffle 对数据存储中的所有数据进行乱序处理
isPartitionable 确定数据存储是否可分区
isSubsettable Determine whether datastore is subsettable
isShuffleable 确定数据存储是否可乱序

自定义数据库--更高级的玩法

MATLAB可以自定义数据,来实现更加客制化的训练操作和框架设计。
我们以Pix2Pix条件生成对抗网络的训练数据为例,该模型训练要求输入的数据为一对强绑定的图像数据,分别输入生成器和判别器,因此我们需要利用自定义数据库来实现这一训练效果。

其需要包含以下功能:

1:批量读取用于训练

2:确保读取的两个文件夹的图像,在读取时都是一一对应

3:可以打乱读取顺序,打乱顺序后数据还是一一对应

为实现以上目标,我们可以使其继承Datastore对象的属性,特别shuffleable和Minibatchable这两个属性。以下是实现方法,各位可以参考。

其中,两组图像分别用ImageDatastore进行储存,需要注意的是,得保证两组数据储存的名称一一对应。

classdef PairedImageDatastore < matlab.io.Datastore & ...
                                matlab.io.datastore.Shuffleable & ...
                                matlab.io.datastore.MiniBatchable
% 配对图像数据存储类(PairedImageDatastore)
% 
% 功能:用于同时读取两个文件夹中的配对图像,支持以下功能:
%   - 小批量读取(Mini-batching):分批加载数据以减少内存消耗
%   - 数据随机打乱(Shuffling):在训练时打乱数据顺序防止模型过拟合
%   - 保持配对关系:确保每次读取的图像A和图像B保持对应关系

% 版权信息(Copyright 2020 The MathWorks, Inc.)

    % 依赖属性(通过其他属性计算得出)
    properties (Dependent)
        MiniBatchSize  % 小批量大小(每次读取的图像对数量)
    end
    
    % 受保护属性(只能通过类方法修改)
    properties (SetAccess = protected)
        DirA            % 文件夹A路径(存放第一组图像)
        DirB            % 文件夹B路径(存放第二组图像)
        ImagesA         % 图像数据存储对象A(读取自DirA)
        ImagesB         % 图像数据存储对象B(读取自DirB)
        NumObservations % 总观测数(即图像对总数)
        MiniBatchSize_  % 实际存储小批量大小的内部变量
        Augmenter       % 图像增强器(用于数据扩增)
        PreSize         % 预处理尺寸(调整图像大小)
        CropSize        % 裁剪尺寸(随机裁剪区域)
        ARange          % 图像A的像素值归一化范围
        BRange          % 图像B的像素值归一化范围
    end
     
    % 静态方法(与类相关但不依赖具体实例)
    methods (Static)
        function [inputs, remaining] = parseInputs(varargin)
            % 解析输入参数
            % 输入参数格式:'参数名',参数值
            % 示例:'PreSize',[256,256],'CropSize',[224,224]
            %% 用户调用示例 [params, extra] = parseInputs('CropSize',[224,224],'RotationRange',30);
            
            parser = inputParser(); % 创建 inputParser 对象,方便定义参数规则并解析
            parser.KeepUnmatched = true;  % 保留未匹配参数供增强器使用
            
            % 定义已知参数及其默认值
            parser.addParameter('PreSize', [256, 256]);  % 预处理尺寸
            parser.addParameter('CropSize', [256, 256]);  % 裁剪尺寸
            parser.addParameter('ARange', 255);           % A图归一化范围
            parser.addParameter('BRange', 255);           % B图归一化范围
            
            parser.parse(varargin{:});   %% 解析输入参数
            inputs = parser.Results;     % 返回已解析参数值
            remaining = parser.Unmatched;% 返回未匹配参数
        end
    end
    
    % 类主要方法
    methods
        function obj = PairedImageDatastore(dirA, dirB, miniBatchSize, varargin)
            % 构造函数:创建配对图像数据存储对象
            % 输入参数:
            %   dirA            - 文件夹路径或文件名列表(存放图像A)
            %   dirB            - 文件夹路径或文件名列表(存放图像B)
            %   miniBatchSize   - 每个小批量包含的图像对数量
            %   varargin        - 可选参数(名称-值对,如'PreSize',[256,256])
            
            includeSubFolders = true;  % 包含子文件夹中的文件
            
            % 初始化基础属性
            obj.DirA = dirA;
            obj.DirB = dirB;
            
            % 创建图像数据存储对象(Matlab内置类)
            obj.ImagesA = imageDatastore(obj.DirA, "IncludeSubfolders", includeSubFolders);
            obj.ImagesB = imageDatastore(obj.DirB, "IncludeSubfolders", includeSubFolders);
            
            % 设置小批量大小
            obj.MiniBatchSize = miniBatchSize;
            
            % 验证两个文件夹中的文件数量是否一致
            assert(numel(obj.ImagesA.Files) == numel(obj.ImagesB.Files), ...
                    'p2p:datastore:notMatched', ...
                    '文件夹A和B中的文件数量不匹配');
            obj.NumObservations = numel(obj.ImagesA.Files);  % 记录总图像对数
            
            % 解析可选参数
            [inputs, remaining] = obj.parseInputs(varargin{:});
            
            % 配置图像处理参数
            obj.ARange = inputs.ARange;
            obj.BRange = inputs.BRange;
            obj.Augmenter = imageDataAugmenter(remaining);  % 创建图像增强器
            obj.PreSize = inputs.PreSize;
            obj.CropSize = inputs.CropSize;
        end
        
        function tf = hasdata(obj)
            % 检查是否还有未读取的数据
            % 返回:
            %   tf - 逻辑值(true表示还有数据)
            tf = obj.ImagesA.hasdata() && obj.ImagesB.hasdata();
        end
        
        function data = read(obj)
            % 读取下一个小批量数据
            % 返回:
            %   data - 包含A/B图像的表格(table)
            
            % 从两个数据存储中分别读取数据
            imagesA = obj.ImagesA.read();
            imagesB = obj.ImagesB.read();
            
            % 确保数据以cell数组形式存储(处理单图像情况)
            if ~iscell(imagesA)
                imagesA = {imagesA};
                imagesB = {imagesB};
            end
            
            % 对图像进行预处理和增强
            [transformedA, transformedB] = ...
                p2p.data.transformImagePair(imagesA, imagesB, ...
                                          obj.PreSize, obj.CropSize, ...
                                          obj.Augmenter);
            
            % 归一化像素值到[-1, 1]范围
            [A, B] = obj.normaliseImages(transformedA, transformedB);
            
            % 将数据打包为表格返回
            data = table(A, B);
        end
        
        function reset(obj)
            % 重置数据读取位置到开头
            obj.ImagesA.reset();
            obj.ImagesB.reset();
        end
        
        function objNew = shuffle(obj)
            % 创建数据存储的随机打乱副本
            % 返回:
            %   objNew - 打乱后的新数据存储对象
            
            objNew = obj.copy();
            numObservations = objNew.NumObservations;
            
            % 复制底层数据存储对象
            objNew.ImagesA = copy(obj.ImagesA);
            objNew.ImagesB = copy(obj.ImagesB);
            
            % 生成随机排列索引
            idx = randperm(numObservations);
            
            % 重新排列文件顺序(保持A/B对应关系)
            objNew.ImagesA.Files = objNew.ImagesA.Files(idx);
            objNew.ImagesB.Files = objNew.ImagesB.Files(idx);
        end
        
        function [aOut, bOut] = normaliseImages(obj, aIn, bIn)
            % 图像归一化处理
            % 公式:2*(像素值/范围) - 1
            % 示例:当范围为255时,像素值128 → 2*(128/255)-1 ≈ 0.0078
            
            aOut = cellfun(@(x) 2*(single(x)/obj.ARange) - 1, aIn, 'UniformOutput', false);
            bOut = cellfun(@(x) 2*(single(x)/obj.BRange) - 1, bIn, 'UniformOutput', false);
        end
        
        % MiniBatchSize的getter方法
        function val = get.MiniBatchSize(obj)
            val = obj.MiniBatchSize_;
        end
        
        % MiniBatchSize的setter方法
        function set.MiniBatchSize(obj, val)
            % 同时设置两个底层数据存储的读取大小
            obj.ImagesA.ReadSize = val;
            obj.ImagesB.ReadSize = val;
            obj.MiniBatchSize_ = val;  % 更新内部存储值
        end
    end
end


网站公告

今日签到

点亮在社区的每一天
去签到