使用无法放入内存的序列数据训练网络
此示例说明如何通过变换和合并数据存储基于无法放入内存的序列数据来训练深度学习网络。
变换后的数据存储对从基础数据存储读取的数据进行变换或处理。您可以使用变换后的数据存储作为深度学习应用的训练数据集、验证数据集、测试数据集以及预测数据集的数据源。使用变换后的数据存储可读取无法放入内存的数据,或者在读取批量数据时执行特定的预处理操作。当您有若干单独的包含预测变量和标签的数据存储时,您可以将它们合并起来,以便将数据输入到深度学习网络中。
在训练网络时,软件通过填充、截断或拆分输入数据来创建长度相同的小批量序列。对于内存数据,trainingOptions
函数提供了填充和截断输入序列的选项,但对于内存外的数据,您必须手动填充和截断序列。
加载训练数据
按照 [1] 和 [2] 中的说明加载日语元音数据集。zip 文件 japaneseVowels.zip
包含不同长度的序列。这些序列分成两个文件夹 Train
和 Test
,分别包含训练序列和测试序列。在每个文件夹中,序列又分成编号从 1
到 9
的子文件夹。这些子文件夹的名称是标签名称。一个 MAT 文件表示一个序列。每个序列均为一个包含 12 行(每个特征占一行)和不同列数(每个时间步占一列)的矩阵。行数是序列的维度,列数是序列的长度。
解压缩序列数据。
filename = "japaneseVowels.zip"; outputFolder = fullfile(tempdir,"japaneseVowels"); unzip(filename,outputFolder);
对于训练预测变量,请创建一个文件数据存储,并将读取函数指定为 load
函数。load
函数将数据从 MAT 文件加载到结构体数组中。要从训练文件夹的子文件夹中读取文件,请将 'IncludeSubfolders'
选项设置为 true
。
folderTrain = fullfile(outputFolder,"Train"); fdsPredictorTrain = fileDatastore(folderTrain, ... 'ReadFcn',@load, ... 'IncludeSubfolders',true);
预览数据存储。返回的结构体包含来自第一个文件的单个序列。
preview(fdsPredictorTrain)
ans = struct with fields:
X: [12×20 double]
对于标签,请创建一个文件数据存储,并将读取函数指定为 readLabel
函数,该函数在示例末尾定义。readLabel
函数从子文件夹名称中提取标签。
classNames = string(1:9); fdsLabelTrain = fileDatastore(folderTrain, ... 'ReadFcn',@(filename) readLabel(filename,classNames), ... 'IncludeSubfolders',true);
预览数据存储。输出对应于第一个文件的标签。
preview(fdsLabelTrain)
ans = categorical
1
变换和合并数据存储
要将预测变量的数据存储中的序列数据输入深度学习网络,序列的小批量必须具有相同的长度。使用在数据存储末尾定义的 padSequence
函数变换数据存储,该函数填充或截断序列以使其长度为 20。
sequenceLength = 20; tdsTrain = transform(fdsPredictorTrain,@(data) padSequence(data,sequenceLength));
预览变换后的数据存储。输出对应于来自第一个文件的填充序列。
X = preview(tdsTrain)
X = 1×1 cell array
{12×20 double}
要将来自两个数据存储的预测变量和标签输入一个深度学习网络,请使用 combine
函数将其合并。
cdsTrain = combine(tdsTrain,fdsLabelTrain);
预览合并后的数据存储。数据存储返回一个 1×2 元胞数组。第一个元素对应于预测变量。第二个元素对应于标签。
preview(cdsTrain)
ans=1×2 cell array
{12×20 double} {[1]}
定义 LSTM 网络架构
定义 LSTM 网络架构。将输入数据的特征数量指定为输入大小。指定具有 100 个隐含单元的 LSTM 层,并输出序列的最后一个元素。最后,指定一个输出大小等于类数的全连接层,后接一个 softmax 层。
numFeatures = 12; numClasses = numel(classNames); numHiddenUnits = 100; layers = [ ... sequenceInputLayer(numFeatures) lstmLayer(numHiddenUnits,'OutputMode','last') fullyConnectedLayer(numClasses) softmaxLayer];
指定训练选项。在选项中进行选择需要经验分析。要通过运行试验探索不同训练选项配置,您可以使用Experiment Manager。
使用 Adam 优化器进行训练。
由于训练数据具有行和列分别对应于通道和时间步的序列,请指定输入数据格式
"CTB"
(通道、时间、批量)。将最大训练轮数设置为 75。
使用小批量大小 27。
使用梯度阈值 2 进行训练。
由于数据存储不支持乱序,因此不要打乱数据。
使用 CPU 进行训练。由于网络和数据都很小,CPU 更适合。要在 GPU(如果可用)上进行训练,请将
'ExecutionEnvironment' option
设置为'auto'
(默认值)。禁用详尽输出。
在图中显示训练进度并监控准确度。
miniBatchSize = 27; options = trainingOptions('adam', ... 'InputDataFormats','CTB', ... 'MaxEpochs',75, ... 'MiniBatchSize',miniBatchSize, ... 'GradientThreshold',2, ... 'Shuffle','never',... 'ExecutionEnvironment','cpu', ... 'Verbose',0, ... 'Plots','training-progress');
使用 trainnet
函数训练神经网络。对于分类,使用交叉熵损失。
net = trainnet(cdsTrain,layers,"crossentropy",options);
测试网络
使用与训练数据相同的步骤来创建包含保留测试数据的变换后的数据存储。
folderTest = fullfile(outputFolder,"Test"); fdsPredictorTest = fileDatastore(folderTest, ... 'ReadFcn',@load, ... 'IncludeSubfolders',true); tdsTest = transform(fdsPredictorTest,@(data) padSequence(data,sequenceLength));
使用 minibatchpredict
函数进行预测。默认情况下,minibatchpredict
函数使用 GPU(如果有)。使用 GPU 需要 Parallel Computing Toolbox™ 许可证和受支持的 GPU 设备。有关受支持设备的信息,请参阅GPU Computing Requirements (Parallel Computing Toolbox)。否则,该函数使用 CPU。要指定执行环境,请使用 ExecutionEnvironment
选项。
由于数据具有行和列分别对应于通道和时间步的序列,请指定输入数据格式 "CTB"
(通道、时间、批量)。
scores = minibatchpredict(net,tdsTest,MiniBatchSize=miniBatchSize,InputDataFormats="CTB");
YPred = scores2label(scores,classNames);
基于测试数据计算分类准确度。要获取测试集的标签,请使用读取函数 readLabel
创建一个文件数据存储,并指定包含子文件夹。通过将 'UniformRead'
选项设置为 true
,指定输出可垂直串联。
fdsLabelTest = fileDatastore(folderTest, ... 'ReadFcn',@(filename) readLabel(filename,classNames), ... 'IncludeSubfolders',true, ... 'UniformRead',true); YTest = readall(fdsLabelTest); accuracy = mean(YPred == YTest)
accuracy = 0.9568
函数
readLabel
函数根据 classNames
中的类别从指定文件名中提取标签。
function label = readLabel(filename,classNames) filepath = fileparts(filename); [~,label] = fileparts(filepath); label = categorical(string(label),classNames); end
padSequence
函数填充或截断 data.X
中的序列,使其具有指定的序列长度,并以 1×1 元胞形式返回结果。
function sequence = padSequence(data,sequenceLength) sequence = data.X; [C,S] = size(sequence); if S < sequenceLength padding = zeros(C,sequenceLength-S); sequence = [sequence padding]; else sequence = sequence(:,1:sequenceLength); end sequence = {sequence}; end
另请参阅
trainnet
| trainingOptions
| dlnetwork
| lstmLayer
| sequenceInputLayer
| combine
| transform