在实习工作的过程中,其中有一个任务就是对一个多标签分类器进行调优,以此写个博客记录一下调优过程
分类器
我做的是一个多标签分类器,标签数大约为300个,根据一篇文章的标题和内容进行预测该文章属于哪几个标签。
比如一篇文章的标题是:“用c++实现冒泡排序”,里面的内容也是使用c++实现冒泡排序的思想和代码,那么预测出来的标签就可以是c++,冒泡排序,数据结构这三个。
分类器模型主体就是一个简单的word2vec+textcnn。
原理可以参考:通俗易懂的TextCNN_长竹Danko的博客-CSDN博客
调优过程
优化测试逻辑
原本的测试逻辑是:
- 创建一个包含所有tag的字典,其中每个tag有三个值“tp”,“fp“,”fn“分别代表真阳、假阳、假阴。
- 每预测一个样本将源数据tag列表和预测结果tag列表对比,分别得到每个tag是否为真阳、假阳、假阴,然后在相应的值上+1,最后用以计算每个tag分别的精确率和召回率
- 每预测一个样本如果预测结果tag列表中有一个预测正确则将正确数+1,最后除以总数的到分值。分值为0.96。
修改得分逻辑
原本的得分逻辑有比较大的缺陷,因为预测结果中之要命中一个就算正确样本,不太合理
修改为计算源tag列表和预测结果tag列表的余弦相似度,当相似度得分大于0.5时才算正确样本
def distinct(word_list1, word_list2):
import numpy as np
"""计算两个向量的余弦相似度距离"""
key_word = list(set(word_list1 + word_list2))
vec1 = np.zeros(len(key_word))
vec2 = np.zeros(len(key_word))
for i in range(len(key_word)):
for j in range(len(word_list1)):
if key_word[i] == word_list1[j]:
vec1[i] += 1
for k in range(len(word_list2)):
if key_word[i] == word_list2[k]:
vec2[i] += 1
cos_sim = vec1.dot(vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
return round(cos_sim, 2)
得到正确样本数后同样再除以总样本数得到最终分数,分数为0.86.
维护分级词典
由于tag数量比较多,难免会出现tag之间有着包含关系,比如:“tcp\ip”
∈
\in
∈"网络协议"
∈
\in
∈"网络"
那么,当源样本中出现tag为tcp\ip的情况时,分类器会将"网络协议"和"网络"均预测出来,从结果来看该预测是没有问题的,但是这样难免会影响到测试时最终得分运算,当这种情况比较多的时候对得分预算就会有比较大的影响了。
为了解决这种情况,我维护了一个分级词典,其中一部分类似:
lever_dict={"网络":"网络协议,tcp/ip,网络协议,http,https",
"ide":"intellij-idea,eclipse,myeclipse",
"深度学习":"神经网络",
"前端":"html,html5,css,css3",
"数据库":"sql,mysql,sqlserver",
"linux":"ubuntu,centos",
"算法":"排序算法,机器学习,leetcode,深度学习,神经网络",
"macos":"xcode",
"人工智能":"机器学习,深度学习,神经网络,cnn,rnn,lstm,tensorflow,自动驾驶,自然语言处理",
}
之后写了一个逻辑,当一级tag出现这预测结果tag列表中时,如果发现该tag下面的二级tag同时出现在源tag列表和预测结果tag列表中并且该一级tag没有出现在源tag列表中时,就将该一级tag临时加入到源tag列表中去。代码如下:
def directory(list1,list2):
#list1 是源tag,list2 是预测结果
for tag in list2:
if tag in lever_dict:
temp_tag = lever_dict[tag].strip().split(",")
for tag_u in list1:
if tag_u in temp_tag and tag not in list1:
list1.append(tag)
return list1,list2
最终计算得分为0.88。
以上两种都是对测试阶段的逻辑进行优化,使测试结果具有准确性
维护严格词典
在大量的样本中,同样也会出现一种情况,某些tag的特征是一些代码关键字,比如java,c,python都包含int、return之类的,所以当一篇比较长的包含代码比较多的文章中可能会出现大量的关键字,这样可能就会使得一些明明是c的文章被预测为java,从而影响各项数据。
但是经过进一步测试发现,这些情况发生时它们的普遍相关系数比较低,比如一个用c写的文章,tag"c"的相关性系数一般为0.3左右,而tag"java"和tag"python"的相关性系数一般为0.18左右,那么我们维护一个严格词典,当命中严格词典中的tag时将相关性系数阈值提高到0.2就能很大程度上避免这些情况的发生了。
对数据集和相似性词典进行更新
对于增量的数据集进行人工的一遍简单过滤,主要过滤一些精确率和召回率比较低的几个标签,对一些错误标注进行人工修改,同时也对相似性词典进行更新,增加一些相似词语对应,比如tag“电脑”增加"平板电脑"、"笔记本电脑"等,这样可以增强相关性。
最终得分为0.90
调优心得
对于一个深度学习模型来说,最重要的还是数据,对模型参数的调优效率比较一般。
首先要确定好测试逻辑,设计一个比较好的没有漏洞的测试逻辑,这样才能测试的时候拥有比较好的调优方向。
其次就是对分类器的分类策略进行调优,避免一些模棱两可的情况发生。
最重要的还是数据,拥有大量的、正确的数据才能训练出一个更好的模型,所以工作的重心要更加放在对数据的处理上。