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

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

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

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

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

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

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

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

  • 将一系列特征向量缩减为单个特征向量的最大池操作。

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

  • 用于学习输出和目标标号之间的二元交叉熵损失的s型运算。

此图显示了通过模型体系结构传播的一段文本,并输出了一个概率向量。这些概率是独立的,所以它们的和不需要等于1。

导入文本数据

使用arXiv API从数学论文中导入一组摘要和类别标签。属性指定要导入的记录数量importSize变量。请注意,arXiv API一次只能查询1000篇文章,并且请求之间需要等待。

importSize = 50000;

导入第一组记录。

url =“https://export.arxiv.org/oai2?verb=ListRecords”+...“集=数学”+...“&metadataPrefix = arXiv”;选项=网络选项(“超时”, 160);代码= webread (url选项);

解析返回的XML内容并创建htmlTree包含记录信息的对象。

树=htmlTree(代码);子树=findElement(树,“记录”);元素个数(子树)

迭代地导入更多的记录块,直到达到所需的数量,或者没有更多的记录。要继续从停止的位置导入记录,请使用resumptionToken属性。要遵守arXiv API施加的速率限制,请使用暂停函数。

numel(子树)“resumptionToken”);如果等空(近尾端)打破结束resumptionToken = extractHTMLText (subtreeResumption);url =“https://export.arxiv.org/oai2?verb=ListRecords”+..." &resumptionToken = "+ resumptionToken;暂停(20)code = webread(url,options);树= htmlTree(代码);子树=[子树;findElement(树,“记录”));结束

提取和预处理文本数据

从解析的HTML树中提取摘要和标签。

找到“< >类别”元素使用findElement函数。

subtreeAbstract = htmlTree ("");subtreegory=htmlTree("");对于subtreeAbstract(i) = findElement(subtrees(i)),“抽象”);subtreeCategory (i) = findElement(子树(我),“类别”);结束

属性从包含摘要的子树中提取文本数据extractHTMLText函数。

textData = extractHTMLText (subtreeAbstract);

的标记和预处理文本数据预处理文本函数,列在示例的最后。

documentsAll = preprocessText (textData);documentsAll (1:5)
ans = 5×1 tokenizedDocument: 72 token:描述新算法$(k,\ell)$卵石游戏颜色获得表征族$(k,\ell)$稀疏图算法解族问题关系树分解图特殊实例稀疏图出现刚性理论近年来受到越来越多的关注New proof tuttenashwilliams characterization arboricity present New decomposition certify sparsity base $(k,\ell)$卵石游戏颜色work expose connection卵石游戏算法previous sparse graph algorithm gabow westermann hendrickson 22 tokens:显示行列式斯特林循环数计数未标记的无环单源自动机证明涉及双射自动机某些有标记的晶格路径信号逆转对合计算行列式18个令牌:"paper" "show" "compute" "$\lambda_{\alpha}$" "norm" "$\alpha\ge 0$" "dyadic" "grid" "result" "consequence" "description" "hardy" "space" "$h^p(r^n)$" "term" "dyadic" "special" "atom" 62 token:偏立方等距子图超立方结构图定义平均半立方德约科维温克尔关系起重要作用理论偏立方结构利用纸刻画二部图偏立方任意维新的刻画建立新的证明已知结果给出运算笛卡尔积粘贴展开收缩过程利用纸张构造新的部分立方体旧的特定等距格维有限部分立方体获得平均运算计算29个令牌:本文给出了计算hecke特征系统hilbertsiegel尖形实二次域窄类数的算法,给出了算例二次域$\q(\根号{5})$的算例,确定了hilbertsiegel特征形可能的提升Hilbert特征形

从包含标签的子树中提取标签。

strLabels=extractHTMLText(subtreeCegory);labelsAll=arrayfun(@split,strLabels,“UniformOutput”、假);

删除不属于的标签“数学”集。

对于i = 1:numel(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);

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

