主要内容

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

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

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

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

为了衡量多标签分类的性能,可以使用标签f得分[2]。标记f评分通过关注具有部分匹配的文本分类来评估多标签分类。该度量是匹配标签的归一化比例相对于真实和预测标签的总数。

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

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

  • 学习嵌入向量之间依赖关系的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如果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游戏颜色工作暴露连接pebble博弈算法前稀疏图算法gabow gabow westermann hendrickson 22个令牌:显示行列式斯特林循环数计数无标记无环单源自动机证明涉及双射自动机某些标记格点路径信号可逆对合计算计算行列式18个令牌:论文显示计算$\lambda_{\alpha}$ norm alpha并格结果结果描述hardy空间$h^p(r^n)$ term并格特殊原子62个令牌:偏立方等距子图超立方结构图定义平均半立方djokovi winklers关系起重要作用理论偏立方结构本文描述偏立方图偏立方任意维新表征建立新证明知道结果给出运算笛卡尔积粘贴展开收缩过程利用本文构造新的偏立方旧的特等距格维有限偏立方得到平均运算计算29令牌:论文提出算法计算hecke本征系统hilbertsiegel cusp形式实二次域窄类数给出举例二次域$\q(\sqrt{5})$举例识别hilbertsiegel本征形式可能提升Hilbert本征形式

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

i = 1:numel(labelsAll) labelsAll{i} = labelsAll{i}(startwith (labelsAll{i},“数学。”));结束

在词云中可视化一些类。找到对应的文档如下:

  • 带有“组合学”标记的摘要和没有标记的摘要“统计理论”

  • 有“统计理论”标签的摘要和没有标签的摘要“组合”

  • 带有这两种标记的抽象“组合”而且“统计理论”

方法查找每个组的文档索引ismember函数。

idxCO = cellfun(@(lbls) ismember(“数学。有限公司”n; n; n; n;“数学。圣”lbls) labelsAll);idxST = cellfun(@(lbls)“数学。圣”n; n; n; n;“数学。有限公司”lbls) labelsAll);= 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(@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 = wordcoding (documentsTrain,Order= .“频率”MaxNumWords = 5000)
enc = wordcoding with properties: NumWords: 5000词汇:[1×5000 string]

为了改进培训,可以使用以下技巧:

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

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

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

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

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

maxSequenceLength = 175;

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

[~,idx] = sort(documentlength);documentsTrain = documentsTrain(idx);labelsTrain = labelsTrain(idx);

定义和初始化模型参数

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

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

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

  • 对于GRU操作,使用initializeGlorot而且initializeZeros函数,分别。

  • 方法初始化权重和偏差initializeGaussian而且initializeZeros函数,分别。

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

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

embeddingDimension = 300;numHiddenUnits = 250;inputSize = en . numwords + 1;参数= struct;sz = [embeddingDimension inputSize];Mu = 0;σ = 0.01;parameters.emb.Weights = initializgauss (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 1];parameters.gru.Bias = initializeZeros(sz);

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

sz = [numClasses numHiddenUnits];Mu = 0;σ = 0.01;parameters.fc.Weights = initializgauss (sz,mu,sigma);sz = [numClasses 1];parameters.fc.Bias = initializeZeros(sz);

查看参数结构体。

参数
参数=带有字段的结构:Emb: [1×1 struct] 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个周期,小批次大小为256。

numEpochs = 5;miniBatchSize = 256;

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

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

用阈值1 using剪辑渐变 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 = zero (numClasses, numObservationsValidation,“单身”);i = 1:numObservationsValidation [~,idx] = ismember(labelsValidation{i},classNames);TValidation(idx,i) = 1;结束

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

对于每个epoch,循环小批量的数据。在每个历元结束时,打乱数据。在每次迭代结束时,更新训练进度图。

对于每个小批次:

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

  • 将序列转换为dlarray对象的基础类型为单一,并指定维度标签“BTC”(批次、时间、渠道)。

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

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

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

  • 剪辑渐变。

  • 更新网络参数adamupdate函数。

  • 方法验证网络(如有必要)modelPredictions函数,在示例末尾列出。

  • 更新训练情节。

迭代= 0;开始= tic;循环遍历各个时代。纪元= 1:numEpochs在小批量上循环。i = 1:numIterationsPerEpoch迭代=迭代+ 1;= (i-1)*miniBatchSize+1:i*miniBatchSize;读取小批量数据,并将标签转换为虚拟%变量。文档= documentsTrain(idx);标签= 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;结束将小批量数据转换为dlarray。X = dlarray(X,“BTC”);如果在GPU上训练,那么将数据转换为GPU array。如果canUseGPU X = gpuArray(X);结束评估模型损失,梯度和预测使用dlfeval和% modelLoss函数。[loss,gradient,Y] = dlfeval(@modelLoss,X,T,parameters);%渐变裁剪。gradient = dlupdate(@(g) thresholdL2Norm(g,gradientThreshold),gradient);使用Adam优化器更新网络参数。[parameters,trailingAvg,trailingAvgSq] = adamupdate(参数,gradient,...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 = modelprediction (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);得分=双(得分);addpoints drawnow (lineFScoreValidation,迭代,分数)结束结束% Shuffle数据。idx = randperm(numObservationsTrain);documentsTrain = documentsTrain(idx);labelsTrain = labelsTrain(idx);结束

测试模型

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

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

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

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

通过尝试阈值的一系列值并比较结果,查看标记阈值对标记f得分的影响。

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

可视化预测

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

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

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

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

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

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

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

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

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

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

计算假阴性和假阳性。

falsennegative = T & ~Y;假阳性= ~T & Y;

计算非对角线元素。

falseNegatives = permute(falseNegatives,[3 2 1]);numconditionalfalse阳性= sum(假阴性和假阳性,2);numconditionalfalse正值=挤压(numconditionalfalse正值);tpfnMatrix = numconditionalfalse positive;

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

idx对角= 1:numClasses+1:numClasses^2;tpfnMatrix(idx对角线)= numtrue正数;

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

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

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

预处理文本命令功能

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

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

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

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

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

  5. 用动词来引理课文normalizeWords函数风格选项设置为“引理”

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

模型函数

这个函数模型接受输入数据作为输入X和模型参数参数,并返回标签的预测。

函数Y =模型(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 = dlarray(zero ([numHiddenUnits 1]));Y = gru(X,hiddenState,inputWeights,recurrentWeights,bias);%最大池化沿时间维度Y = max(Y,[],3);%完全连接weights = parameters.fc.Weights;bias = parameters.fc.Bias;Y =完全连接(Y,权重,偏差);% s形的Y = sigmoid(Y);结束

模型损失函数

modelLoss函数接受一个小批量输入数据作为输入X有相应的目标T包含标签并返回损耗、损耗相对于可学习参数的梯度和网络输出。

函数[loss,gradient,Y] = model loss (X,T,parameters) Y = model(X,parameters);loss = crossentropy(Y,T,TargetCategories=“独立”);gradient = dlgradient(损失,参数);结束

模型预测函数

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

函数Y = modelprediction (parameters,enc,documents,miniBatchSize,maxSequenceLength) inputSize = en . 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(docclth (documents(idx))));X = doc2sequence(enc,documents(idx),...PaddingValue = inputSize,...长度= len);X = cat(1,X{:});X = dlarray(X,“BTC”);Y(:,idx) =模型(X,参数);结束结束

F-Score函数

标记f评分函数[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));如果梯度阈值=梯度*(梯度阈值/梯度norm);结束结束

参考文献

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

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

另请参阅

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

相关的话题