主要内容

基于深度学习的多标签文本分类

这个例子展示了如何对具有多个独立标签的文本数据进行分类。

对于每个观察都可能有多个独立标签的分类任务(例如,一篇科学文章上的标签),您可以训练深度学习模型来预测每个独立类的概率。为了使网络能够学习多标签分类目标,可以使用二进制交叉熵损失独立优化每个类的损失。

这个例子定义了一个深度学习模型,该模型根据使用arXiv API[1]收集的数学论文摘要对主题领域进行分类。该模型由单词嵌入和GRU、最大池化操作、全连接和sigmoid操作组成。

为了衡量多标签分类的性能,可以使用标记F-score[2]。标记F-score通过关注具有部分匹配的每个文本分类来评估多标签分类。该度量是匹配标签与真实和预测标签总数的标准化比例。

这个例子定义了以下模型:

  • 一种将单词序列映射到数字向量序列的词嵌入。

  • 学习嵌入向量之间依赖关系的GRU操作。

  • 最大池化操作,将特征向量序列减少为单个特征向量。

  • 将特征映射到二进制输出的全连接层。

  • 用于学习输出和目标标签之间的二进制交叉熵损失的sigmoid运算。

该图显示了一段通过模型架构传播的文本,并输出了一个概率向量。这些概率是相互独立的,所以它们的和不必等于1。

导入文本数据

使用arXiv API从数学论文中导入一组摘要和类别标签。方法指定要导入的记录数importSize变量。

importSize = 50000;

创建一个使用set查询记录的URL“数学”元数据前缀“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 . txt如果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 = preprocessText(textData);documentsAll (1:5)
ans = 5×1 tokenizedDocument: 72 token:描述新算法$(k,\ell)$ pebble游戏颜色获得刻画族$(k,\ell)$稀疏图算法解族问题关于树分解图的特殊实例稀疏图出现刚性理论近年来受到越来越多的关注特定颜色卵石推广加强以前的结果lee streinu给出新的证明tuttenashwilliams给出新的分解证明稀疏基$(k,\ell)$ pebble游戏颜色工作暴露连接圆球博弈算法先前的稀疏图算法gabow gabow westermann hendrickson 22个令牌:显示行列式斯特林循环数计数无标记无循环单源自动机证明涉及双射自动机某些标记格路径信号反向对折计算行列式18个令牌:论文显示计算$\lambda_{\alpha}$范数alpha二进网格结果结果描述hardy空间$h^p(r^n)$ term二进特殊原子62个令牌:局部立方等距子图超立方体结构图定义均值半立方djokovi winklers关系起重要作用理论局部立方结构采用论文描述二部图局部立方任意维新的表征建立新的证明知道结果给出运算笛卡尔积粘贴展开收缩过程采用论文构造新的局部立方旧的特定等距格维有限局部立方得到均值运算计算29令牌:论文给出算法计算hecke特征系hilbertsiegel尖形实二次场窄类数给出举例二次场$\q(\sqrt{5})$示例识别hilbertsiegel特征形可能提升Hilbert特征形

删除不属于“数学”集。

i = 1: number (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);

在一个词云中可视化每个组的文档。

图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 = numel(classNames)
numClasses = 32

使用直方图可视化每个文档标签的数量。

