def divide_row_by_sum(e): e = _to_np(e) if normalize_rows: e = e / np.sum(e + 1e-100, axis=1, keepdims=True) return e else: return e
def __SIIS(self, X, W, Y, labeledIndexes, m, alpha, beta, rho, max_iter, hook=None): Y = np.copy(Y) if Y.ndim == 1: Y = gutils.init_matrix(Y, labeledIndexes) Y[np.logical_not(labeledIndexes), :] = 0 if not W.shape[0] == Y.shape[0]: raise ValueError("W,Y shape not compatible") if m is None: m = W.shape[0] c = Y.shape[1] W = scipy.sparse.csr_matrix(W) / np.mean(W.data) D = gutils.deg_matrix(W, pwr=1.0) L = gutils.lap_matrix(W, is_normalized=True) U, SIGMA = gutils.extract_lap_eigvec(L, m, remove_first_eig=True) U = scipy.sparse.csr_matrix(U) SIGMA = _to_np(SIGMA) J = gutils.labels_indicator(labeledIndexes) """ !!! """ P = SIISClassifier.edge_mat(W) """ Initialize params """ LAMB_1 = np.ones((P.shape[0], c)) LAMB_2 = np.ones((Y.shape[0], c)) mu = 1.0 mu_max = 10000000.0 eps = 1 / (10000) """ Reusable matrices """ JU = _to_np(J @ U) PU = _to_np(P @ U) PU_T = PU.transpose() JU_T = JU.transpose() A = np.zeros((m, c)) Q = None B = None improvement = 1 iter = 0 """ TODO: Tensorflow version import tensorflow as tf with tf.Session() as sess: A = tf.Variable(1e-06*tf.ones((m,c),dtype=tf.float64)) sess.run(tf.global_variables_initializer()) C = tf.reduce_sum(tf.linalg.norm(tf.matmul(PU,A),axis=1)) +\ alpha*tf.reduce_sum(tf.linalg.norm(tf.matmul(_to_np(U)[labeledIndexes,:],A)-Y[labeledIndexes,:],axis=1)) +\ beta* tf.trace(tf.matmul(tf.matmul(tf.transpose(A),SIGMA),A)) opt = tf.train.AdamOptimizer(learning_rate=0.5*1e-02) opt_min = opt.minimize(C) sess.run(tf.global_variables_initializer()) for i in range(2000): sess.run(opt_min) LOG.debug(sess.run(C),LOG.ll.CLASSIFIER) LOG.debug(sess.run(C),LOG.ll.CLASSIFIER) F = _to_np(U)@sess.run(A) LOG.debug(F.shape,LOG.ll.CLASSIFIER) """ A = np.zeros((m, c)) while iter <= max_iter and improvement > eps: """ Update Q """ N = PU @ A - (1 / mu) * LAMB_1 N_norm = np.linalg.norm(N, axis=1) to_zero = N_norm <= (1 / mu) mult = ((N_norm - (1 / mu)) / N_norm) N = N * mult[:, np.newaxis] N[to_zero, :] = 0.0 Q = N """ Update B """ M = JU @ A - Y - (1 / mu) * LAMB_2 M_norm = np.linalg.norm(M, axis=1) to_zero = M_norm <= (alpha / mu) mult = ((M_norm - (alpha / mu)) / M_norm) M = M * mult[:, np.newaxis] M[to_zero, :] = 0.0 B = M old_A = A """ Update A """ A_inv_term = 2 * beta * SIGMA + mu * PU_T @ PU + mu * JU_T @ JU A_inv_term = np.linalg.inv(A_inv_term) A = A_inv_term @ \ (PU_T@ LAMB_1 + JU_T@LAMB_2 +\ mu * PU_T@Q + mu* JU_T @ (B + Y) ) """ Update Lagrangian coeffs """ LAMB_1 = LAMB_1 + mu * (Q - PU @ A) LAMB_2 = LAMB_2 + mu * (B - JU @ A + Y) """ Update penalty coeffficients """ mu = min(rho * mu, mu_max) if not old_A is None: improvement = (np.max(np.abs(A - old_A))) / np.max( np.abs(old_A)) LOG.debug("Iter {}".format(iter), LOG.ll.CLASSIFIER) iter += 1 C = np.sum(np.linalg.norm(PU@A,axis=1)) + alpha*np.sum(np.linalg.norm(JU@A - Y,axis=1)) +\ beta*np.trace(A.T@SIGMA@A) LOG.debug("Iter {} - Cost {}".format(iter, C), LOG.ll.CLASSIFIER) F = U @ A for i in range(F.shape[0]): mx = np.argmax(F[i, :]) F[i, :] = 0.0 F[i, mx] = 1.0 return F
def LGCLVO(self, X, W, Y, labeledIndexes, mu=99.0, useEstimatedFreq=True, tuning_iter=0, hook=None, constant_prop=False, useZ=True, normalize_rows=True): Y = np.copy(Y) #We make a deep copy of labeledindexes labeledIndexes = np.array(labeledIndexes) lids = np.where(labeledIndexes)[0] if Y.ndim == 1: Y = gutils.init_matrix(Y, labeledIndexes) Y[np.logical_not(labeledIndexes), :] = 0 if not W.shape[0] == Y.shape[0]: raise ValueError("W,Y shape not compatible") W = 0.5 * (W + W.transpose()) num_labeled = Y[labeledIndexes].shape[0] num_unlabeled = Y.shape[0] - num_labeled num_classes = Y.shape[1] D = gutils.deg_matrix(W, flat=True) if not useEstimatedFreq is None: if isinstance(useEstimatedFreq, bool): estimatedFreq = np.sum(Y[labeledIndexes], axis=0) / num_labeled else: estimatedFreq = useEstimatedFreq else: estimatedFreq = np.repeat(1 / num_classes, num_classes) if scipy.sparse.issparse(W): l = np.sum(labeledIndexes) itertool_prod = [[i, j] for i in range(l) for j in range(l)] row = np.asarray([lids[i] for i in range(l)]) col = np.asarray([i for i in range(l)]) data = np.asarray([1.0] * l) temp_Y = _to_np( scipy.sparse.coo_matrix((data, (row, col)), shape=(W.shape[0], l))) PL = LGC_iter_TF(X, W, Y=temp_Y, labeledIndexes=labeledIndexes, alpha=1 / (1 + mu), num_iter=10000) PL = PL[labeledIndexes, :] PL[range(PL.shape[0]), range(PL.shape[0])] = 0 #Set diagonal to 0 PL = PL del temp_Y row = np.asarray( [lids[x[0]] for x in itertool_prod if x[0] != x[1]]) col = np.asarray( [lids[x[1]] for x in itertool_prod if x[0] != x[1]]) data = [PL[x[0], x[1]] for x in itertool_prod if x[0] != x[1]] P = scipy.sparse.coo_matrix((data, (row, col)), shape=W.shape).tocsr() P = P else: #Identity matrix I = np.identity(W.shape[0]) #Get graph laplacian L = gutils.lap_matrix(W, is_normalized=True) #Propagation matrix P = np.zeros(W.shape) P[np.ix_(labeledIndexes, labeledIndexes)] = np.linalg.inv(I + 0.5 * (L + L.transpose()) / mu)[np.ix_( labeledIndexes, labeledIndexes)] P[labeledIndexes, labeledIndexes] = 0 P[np.ix_(labeledIndexes, labeledIndexes)] = P[np.ix_( labeledIndexes, labeledIndexes)] / np.sum(P[np.ix_( labeledIndexes, labeledIndexes)], axis=0, keepdims=False) W = scipy.sparse.csr_matrix(W) Z = [] detected_noisylabels = [] suggested_labels = [] where_noisylabels = [] Q_values = [] Y_flat = np.argmax(Y, axis=1) def divide_row_by_sum(e): e = _to_np(e) if normalize_rows: e = e / np.sum(e + 1e-100, axis=1, keepdims=True) return e else: return e def find_argmin(Q, class_to_unlabel): id_min_line = np.argmin(Q[:, class_to_unlabel]) id_min_col = class_to_unlabel return id_min_line, id_min_col, Q[id_min_line, id_min_col] ####################################################################################### '''BEGIN iterations''' Q = None cleanIndexes = np.copy(labeledIndexes) for i_iter in range(tuning_iter): found_noisy = True if np.sum(labeledIndexes) > 0 and found_noisy: '''Z matrix - The binary values of current Y are replaced with their corresponding D entries. Then, we normalize each row so that row sums to its estimated influence ''' if (not self.use_baseline) or Q is None: if useZ: Z = gutils.calc_Z(Y, labeledIndexes, D, estimatedFreq, weigh_by_degree=False) F = P @ Z if scipy.sparse.issparse(F): F = np.asarray(F.toarray()) #Compute graph gradient Q = (divide_row_by_sum(F) - divide_row_by_sum(Z)) else: F = P @ Y if scipy.sparse.issparse(F): F = np.asarray(F.toarray()) Q = (divide_row_by_sum(F) - divide_row_by_sum(Y)) #import scipy.stats #During label tuning, we'll also 'unlabel' the argmax unlabeledIndexes = np.logical_not(cleanIndexes) if self.early_stop: Q[np.sum(F, axis=1) == 0.0, :] = 9999 Q[unlabeledIndexes, :] = np.inf #Find minimum unlabeled index if constant_prop: expectedNumLabels = estimatedFreq * np.sum(labeledIndexes) actualNumLabels = np.sum(Y[labeledIndexes, :], axis=0) temp = expectedNumLabels - actualNumLabels class_priority = np.argsort(temp) found_noisy = False for class_to_unlabel in class_priority: id_min_line, id_min_col, val = find_argmin( Q, class_to_unlabel) if val < 0: #This means that the class would have a different label under the modified label prop found_noisy = True break else: id_min = np.argmin(Q) id_min_line = id_min // num_classes id_min_col = id_min % num_classes #The class previously assigned to instance X_{id_min_line} found_noisy = Q[id_min_line, id_min_col] < 0 if found_noisy: id_max_col = np.argmax( Q[id_min_line, :]) #The new, suggested class detected_noisylabels.append(id_min_col) where_noisylabels.append(id_min_line) suggested_labels.append(id_max_col) Q_values.append(Q[id_min_line, id_min_col]) #Unlabel OP if labeledIndexes[id_min_line] == False: raise Exception( "Error: unlabeled instance was selected") if not Y[id_min_line, id_min_col] == 1: raise Exception("Error: picked wrong class to unlabel") labeledIndexes[id_min_line] = False cleanIndexes[id_min_line] = False if not Y[id_min_line, id_min_col] == 1: raise Exception( "Tried to remove label from unlabeled instance") Y[id_min_line, id_min_col] = 0 if self.relabel: labeledIndexes[id_min_line] = True Y[id_min_line, :] = 0 Y[id_min_line, id_max_col] = 1 if not hook is None: hook._step(step=(i_iter + 1), X=X, W=W, Y=Y, labeledIndexes=labeledIndexes) ''' MATPLOTLIB stuff ''' """ import cv2 as cv #ret2,th2 = cv.threshold(255*np.asarray(Q_values).astype(np.uint8),0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) from skimage.filters import threshold_multiotsu Q_values = np.asarray(Q_values) th = threshold_multiotsu(Q_values) th = np.where(Q_values < th[0])[0] for i in range(th.shape[0]): th2 = max(0,i - 1) if not th[i] == i: break import matplotlib matplotlib.use("TkAgg") import matplotlib.pyplot as plt fig = plt.figure(figsize=(5,2)) ax = fig.add_subplot() ax.plot(np.arange(len(Q_values)),Q_values) ax.axvline(10,color='red') #plt.axvline(th2,color='purple') #plt.axhline(-0.5,color='green') print(th2) plt.show() """ '''END iterations''' LOG.info( "NUMBER OF DETECTED NOISY INSTANCES:{}".format( len(detected_noisylabels)), LOG.ll.FILTER) return Y, labeledIndexes