使用深度学习的多标签文本分类
这个例子展示了如何对具有多个独立标签的文本数据进行分类。
对于每个观察都有多个独立标签的分类任务(例如,科学文章上的标签),您可以训练一个深度学习模型来预测每个独立类的概率。为了使网络能够学习多标签分类目标,您可以使用二元交叉熵损失独立地优化每个类的损失。
这个例子定义了一个深度学习模型,该模型根据使用arXiv API[1]收集的数学论文摘要对主题领域进行分类。该模型由词嵌入和GRU、最大池化操作、全连接操作和sigmoid操作组成。
为了衡量多标签分类的性能,您可以使用标记F-score[2]。标记F-score通过关注具有部分匹配的每个文本分类来评估多标签分类。度量是匹配标签相对于真实和预测标签总数的归一化比例。
这个例子定义了以下模型:
一种将单词序列映射到数字向量序列的词嵌入。
学习嵌入向量之间依赖关系的GRU操作。
一种最大池化操作,将一系列特征向量减少为单个特征向量。
将特征映射到二进制输出的全连接层。
用于学习输出和目标标签之间的二进制交叉熵损失的sigmoid操作。
该图显示了一段文本通过模型体系结构传播并输出一个概率向量。概率是独立的,所以它们的和不需要等于1。
导入文本数据
使用arXiv API从数学论文中导入一组摘要和类别标签。属性指定要导入的记录数量importSize
变量。
importSize = 50000;
创建一个URL,查询带有set的记录“数学”
元数据前缀“arXiv”
.
url =“https://export.arxiv.org/oai2?verb=ListRecords”+…“集=数学”+…“&metadataPrefix = arXiv”;
提取查询URL返回的摘要文本、类别标签和恢复令牌parseArXivRecords
函数,该函数作为支持文件附加到此示例。金宝app要访问该文件,请将此示例作为活动脚本打开。请注意,arXiv API是速率有限的,并且需要在多个请求之间等待。
[textData,labelsAll,resumptionToken] = parseArXivRecords(url);
迭代地导入更多的记录块,直到达到所需的数量,或者没有更多的记录。要从中断的位置继续导入记录,请在查询URL中使用前一个结果中的恢复令牌。要遵守arXiv API施加的速率限制,在使用的每个查询之前添加20秒的延迟暂停
函数。
而numel(textData) < importSize如果resumptionToken = =”“打破结束url =“https://export.arxiv.org/oai2?verb=ListRecords”+…" &resumptionToken = "+ resumptionToken;pause(20) [textDataNew,labelsNew,resumptionToken] = parseArXivRecords(url);textData = [textData;textDataNew];labelsAll = [labelsAll;labelsNew];结束
预处理文本数据
标记文本数据并对其进行预处理preprocessText
函数,在示例末尾列出。
documentsAll = preprocestext (textData);documentsAll (1:5)
ans = 5×1 tokenizedDocument: 72个token:描述新算法$(k,\ well)$卵石游戏颜色获得表征族$(k,\ well)$稀疏图算法解关于树分解图的表征族问题$(k,\ well)$稀疏图出现刚性理论近年来受到越来越多的关注特殊有色卵石推广加强以前的成果lee streinu给出新的证明tuttenashwilliams表征树性提出新的分解证明稀疏性基$(k,\ well)$卵石游戏颜色的工作揭露连接小圆游戏算法先前的稀疏图算法gabow gabow westermann hendrickson 22个令牌:显示行列式斯特林环数计数无标记的无环单源自动机证明涉及双射自动机某些标记的晶格路径符号反转对合求行列式18个令牌:论文显示计算$\lambda_{\alpha}$范数α并矢网格结果结果描述hardy空间$h^p(r^n)$项并矢特殊原子62个令牌部分立方体等距子图超立方体结构图定义平均半立方体djokovi winklers关系理论部分立方体结构采用纸表征二部分图部分立方体任意维新的表征建立新的证明知道结果给出运算笛卡尔积粘贴膨胀收缩过程利用纸构造新的部分立方体旧的特定等距晶格维有限部分立方体获得平均运算计算29令牌:本文给出了计算hecke特征系hilbertsiegel尖形实二次域窄类数的算法,给出了二次域$\q(\sqrt{5})$示例,确定了hilbertsiegel特征形可能的提升Hilbert特征形
删除不属于的标签“数学”
集。
为i = 1: nummel (labelsAll) labelsAll{i} = labelsAll{i}(startsWith(labelsAll{i}),“数学。”));结束
把一些类想象成一个词云。查找以下对应的文件:
摘要标记了“组合学”,而没有标记
“统计理论”
摘要标注了“统计理论”而没有标注
“组合”
用两者标记的摘要
“组合”
和“统计理论”
方法查找每个组的文档索引ismember
函数。
idxCO = cellfun(@(lbls) ismember()“数学。有限公司”,lbls) && ~ismember(“数学。圣”lbls) labelsAll);idxST = cellfun(@(lbls) ismember()“数学。圣”,lbls) && ~ismember(“数学。有限公司”lbls) labelsAll);idxCOST = cellfun(@(lbls) ismember()“数学。有限公司”,lbls) && ismember(“数学。圣”lbls) labelsAll);
将每个组的文档可视化到一个词云中。
figure subplot(1,3,1) wordcloud(documentsAll(idxCO));标题(“组合”) subplot(1,3,2) wordcloud(documentsAll(idxST));标题(“统计理论”) subplot(1,3,3) wordcloud(documentsAll(idxCOST));标题(“两个”)
查看类的数量。
classNames = unique(cat(1,labelsAll{:}));numClasses = nummel (classNames)
numClasses = 32
使用直方图可视化每个文档标签的数量。
labelCounts = cellfun(@numel,labelsAll);图直方图(labelCounts)“标签数目”) ylabel (“频率”)标题(”标签项”)
为深度学习准备文本数据
将数据划分为训练分区和验证分区cvpartition
函数。保留10%的数据用于验证坚持
选项为0.1。
cvp = cvpartition(numel(documentsAll),HoldOut=0.1);documentsTrain = documentsAll(training(cvp));documentsValidation = documentsAll(test(cvp));labelsTrain = labelsAll(training(cvp));labelsValidation = labelsAll(test(cvp));
创建一个单词编码对象,将训练文档编码为单词索引序列。指定5000个单词的词汇表订单
选项“频率”
,和MaxNumWords
选项5000
.
enc = wordEncoding(documentsTrain,Order= .“频率”MaxNumWords = 5000)
enc = wordEncoding with properties: NumWords: 5000 Vocabulary: [1×5000 string]
要改善训练,可以使用以下技巧:
在训练时,截断文档的长度,以减少使用的填充量,并且不会丢弃过多的数据。
用按长度升序排序的文档训练一个epoch,然后对每个epoch的数据进行洗牌。这种技术被称为sortagrad.
要选择截断的序列长度,请在直方图中可视化文档长度,并选择捕获大部分数据的值。
documentlength = doclength(documentsTrain);figure histogram(documentlength) xlabel(“文档长度”) ylabel (“频率”)标题(“文档长度”)
大多数培训文档的标记都少于175个。使用175个标记作为截断和填充的目标长度。
maxsequencelth = 175;
要使用排序技术,请按长度升序对文档进行排序。
[~,idx] = sort(documentlength);documentsTrain = documentsTrain(idx);labelsTrain = labelsTrain(idx);
定义和初始化模型参数
为每个操作定义参数,并将它们包含在结构体中。使用格式parameters.OperationName.ParameterName
,在那里参数
结构是什么perationName
操作的名称(例如?“俱乐部”
),ParameterName
是参数的名称(例如,“重量”
).
创建一个结构体参数
包含模型参数。用零初始化偏置。对这些操作使用以下权重初始化式:
对于嵌入,使用初始化权重
initializeGaussian
函数。对于GRU操作,使用。初始化权重和偏差
initializeGlorot
和initializeZeros
函数,分别。对于完全连接操作,使用初始化权重和偏差
initializeGaussian
和initializeZeros
函数,分别。
初始化函数initializeGlorot
,initializeGaussian
,initializeZeros
作为支持文件附加到示例中。金宝app要访问这些函数,请将示例作为活动脚本打开。
初始化嵌入的可学习参数。
embeddingDimension = 300;numHiddenUnits = 250;inputSize = encs . numwords + 1;Parameters = struct;sz = [embeddingDimension inputSize];Mu = 0;Sigma = 0.01;parameters.emb.Weights = initializeGaussian(sz,mu,sigma);
初始化GRU操作的可学习参数。
sz = [3*numHiddenUnits embeddingDimension];numOut = 3*numHiddenUnits;numIn = embeddingDimension;parameters.gru.InputWeights = initializeGlorot(sz,numOut,numIn);sz = [3*numHiddenUnits];numOut = 3*numHiddenUnits;numIn = numHiddenUnits;parameters.gru.RecurrentWeights = initializeGlorot(sz,numOut,numIn);sz = [3*numHiddenUnits];parameters.gru.Bias = initializezero (sz);
为完全连接操作初始化可学习参数。
sz = [numClasses numHiddenUnits];Mu = 0;Sigma = 0.01;parameters.fc.Weights = initializeGaussian(sz,mu,sigma);sz = [numClasses 1];parameters.fc.Bias = initializezero (sz);
查看参数
结构体。
参数
参数=带有字段的结构体:Emb: [1×1 structure] gru: [1×1 struct] fc: [1×1 struct]
查看GRU操作的相关参数。
parameters.gru
ans =带有字段的结构体:InputWeights: [750×300 dlarray] RecurrentWeights: [750×250 dlarray] Bias: [750×1 dlarray]
定义模型功能
创建函数模型
,它计算前面描述的深度学习模型的输出。这个函数模型
将输入数据和模型参数作为输入。网络输出标签的预测结果。
定义模型损失函数
创建函数modelLoss
,它将一小批输入数据和相应的目标作为输入,并返回损失、损失相对于可学习参数的梯度以及网络输出。
指定培训方案
训练5个epoch,小批量大小为256。
nummepochs = 5;miniBatchSize = 256;
使用Adam优化器进行训练,学习率为0.01,并指定梯度衰减因子和平方梯度衰减因子分别为0.5和0.999。
learnRate = 0.01;gradientDecayFactor = 0.5;squaredGradientDecayFactor = 0.999;
剪辑梯度与阈值为1使用 范数梯度裁剪。
gradientThreshold = 1;
若要将概率向量转换为标签,请使用概率高于指定阈值的标签。将标签阈值设置为0.5。
labelThreshold = 0.5;
每个epoch验证网络。
numObservationsTrain = numel(documentsTrain);numIterationsPerEpoch = floor(nummobservationstrain /miniBatchSize);validationFrequency = numIterationsPerEpoch;
火车模型
初始化训练进度图。为f分和损失创建动画线。
图C = colororder;subplot(2,1,1) lineFScoreTrain = animatedline(Color=C(1,:));lineFScoreValidation = animatedline(…线型=”——“,…标志=“o”,…MarkerFaceColor =“黑色”);xlabel([0 1])“迭代”) ylabel (“标签f值”网格)在subplot(2,1,2) lineLossTrain = animatedline(Color=C(2,:));lineLossValidation = animatedline(…线型=”——“,…标志=“o”,…MarkerFaceColor =“黑色”);Ylim ([0 inf]) xlabel(“迭代”) ylabel (“损失”网格)在
初始化Adam优化器的参数。
trailingAvg = [];trailingAvgSq = [];
准备验证数据。创建一个单热编码矩阵,其中非零条目对应于每个观测值的标签。
numObservationsValidation = numel(documentsValidation);TValidation = 0 (numClasses, nummobservationsvalidation,“单身”);为i = 1:numObservationsValidation [~,idx] = ismember(labelsValidation{i},classNames);TValidation(idx,i) = 1;结束
使用自定义训练循环训练模型。
对于每个epoch,循环遍历小批数据。在每个历元结束时,对数据进行洗牌。在每次迭代结束时,更新训练进度图。
对于每个小批量:
将文档转换为单词索引序列,并将标签转换为虚拟变量。
将序列转换为
dlarray
对象,并指定维度标签“BTC”
(批次、时间、通道)。如果GPU可用,在GPU上进行训练。这需要并行计算工具箱™。使用GPU需要Parallel Computing Toolbox™和支持的GPU设备。金宝app有关支持的设备的信息,请参见金宝appGPU计算要求(并行计算工具箱).
对于GPU训练,转换为
gpuArray
对象。评估模型损失和梯度使用
dlfeval
和modelLoss
函数。剪辑渐变。
更新网络参数
adamupdate
函数。方法验证网络
modelPredictions
函数,在示例末尾列出。更新训练图。
迭代= 0;Start = tic;%循环遍历。为epoch = 1: nummepochs%小批量循环。为i = 1:numIterationsPerEpoch迭代=迭代+ 1;idx = (i-1)*miniBatchSize+1:i*miniBatchSize;读取小批量数据并将标签转换为虚拟%变量。documents = documentsTrain(idx);labels = labelsTrain(idx);%将文档转换为序列。len = min(maxSequenceLength,max(doclength(documents)));X = doc2sequence(enc,documents,…PaddingValue = inputSize,…长度= len);X = cat(1,X{:});%虚化标签。T = 0 (numClasses,miniBatchSize,“单身”);为j = 1:miniBatchSize [~,idx2] = ismember(labels{j},classNames);T(idx2,j) = 1;结束将小批量数据转换为数组。X = darray (X,“BTC”);%如果在GPU上训练,则将数据转换为gpuArray。如果canUseGPU X = gpuArray(X);结束使用dlfeval和the评估模型损失、梯度和预测% modelLoss函数。[loss,gradients,Y] = dlfeval(@modelLoss,X,T,parameters);%渐变裁剪。gradients = dlupdate(@(g) thresholdL2Norm(g,gradientThreshold),gradients);使用Adam优化器更新网络参数。[parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients,…learnRate trailingAvg trailingAvgSq,迭代,…gradientDecayFactor squaredGradientDecayFactor);%显示培训进度。subplot(2,1,1) D = duration(0,0,toc(start),Format=“hh: mm: ss”);标题(”时代:“+ epoch +“,流逝:”+字符串(D))%的损失。损失=双倍(损失);addpoints (lineLossTrain、迭代、失去)%标记f分数。Y = Y > labelThreshold;score = labelingFScore(Y,T);addpoints (lineFScoreTrain、迭代、双(收集(分数)))drawnow%显示验证指标。如果迭代== 1 || mod(iteration,validationFrequency) == 0 YValidation = modelPredictions(parameters,enc,documentsValidation,miniBatchSize,maxSequenceLength);%的损失。lossValidation = crossentropy(YValidation,TValidation,…TargetCategories =“独立”,…DataFormat =“CB”);lossValidation = double(lossValidation);addpoints (lineLossValidation迭代,lossValidation)%标记f分数。YValidation = YValidation > labelThreshold;score = labelingFScore(YValidation,TValidation);Score = double(分数);addpoints drawnow (lineFScoreValidation,迭代,分数)结束结束% Shuffle数据。idx = randperm(numObservationsTrain);documentsTrain = documentsTrain(idx);labelsTrain = labelsTrain(idx);结束
测试模型
要对一组新数据进行预测,请使用modelPredictions
函数,在示例末尾列出。的modelPredictions
函数将模型参数、单词编码和标记化文档数组作为输入,并输出与指定的小批量大小和最大序列长度相对应的模型预测。
YValidation = modelPredictions(parameters,enc,documentsValidation,miniBatchSize,maxSequenceLength);
为了评估性能,使用labelingFScore
函数,在示例末尾列出。标记F-score通过关注具有部分匹配的每个文本分类来评估多标签分类。要将网络输出转换为标签数组,请查找得分高于指定标签阈值的标签。
= labelingFScore(YValidation > labelThreshold,TValidation)
分数=单0.5663
通过尝试一系列阈值并比较结果,查看标记阈值对标记f分数的影响。
THR = linspace(0,1,10);Score = 0 (size(thr));为i = 1:num (thr) YPredValidationThr = YValidation >= thr(i);score(i) = labelingFScore(YPredValidationThr,TValidation);结束figure plot(thr,score) xline(labelThreshold,“r——”);包含(“阈值”) ylabel (“标签f值”)标题(“标签阈值效应”)
可视化预测
为了可视化分类器的正确预测,计算真阳性的数量。真正是分类器正确预测观察到的特定类别的实例。
Y = YValidation > labelThreshold;T = TValidation;numtruepositive = sum(T & Y,2);numObservationsPerClass = sum(T,2);truePositiveRates = numtruepositive ./ numObservationsPerClass;
在直方图中可视化每个类别的真阳性数。
figure truePositiveRates = extractdata(truePositiveRates);[~,idx] = sort(true)“下”);直方图(类别(idx) =类名,BinCounts = truePositiveRates (idx))包含(“类别”) ylabel (“真阳性率”)标题(“真阳性率”)
通过显示真阳性、假阳性和假阴性的分布,可视化分类器预测错误的实例。假阳性是分类器将特定的错误类分配给观察值的实例。假阴性是分类器未能将特定正确的类分配给观察值的实例。
创建一个混淆矩阵,显示真阳性,假阳性和假阴性计数:
对于每个类,在对角线上显示真正的正计数。
对于每一对类(我,j),显示误报的实例数j当实例也为假阴性时我.
即,元素的混淆矩阵为:
计算假阴性和假阳性。
falnegative = T & ~Y;false positive = ~T & Y;
计算非对角线元素。
falseNegatives = permute(falseNegatives,[3 2 1]);numconditionalfalse positive = sum(falnegative & false positive, 2);numconditionalfalse positive = squeeze(numconditionalfalse positive);tpfnMatrix = numconditionalfalse positive;
将对角线元素设置为真正计数。
idxDiagonal = 1:numClasses+1:numClasses^2tpfnMatrix(idxDiagonal) = numtruepositive;
在混淆矩阵中可视化真阳性和假阳性计数confusionchart
函数和排序矩阵,使对角线上的元素按降序排列。
figure tpfnMatrix = extractdata(tpfnMatrix);cm = confusionchart(tpfnMatrix,classNames);sortClasses(厘米,“descending-diagonal”);标题(“真阳性,假阳性”)
要更详细地查看矩阵,请将此示例作为活动脚本打开,并在新窗口中打开该图形。
预处理文本函数
的preprocessText
函数使用以下步骤对输入的文本数据进行标记和预处理:
标记文本
tokenizedDocument
函数。将数学方程提取为单个标记RegularExpressions
选项“\ $ * ?。\ $”
,它捕获出现在两个“$”符号之间的文本。擦去标点符号
erasePunctuation
函数。将文本转换为小写
较低的
函数。删除停止词使用
removeStopWords
函数。将文本按序排列
normalizeWords
函数风格
选项设置为“引理”
.
函数documents = preprocessText(textData)%标记文本。正则表达式=表;regularExpressions。模式=“\ $ * ?。\ $”;regularExpressions。类型=“方程”;documents = tokenizedDocument(textData, regulareexpressions = regulareexpressions);删除标点符号。documents = eraspunctuation(文档);%转换为小写。Documents = lower(Documents);% Lemmatize。documents = addPartOfSpeechDetails(documents);文档= normalizeWords(文档,样式=“引理”);删除停止词。documents = removeStopWords(documents);删除短单词。documents = removesshortwords (documents,2);结束
模型函数
这个函数模型
将输入数据作为输入X
模型参数参数
,并返回标签的预测结果。
函数Y = model(X,参数)%嵌入weights = parameters.emb.Weights;X = embed(X,weights);%格勒乌inputWeights = parameters.gru.InputWeights;recurrentWeights = parameters.gru.RecurrentWeights;bias = parameters.gru.Bias;numHiddenUnits = size(inputWeights,1)/3;hiddenState = darray (zero ([numHiddenUnits 1]));Y = gru(X,hiddenState,inputWeights,recurrentWeights,bias);沿着时间维度的最大池化Y = max(Y,[],3);%完全连接weights = parameters.fc.Weights;bias = parameters.fc.Bias;Y = fullyconnect(Y,weights,bias);% s形的Y = sigmoid(Y);结束
模型损失函数
的modelLoss
函数将一小批输入数据作为输入X
有相应的目标T
包含标签并返回损失,损失相对于可学习参数的梯度,以及网络输出。
函数[loss,gradients,Y] = modelLoss(X,T,parameters) Y = model(X,parameters);loss = crossentropy(Y,T,TargetCategories= .“独立”);Gradients = dlgradient(loss,parameters);结束
模型预测函数
的modelPredictions
函数将模型参数、单词编码、标记化文档数组、小批量大小和最大序列长度作为输入,并通过迭代指定大小的小批量来返回模型预测。
函数Y = modelPredictions(parameters,enc,documents,miniBatchSize,maxSequenceLength) inputSize = enc. numwords + 1;numObservations = numel(documents);numIterations = ceil(nummobservations / miniBatchSize);numFeatures = size(parameters.fc.Weights,1);Y = 0 (numFeatures, nummobobservations,“喜欢”, parameters.fc.Weights);为i = 1:numIterations; idx = (i-1)*miniBatchSize+1:min(i*miniBatchSize, nummobservations);len = min(maxSequenceLength,max(doclength) documents(idx))));X = doc2sequence(enc,documents(idx),…PaddingValue = inputSize,…长度= len);X = cat(1,X{:});X = darray (X,“BTC”);Y(:,idx) = model(X,参数);结束结束
标记F-Score函数
标记F-score函数[2]通过关注具有部分匹配的每个文本分类来评估多标签分类。该度量是匹配标签与给出的真实和预测标签总数的归一化比例
在哪里N和C分别对应于观察值和类别的数量,和Y和T分别对应于预测和目标。
函数number = labelingFScore(Y,T); number = size(T,2);scores = (2 * sum(Y .* T)) ./ sum(Y + T);score = sum(scores) / numobobservations;结束
梯度裁剪函数
的thresholdL2Norm
函数缩放输入梯度,使其
时,规范值等于指定的梯度阈值
可学习参数的梯度范数大于设定的阈值。
函数gradients = thresholdL2Norm(gradients,gradientThreshold) gradientNorm = sqrt(sum(gradients(:).^2));如果gradientThreshold = gradients * (gradientThreshold / gradientNorm);结束结束
参考文献
出来了。“arXiv API”。于2020年1月15日发布。https://arxiv.org/help/api
Sokolova, Marina和Guy Lapalme。分类任务绩效指标的系统分析信息处理与管理45岁的没有。[4](2009): 427-437。
另请参阅
tokenizedDocument
(文本分析工具箱)|fullyconnect
|格勒乌
|dlupdate
|adamupdate
|dlarray
|dlfeval
|dlgradient
|wordEncoding
(文本分析工具箱)|doc2sequence
(文本分析工具箱)|extractHTMLText
(文本分析工具箱)|htmlTree
(文本分析工具箱)