labelCounts = cellfun(@ number,labelsAll);图直方图(labelCounts)“标签数目”) ylabel (“频率”)标题(”标签项”

为深度学习准备文本数据

方法将数据划分为训练分区和验证分区cvpartition函数。属性保留10%的数据进行验证坚持选项为0.1。

cvp = cvpartition(numel(documentsAll),HoldOut=0.1);documentsTrain = documentsAll(训练(cvp));documentsValidation = documentsAll(test(cvp));labelsTrain = labelsAll(训练(cvp));labelsValidation = labelsAll(测试(cvp));

创建一个单词编码对象,将训练文档编码为单词索引序列。属性指定5000个单词的词汇表订单选项“频率”,以及MaxNumWords选项5000

enc = worddencoding (documentsTrain,Order=“频率”MaxNumWords = 5000)
enc = worddencoding with properties: NumWords: 5000词汇:[1×5000 string]

为了提高训练质量,可以使用以下技巧:

  1. 训练时,将文档截短到可以减少使用的填充量且不会丢弃太多数据的长度。

  2. 用文档按长度升序排序来训练一个纪元,然后在每个纪元对数据进行洗牌。这种技术被称为sortagrad

若要选择用于截断的序列长度,请在直方图中可视化文档长度,并选择捕获大部分数据的值。

documentlength = documentlength (documentsTrain);图直方图(documentlength)“文档长度”) ylabel (“频率”)标题(“文档长度”

大多数培训文档的标记少于175个。使用175个标记作为截断和填充的目标长度。

maxSequenceLength = 175;

要使用sortagrad技术,请按长度升序对文档进行排序。

[~,idx] = sort(documentlength);documentsTrain = documentsTrain(idx);labelsTrain =标签应变(idx);

定义和初始化模型参数

为每个操作定义参数,并将它们包含在一个结构中。使用格式parameters.OperationName.ParameterName,在那里参数结构是什么,OperationName操作的名称(例如“俱乐部”),ParameterName参数的名称(例如,“重量”).

创建一个结构参数包含模型参数。用0初始化偏差。使用以下权重初始化器进行操作:

  • 方法初始化权重initializeGaussian函数。

  • 对于GRU操作,使用初始化权重和偏差initializeGlorot而且initializeZeros函数,分别。

  • 对于完全连接操作,请使用initializeGaussian而且initializeZeros函数,分别。

初始化函数initializeGlorotinitializeGaussian,initializeZeros作为支持文件附加到示例中。金宝app要访问这些函数,请将示例作为活动脚本打开。

初始化嵌入的可学习参数。

embeddingDimension = 300;numHiddenUnits = 250;inputSize = c. numwords + 1;参数= struct;sz = [embeddingDimension inputSize];Mu = 0;σ = 0.01;parameters.emb.Weights = initializ高斯(sz,mu,sigma);

初始化GRU操作的可学习参数。

sz = [3*numHiddenUnits embeddingDimension];numOut = 3*numHiddenUnits;numIn = embeddingDimension;parameters.gru. inputwights = initializeGlorot(sz,numOut,numIn);sz = [3*numHiddenUnits numHiddenUnits];numOut = 3*numHiddenUnits;numIn = numHiddenUnits;parameters.gru.RecurrentWeights = initializeGlorot(sz,numOut,numIn);sz = [3*numHiddenUnits 1];parameters.gru.Bias = initializezero (sz);

初始化完全连接操作的可学习参数。

sz = [numClasses numHiddenUnits];Mu = 0;σ = 0.01;parameters.fc.Weights = initializ高斯(sz,mu,sigma);sz = [numClasses 1];parameters.fc.Bias = initializezero (sz);

查看参数结构体。

参数
参数=带字段的结构:Emb: [1×1 struct] gru: [1×1 struct] fc: [1×1 struct]

查看GRU操作的相关参数。

parameters.gru
ans =带字段的结构:inputwights: [750×300 dlarray] recurrentwights: [750×250 dlarray] Bias: [750×1 dlarray]

定义模型函数

创建函数模型,列在示例末尾,它计算前面描述的深度学习模型的输出。这个函数模型将输入数据和模型参数作为输入。网络输出对标签的预测。

定义模型损失函数

创建函数modelLoss,该方法将一小批输入数据和相应的目标作为输入,并返回损失、损失相对于可学习参数的梯度以及网络输出。

指定培训项目

训练5个周期,小批量大小为256。

numEpochs = 5;miniBatchSize = 256;

使用Adam优化器进行训练,学习率为0.01,并指定梯度衰减和平方梯度衰减因子分别为0.5和0.999。

learnRate = 0.01;gradientDecayFactor = 0.5;squaredGradientDecayFactor = 0.999;

使用阈值1剪辑渐变 l 2 范数梯度裁剪。

gradientThreshold = 1;

若要将概率向量转换为标签,请使用概率高于指定阈值的标签。指定标签阈值为0.5。

labelThreshold = 0.5;

每个纪元都要验证网络。

numObservationsTrain = numel(documentsTrain);numIterationsPerEpoch = floor(numObservationsTrain/miniBatchSize);validationFrequency = numIterationsPerEpoch;

火车模型

初始化培训进度图。为f分数和损失创建动画线。

图C = colororder;subplot(2,1,1) lineFScoreTrain = animatedline(Color=C(1,:));lineFScoreValidation = animatedline(...线型="--"...标志=“o”...MarkerFaceColor =“黑色”);Ylim ([0 1]) xlabel(“迭代”) 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, numObservationsValidation,“单身”);i = 1:numObservationsValidation [~,idx] = ismember(labelsValidation{i},classNames);TValidation(idx,i) = 1;结束

