├── NeurIPS2022-Camera Ready-Revised.pdf
├── NeurIPS2022_poster.pdf
├── NeurIPS2022_slides.pdf
├── README.md
├── coco.py
├── main_reweight.py
├── models.py
├── noise_rate_estimation_DualT.py
├── noise_rate_estimation_T.py
├── noise_rate_estimation_ours.py
├── script
├── run_coco_ours.sh
├── run_voc2007_ours.sh
└── run_voc2012_ours.sh
├── test.py
├── tools.py
├── util.py
└── voc.py
/NeurIPS2022-Camera Ready-Revised.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tmllab/2022_NeurIPS_Multi-Label-T/2ad38c66fb57646706165537e1a2d305bec90d97/NeurIPS2022-Camera Ready-Revised.pdf
--------------------------------------------------------------------------------
/NeurIPS2022_poster.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tmllab/2022_NeurIPS_Multi-Label-T/2ad38c66fb57646706165537e1a2d305bec90d97/NeurIPS2022_poster.pdf
--------------------------------------------------------------------------------
/NeurIPS2022_slides.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tmllab/2022_NeurIPS_Multi-Label-T/2ad38c66fb57646706165537e1a2d305bec90d97/NeurIPS2022_slides.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Estimating Noise Transition Matrix with Label Correlations for Noisy Multi-Label Learning - Official PyTorch Code (NeurIPS 2022)
2 |
3 | ### Abstract:
4 | In label-noise learning, the noise transition matrix, bridging the class posterior for noisy and clean data, has been widely exploited to learn statistically consistent classifiers. The effectiveness of these algorithms relies heavily on estimating the transition matrix. Recently, the problem of label-noise learning in multi-label classification has received increasing attention, and these consistent algorithms can be applied in multi-label cases. However, the estimation of transition matrices in noisy multi-label learning has not been studied and remains challenging, since most of the existing estimators in noisy multi-class learning depend on the existence of anchor points and the accurate fitting of noisy class posterior. To address this problem, in this paper, we first study the identifiability problem of the class-dependent transition matrix in noisy multi-label learning, and then inspired by the identifiability results, we propose a new estimator by exploiting label correlations without neither anchor points nor accurate fitting of noisy class posterior. Specifically, we estimate the occurrence probability of two noisy labels to get noisy label correlations. Then, we perform sample selection to further extract information that implies clean label correlations, which is used to estimate the occurrence probability of one noisy label when a certain clean label appears. By utilizing the mismatch of label correlations implied in these occurrence probabilities, the transition matrix is identifiable, and can then be acquired by solving a simple bilinear decomposition problem. Empirical results demonstrate the effectiveness of our estimator to estimate the transition matrix with label correlations, leading to better classification performance.
5 |
6 | ### Requirements:
7 | * Python 3.8.10
8 | * Pytorch 1.8.0 (torchvision 0.9.0)
9 | * Numpy 1.19.5
10 | * Scikit-learn 1.0.1
11 |
12 |
13 | ### Running the code:
14 | To run the code use the provided scripts in the script folder. The dataset has to be placed in the data folder (should be done automatically).
15 | The arguments for running the code are as follows:
16 |
17 | ```
18 | * batch_size, the batch size for learning
19 | * num_classes, the number of classes in the dataset
20 | * warmup_epoch, the number of warmup epoch for standard training
21 | * nepochs, the number of training epoch for reweighting
22 | * sample_epoch, the number of epoch for sample selection during warmup training
23 | * sample_th, the threshold of sample selection
24 | * nworkers, the number of workers in dataloder
25 | * dataset, the name of the used dataset, i.e., 'voc2007', 'voc2012', or 'coco'
26 | * seed, the random seed for label noise simulation
27 | * root, the path for the data folder
28 | * out, the path for the output folder
29 | * noise_rate_p, the noise rate for positive class value
30 | * noise_rate_n, the noise rate for negative class value
31 | * lr, learning rate for learning
32 | * weight-decay, the weight decay for learning,
33 | * estimator, the method for estimating transition matrix, i.e.,'T', 'dualT', or 'ours'
34 | * filter_outlier, the parameter for T estimator and dualT estimator
35 | ```
36 |
37 | ### Citation:
38 | If you find the code useful in your research, please consider citing our paper:
39 |
40 | ```
41 | @InProceedings{Li2022MLT,
42 | title = {Estimating Noise Transition Matrix with Label Correlations for Noisy Multi-Label Learning},
43 | authors = {Shikun Li and Xiaobo Xia and Hansong Zhang and Yibing Zhan and Shiming Ge and Tongliang Liu},
44 | year={2022},
45 | booktitle ={36th Conference on Neural Information Processing Systems (NeurIPS 2022)},
46 | }
47 | ```
48 |
49 | Note: Our implementation uses parts of some public codes [1-2].
50 |
51 | [1] "Dual T: Reducing Estimation Error for Transition Matrix in Label-noise Learning" https://github.com/a5507203/dual-t-reducing-estimation-error-for-transition-matrix-in-label-noise-learning
52 |
53 | [2] "Classification with Noisy Labels by Importance Reweighting" https://github.com/xiaoboxia/Classification-with-noisy-labels-by-importance-reweighting
54 |
--------------------------------------------------------------------------------
/coco.py:
--------------------------------------------------------------------------------
1 | #for coco dataset load
2 | import torch.utils.data as data
3 | import json
4 | import os
5 | import subprocess
6 | from PIL import Image
7 | import numpy as np
8 | import torch
9 | import pickle
10 | from util import *
11 | import sys
12 |
13 | urls = {'train_img':'http://images.cocodataset.org/zips/train2014.zip',
14 | 'val_img' : 'http://images.cocodataset.org/zips/val2014.zip',
15 | 'annotations':'http://images.cocodataset.org/annotations/annotations_trainval2014.zip'}
16 |
17 | def download_coco2014(root, phase):
18 | if not os.path.exists(root):
19 | os.makedirs(root)
20 | tmpdir = os.path.join(root, 'tmp/')
21 | data = os.path.join(root, 'data/')
22 | if not os.path.exists(data):
23 | os.makedirs(data)
24 | if not os.path.exists(tmpdir):
25 | os.makedirs(tmpdir)
26 | if phase == 'train':
27 | filename = 'train2014.zip'
28 | elif phase == 'val':
29 | filename = 'val2014.zip'
30 | cached_file = os.path.join(tmpdir, filename)
31 | if not os.path.exists(cached_file):
32 | print('Downloading: "{}" to {}\n'.format(urls[phase + '_img'], cached_file))
33 | os.chdir(tmpdir)
34 | subprocess.call('wget ' + urls[phase + '_img'], shell=True)
35 | os.chdir(root)
36 | # extract file
37 | img_data = os.path.join(data, filename.split('.')[0])
38 | if not os.path.exists(img_data):
39 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=data))
40 | command = 'unzip {} -d {}'.format(cached_file,data)
41 | os.system(command)
42 | print('[dataset] Done!')
43 |
44 | # train/val images/annotations
45 | cached_file = os.path.join(tmpdir, 'annotations_trainval2014.zip')
46 | if not os.path.exists(cached_file):
47 | print('Downloading: "{}" to {}\n'.format(urls['annotations'], cached_file))
48 | os.chdir(tmpdir)
49 | subprocess.Popen('wget ' + urls['annotations'], shell=True)
50 | os.chdir(root)
51 | annotations_data = os.path.join(data, 'annotations')
52 | if not os.path.exists(annotations_data):
53 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=data))
54 | command = 'unzip {} -d {}'.format(cached_file, data)
55 | os.system(command)
56 | print('[annotation] Done!')
57 |
58 | anno = os.path.join(data, '{}_anno.json'.format(phase))
59 | anno2 = os.path.join(data, '{}_anno2.json'.format(phase))
60 | img_id = {}
61 | annotations_id = {}
62 | if not (os.path.exists(anno) and os.path.exists(anno2)):
63 | annotations_file = json.load(open(os.path.join(annotations_data, 'instances_{}2014.json'.format(phase))))
64 | annotations = annotations_file['annotations']
65 | category = annotations_file['categories']
66 | category_id = {}
67 | for cat in category:
68 | category_id[cat['id']] = cat['name']
69 | cat2idx = categoty_to_idx(sorted(category_id.values()))
70 | images = annotations_file['images']
71 | for annotation in annotations:
72 | if annotation['image_id'] not in annotations_id:
73 | annotations_id[annotation['image_id']] = set()
74 | annotations_id[annotation['image_id']].add(cat2idx[category_id[annotation['category_id']]])
75 | for img in images:
76 | if img['id'] not in annotations_id:
77 | continue
78 | if img['id'] not in img_id:
79 | img_id[img['id']] = {}
80 | img_id[img['id']]['file_name'] = img['file_name']
81 | img_id[img['id']]['labels'] = list(annotations_id[img['id']])
82 | anno_list = []
83 | for k, v in img_id.items():
84 | anno_list.append(v)
85 | json.dump(anno_list, open(anno, 'w'))
86 | anno_list_2 = []
87 | for k, v in img_id.items():
88 | anno_list_2.append(v['labels'])
89 | json.dump(anno_list_2, open(anno2, 'w'))
90 | if not os.path.exists(os.path.join(data, 'category.json')):
91 | json.dump(cat2idx, open(os.path.join(data, 'category.json'), 'w'))
92 | del img_id
93 | del anno_list
94 | del anno_list_2
95 | del images
96 | del annotations_id
97 | del annotations
98 | del category
99 | del category_id
100 | print('[json] Done!')
101 |
102 | def categoty_to_idx(category):
103 | cat2idx = {}
104 | for cat in category:
105 | cat2idx[cat] = len(cat2idx)
106 | return cat2idx
107 |
108 |
109 | class COCO2014(data.Dataset):
110 | def __init__(self, root, transform=None, phase='train', Train=True, noise_rate=[0,0],random_seed=1):
111 | self.root = root
112 | self.phase = phase
113 | self.img_list = []
114 | self.transform = transform
115 | download_coco2014(root, phase)
116 | self.get_anno()
117 | self.true_labels=self.get_true_labels()
118 | self.num_classes = len(self.cat2idx)
119 | self.img_list=np.array(self.img_list)
120 | if(phase=='train'):
121 | self.labels= generate_noisy_labels(self.true_labels , noise_rate,random_seed)
122 | if(Train):
123 | self.img_list , self.labels, self.true_labels , _, _, _=dataset_split(self.img_list ,self.labels,self.true_labels, num_classes=self.num_classes)
124 | else:
125 | _, _, _, self.img_list , self.labels, self.true_labels =dataset_split(self.img_list ,self.labels,self.true_labels, num_classes=self.num_classes)
126 | else:
127 | self.labels= self.true_labels
128 |
129 | def get_anno(self):
130 | list_path = os.path.join(self.root, 'data', '{}_anno.json'.format(self.phase))
131 | self.img_list = json.load(open(list_path, 'r'))
132 | self.cat2idx = json.load(open(os.path.join(self.root, 'data', 'category.json'), 'r'))
133 |
134 | def get_true_labels(self):
135 | list_path = os.path.join(self.root, 'data', '{}_anno2.json'.format(self.phase))
136 | labels=json.load(open(list_path, 'r'))
137 | true_labels=np.zeros((len(labels),len(self.cat2idx)))-1
138 | for i,label in enumerate(labels):
139 | true_labels[i,label]=1
140 | return true_labels
141 |
142 | def __len__(self):
143 | return len(self.img_list)
144 |
145 | def __getitem__(self, index):
146 | item = self.img_list[index]
147 | target=self.labels[index]
148 | return self.get(item),target
149 |
150 | def get(self, item):
151 | filename = item['file_name']
152 | #labels = sorted(item['labels'])
153 | img = Image.open(os.path.join(self.root, 'data', '{}2014'.format(self.phase), filename)).convert('RGB')
154 | if self.transform is not None:
155 | img = self.transform(img)
156 | # target = np.zeros(self.num_classes, np.float32) - 1
157 | # target[labels] = 1
158 | return img
159 |
160 |
161 | def generate_noisy_labels(labels, noise_rate,random_seed):
162 |
163 | N, nc = labels.shape
164 | np.random.seed(random_seed)
165 | rand_mat = np.random.rand(N,nc)
166 | mask = np.zeros((N,nc), dtype = np.float)
167 | for j in range(nc):
168 | yj = labels[:,j]
169 | mask[yj!=1,j] = rand_mat[yj!=1,j] 1:
164 | net = nn.DataParallel(net)
165 | net=net.to(device)
166 | if torch.cuda.device_count() > 1:
167 | optimizer_es = torch.optim.Adam(net.parameters(),lr=args.lr,weight_decay=args.weight_decay)
168 | else:
169 | optimizer_es = torch.optim.Adam(net.parameters(),lr=args.lr,weight_decay=args.weight_decay)
170 |
171 | true_tm = np.zeros((args.nc,2,2))
172 | for i in range(args.nc):
173 | true_tm[i,0,0]=1-noise_rate[0]
174 | true_tm[i,0,1]=noise_rate[0]
175 | true_tm[i,1,0]=noise_rate[1]
176 | true_tm[i,1,1]=1-noise_rate[1]
177 |
178 | # Estimate Transition Matrices
179 | if(args.estimator=="ours"):
180 | from noise_rate_estimation_ours import *
181 | t_m=estimate_noise_rate(net,train_loader, val_loader, estimate_loader,optimizer_es,args,true_tm=true_tm)
182 | elif(args.estimator=="dualT"):
183 | from noise_rate_estimation_DualT import *
184 | t_m=estimate_noise_rate(net,train_loader, val_loader, estimate_loader,optimizer_es,args,true_tm=true_tm,filter_outlier=args.filter_outlier)
185 | elif(args.estimator=="T"):
186 | from noise_rate_estimation_T import *
187 | t_m=estimate_noise_rate(net,train_loader, val_loader, estimate_loader,optimizer_es,args,true_tm=true_tm,filter_outlier=args.filter_outlier)
188 | print('t_m',t_m)
189 |
190 | if torch.cuda.device_count() > 1:
191 | optimizer = torch.optim.Adam(net.parameters(),lr=args.lr,weight_decay=args.weight_decay)
192 | else:
193 | optimizer = torch.optim.Adam(net.parameters(),lr=args.lr,weight_decay=args.weight_decay)
194 |
195 | # Train the network with reweighting
196 |
197 | val_metric=[]
198 | test_metric=[]
199 |
200 | best_val=0
201 | for i in range(args.nepochs):
202 | start = time.time()
203 | train_loss = train(net, train_loader, optimizer,t_m)
204 | map, OP, OR, OF1, CP, CR, CF1= test(net, val_loader)
205 |
206 | print('Epoch',i,' val_map, OP, OR, OF1, CP, CR, CF1 ',round(map,2), round(OP,2), round(OR,2), round(OF1,2), round(CP,2),round(CR,2),round(CF1,2))
207 | val_metric.append([map, OP, OR, OF1, CP, CR, CF1])
208 | if(map>best_val):
209 | best_val=map
210 | best_state_dict = copy.deepcopy(net.state_dict())
211 |
212 | map, OP, OR, OF1, CP, CR, CF1= test(net, test_loader)
213 | print('Epoch',i,'test_map, OP, OR, OF1, CP, CR, CF1 ',round(map,2), round(OP,2), round(OR,2), round(OF1,2), round(CP,2),round(CR,2),round(CF1,2))
214 | test_metric.append([map, OP, OR, OF1, CP, CR, CF1])
215 |
216 | end = time.time()
217 | print('time', round(end-start,3))
218 | log_file.flush()
219 |
220 | # early stop
221 | net.load_state_dict(best_state_dict)
222 |
223 | val_metric=np.array(val_metric)
224 | test_metric=np.array(test_metric)
225 | best_map,_, _,best_OF1,_, _, best_CF1=np.argmax(val_metric,axis=0)
226 |
227 | print('Best map',' val_map, OP, OR, OF1, CP, CR, CF1 ',round(val_metric[best_map,0],2), round(val_metric[best_map,1],2), round(val_metric[best_map,2],2), round(val_metric[best_map,3],2), round(val_metric[best_map,4],2),round(val_metric[best_map,5],2),round(val_metric[best_map,6],2))
228 |
229 | print('Best map','test_map, OP, OR, OF1, CP, CR, CF1 ',round(test_metric[best_map,0],2), round(test_metric[best_map,1],2), round(test_metric[best_map,2],2), round(test_metric[best_map,3],2), round(test_metric[best_map,4],2),round(test_metric[best_map,5],2),round(test_metric[best_map,6],2))
230 |
231 |
232 | log_file.close()
233 |
--------------------------------------------------------------------------------
/models.py:
--------------------------------------------------------------------------------
1 | #used Resnet50
2 | import torchvision.models as models
3 | from torch.nn import Parameter
4 | import torch
5 | import torch.nn as nn
6 | import sys
7 |
8 | class Resnet(nn.Module):
9 | def __init__(self, model, num_classes):
10 | super(Resnet, self).__init__()
11 |
12 | modules = list(model.children())[:-1]
13 | self.features = nn.Sequential(*modules)
14 |
15 | self.linear = nn.Linear(2048, num_classes)
16 |
17 | def forward(self, feature, get_feature=False):
18 | feature = self.features(feature)
19 |
20 | gf = self.linear(feature.view(feature.size(0), -1))
21 | gf = gf.view(gf.size(0), -1)
22 |
23 | if(get_feature):
24 | return feature
25 | else:
26 | return gf
27 |
28 | def get_resnet50(num_classes, pretrained=True):
29 | model = models.resnet50(pretrained=pretrained)
30 | return Resnet(model, num_classes)
31 |
--------------------------------------------------------------------------------
/noise_rate_estimation_DualT.py:
--------------------------------------------------------------------------------
1 | # DualT estimator
2 | import torch
3 | import sys
4 | import numpy as np
5 | import tools
6 | import copy
7 | import torch.nn as nn
8 | def loss_func_bce(out, batch_y):
9 | bce = torch.nn.BCELoss()
10 | loss = bce(torch.sigmoid(out), batch_y.float())
11 | return loss
12 |
13 | def loss_func_bce2(out, batch_y):
14 | bce = torch.nn.BCELoss(reduction='sum')
15 | loss = bce(torch.sigmoid(out), batch_y.float())
16 | return loss
17 |
18 | def estimate_noise_rate(model,train_loader, val_loader, estimate_loader,optimizer_es,args,true_tm,filter_outlier=False):
19 | print('Estimate transition matirx......Waiting......')
20 |
21 | A= torch.zeros((args.nc, args.warmup_epoch, len(train_loader.dataset), 2))
22 | val_list = [] # for val_loss
23 | val_list_2 = [] # for val_acc
24 | val_list_list = [] # for each class's val_loss
25 | val_list_list_2=[] # for each class's val_acc
26 | best_val=100
27 | for i in range(args.nc):
28 | val_list_list.append([])
29 | val_list_list_2.append([])
30 | # warmup training
31 | for epoch in range(args.warmup_epoch):
32 |
33 | print('epoch {}'.format(epoch + 1))
34 | model.train()
35 | train_loss = 0.
36 |
37 | for batch_x, batch_y in train_loader:
38 | batch_x = batch_x.cuda().float()
39 | batch_y = batch_y.cuda().float()
40 | batch_y[batch_y==0]=1
41 | batch_y[batch_y==-1]=0
42 | optimizer_es.zero_grad()
43 | out = model(batch_x)
44 | loss = loss_func_bce(out, batch_y)
45 | train_loss += loss.item()
46 | loss.backward()
47 | optimizer_es.step()
48 |
49 |
50 | print('Train Loss: {:.6f}'.format(train_loss / (len(train_loader.dataset))*args.bs))
51 |
52 | with torch.no_grad():
53 | model.eval()
54 | val_loss = 0.
55 | val_loss_2=[0]*args.nc
56 | val_acc=0
57 | val_acc_2=[0]*args.nc
58 | for batch_x, batch_y in val_loader:
59 | batch_x = batch_x.cuda().float()
60 | batch_y = batch_y.cuda().float()
61 | batch_y[batch_y==0]=1
62 | batch_y[batch_y==-1]=0
63 | out = model(batch_x)
64 | loss = loss_func_bce(out, batch_y)
65 | val_loss += loss.item()
66 | pred = out>0.5
67 | val_correct = (pred == batch_y).sum()
68 | val_acc += val_correct.item()
69 | for i in range(args.nc):
70 | loss_2 = loss_func_bce2(out[:,i], batch_y[:,i])
71 | val_loss_2[i] += loss_2.item()
72 | val_correct_2 = (pred[:,i] == batch_y[:,i]).sum()
73 | val_acc_2[i] += val_correct_2.item()
74 | if(val_loss0.5
68 | val_correct = (pred == batch_y).sum()
69 | val_acc += val_correct.item()
70 | for i in range(args.nc):
71 | loss_2 = loss_func_bce2(out[:,i], batch_y[:,i])
72 | val_loss_2[i] += loss_2.item()
73 | val_correct_2 = (pred[:,i] == batch_y[:,i]).sum()
74 | val_acc_2[i] += val_correct_2.item()
75 | if(val_loss0.5
70 | val_correct = (pred == batch_y).sum()
71 | val_acc += val_correct.item()
72 | for i in range(args.nc):
73 | loss_2 = loss_func_bce2(out[:,i], batch_y[:,i])
74 | val_loss_2[i] += loss_2.item()
75 | val_correct_2 = (pred[:,i] == batch_y[:,i]).sum()
76 | val_acc_2[i] += val_correct_2.item()
77 | if(val_lossth)
149 |
150 | if(True_T[i,0,1]==0): # for multi-label learning with missing labels
151 | select_vec[Y[:,i]==1,i]=1
152 | if(True_T[i,1,0]==0): # for partial multi-label learning
153 | select_vec[Y[:,i]==0,i]=1
154 |
155 |
156 | print('class ',i,' positive ', sum(Y[:,i]),' negative ', sum(1-Y[:,i]), ' selected positive ', sum(Y[select_vec[:,i]==1,i]),' negative ', sum(1-Y[select_vec[:,i]==1,i]))
157 |
158 | print('positive precision',sum((true_train_labels[:,i]==1)*(select_vec[:,i]==1)*(Y[:,i]==1))/sum(Y[select_vec[:,i]==1,i]), 'negative precision', sum((true_train_labels[:,i]==0)*(select_vec[:,i]==1)*(Y[:,i]==0))/sum(1-Y[select_vec[:,i]==1,i]))
159 |
160 | error=0
161 | est_T = np.zeros_like(True_T)
162 | temp_list=[]
163 | for i in range(args.nc):
164 | temp_estimation=[]
165 | selected_i=select_vec[:,i]
166 | selected_Y_i = Y[selected_i==1,:]
167 | if(sum(selected_Y_i[:,i])<50 or sum(1-selected_Y_i[:,i])<50):
168 | continue
169 | for k in range(args.nc):
170 | if(i==k):
171 | continue
172 | print('i',i,' k',k)
173 | P_noisy_11=sum(Y[:,i]*Y[:,k])/len(Y)
174 | P_noisy_10=sum(Y[:,i]*(1-Y[:,k]))/len(Y)
175 | P_noisy_01=sum((1-Y[:,i])*Y[:,k])/len(Y)
176 | P_noisy_00=1-P_noisy_11-P_noisy_10-P_noisy_01
177 |
178 | Pc_clean_1_noisy_1=sum(selected_Y_i[:,k]*selected_Y_i[:,i])/sum(selected_Y_i[:,i])
179 | Pc_clean_1_noisy_0=sum((1-selected_Y_i[:,k])*selected_Y_i[:,i])/sum(selected_Y_i[:,i])
180 | Pc_clean_0_noisy_1=sum(selected_Y_i[:,k]*(1-selected_Y_i[:,i]))/sum(1-selected_Y_i[:,i])
181 | Pc_clean_0_noisy_0=sum((1-selected_Y_i[:,k])*(1-selected_Y_i[:,i]))/sum(1-selected_Y_i[:,i])
182 |
183 | pi_1 = -(1.0*(P_noisy_00 - 1.0*Pc_clean_0_noisy_0 + P_noisy_10))/(Pc_clean_0_noisy_0 - 1.0*Pc_clean_1_noisy_0)
184 | print( 'pi_1',pi_1 )
185 |
186 | if( pi_1<0 or pi_1>1 ):
187 | continue
188 |
189 | P_clean_1_noisy_1=Pc_clean_1_noisy_1*pi_1
190 | P_clean_1_noisy_0=Pc_clean_1_noisy_0*pi_1
191 | P_clean_0_noisy_1=Pc_clean_0_noisy_1*(1-pi_1)
192 | P_clean_0_noisy_0=Pc_clean_0_noisy_0*(1-pi_1)
193 |
194 | lo_i_0 = -(1.0*P_clean_1_noisy_0*P_noisy_11 - 1.0*P_clean_1_noisy_1*P_noisy_10)/(P_clean_0_noisy_0*P_clean_1_noisy_1 - 1.0*P_clean_0_noisy_1*P_clean_1_noisy_0)
195 | lo_i_1 =(1.0*(P_clean_0_noisy_0*P_noisy_01 - 1.0*P_clean_0_noisy_1*P_noisy_00))/(P_clean_0_noisy_0*P_clean_1_noisy_1 - 1.0*P_clean_0_noisy_1*P_clean_1_noisy_0)
196 | print('lo_i_0, lo_i_1',lo_i_0, lo_i_1)
197 |
198 | if(lo_i_0<0 and lo_i_0>-0.3/args.nc):
199 | lo_i_0=0
200 | if(lo_i_1<0 and lo_i_1>-0.3/args.nc):
201 | lo_i_1=0
202 | if(lo_i_1<-0.3/args.nc or lo_i_1<-0.3/args.nc):
203 | continue
204 |
205 | if(True_T[i,0,1]==0): # for multi-label learning with missing labels
206 | lo_i_0=0
207 | if(True_T[i,1,0]==0): # for partial multi-label learning
208 | lo_i_1=0
209 |
210 | if(lo_i_0>=0 and lo_i_0<=1 and lo_i_1>=0 and lo_i_1<=1 and lo_i_0+lo_i_1<1):
211 | T=np.array([[1-lo_i_0, lo_i_0], [lo_i_1,1-lo_i_1]])
212 |
213 | estimate_error = tools.error(T, True_T[i])
214 | print('class i ', i, ' class k ',k,' our estimation', T[range(2),[1,0]], 'True_T', True_T[i,range(2),[1,0]], 'error', estimate_error,'\n')
215 | temp_estimation.append(T[range(2),[1,0]])
216 | continue
217 | else:
218 | continue
219 | # select best one from temp estimations
220 | if(len(temp_estimation)==0):
221 | temp_list.append(i)
222 | else:
223 | temp_error=[0]*len(temp_estimation)
224 | for k in range(args.nc):
225 | if(i==k):
226 | continue
227 | P_noisy_11=sum(Y[:,i]*Y[:,k])/len(Y)
228 | P_noisy_10=sum(Y[:,i]*(1-Y[:,k]))/len(Y)
229 | P_noisy_01=sum((1-Y[:,i])*Y[:,k])/len(Y)
230 | P_noisy_00=1-P_noisy_11-P_noisy_10-P_noisy_01
231 |
232 | selected_i=select_vec[:,i]
233 | selected_Y_i = Y[selected_i==1,:]
234 |
235 | Pc_clean_1_noisy_1=sum(selected_Y_i[:,k]*selected_Y_i[:,i])/sum(selected_Y_i[:,i])
236 | Pc_clean_1_noisy_0=sum((1-selected_Y_i[:,k])*selected_Y_i[:,i])/sum(selected_Y_i[:,i])
237 | Pc_clean_0_noisy_1=sum(selected_Y_i[:,k]*(1-selected_Y_i[:,i]))/sum(1-selected_Y_i[:,i])
238 | Pc_clean_0_noisy_0=sum((1-selected_Y_i[:,k])*(1-selected_Y_i[:,i]))/sum(1-selected_Y_i[:,i])
239 |
240 |
241 | pi_1 = -(1.0*(P_noisy_00 - 1.0*Pc_clean_0_noisy_0 + P_noisy_10))/(Pc_clean_0_noisy_0 - 1.0*Pc_clean_1_noisy_0)
242 |
243 |
244 | P_clean_1_noisy_1=Pc_clean_1_noisy_1*pi_1
245 | P_clean_1_noisy_0=Pc_clean_1_noisy_0*pi_1
246 | P_clean_0_noisy_1=Pc_clean_0_noisy_1*(1-pi_1)
247 | P_clean_0_noisy_0=Pc_clean_0_noisy_0*(1-pi_1)
248 |
249 | for index, lo in enumerate(temp_estimation):
250 | lo_i_0, lo_i_1 =lo[0],lo[1]
251 | temp_error[index] += abs(lo_i_0*(P_clean_0_noisy_0*P_clean_1_noisy_1 - 1.0*P_clean_0_noisy_1*P_clean_1_noisy_0)+(1.0*P_clean_1_noisy_0*P_noisy_11 - 1.0*P_clean_1_noisy_1*P_noisy_10))
252 | temp_error[index] += abs(lo_i_1*(P_clean_0_noisy_0*P_clean_1_noisy_1 - 1.0*P_clean_0_noisy_1*P_clean_1_noisy_0) - (1.0*(P_clean_0_noisy_0*P_noisy_01 - 1.0*P_clean_0_noisy_1*P_noisy_00)))
253 | temp_error=np.array(temp_error)
254 | print('temp_error',temp_error)
255 | lo=temp_estimation[np.argmin(temp_error)]
256 | lo_i_0, lo_i_1 =lo[0],lo[1]
257 | T=np.array([[1-lo_i_0, lo_i_0], [lo_i_1,1-lo_i_1]])
258 |
259 | estimate_error = tools.error(T, True_T[i])
260 | error+=estimate_error
261 | est_T[i] = T
262 | print('class', i, ' final ours estimation', T[range(2),[1,0]], 'True_T', True_T[i,range(2),[1,0]], 'error', estimate_error,'\n')
263 |
264 | # use dualT estimator for unsolvable classes
265 | for i in temp_list:
266 | val_array = np.array(val_list_list[i]) # we use the val loss here for selecting each class's model
267 | model_index = np.argmax(-val_array)
268 | print('model_index',model_index)
269 | prob_=copy.deepcopy(A[i])
270 | transition_matrix_ = tools.fit(prob_[model_index, :, :], 2, False)
271 | transition_matrix = tools.norm(transition_matrix_)
272 |
273 | T_ = transition_matrix
274 |
275 | T=copy.deepcopy(T_)
276 |
277 | if(True_T[i,0,1]==0): # for multi-label learning with missing labels
278 | T[0,1]=0
279 | T[0,0]=1
280 | if(True_T[i,1,0]==0): # for partial multi-label learning
281 | T[1,0]=0
282 | T[1,1]=1
283 |
284 | estimate_error = tools.error(T, True_T[i])
285 | print('class', i, 'T estimation max',T[range(2),[1,0]], 'True_T', True_T[i,range(2),[1,0]], 'error', estimate_error)
286 |
287 | pred= np.argmax(prob_[model_index, :, :],axis=-1)
288 | T_spadesuit = np.zeros((2,2))
289 | for j in range(len(Y)):
290 | T_spadesuit[int(pred[j])][int(Y[j,i])]+=1
291 | T_spadesuit = np.array(T_spadesuit)
292 | sum_matrix = np.tile(T_spadesuit.sum(axis = 1),(2,1)).transpose()
293 | T_spadesuit = T_spadesuit/sum_matrix
294 | T_spadesuit = np.nan_to_num(T_spadesuit)
295 | dual_t_matrix = np.matmul(T_, T_spadesuit)
296 |
297 | if(True_T[i,0,1]==0): # for multi-label learning with missing labels
298 | dual_t_matrix[0,1]=0
299 | dual_t_matrix[0,0]=1
300 | if(True_T[i,1,0]==0): # for partial multi-label learning
301 | dual_t_matrix[1,0]=0
302 | dual_t_matrix[1,1]=1
303 |
304 |
305 | estimate_error = tools.error(dual_t_matrix, True_T[i])
306 | est_T[i]= dual_t_matrix
307 | error+=estimate_error
308 | print('class', i, 'Dual-T estimation max', dual_t_matrix[range(2),[1,0]], 'True_T', True_T[i,range(2),[1,0]], 'error', estimate_error,'\n')
309 |
310 | print('total error', error)
311 |
312 | return est_T
--------------------------------------------------------------------------------
/script/run_coco_ours.sh:
--------------------------------------------------------------------------------
1 | for ((i=2;i<=4;i++))
2 | do
3 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.2_coco_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 15
4 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.6_coco_ours_resnet50/ --noise_rate_p 0.6 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 15
5 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_n0.2_coco_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 15
6 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_n0.6_coco_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.6 --estimator 'ours' --sample_epoch 15
7 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.1n0.1_coco_ours_resnet50/ --noise_rate_p 0.1 --noise_rate_n 0.1 --estimator 'ours' --sample_epoch 15
8 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.2n0.2_coco_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 15
9 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.2n0.00752_coco_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.00752 --estimator 'ours' --sample_epoch 15
10 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 30 --num_classes 80 --nworkers 4 --seed $i --warmup_epoch 30 --dataset 'coco' --root ./data/MS-COCO/ --out ./results/multi-label-reweight_p0.4n0.01504_coco_ours_resnet50/ --noise_rate_p 0.4 --noise_rate_n 0.01504 --estimator 'ours' --sample_epoch 15
11 | done
--------------------------------------------------------------------------------
/script/run_voc2007_ours.sh:
--------------------------------------------------------------------------------
1 | for ((i=2;i<=6;i++))
2 | do
3 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2_voc2007_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 10
4 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.6_voc2007_ours_resnet50/ --noise_rate_p 0.6 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 10
5 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_n0.2_voc2007_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 10
6 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_n0.6_voc2007_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.6 --estimator 'ours' --sample_epoch 10
7 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.1n0.1_voc2007_ours_resnet50/ --noise_rate_p 0.1 --noise_rate_n 0.1 --estimator 'ours' --sample_epoch 10
8 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2n0.2_voc2007_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 10
9 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2n0.0172_voc2007_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.0172 --estimator 'ours' --sample_epoch 10
10 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2007' --root ./data/voc/ --out ./results/multi-label-reweight_p0.4n0.0343_voc2007_ours_resnet50/ --noise_rate_p 0.4 --noise_rate_n 0.0343 --estimator 'ours' --sample_epoch 10
11 | done
--------------------------------------------------------------------------------
/script/run_voc2012_ours.sh:
--------------------------------------------------------------------------------
1 | for ((i=2;i<=6;i++))
2 | do
3 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2_voc2012_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 10
4 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.6_voc2012_ours_resnet50/ --noise_rate_p 0.6 --noise_rate_n 0.0 --estimator 'ours' --sample_epoch 10
5 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_n0.2_voc2012_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 10
6 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_n0.6_voc2012_ours_resnet50/ --noise_rate_p 0 --noise_rate_n 0.6 --estimator 'ours' --sample_epoch 10
7 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.1n0.1_voc2012_ours_resnet50/ --noise_rate_p 0.1 --noise_rate_n 0.1 --estimator 'ours' --sample_epoch 10
8 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2n0.2_voc2012_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.2 --estimator 'ours' --sample_epoch 10
9 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.2n0.0172_voc2012_ours_resnet50/ --noise_rate_p 0.2 --noise_rate_n 0.0172 --estimator 'ours' --sample_epoch 10
10 | CUDA_VISIBLE_DEVICES=0 python3 main_reweight.py --batch_size 128 --nepochs 20 --num_classes 20 --nworkers 4 --seed $i --warmup_epoch 20 --dataset 'voc2012' --root ./data/voc/ --out ./results/multi-label-reweight_p0.4n0.0343_voc2012_ours_resnet50/ --noise_rate_p 0.4 --noise_rate_n 0.0343 --estimator 'ours' --sample_epoch 10
11 | done
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | #for evaluating model
2 | import numpy as np
3 | import torch
4 | import math
5 |
6 | def test(net, loader):
7 |
8 | net.eval()
9 |
10 | ap_meter= AveragePrecisionMeter()
11 | for i, (X, y) in enumerate(loader):
12 | # Pass to gpu or cpu
13 | X, y = X.cuda().float(), y.cuda().float()
14 |
15 | with torch.no_grad():
16 | out = net(X)
17 |
18 |
19 | ap_meter.add(out.cpu().detach(), y.cpu())
20 |
21 | map = ap_meter.value().mean().cpu().detach().numpy()
22 | OP, OR, OF1, CP, CR, CF1 = ap_meter.overall()
23 |
24 | return map*100, OP*100, OR*100, OF1*100, CP*100, CR*100, CF1*100
25 |
26 | def compute_cover(labels, outputs):
27 | n_labels = labels.shape[1]
28 | loss = coverage_error(labels, outputs)
29 |
30 | return (loss-1)/n_labels
31 |
32 | class AveragePrecisionMeter(object):
33 | """
34 | The APMeter measures the average precision per class.
35 | The APMeter is designed to operate on `NxK` Tensors `output` and
36 | `target`, and optionally a `Nx1` Tensor weight where (1) the `output`
37 | contains model output scores for `N` examples and `K` classes that ought to
38 | be higher when the model is more convinced that the example should be
39 | positively labeled, and smaller when the model believes the example should
40 | be negatively labeled (for instance, the output of a sigmoid function); (2)
41 | the `target` contains only values 0 (for negative examples) and 1
42 | (for positive examples); and (3) the `weight` ( > 0) represents weight for
43 | each sample.
44 | """
45 |
46 | def __init__(self, difficult_examples=True):
47 | super(AveragePrecisionMeter, self).__init__()
48 | self.reset()
49 | self.difficult_examples = difficult_examples
50 |
51 | def reset(self):
52 | """Resets the meter with empty member variables"""
53 | self.scores = torch.FloatTensor(torch.FloatStorage())
54 | self.targets = torch.LongTensor(torch.LongStorage())
55 |
56 | def add(self, output, target):
57 | """
58 | Args:
59 | output (Tensor): NxK tensor that for each of the N examples
60 | indicates the probability of the example belonging to each of
61 | the K classes, according to the model. The probabilities should
62 | sum to one over all classes
63 | target (Tensor): binary NxK tensort that encodes which of the K
64 | classes are associated with the N-th input
65 | (eg: a row [0, 1, 0, 1] indicates that the example is
66 | associated with classes 2 and 4)
67 | weight (optional, Tensor): Nx1 tensor representing the weight for
68 | each example (each weight > 0)
69 | """
70 | if not torch.is_tensor(output):
71 | output = torch.from_numpy(output)
72 | if not torch.is_tensor(target):
73 | target = torch.from_numpy(target)
74 |
75 | if output.dim() == 1:
76 | output = output.view(-1, 1)
77 | else:
78 | assert output.dim() == 2, \
79 | 'wrong output size (should be 1D or 2D with one column \
80 | per class)'
81 | if target.dim() == 1:
82 | target = target.view(-1, 1)
83 | else:
84 | assert target.dim() == 2, \
85 | 'wrong target size (should be 1D or 2D with one column \
86 | per class)'
87 | if self.scores.numel() > 0:
88 | assert target.size(1) == self.targets.size(1), \
89 | 'dimensions for output should match previously added examples.'
90 |
91 | # make sure storage is of sufficient size
92 | if self.scores.storage().size() < self.scores.numel() + output.numel():
93 | new_size = math.ceil(self.scores.storage().size() * 1.5)
94 | self.scores.storage().resize_(int(new_size + output.numel()))
95 | self.targets.storage().resize_(int(new_size + output.numel()))
96 |
97 | # store scores and targets
98 | offset = self.scores.size(0) if self.scores.dim() > 0 else 0
99 | self.scores.resize_(offset + output.size(0), output.size(1))
100 | self.targets.resize_(offset + target.size(0), target.size(1))
101 | self.scores.narrow(0, offset, output.size(0)).copy_(output)
102 | self.targets.narrow(0, offset, target.size(0)).copy_(target)
103 |
104 | def value(self):
105 | """Returns the model's average precision for each class
106 | Return:
107 | ap (FloatTensor): 1xK tensor, with avg precision for each class k
108 | """
109 |
110 | if self.scores.numel() == 0:
111 | return 0
112 | ap = torch.zeros(self.scores.size(1))
113 | rg = torch.arange(1, self.scores.size(0)).float()
114 | # compute average precision for each class
115 | for k in range(self.scores.size(1)):
116 | # sort scores
117 | scores = self.scores[:, k]
118 | targets = self.targets[:, k]
119 | # compute average precision
120 | ap[k] = AveragePrecisionMeter.average_precision(scores, targets, self.difficult_examples)
121 | return ap
122 |
123 | @staticmethod
124 | def average_precision(output, target, difficult_examples=True):
125 |
126 | # sort examples
127 | sorted, indices = torch.sort(output, dim=0, descending=True)
128 |
129 | # Computes prec@i
130 | pos_count = 0.
131 | total_count = 0.
132 | precision_at_i = 0.
133 | for i in indices:
134 | label = target[i]
135 | if difficult_examples and label == 0:
136 | continue
137 | if label == 1:
138 | pos_count += 1
139 | total_count += 1
140 | if label == 1:
141 | precision_at_i += pos_count / total_count
142 | precision_at_i /= pos_count
143 | return precision_at_i
144 |
145 | def overall(self):
146 | if self.scores.numel() == 0:
147 | return 0
148 | scores = self.scores.cpu().numpy()
149 | targets = self.targets.cpu().numpy()
150 | targets[targets == -1] = 0
151 | return self.evaluation(scores, targets)
152 |
153 | def overall_topk(self, k):
154 | targets = self.targets.cpu().numpy()
155 | targets[targets == -1] = 0
156 | n, c = self.scores.size()
157 | scores = np.zeros((n, c)) - 1
158 | index = self.scores.topk(k, 1, True, True)[1].cpu().numpy()
159 | tmp = self.scores.cpu().numpy()
160 | for i in range(n):
161 | for ind in index[i]:
162 | scores[i, ind] = 1 if tmp[i, ind] >= 0 else -1
163 | return self.evaluation(scores, targets)
164 |
165 |
166 | def evaluation(self, scores_, targets_):
167 | n, n_class = scores_.shape
168 | Nc, Np, Ng = np.zeros(n_class), np.zeros(n_class), np.zeros(n_class)
169 | for k in range(n_class):
170 | scores = scores_[:, k]
171 | targets = targets_[:, k]
172 | targets[targets == -1] = 0
173 | Ng[k] = np.sum(targets == 1)
174 | Np[k] = np.sum(scores >= 0)
175 | Nc[k] = np.sum(targets * (scores >= 0))
176 | Np[Np == 0] = 1
177 | OP = np.sum(Nc) / np.sum(Np)
178 | OR = np.sum(Nc) / np.sum(Ng)
179 | OF1 = (2 * OP * OR) / (OP + OR)
180 |
181 | CP = np.sum(Nc / Np) / n_class
182 | CR = np.sum(Nc / Ng) / n_class
183 | CF1 = (2 * CP * CR) / (CP + CR)
184 | return OP, OR, OF1, CP, CR, CF1
--------------------------------------------------------------------------------
/tools.py:
--------------------------------------------------------------------------------
1 | #for T estimator and DualT estimator
2 | import numpy as np
3 |
4 | def norm(T):
5 | row_sum = np.sum(T, 1)
6 | T_norm = T / row_sum
7 | return T_norm
8 |
9 | def error(T, T_true):
10 | error = np.sum(np.abs(T-T_true)) / np.sum(np.abs(T_true))
11 | return error
12 |
13 | def fit(X, num_classes, filter_outlier=False):
14 | # number of classes
15 | c = num_classes
16 | T = np.empty((c, c))
17 | eta_corr = X
18 | for i in np.arange(c):
19 | if not filter_outlier:
20 | idx_best = np.argmax(eta_corr[:, i])
21 | else:
22 | eta_thresh = np.percentile(eta_corr[:, i], 97,interpolation='higher')
23 | robust_eta = eta_corr[:, i]
24 | robust_eta[robust_eta >= eta_thresh] = 0.0
25 | idx_best = np.argmax(robust_eta)
26 | for j in np.arange(c):
27 | T[i, j] = eta_corr[idx_best, j]
28 | return T
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | #for data augmentation and download datasets
2 | import math
3 | from urllib.request import urlretrieve
4 | import torch
5 | from PIL import Image
6 | from tqdm import tqdm
7 | import numpy as np
8 | import random
9 | import torch.nn.functional as F
10 |
11 | class Warp(object):
12 | def __init__(self, size, interpolation=Image.BILINEAR):
13 | self.size = int(size)
14 | self.interpolation = interpolation
15 |
16 | def __call__(self, img):
17 | return img.resize((self.size, self.size), self.interpolation)
18 |
19 | def __str__(self):
20 | return self.__class__.__name__ + ' (size={size}, interpolation={interpolation})'.format(size=self.size,
21 | interpolation=self.interpolation)
22 |
23 | class MultiScaleCrop(object):
24 |
25 | def __init__(self, input_size, scales=None, max_distort=1, fix_crop=True, more_fix_crop=True):
26 | self.scales = scales if scales is not None else [1, 875, .75, .66]
27 | self.max_distort = max_distort
28 | self.fix_crop = fix_crop
29 | self.more_fix_crop = more_fix_crop
30 | self.input_size = input_size if not isinstance(input_size, int) else [input_size, input_size]
31 | self.interpolation = Image.BILINEAR
32 |
33 | def __call__(self, img):
34 | im_size = img.size
35 | crop_w, crop_h, offset_w, offset_h = self._sample_crop_size(im_size)
36 | crop_img_group = img.crop((offset_w, offset_h, offset_w + crop_w, offset_h + crop_h))
37 | ret_img_group = crop_img_group.resize((self.input_size[0], self.input_size[1]), self.interpolation)
38 | return ret_img_group
39 |
40 | def _sample_crop_size(self, im_size):
41 | image_w, image_h = im_size[0], im_size[1]
42 |
43 | # find a crop size
44 | base_size = min(image_w, image_h)
45 | crop_sizes = [int(base_size * x) for x in self.scales]
46 | crop_h = [self.input_size[1] if abs(x - self.input_size[1]) < 3 else x for x in crop_sizes]
47 | crop_w = [self.input_size[0] if abs(x - self.input_size[0]) < 3 else x for x in crop_sizes]
48 |
49 | pairs = []
50 | for i, h in enumerate(crop_h):
51 | for j, w in enumerate(crop_w):
52 | if abs(i - j) <= self.max_distort:
53 | pairs.append((w, h))
54 |
55 | crop_pair = random.choice(pairs)
56 | if not self.fix_crop:
57 | w_offset = random.randint(0, image_w - crop_pair[0])
58 | h_offset = random.randint(0, image_h - crop_pair[1])
59 | else:
60 | w_offset, h_offset = self._sample_fix_offset(image_w, image_h, crop_pair[0], crop_pair[1])
61 |
62 | return crop_pair[0], crop_pair[1], w_offset, h_offset
63 |
64 | def _sample_fix_offset(self, image_w, image_h, crop_w, crop_h):
65 | offsets = self.fill_fix_offset(self.more_fix_crop, image_w, image_h, crop_w, crop_h)
66 | return random.choice(offsets)
67 |
68 | @staticmethod
69 | def fill_fix_offset(more_fix_crop, image_w, image_h, crop_w, crop_h):
70 | w_step = (image_w - crop_w) // 4
71 | h_step = (image_h - crop_h) // 4
72 |
73 | ret = list()
74 | ret.append((0, 0)) # upper left
75 | ret.append((4 * w_step, 0)) # upper right
76 | ret.append((0, 4 * h_step)) # lower left
77 | ret.append((4 * w_step, 4 * h_step)) # lower right
78 | ret.append((2 * w_step, 2 * h_step)) # center
79 |
80 | if more_fix_crop:
81 | ret.append((0, 2 * h_step)) # center left
82 | ret.append((4 * w_step, 2 * h_step)) # center right
83 | ret.append((2 * w_step, 4 * h_step)) # lower center
84 | ret.append((2 * w_step, 0 * h_step)) # upper center
85 |
86 | ret.append((1 * w_step, 1 * h_step)) # upper left quarter
87 | ret.append((3 * w_step, 1 * h_step)) # upper right quarter
88 | ret.append((1 * w_step, 3 * h_step)) # lower left quarter
89 | ret.append((3 * w_step, 3 * h_step)) # lower righ quarter
90 |
91 | return ret
92 |
93 |
94 | def __str__(self):
95 | return self.__class__.__name__
96 |
97 | def download_url(url, destination=None, progress_bar=True):
98 | """Download a URL to a local file.
99 |
100 | Parameters
101 | ----------
102 | url : str
103 | The URL to download.
104 | destination : str, None
105 | The destination of the file. If None is given the file is saved to a temporary directory.
106 | progress_bar : bool
107 | Whether to show a command-line progress bar while downloading.
108 |
109 | Returns
110 | -------
111 | filename : str
112 | The location of the downloaded file.
113 |
114 | Notes
115 | -----
116 | Progress bar use/example adapted from tqdm documentation: https://github.com/tqdm/tqdm
117 | """
118 |
119 | def my_hook(t):
120 | last_b = [0]
121 |
122 | def inner(b=1, bsize=1, tsize=None):
123 | if tsize is not None:
124 | t.total = tsize
125 | if b > 0:
126 | t.update((b - last_b[0]) * bsize)
127 | last_b[0] = b
128 |
129 | return inner
130 |
131 | if progress_bar:
132 | with tqdm(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
133 | filename, _ = urlretrieve(url, filename=destination, reporthook=my_hook(t))
134 | else:
135 | filename, _ = urlretrieve(url, filename=destination)
--------------------------------------------------------------------------------
/voc.py:
--------------------------------------------------------------------------------
1 | #for voc2007/2012 dataset load
2 | import csv
3 | import os
4 | import os.path
5 | import tarfile
6 | from urllib.parse import urlparse
7 | import sys
8 | import numpy as np
9 | import torch
10 | import torch.utils.data as data
11 | from PIL import Image
12 | import pickle
13 | import util
14 | from util import *
15 |
16 | object_categories = ['aeroplane', 'bicycle', 'bird', 'boat',
17 | 'bottle', 'bus', 'car', 'cat', 'chair',
18 | 'cow', 'diningtable', 'dog', 'horse',
19 | 'motorbike', 'person', 'pottedplant',
20 | 'sheep', 'sofa', 'train', 'tvmonitor']
21 |
22 | urls = {
23 | 'devkit': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar',
24 | 'trainval_2007': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar',
25 | 'test_images_2007': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar',
26 | 'test_anno_2007': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtestnoimgs_06-Nov-2007.tar',
27 | }
28 |
29 | voc12urls = {
30 | 'devkit': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar',
31 | 'trainval_2012': 'http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar',
32 | 'test_images_2012': 'http://pjreddie.com/media/files/VOC2012test.tar',
33 | }
34 |
35 | def read_image_label(file):
36 | print('[dataset] read ' + file)
37 | data = dict()
38 | with open(file, 'r') as f:
39 | for line in f:
40 | tmp = line.split(' ')
41 | name = tmp[0]
42 | label = int(tmp[-1])
43 | data[name] = label
44 | # data.append([name, label])
45 | # print('%s %d' % (name, label))
46 | return data
47 |
48 |
49 | def read_object_labels(root, dataset, set):
50 | path_labels = os.path.join(root, 'VOCdevkit', dataset, 'ImageSets', 'Main')
51 | labeled_data = dict()
52 | num_classes = len(object_categories)
53 |
54 | for i in range(num_classes):
55 | file = os.path.join(path_labels, object_categories[i] + '_' + set + '.txt')
56 | data = read_image_label(file)
57 |
58 | if i == 0:
59 | for (name, label) in data.items():
60 | labels = np.zeros(num_classes)
61 | labels[i] = label
62 | labeled_data[name] = labels
63 | else:
64 | for (name, label) in data.items():
65 | labeled_data[name][i] = label
66 |
67 | return labeled_data
68 |
69 |
70 | def write_object_labels_csv(file, labeled_data):
71 | # write a csv file
72 | print('[dataset] write file %s' % file)
73 | with open(file, 'w') as csvfile:
74 | fieldnames = ['name']
75 | fieldnames.extend(object_categories)
76 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
77 |
78 | writer.writeheader()
79 | for (name, labels) in labeled_data.items():
80 | example = {'name': name}
81 | for i in range(20):
82 | example[fieldnames[i + 1]] = int(labels[i])
83 | writer.writerow(example)
84 |
85 | csvfile.close()
86 |
87 |
88 | def read_object_labels_csv(file, header=True):
89 | images = []
90 | labels_list = []
91 | num_categories = 0
92 | print('[dataset] read', file)
93 | with open(file, 'r') as f:
94 | reader = csv.reader(f)
95 | rownum = 0
96 | for row in reader:
97 | if header and rownum == 0:
98 | header = row
99 | else:
100 | if num_categories == 0:
101 | num_categories = len(row) - 1
102 | name = row[0]
103 | labels = (np.asarray(row[1:num_categories + 1])).astype(np.float32)
104 | labels = torch.from_numpy(labels)
105 | #item = (name, labels)
106 | #images.append(item)
107 | images.append(name)
108 | labels_list.append(labels)
109 | rownum += 1
110 | return np.stack(images), np.stack(labels_list)
111 |
112 |
113 | def find_images_classification(root, dataset, set):
114 | path_labels = os.path.join(root, 'VOCdevkit', dataset, 'ImageSets', 'Main')
115 | images = []
116 | file = os.path.join(path_labels, set + '.txt')
117 | with open(file, 'r') as f:
118 | for line in f:
119 | images.append(line)
120 | return images
121 |
122 |
123 | def download_voc2007(root):
124 | path_devkit = os.path.join(root, 'VOCdevkit')
125 | path_images = os.path.join(root, 'VOCdevkit', 'VOC2007', 'JPEGImages')
126 | tmpdir = os.path.join(root, 'tmp')
127 |
128 | # create directory
129 | if not os.path.exists(root):
130 | os.makedirs(root)
131 |
132 | if not os.path.exists(path_devkit):
133 |
134 | if not os.path.exists(tmpdir):
135 | os.makedirs(tmpdir)
136 |
137 | parts = urlparse(urls['devkit'])
138 | filename = os.path.basename(parts.path)
139 | cached_file = os.path.join(tmpdir, filename)
140 |
141 | if not os.path.exists(cached_file):
142 | print('Downloading: "{}" to {}\n'.format(urls['devkit'], cached_file))
143 | util.download_url(urls['devkit'], cached_file)
144 |
145 | # extract file
146 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=root))
147 | cwd = os.getcwd()
148 | tar = tarfile.open(cached_file, "r")
149 | os.chdir(root)
150 | tar.extractall()
151 | tar.close()
152 | os.chdir(cwd)
153 | print('[dataset] Done!')
154 |
155 | # train/val images/annotations
156 | if not os.path.exists(path_images):
157 |
158 | # download train/val images/annotations
159 | parts = urlparse(urls['trainval_2007'])
160 | filename = os.path.basename(parts.path)
161 | cached_file = os.path.join(tmpdir, filename)
162 |
163 | if not os.path.exists(cached_file):
164 | print('Downloading: "{}" to {}\n'.format(urls['trainval_2007'], cached_file))
165 | util.download_url(urls['trainval_2007'], cached_file)
166 |
167 | # extract file
168 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=root))
169 | cwd = os.getcwd()
170 | tar = tarfile.open(cached_file, "r")
171 | os.chdir(root)
172 | tar.extractall()
173 | tar.close()
174 | os.chdir(cwd)
175 | print('[dataset] Done!')
176 |
177 | # test annotations
178 | test_anno = os.path.join(path_devkit, 'VOC2007/ImageSets/Main/aeroplane_test.txt')
179 | if not os.path.exists(test_anno):
180 |
181 | # download test annotations
182 | parts = urlparse(urls['test_anno_2007'])
183 | filename = os.path.basename(parts.path)
184 | cached_file = os.path.join(tmpdir, filename)
185 |
186 | if not os.path.exists(cached_file):
187 | print('Downloading: "{}" to {}\n'.format(urls['test_anno_2007'], cached_file))
188 | util.download_url(urls['test_anno_2007'], cached_file)
189 |
190 | # extract file
191 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=root))
192 | cwd = os.getcwd()
193 | tar = tarfile.open(cached_file, "r")
194 | os.chdir(root)
195 | tar.extractall()
196 | tar.close()
197 | os.chdir(cwd)
198 | print('[dataset] Done!')
199 |
200 | # test images
201 | test_image = os.path.join(path_devkit, 'VOC2007/JPEGImages/000001.jpg')
202 | if not os.path.exists(test_image):
203 |
204 | # download test images
205 | parts = urlparse(urls['test_images_2007'])
206 | filename = os.path.basename(parts.path)
207 | cached_file = os.path.join(tmpdir, filename)
208 |
209 | if not os.path.exists(cached_file):
210 | print('Downloading: "{}" to {}\n'.format(urls['test_images_2007'], cached_file))
211 | util.download_url(urls['test_images_2007'], cached_file)
212 |
213 | # extract file
214 | print('[dataset] Extracting tar file {file} to {path}'.format(file=cached_file, path=root))
215 | cwd = os.getcwd()
216 | tar = tarfile.open(cached_file, "r")
217 | os.chdir(root)
218 | tar.extractall()
219 | tar.close()
220 | os.chdir(cwd)
221 | print('[dataset] Done!')
222 |
223 |
224 | class Voc2007Classification(data.Dataset):
225 | def __init__(self, root, set_name, transform=None, target_transform=None,noise_rate=[0,0],random_seed=1):
226 | self.root = root
227 | self.path_devkit = os.path.join(root, 'VOCdevkit')
228 | self.path_images = os.path.join(root, 'VOCdevkit', 'VOC2007', 'JPEGImages')
229 | if(set_name=='train' or set_name=='val' or set_name=='trainval'):
230 | set = 'trainval'
231 | else:
232 | set = set_name
233 | self.set = set
234 | self.transform = transform
235 | self.target_transform = target_transform
236 |
237 | # download dataset
238 | download_voc2007(self.root)
239 |
240 | # define path of csv file
241 | path_csv = os.path.join(self.root, 'files', 'VOC2007')
242 | # define filename of csv file
243 | file_csv = os.path.join(path_csv, 'classification_' + set + '.csv')
244 |
245 | # create the csv file if necessary
246 | if not os.path.exists(file_csv):
247 | if not os.path.exists(path_csv): # create dir if necessary
248 | os.makedirs(path_csv)
249 | # generate csv file
250 | labeled_data = read_object_labels(self.root, 'VOC2007', self.set)
251 | # write csv file
252 | write_object_labels_csv(file_csv, labeled_data)
253 |
254 | self.classes = object_categories
255 | self.images, self.true_labels = read_object_labels_csv(file_csv)
256 |
257 | self.true_labels[self.true_labels==0]=1
258 |
259 | if(noise_rate[0]==0 and noise_rate[1]==0):
260 | self.labels=self.true_labels
261 | else:
262 | self.labels= generate_noisy_labels(self.true_labels , noise_rate,random_seed)
263 |
264 | if(set_name=='train'):
265 | self.images, self.labels, self.true_labels , _, _, _=dataset_split(self.images,self.labels,self.true_labels, num_classes=len(self.classes))
266 | elif(set_name=='val'):
267 | _, _, _, self.images, self.labels, self.true_labels=dataset_split(self.images,self.labels,self.true_labels, num_classes=len(self.classes))
268 | print('[dataset] VOC 2007 classification set=%s number of classes=%d number of images=%d' % (
269 | set_name, len(self.classes), len(self.images)))
270 |
271 | def __getitem__(self, index):
272 | path, target = self.images[index], self.labels[index]
273 | img = Image.open(os.path.join(self.path_images, path + '.jpg')).convert('RGB')
274 | #img = np.asarray(img,dtype="float32")
275 | if self.transform is not None:
276 | img = self.transform(img)
277 | if self.target_transform is not None:
278 | target = self.target_transform(target)
279 |
280 | return img, target
281 |
282 | def __len__(self):
283 | return len(self.images)
284 |
285 | def get_number_classes(self):
286 | return len(self.classes)
287 |
288 |
289 | def generate_noisy_labels(labels, noise_rate,random_seed):
290 |
291 | N, nc = labels.shape
292 | np.random.seed(random_seed)
293 | rand_mat = np.random.rand(N,nc)
294 | mask = np.zeros((N,nc), dtype = np.float)
295 | for j in range(nc):
296 | yj = labels[:,j]
297 | mask[yj!=1,j] = rand_mat[yj!=1,j]