图子地块(1,3,1)wordcloud(文档所有(idxCO));标题(“组合学”次要情节(1、3、2)wordcloud (documentsAll (idxST));标题(“统计理论”)子批次(1,3,3)wordcloud(文档所有(idxCOST));标题(“两个”

查看类个数。

一会=独特(猫(labelsAll {:}));numClasses =元素个数(类名)
numClasses = 32

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

标签计数= cellfun(@numel, labelsAll);图直方图(labelCounts)包含(“标签”) ylabel (“频率”)标题(”标签项”

为深度学习准备文本数据

将数据划分为训练分区和验证分区CVD分区函数。通过设置,保留10%的数据进行验证“坚持”选择0.1。

本量利= cvpartition(元素个数(documentsAll),“坚持”, 0.1);documentsTrain = documentsAll(培训(cvp));documentsValidation = documentsAll(测试(cvp));labelsTrain = labelsAll(培训(cvp));labelsValidation = labelsAll(测试(cvp));

创建一个单词编码对象,将培训文档编码为单词索引序列。通过设置“秩序”选项“频率”,“MaxNumWords”选择5000。

内附= wordEncoding (documentsTrain,“秩序”“频率”“MaxNumWords”, 5000)
enc = wordEncoding with properties: NumWords: 5000 Vocabulary: [1×5000 string]

要改进培训,请使用以下技巧:

  1. 在训练时,截断文档的长度,以减少使用的填充量,并且不丢弃太多的数据。

  2. 使用按长度升序排序的文档训练一个历元,然后在每个历元中洗牌数据。这种技术称为sortagrad

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

documentLengths = doclength (documentsTrain);图直方图(documentLengths)包含(“文档长度”) ylabel (“频率”)标题(“文档长度”

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

maxSequenceLength = 175;

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

[~, idx] = (documentLengths)进行排序;documentsTrain = documentsTrain (idx);labelsTrain = labelsTrain (idx);

定义和初始化模型参数

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

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

  • 对于嵌入,用随机的法线值初始化权值。

  • 对于GRU操作,使用initializeGlorot函数,列在示例的最后。

  • 属性初始化权值,用于完全连接操作initializeGaussian函数,列在示例的最后。

embeddingDimension = 300;numHiddenUnits = 250;inputSize = cn . numwords + 1;参数=结构;[endnoterp.com] . weights = dlarray(randn([embedingdimension inputSize]));parameters.gru.InputWeights = dlarray (initializeGlorot (3 * numHiddenUnits embeddingDimension));parameters.gru.RecurrentWeights = dlarray (initializeGlorot (3 * numHiddenUnits numHiddenUnits));parameters.gru.Bias = dlarray (0 (3 * numHiddenUnits, 1,“单一”));parameters.fc.Weights=dlarray(初始化高斯([numclass,numHiddenUnits]);parameters.fc.Bias=dlarray(零(numclass,1,“单一”));

查看参数结构。

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

查看GRU操作的参数。

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

定义模型函数

创建函数模型,它计算前面描述的深度学习模型的输出。这个函数模型将输入数据作为输入dlX模型参数参数.网络输出标签的预测。

定义模型梯度函数

创建函数modelGradients,它接受一小批输入数据作为输入dlX以及相应的目标T包含标签,并返回损耗相对于可学习参数的梯度、相应的损耗和网络输出。

指定培训选项

训练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 =元素个数(documentsTrain);numIterationsPerEpoch =地板(numObservationsTrain / miniBatchSize);validationFrequency = numIterationsPerEpoch;

在可用的GPU上进行训练。这需要并行计算工具箱™。使用GPU需要并行计算工具箱™和支持CUDA®的NVIDIA®GPU,其计算能力为3.0或更高。

executionEnvironment =“汽车”

火车模型

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

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

对于每个小批量:

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

  • 将序列转换为dlarray基础类型为single的对象,并指定标注标签“BCT”(批处理、通道、时间)。

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

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

  • 剪辑的梯度。

  • 使用以下命令更新网络参数:adamupdate函数。

  • 如有必要,请使用modelPredictions函数,列在示例的最后。

  • 更新培训计划。

初始化培训进度图。

如果情节= =“训练进步”数字%标记F分数。subplot(2,1,1) lineFScoreTrain = animatedline(“颜色”0.447 - 0.741 [0]);lineFScoreValidation = animatedline (...“线型”'--'...“标记”“o”...“MarkerFaceColor”“黑”);ylim([01])xlabel(“迭代”) ylabel (“标签f值”网格)%的损失。subplot(2,1,2) lineLossTrain = animatedline(“颜色”[0.85 0.325 0.098]);lineLossValidation = animatedline (...“线型”'--'...“标记”“o”...“MarkerFaceColor”“黑”);ylim([0正])包含(“迭代”) ylabel (“损失”网格)结束

初始化亚当优化器的参数。

trailingAvg=[];trailingAvgSq=[];

准备验证数据。创建一个一次性编码矩阵,其中非零项对应于每个观测的标签。

numObservationsValidation =元素个数(documentsValidation);TValidation = 0 (numClasses, nummobservationsvalidation,“单一”);对于i=1:numObservationsValidation[~,idx]=ismember(labelvalidation{i},classNames);TValidation(idx,i)=1;结束

训练模型。

迭代= 0;开始=抽搐;%循环纪元。对于时代= 1:numEpochs%循环小批。对于i = 1:numIterationsPerEpoch iteration = iteration + 1;idx =(张)* miniBatchSize + 1:我* miniBatchSize;%读取小批量数据并将标签转换为虚拟%变量。文件= documentsTrain (idx);标签= labelsTrain (idx);%将文档转换为序列。len = min (maxSequenceLength马克斯(doclength(文档)));X = doc2sequence (enc、文档...“PaddingValue”inputSize,...“长度”len);X =猫(1,X {:});% Dummify标签。T = 0 (numClasses, miniBatchSize,“单一”);对于j=1:miniBatchSize[~,idx2]=ismember(标签{j},类名);T(idx2,j)=1;结束%转换小批数据到美元。dlX = dlarray (X,“BTC”);%如果在GPU上训练,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&&canUseGPU)| |执行环境==“图形”dlX = gpuArray (dlX);结束使用dlfeval和% modelGradients函数。[gradient,loss,dlYPred] = dlfeval(@modelGradients, dlX, T, parameters);%梯度剪辑。gradient = dlupdate(@(g) thresholdL2Norm(g, gradientThreshold),gradient);%使用Adam优化器更新网络参数。(参数、trailingAvg trailingAvgSq) = adamupdate(参数、渐变...trailingAvg trailingAvgSq,迭代,learnRate、gradientDecayFactor squaredGradientDecayFactor);%显示训练进度。如果情节= =“训练进步”子批次(2,1,1)D=持续时间(0,0,toc(开始),“格式”“hh: mm: ss”);标题(”时代:“+纪元+”,过去:“+字符串(D))%的损失。addpoints (lineLossTrain、迭代、双(收集(extractdata(损失))))% f值标签。YPred = extractdata(dlYPred) > labelThreshold;分数= labelingFScore (YPred T);addpoints (lineFScoreTrain、迭代、双(收集(分数)))drawnow%显示验证指标。如果迭代== 1 || mod(iteration,validationFrequency) == 0 dlYPredValidation = modelPredictions(parameters,enc,documentsValidation,miniBatchSize,maxSequenceLength);%的损失。lossValidation = crossentropy (dlYPredValidation TValidation,...“TargetCategories”“独立”...“DataFormat”“CB”);addpoints (lineLossValidation、迭代、双(收集(extractdata (lossValidation))))% f值标签。YPredValidation = extractdata(dlYPredValidation) > labelThreshold;分数= labelingFScore (YPredValidation TValidation);addpoints (lineFScoreValidation、迭代、双(收集(分数)))drawnow结束结束结束%洗牌数据。idx = randperm (numObservationsTrain);documentsTrain = documentsTrain (idx);labelsTrain = labelsTrain (idx);结束

测试模型

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

dlYPredValidation = modelPredictions(参数、内附documentsValidation、miniBatchSize maxSequenceLength);

如果要将网络输出转换为标签数组,需要查找评分高于指定标签阈值的标签。

YPredValidation = extractdata(dlYPredValidation) > labelThreshold;

为了评估绩效,使用labelingFScore函数,列在示例末尾。标记F-score通过关注具有部分匹配的文本分类来评估多标签分类。

分数= labelingFScore (YPredValidation TValidation)
得分=0.5852

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

用力推= linspace (0, 1, 10);分数= 0(大小(刺));对于i = 1:numel(thr) YPredValidationThr = extractdata(dlYPredValidation) >= thr(i);分数(i) = labelingFScore (YPredValidationThr TValidation);结束图绘制(用力推,得分)参照线(labelThreshold,“r——”);包含(“阈值”) ylabel (“标签f值”)标题(“标记阈值效应”

可视化预测

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

Y = YPredValidation;T = TValidation;numTruePositives = sum(T & Y,2);numObservationsPerClass = (T, 2)之和;true = numTruePositives ./ numObservationsPerClass;

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

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

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

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

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

  • 对于每一对类(j),显示误报的实例数j当实例也是假否定for时

即,给定元素的混淆矩阵为:

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 cm = confectionchart (tpfnMatrix,classNames);sortClasses(厘米,“descending-diagonal”);标题(“真阳性,假阳性”

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

文本预处理功能

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

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

  2. 使用erasePunctuation函数。

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

  4. 使用移除单词函数。

  5. 用the使文本义化normalizeWords函数与“风格”选项设置为“引理”

函数文件= preprocessText (textData)标记文本。regularExpressions =表;regularExpressions。模式=“\ $ * ?。\ $”;regularExpressions。类型=“方程”;文件= tokenizedDocument (textData,“常规表达”, regularExpressions);%擦掉标点符号。= erasePunctuation文件(文档);%转换为小写。文件=低(文件);% Lemmatize。= addPartOfSpeechDetails文件(文档);文档= normalizeWords(文档,“风格”“引理”);删除停止字。= removeStopWords文件(文档);删除短单词。文件= removeShortWords(文件,2);结束

模型函数

这个函数模型将输入数据作为输入dlX模型参数参数,并返回标签的预测。

函数海底=模型(dlX、参数)%嵌入权重=parameters.emb.weights;dlX=嵌入(dlX,权重);%格勒乌inputWeights = parameters.gru.InputWeights;recurrentWeights = parameters.gru.RecurrentWeights;偏见= parameters.gru.Bias;numHiddenUnits =大小(inputWeights, 1) / 3;hiddenState = dlarray(zeros([numHiddenUnits 1]));dlY = gru(dlX, hiddenState, inputwights, recurrentwights, bias,“DataFormat”“认知行为治疗”);时间维度的最大池海底= max(海底,[],3);%完全连接重量= parameters.fc.Weights;偏见= parameters.fc.Bias;海底= fullyconnect(海底,重量、偏见,“DataFormat”“CB”);%乙状结肠海底=乙状结肠(海底);结束

模型梯度函数

modelGradients函数接受一小批输入数据作为输入dlX与相应的目标T包含标签并返回关于可学习参数、相应损耗和网络输出的损耗梯度。

函数[gradient,loss,dlYPred] = modelGradients(dlX,T,parameters)损失= crossentropy (dlYPred T“TargetCategories”“独立”“DataFormat”“CB”);梯度=dlgradient(损失、参数);结束

模型的预测函数

modelPredictions函数接受模型参数、单词编码、令牌化文档数组、迷你批大小和最大序列长度作为输入,并通过在指定大小的迷你批上迭代返回模型预测。

函数dlYPred = modelPredictions(参数,enc,文档,miniBatchSize,maxSequenceLength) inputSize = enc.NumWords + 1;numObservations =元素个数(文件);numIterations = cell (nummobations / miniBatchSize);numFeatures =大小(parameters.fc.Weights, 1);dlYPred = 0 (numFeatures numObservations,“喜欢”, parameters.fc.Weights);对于i = 1:numIterations idx = (i-1)*miniBatchSize+1:min(i*miniBatchSize,numObservations);len = min (maxSequenceLength马克斯(doclength(文档(idx))));X = doc2sequence (enc,文档(idx),...“PaddingValue”inputSize,...“长度”len);X =猫(1,X {:});dlX = dlarray (X,“BTC”);dlYPred (:, idx) =模型(dlX、参数);结束结束

标签f值函数

标记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值

在哪里NC分别对应观测值和类的数量,和YT分别对应于预测和目标。

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

Glorot权重初始化函数

initializeGlorot函数根据gloria初始化生成权重数组。

函数weights = initializeGlorot(numOut, numIn) varWeights = sqrt(6 / (numIn + numOut));= varWeights * (2 * rand([numOut, numIn]),“单一”) - 1);结束

高斯权重初始化函数

initializeGaussian函数从均值为0,标准差为0.01的高斯分布中抽样权重。

函数参数=初始化高斯(sz)参数=随机数(sz,“单一”) * 0.01;结束

嵌入函数

嵌入函数将数值索引映射到由输入权值给出的相应向量。

函数Z=嵌入(X,权重)将输入重塑为向量。[N, T] = size(X, 2:3);X =重塑(X, N*T, 1);索引到嵌入矩阵中。Z=重量(:,X);%通过分离批处理和序列维度来重塑输出。Z =重塑(Z, [], N, T);结束

l 2 范数梯度裁剪函数

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

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

参考文献

  1. arXiv.《arXiv API》,于2020年1月15日发布。https://arxiv.org/help/api

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

另见

|||||||||||

相关的话题