使用自定义训练循环训练模型。

对于每个纪元,循环小批数据。在每个纪元结束时,对数据进行洗牌。在每次迭代结束时,更新训练进度图。

对于每个小批量:

  • 将文档转换为单词索引序列,并将标签转换为虚拟变量。

  • 将序列转换为dlarray具有底层类型single的对象,并指定维度标签“BTC”(批次,时间,通道)。

  • 如果有GPU,可以在GPU上进行训练。这需要并行计算工具箱™。使用GPU需要并行计算工具箱™和受支持的GPU设备。金宝app有关受支持设备的信息,请参见金宝appGPU支金宝app持版本(并行计算工具箱)

  • 对于GPU训练,转换为gpuArray对象。

  • 评估模型损失和梯度使用dlfevalmodelLoss函数。

  • 剪辑渐变。

  • 方法更新网络参数adamupdate函数。

  • 如有必要,请使用modelPredictions函数,在示例末尾列出。

  • 更新训练图。

迭代= 0;开始= tic;%遍历epoch。epoch = 1:numEpochs在小批上循环。i = 1:numIterationsPerEpoch迭代=迭代+ 1;idx = (i-1)*miniBatchSize+1:i*miniBatchSize;读取小批数据并将标签转换为虚拟标签%变量。documents = documentsTrain(idx);标签= labelsTrain(idx);将文档转换为序列。len = min(maxSequenceLength,max(docclength (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 = dlarray(X,“BTC”);如果在GPU上训练,则将数据转换为gpuArray。如果canUseGPU X = gpuArray(X);结束评估模型损失,梯度和预测使用dlfeval和% modelLoss函数。[loss,gradients,Y] = dlfeval(@modelLoss,X,T,parameters);%渐变剪辑。gradients = dlupdate(@(g) thresholdL2Norm(g,gradientThreshold),gradients);使用Adam优化器更新网络参数。。[parameters,trailingAvg,trailingAvgSq] = adamupdate(参数,梯度,...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(迭代,validationFrequency) == 0 YValidation = modelPredictions(parameters,enc,documentsValidation,miniBatchSize,maxSequenceLength);%的损失。lossValidation = crossentropy(YValidation,TValidation,...TargetCategories =“独立”...DataFormat =“CB”);lossValidation = double(lossValidation);addpoints (lineLossValidation迭代,lossValidation)标记f分。> labelThreshold;得分= labelingFScore(YValidation,TValidation);分数=双倍(分数);addpoints drawnow (lineFScoreValidation,迭代,分数)结束结束% Shuffle数据。idx = randperm(numObservationsTrain);documentsTrain = documentsTrain(idx);labelsTrain =标签应变(idx);结束

测试模型

要对一组新数据进行预测,请使用modelPredictions函数,在示例末尾列出。的modelPredictions函数将模型参数、单词编码和标记文档数组作为输入,输出与指定的小批大小和最大序列长度相对应的模型预测。

YValidation = modelPredictions(parameters,enc,documentsValidation,miniBatchSize,maxSequenceLength);

为了评估性能,计算标记f分数使用labelingFScore函数,在示例末尾列出。标记F-score通过关注具有部分匹配的每个文本分类来评估多标签分类。若要将网络输出转换为标签数组,请找到得分高于指定标签阈值的标签。

得分= labelingFScore(YValidation > labelThreshold,TValidation)
分数=0.5663

通过尝试阈值范围并比较结果,查看标记阈值对标记f分数的影响。

THR = linspace(0,1,10);分数=零(大小(thr));i = 1:数字(thr) YPredValidationThr = YValidation >= thr(i);score(i) = labelingFScore(YPredValidationThr,TValidation);结束图plot(thr,score) xline(labelThreshold,“r——”);包含(“阈值”) ylabel (“标签f值”)标题(“标签门槛的影响”

可视化预测

为了使分类器的正确预测可视化,计算真阳性的数量。真阳性是一个分类器正确预测一个观察的特定类别的实例。

Y = YValidation > labelThreshold;T = TValidation;numTruePositives = sum(T & Y,2);numObservationsPerClass = sum(T,2);truePositiveRates = numTruePositives ./ numObservationsPerClass;

在直方图中可视化每个类的真阳性数。

figure truePositiveRates = extractdata(truePositiveRates);[~,idx] = sort(truePositiveRates,“下”);直方图(类别(idx) =类名,BinCounts = truePositiveRates (idx))包含(“类别”) ylabel (“真阳性率”)标题(“真阳性率”

通过显示真阳性、假阳性和假阴性的分布,可视化分类器预测错误的实例。假阳性是分类器将一个特定的不正确的类分配给观察结果的实例。假阴性是分类器未能将特定的正确类分配给观察结果的实例。

创建一个混淆矩阵,显示真阳性、假阳性和假阴性的计数:

  • 对于每个类,在对角线上显示真实的正计数。

  • 对于每对类(j),显示误报的实例数j当实例也为假阴性时

即,包含下列元素的混淆矩阵:

TPFN j numTruePositives 如果 j numFalsePositives j | 一个 如果 j 真阳性,假阴性

计算假阴性和假阳性。

假否定= T & ~Y;假阳性= ~T & Y;

计算非对角线元素。

falseNegatives = permute(falseNegatives,[3 2 1]);numconditional假阳性= sum(假阳性和假阳性,2);numConditionalFalsePositives =挤压(numConditionalFalsePositives);tpfnMatrix = numConditionalFalsePositives;

将对角线元素设置为真正计数。

idxDiagonal = 1:numClasses+1:numClasses^2;tpfnMatrix(idxDiagonal) = numTruePositives;

在混淆矩阵中可视化真阳性和假阳性计数confusionchart对矩阵进行函数化和排序,使对角线上的元素按降序排列。

figure tpfnMatrix = extractdata(tpfnMatrix);cm = confusionchart(tpfnMatrix,classNames);sortClasses(厘米,“descending-diagonal”);标题(“真阳性,假阳性”

要更详细地查看矩阵,请将此示例作为活动脚本打开,并在一个新窗口中打开图形。

预处理文本功能

preprocessText函数标记和预处理输入文本数据,使用以下步骤:

  1. 标记文本tokenizedDocument函数。方法将数学方程提取为单个标记RegularExpressions选项,指定正则表达式“\ $ * ?。\ $”,它捕捉出现在两个“$”符号之间的文本。

  2. 删除标点符号erasePunctuation函数。

  3. 属性将文本转换为小写较低的函数。

  4. 删除停止词使用removeStopWords函数。

  5. 元素对文本进行lemm化normalizeWords函数与风格选项设置为“引理”

函数documents = preprocessText(textData)标记文本。正则表达式=表;regularExpressions。模式=“\ $ * ?。\ $”;regularExpressions。类型=“方程”;documents = tokenizedDocument(textData,RegularExpressions= RegularExpressions);删除标点符号。documents = eraspunctuation(文档);%转换为小写字母。文档=较低(文档);% Lemmatize。文档= addPartOfSpeechDetails(文档);文档= normalizeWords(文档,样式=“引理”);删除停止词。documents = removeStopWords(文档);删除短单词。文档= removeShortWords(文档,2);结束

模型函数

这个函数模型将输入数据作为输入X以及模型参数参数,并返回标签的预测结果。

函数Y =模型(X,参数)%嵌入weights = parameters.emb.Weights;X = embed(X,weights);%格勒乌inputwights = parameters.gru. inputwights;recurrentwights = parameters.gru. recurrentwights;bias = parameters.gru.Bias;numHiddenUnits = size(inputwights,1)/3;hiddenState = dlarray(0 ([numHiddenUnits 1]));Y = gru(X,hiddenState, inputwights, recurrentwights,bias);沿时间维度最大池化Y = max(Y,[],3);%完全连接weights = parameters.fc.Weights;偏差=参数。fc.偏差;Y = fulllyconnect (Y,权重,偏差);% 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(损失,参数);结束

模型预测函数

modelPredictions函数将模型参数、单词编码、标记文档数组、迷你批大小和最大序列长度作为输入,并通过迭代指定大小的迷你批来返回模型预测。

函数Y = modelPredictions(parameters,enc,documents,miniBatchSize,maxSequenceLength) inputSize = enc. numwords + 1;numObservations = numel(文档);numIterations = ceil(numObservations / miniBatchSize);numFeatures = size(parameters.fc.Weights,1);Y = 0 (numFeatures,numObservations,“喜欢”, parameters.fc.Weights);i = 1:numIterations idx = (i-1)*miniBatchSize+1:min(i*miniBatchSize,numObservations);len = min(maxSequenceLength,max(文档(idx))));X = doc2sequence(enc,documents(idx),...PaddingValue = inputSize,...长度= len);X = cat(1,X{:});X = dlarray(X,“BTC”);Y(:,idx) =模型(X,参数);结束结束

F-Score函数

标记F-score函数[2]通过关注具有部分匹配的每个文本分类来评估多标签分类。该度量是匹配标签与由给出的真实和预测标签总数的归一化比例

1 N n 1 N 2 c 1 C Y n c T n c c 1 C Y n c + T n c 标签f值

在哪里N而且C分别对应于观察数和类数,和Y而且T分别对应预测和目标。

函数score = labelingFScore(Y,T) numObservations = size(T,2);scores = (2 * sum(Y .* T)) ./ sum(Y + T);score = sum(scores) / numObservations;结束

梯度裁剪函数

thresholdL2Norm函数缩放输入梯度,以便它们的 l 2 时,范数等于指定的梯度阈值 l 2 可学习参数梯度的范数大于设置的阈值。

函数gradient = thresholdL2Norm(gradients,gradientThreshold) gradientNorm =√(sum(gradients(:).^2));如果gradientNorm > gradientThreshold gradientThreshold = gradients * (gradientThreshold / gradientNorm);结束结束

参考文献

  1. 出来了。“arXiv API”。2020年1月15日访问。https://arxiv.org/help/api

  2. Sokolova, Marina和Guy Lapalme。分类任务绩效指标的系统分析信息处理与管理45岁的没有。4(2009): 427-437。

另请参阅

|(深度学习工具箱)|(深度学习工具箱)|(深度学习工具箱)|(深度学习工具箱)|(深度学习工具箱)|(深度学习工具箱)||

相关的话题