前言
最近刷完了李航老师的《统计学习与方法》,手痒到又想复现几个算法,正好碰上在云音乐的云村视频标签运维标注不完全问题,也算是弱标注数据吧,之前这比数据作了多标签分类,尽管特征上线后各项数据都有所提升,但总感觉用神经网络直接对弱标签数据进行多标签分类很不舒服。
基于以下两个思考点:
- 存在标签缺失的问题,神经网络的意识在于我竟可能相信你给我的数据都是准确的,某个样本有某个标签是准确的,没有某个标签也是准确的。这会导致对于一些有缺失的标签,尽管网络见过相似的特征,但拟合的标签组却大不相同,无法有效学习到标签与特征的关系。
- 当标签缺失情况较少,而样本数据量足够多时,神经网络确实能比较好的应付脏数据带来的错误信息,但是对于那些本来就很少被标注,而且缺失情况较多的标签,最后模型在推理时,该类标签的产出也会非常稀有,预测不准确。尽管我们在最后加入了先验:即有效标注越少的标签越有可能被遗漏,对每个标签结果都乘以其IDF来纠正,但效果不是非常明显。这就导致最后输出的样本标签区分度较低,作为特征效果较差。
- 比较明显的问题:无法考虑标签之间的关联性,在MLOG方面,举例来说:风景标签与拍摄标签具有较强的关联性,女团标签与舞蹈、演唱等关联性较强,而神经网络无法利用这一信息。这个问题对于文本多标签分类任务已经有了一些解决方案,详情见论文:A Deep Reinforced Sequence-to-Set Model for Multi-Label Text Classification
MCWD算法
其实在寻找办法的过程中也看到了周志华团队的《Learning from Semi-Supervised Weak-Label Data》无奈其中数学公式复杂,尽管看懂了原理,但用python实现起来还是有一定的难度。
最后将目标锁定在了《针对弱标记数据的多标签分类算法(王晶晶,杨有龙)》(原文请大家自行搜索)文章利用了样本间的加权KNN + 标签相关性的二阶策略 对弱标注数据的标签矩阵进行恢复,再通过多标签分类算法进行标注。
算法展示
原论文中算法具体步骤如下:
![](https://i-blog.csdnimg.cn/blog_migrate/3c7afc1eb1bf1c46e82b7fa7797d0fa6.png)
算法改进
- 引入先验,适当增加随机的不相关标签数量: 在算法初始化第二步中,论文对于初始标签矩阵 C 中的每个标签 j ,在 C中随机选择 pj 个 Cij = 0 的实例,同时将选定实例的 Cij 值由原来的0变为−1,其中 pj 是每个标签 j 中所有相关标签的总数目。目的是为了,在训练数据的标签信息中添加一些不相关的标签信息,从而将缺失标签和不相关标签进行有效区分。但在复现过程中,发现对于稀疏的标签,往往由于初始的不相关标签信息不足,容易导致在迭代过程中过分补全。根据上述即有效标注越少的标签其本身先验概率也越低,我将该Pj值调整为—不包含该标签的样本数量 / n ( n为超参数,在实验过程中以 4 为佳)
- 在整体迭代结束后根据权重矩阵Wij来有选择的对Cij进行恢复,而是不论文中直接取sign(Cij),Wij * Cij 值高说明该标签越置信,而对于 Wij*Cij 值较低的,倾向于重置为0,让之后的标签相关矩阵去学习。较好的抑制算法过拟合。此处加入 阈值w(超参)
- 以及部分对原论文不合理之处的修改,详情见代码。
实现代码
import numpy as np
import pandas as pd
from sklearn.metrics import f1_score,hamming_loss
def data_get_2(path,p):
data = pd.read_csv(path)
feature = np.array(data.iloc[:,:103])
tag = np.array(data.iloc[:,103:])
real_tag = tag.copy()
for i in range(len(tag)):
if len(np.where(tag[i,:] == 1)[0]) > p:
index_list = np.random.choice(np.where(tag[i,:] == 1)[0],len(np.where(tag[i, :] == p)[0])-p,replace=False)
tag[i,:][index_list] = 0
'''
随机drop掉每个样本的标签,至每个样本最多拥有p个标签。
'''
return feature,tag,real_tag
def data_get_3(path,p):
data = pd.read_csv(path)
feature = np.array(data.iloc[:,1:1187])
tag = np.array(data.iloc[:,1187:])
real_tag = tag.copy()
for i in range(len(tag)):
if len(np.where(tag[i,:] == 1)[0]) > p:
index_list = np.random.choice(np.where(tag[i,:] == 1)[0],len(np.where(tag[i, :] == p)[0])-p,replace=False)
tag[i,:][index_list] = 0
return feature,tag,real_tag
class MCWD:
def __init__(self,e=0.8,c=0.2,k_t=10,s=1,w_e=0.3,rate=0.5):
self.e = e
self.c = c
self.k_t