├── CEL.py ├── loss.py ├── main.py ├── ops.py ├── readme.md ├── requirements.txt └── utils.py /CEL.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ["CUDA_VISIBLE_DEVICES"] = "" 3 | 4 | import tensorflow as tf 5 | import numpy as np 6 | 7 | class CEL(tf.Module): 8 | """ in this model, the centroids are updated with SGD instead of hard assign to center.""" 9 | def __init__(self, 10 | initialization="default", 11 | structure="growth", 12 | base_model="NMF", 13 | hierarchy=True, 14 | initial_cluster=1, 15 | rank=64, 16 | reduced_rank=16, 17 | N_I=1000, 18 | N_U=1000, 19 | centroid=100, 20 | centroid_1=1, 21 | initial_frac_logit=0.0, 22 | **kwargs): 23 | super().__init__(**kwargs) 24 | self.base_model = base_model 25 | self.hierarchy = hierarchy 26 | # Initializing random I and U 27 | self.N_C = initial_cluster 28 | self.structure = structure 29 | self.N_I=N_I 30 | self.N_U=N_U 31 | temp_I = np.random.randn(rank, N_I).astype(np.float32) 32 | temp_I = np.divide(temp_I, temp_I.max()) 33 | if initialization == 'abs': 34 | temp_I = np.abs(temp_I) 35 | if 'growth' in structure: 36 | temp_I_assign = np.zeros((centroid, N_I)).astype(np.float32) 37 | for i in range(N_I): 38 | c = i % self.N_C # modulo hash as initialization 39 | temp_I_assign[c,i] += 1 40 | distribution = np.sum(temp_I_assign,1) 41 | tf.print("Initialization: ", distribution) 42 | distribution = np.maximum(np.ones_like(distribution), distribution) # well condition for division 43 | temp_I_C = np.matmul(np.matmul(temp_I, np.transpose(temp_I_assign)) , np.diag(1/distribution)) 44 | temp_I_C = np.abs(temp_I_C).astype(np.float32) # initialize centroids in positive region 45 | if self.hierarchy: 46 | temp_I_assign_1 = np.random.randn(centroid_1, centroid).astype(np.float32) 47 | temp_I_assign_1 = 1.0 * (temp_I_assign_1.min(axis=0, keepdims=1) == temp_I_assign_1) # random assign 48 | distribution_1 = np.sum(temp_I_assign_1, 1) 49 | for i in range(centroid_1): # check well condition 50 | if distribution_1[i] == 0: 51 | distribution_1[i] = 1 52 | print("Initialization_1: ", distribution_1) 53 | temp_I_C_1 = np.matmul(np.matmul(temp_I_C, np.transpose(temp_I_assign_1)), np.diag(1 / distribution_1)) 54 | temp_I_C_1 = np.abs(temp_I_C_1).astype(np.float32) # initialize centroids in positive region 55 | 56 | temp_U = np.random.randn(N_U, rank).astype(np.float32) 57 | temp_U = np.divide(temp_U, temp_U.max()) 58 | if initialization == 'abs': 59 | temp_U = np.abs(temp_U) 60 | self.I = tf.Variable(temp_I) 61 | self.U = tf.Variable(temp_U) 62 | self.I_C = tf.Variable(temp_I_C) 63 | self.I_assign = tf.Variable(temp_I_assign.astype(np.float32)) 64 | if self.hierarchy: 65 | self.I_C_1 = tf.Variable(temp_I_C_1) 66 | self.I_assign_1 = tf.Variable(temp_I_assign_1.astype(np.float32)) 67 | if self.base_model == "MLP" or self.base_model == "NeuNMF": 68 | temp_wu1 = 1 / np.sqrt(rank / 2) * np.random.randn(rank, int(rank / 2)).astype(np.float32) 69 | temp_wu2 = 1 / np.sqrt(1/2 * np.sqrt(reduced_rank * rank / 2)) * np.random.randn(int(rank / 2), reduced_rank).astype(np.float32) 70 | temp_bu1 = np.zeros([int(rank / 2)]).astype(np.float32) 71 | temp_bu2 = np.zeros([reduced_rank]).astype(np.float32) 72 | self.wu1 = tf.Variable(temp_wu1) 73 | self.wu2 = tf.Variable(temp_wu2) 74 | self.bu1 = tf.Variable(temp_bu1) 75 | self.bu2 = tf.Variable(temp_bu2) 76 | temp_wi1 = 1 / np.sqrt(rank / 2) * np.random.randn(rank, int(rank / 2)).astype(np.float32) 77 | temp_wi2 = 1 / np.sqrt(1 / 2 * np.sqrt(reduced_rank * rank / 2)) * np.random.randn(int(rank / 2), reduced_rank).astype(np.float32) 78 | temp_bi1 = np.zeros([int(rank / 2)]).astype(np.float32) 79 | temp_bi2 = np.zeros([reduced_rank]).astype(np.float32) 80 | self.wi1 = tf.Variable(temp_wi1) 81 | self.wi2 = tf.Variable(temp_wi2) 82 | self.bi1 = tf.Variable(temp_bi1) 83 | self.bi2 = tf.Variable(temp_bi2) 84 | if self.base_model == "NeuNMF": 85 | self.frac = tf.Variable([initial_frac_logit], dtype=tf.float32) 86 | def __call__(self, pred_with_centroid = False, temp_I = None): 87 | if temp_I is None: 88 | if pred_with_centroid: 89 | temp_I = tf.matmul(self.I_C, self.I_assign) 90 | else: 91 | temp_I = self.I 92 | if self.base_model == "NMF": 93 | return tf.matmul(self.U, temp_I) 94 | elif self.base_model == "MLP" or self.base_model == "NeuNMF": 95 | U_1 = tf.nn.relu(tf.einsum("ab,bc->ac", self.U, self.wu1) + self.bu1) 96 | U_2 = tf.nn.relu(tf.einsum("ab,bc->ac", U_1, self.wu2) + self.bu2) 97 | I_1t = tf.nn.relu(tf.einsum("ab,bc->ac", tf.transpose(temp_I), self.wi1) + self.bi1) 98 | I_2t = tf.nn.relu(tf.einsum("ab,bc->ac", I_1t, self.wi2) + self.bi2) 99 | input_3 = tf.expand_dims(tf.matmul(U_2,tf.transpose(I_2t)),2) 100 | if self.base_model == "MLP": 101 | return tf.squeeze(input_3, 2) 102 | elif self.base_model == "NeuNMF": 103 | frac = tf.math.sigmoid(self.frac) 104 | return frac * tf.squeeze(input_3, 2) + (1-frac) * tf.matmul(self.U, temp_I) 105 | else: 106 | raise ValueError("Unknown base model.") -------------------------------------------------------------------------------- /loss.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tensorflow as tf 3 | import numpy as np 4 | 5 | 6 | def lossFrobenius(A, UI, tf_mask): 7 | # cost of masked, unnormalized Frobenius norm 8 | diff = tf.boolean_mask(tf.cast(A, tf.float32) - UI, tf_mask) 9 | cost = tf.reduce_sum(tf.pow(diff, 2)) 10 | return cost 11 | def lossCentroid(model): # cost of kmeans, unnormalized, only appears in train_allI() 12 | centroid_map = tf.matmul(model.I_C, model.I_assign) 13 | cost = tf.reduce_sum(tf.square(centroid_map - model.I)) 14 | return cost 15 | def lossCentroid_1(model): # cost of kmeans, unnormalized, only appears in train() 16 | if model.hierarchy: 17 | centroid_map = tf.matmul(model.I_C_1, model.I_assign_1) 18 | cost = tf.reduce_sum(tf.square(centroid_map - model.I_C)) 19 | return cost 20 | else: 21 | return 0.0 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ["CUDA_VISIBLE_DEVICES"] = "" 3 | 4 | import tensorflow as tf 5 | import numpy as np 6 | import pandas as pd 7 | from utils import * 8 | from CEL import * 9 | from loss import * 10 | from ops import * 11 | 12 | """==================== read data ====================""" 13 | seed = 0 14 | np.random.seed(seed) 15 | train_frac = 0.8 16 | data = read_data_ml100k() 17 | N_U = 943 18 | N_I = 1682 19 | shape = (N_U, N_I) 20 | data = data.sample(frac=1) 21 | length = len(data.index) 22 | train_length = int(train_frac*length) 23 | 24 | A_orig, mask_0, mask_train, mask_val = np.zeros(shape), np.zeros(shape), np.zeros(shape), np.zeros(shape) 25 | _ = 0 26 | for line in data.itertuples(): 27 | user_index, item_index = int(line[1] - 1), int(line[2] - 1) 28 | score = int(line[3]) 29 | A_orig[user_index, item_index] = score 30 | mask_0[user_index, item_index] = 1 31 | if _>train_length: 32 | mask_val[user_index, item_index] = 1 33 | else: 34 | mask_train[user_index, item_index] = 1 35 | _ += 1 36 | A_orig_df = pd.DataFrame(A_orig) 37 | total_data = np.sum(mask_0) 38 | # Boolean mask for computing cost only on valid (not missing) entries 39 | tf_mask_train = mask_train 40 | tf_mask_validation = mask_val 41 | A = tf.constant(A_orig_df.values) 42 | 43 | """==================== hyper-parameters ====================""" 44 | base_model = "NMF" # "NMF"; "MLP"; or "NeuNMF" which combines MLP and NMF 45 | growth_mode = "data_number" 46 | balance = "nonzero" 47 | split_mode = "PCA" 48 | threshold = "zero" # zero/median/data 49 | centroid = 50 # max M_q 50 | initial_centroid = 1 # M_0 51 | centroid_1 = 1 52 | rank = 64 53 | reduced_rank = 16 54 | steps = range(6000) # Number of steps 55 | start_train_all_I = 800 56 | c1 = 1 # Norm penalty 57 | stop_norm_step = 400000 58 | c2 = 50 # lambda_reg for personalization 59 | c2_1 = 500 60 | lr = 0.0001/train_frac # Learning rate 61 | lrcr = 1.0 # Learning rate ratio for centroid vs user embbeding 62 | lrpr = 1.0 # Learning rate personalization ratio 63 | lr_params = 0.001/train_length # Learning rate for parameters in MLP 64 | reduce_lr_step = 10000 65 | reassign_interval = 40 # t_1 (how frequent reassign) 66 | growth_interval = 10 # t_2 67 | initial_frac_logit = -1.0 # Determine the initial weight of the MLP prediction 68 | 69 | """==================== model setup ====================""" 70 | model = CEL(initialization="abs", 71 | base_model=base_model, 72 | initial_cluster=initial_centroid, 73 | rank=rank, 74 | reduced_rank=reduced_rank, 75 | N_I=N_I, 76 | N_U=N_U, 77 | centroid=centroid, 78 | centroid_1=centroid_1, 79 | initial_frac_logit=initial_frac_logit) 80 | opt_U = tf.keras.optimizers.SGD(learning_rate=lr) 81 | opt_I_C = tf.keras.optimizers.SGD(learning_rate=lrcr*lr) 82 | opt_I = tf.keras.optimizers.SGD(learning_rate=lrpr*lr) 83 | opt_params = tf.keras.optimizers.Adam(learning_rate=lr_params) 84 | 85 | """==================== define the embedding optimization ====================""" 86 | def train(step, model, A, tf_mask, normalization = "I"): 87 | model.I.assign(tf.matmul(model.I_C, model.I_assign)) 88 | with tf.GradientTape() as t: 89 | current_loss = lossFrobenius(A, model(pred_with_centroid=True), tf_mask) # centroid prediction loss 90 | current_loss += 0.5*(np.sign(stop_norm_step-step)+1) * c1 * (tf.reduce_sum(tf.square(model.U)) + tf.reduce_sum(tf.square(model.I_C))) 91 | if model.hierarchy: 92 | current_loss += c2_1 * lossCentroid_1(model) 93 | dU, dI_C = t.gradient(current_loss, [model.U, model.I_C]) 94 | if "I" in normalization: # Gradient averaging 95 | distribution = tf.reduce_sum(model.I_assign, 1) 96 | distribution = tf.maximum(distribution, tf.ones_like(distribution)) # maintain well condition of division 97 | dI_C = tf.matmul(dI_C, tf.linalg.diag(1/distribution)) # normalization, MOST important step 98 | #### sub-grad 99 | reduced_ratio = int(step / reduce_lr_step)+1.0 100 | dU, dI_C = 1.0 / reduced_ratio * dU, 1.0 / reduced_ratio * dI_C 101 | 102 | opt_U.apply_gradients(zip([dU], [model.U])) 103 | opt_I_C.apply_gradients(zip([dI_C], [model.I_C])) 104 | ################################################################## 105 | if model.base_model == "MLP" or model.base_model == "NeuNMF": 106 | with tf.GradientTape() as t2: 107 | current_loss = lossFrobenius(A, model(pred_with_centroid=True), tf_mask) # centroid prediction loss 108 | dParams = t2.gradient(current_loss, [model.wu1, model.wu2, model.bu1, model.bu2, 109 | model.wi1, model.wi2, model.bi1, model.bi2]) 110 | opt_params.apply_gradients(zip(dParams, [model.wu1, model.wu2, model.bu1, model.bu2, 111 | model.wi1, model.wi2, model.bi1, model.bi2])) 112 | if model.base_model == "NeuNMF": 113 | with tf.GradientTape() as t3: 114 | current_loss = lossFrobenius(A, model(pred_with_centroid=True), tf_mask) 115 | dFrac = t3.gradient(current_loss, [model.frac]) 116 | model.frac.assign_sub(tf.cast(lr_params,tf.float32)*dFrac[0]) 117 | pass 118 | # Clipping operation. This ensures non-negative 119 | model.U.assign(tf.maximum(tf.zeros_like(model.U), model.U)) 120 | model.I_C.assign(tf.maximum(tf.zeros_like(model.I_C), model.I_C)) 121 | if model.hierarchy: 122 | filter_C = np.zeros([centroid]) 123 | for i in range(model.N_C): 124 | filter_C[i] += 1 125 | filter_C = tf.cast(tf.linalg.diag(filter_C), tf.float32) 126 | filtered_distribution_1 = tf.reduce_sum(tf.matmul(model.I_assign_1,filter_C), 1) 127 | filtered_distribution_1 = tf.maximum(filtered_distribution_1, tf.ones_like(filtered_distribution_1)) # maintain well condition of division 128 | filtered_I_assign_1_T = tf.transpose(tf.matmul(model.I_assign_1,filter_C)) 129 | new_I_C_1 = tf.matmul(tf.matmul(model.I_C, filtered_I_assign_1_T), 130 | tf.linalg.diag(1 / filtered_distribution_1)) 131 | model.I_C_1.assign(new_I_C_1) 132 | def train_allI(step, model, A, tf_mask, 133 | update_U=True, update_centroid=True, update_MLP=False): # Personalization 134 | with tf.GradientTape() as t: 135 | current_loss = lossFrobenius(A, model(), tf_mask) + c2 * lossCentroid(model) 136 | current_loss += 0.5*(np.sign(stop_norm_step-step)+1) * c1 * (tf.reduce_sum(tf.square(model.U)) + tf.reduce_sum(tf.square(model.I))) 137 | dU, dI = t.gradient(current_loss, [model.U, model.I]) 138 | reduced_ratio = int(step/reduce_lr_step)+1.0 139 | dU, dI = 1.0/reduced_ratio*dU, 1.0/reduced_ratio*dI 140 | opt_I.apply_gradients(zip([dI], [model.I])) 141 | model.I.assign(tf.maximum(tf.zeros_like(model.I), model.I)) 142 | if update_U: 143 | opt_U.apply_gradients(zip([dU], [model.U])) 144 | model.U.assign(tf.maximum(tf.zeros_like(model.U), model.U)) 145 | if update_centroid: 146 | distribution = tf.reduce_sum(model.I_assign, 1) 147 | distribution = tf.maximum(distribution, tf.ones_like(distribution)) 148 | model.I_C.assign(tf.matmul(tf.matmul(model.I, tf.transpose(model.I_assign)), tf.linalg.diag(1 / distribution))) 149 | if update_MLP: 150 | raise ValueError("TBI.") 151 | 152 | 153 | """==================== main function ====================""" 154 | for step in steps: 155 | if model.hierarchy: 156 | reassign_vanilla_1(model, centroid, centroid_1) 157 | if step % growth_interval == 0 and step < start_train_all_I and model.N_C < centroid: 158 | growth(model, A, tf_mask_train, centroid, threshold, 159 | mode=growth_mode,split_mode=split_mode) 160 | distribution = tf.reduce_sum(model.I_assign, 1) 161 | if step < start_train_all_I: 162 | train(step, model, A, tf_mask_train) 163 | else: # Personalization 164 | train_allI(step, model, A, tf_mask_train) 165 | 166 | if (step+1) % reassign_interval == 0 and step < start_train_all_I: 167 | n = int((step+1) / reassign_interval) 168 | if isprime(n): 169 | changes = reassign(model, A, tf_mask_train, centroid) 170 | elif step >= start_train_all_I: 171 | reassign_vanilla(model, centroid, N_I) 172 | if step % 10 == 0 or step <= 10: 173 | print_info(step, A, model, tf_mask_train, tf_mask_validation, train_frac, total_data) 174 | -------------------------------------------------------------------------------- /ops.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tensorflow as tf 3 | import numpy as np 4 | os.environ["CUDA_VISIBLE_DEVICES"] = "" 5 | from utils import * 6 | from loss import * 7 | 8 | """==================== cluster optimization ====================""" 9 | 10 | def reassign(model, A, tf_mask, centroid, balancing="nonzero"): 11 | for i in range(model.N_C): 12 | centroid_slice_repeat = tf.repeat(tf.expand_dims(model.I_C[:,i], axis=1), model.N_I, axis=1) # (rank, N_I) 13 | cost_slice = tf.square(tf.cast(A,tf.float32) - model(temp_I=centroid_slice_repeat)) 14 | cost_slice = tf.einsum("ui,ui->i",cost_slice,tf_mask) 15 | if i==0: 16 | previous_cost_slice = cost_slice 17 | new_assign = tf.zeros(model.N_I) 18 | else: 19 | compare = tf.sign(previous_cost_slice - cost_slice) 20 | compare = tf.maximum(compare, tf.zeros_like(compare)) # N_I of 1s and 0s 21 | new_assign = new_assign - new_assign*compare + i*compare 22 | previous_cost_slice = tf.minimum(previous_cost_slice, cost_slice) 23 | new_assign_temp = tf.transpose(tf.one_hot(tf.cast(new_assign, tf.int32), depth=centroid)) 24 | for i in range(model.N_I): 25 | c = np.argmax(model.I_assign[:,i]) 26 | if np.sum(new_assign_temp[c])==0 or np.random.randn()<-0.5: 27 | new_assign = tf.tensor_scatter_nd_update(new_assign, [[i]], [c]) 28 | new_assign_temp = tf.transpose(tf.one_hot(tf.cast(new_assign, tf.int32), depth=centroid)) 29 | new_assign = tf.transpose(tf.one_hot(tf.cast(new_assign, tf.int32), depth=centroid)) 30 | changes = tf.reduce_sum(tf.square(model.I_assign - new_assign)) 31 | if tf.reduce_sum(new_assign)!=model.N_I: 32 | raise ValueError("Reassign error!") 33 | model.I_assign.assign(new_assign) 34 | if tf.reduce_sum(model.I_assign)!=model.N_I: 35 | raise ValueError("Unrecognized error!") 36 | else: 37 | # print("Error check for reassign...passed!", tf.cast(tf.reduce_sum(model.I_assign, 1), tf.int32)) 38 | pass 39 | if balancing=="nonzero": 40 | balancing_nonzero(model) 41 | return changes 42 | def reassign_vanilla(model, centroid, balancing="nonzero"): # from K-MEANS 43 | assert model.N_C == centroid 44 | centroid_map_repeat = tf.repeat(tf.expand_dims(model.I_C,axis=2), model.N_I, axis=2) 45 | I_repeat = tf.repeat(tf.expand_dims(model.I,axis=1), centroid, axis=1) 46 | cost = tf.reduce_sum(tf.square(centroid_map_repeat - I_repeat), axis=0) # L2 loss shape = (centroid,N_I) 47 | new_assign = tf.transpose(tf.one_hot(tf.math.argmin(cost, 0), depth=centroid)) 48 | changes = tf.reduce_sum(tf.square(model.I_assign - new_assign)) 49 | model.I_assign.assign(new_assign) 50 | if balancing == "nonzero": 51 | balancing_nonzero(model) 52 | return changes 53 | def reassign_vanilla_1(model, centroid, centroid_1): # for the hierarchical layer 54 | if model.hierarchy: 55 | centroid_map_repeat = tf.repeat(tf.expand_dims(model.I_C_1,axis=2), centroid, axis=2) 56 | I_repeat = tf.repeat(tf.expand_dims(model.I_C,axis=1), centroid_1, axis=1) 57 | cost = tf.reduce_sum(tf.square(centroid_map_repeat - I_repeat), axis=0) # 58 | new_assign = tf.transpose(tf.one_hot(tf.math.argmin(cost, 0), depth=centroid_1)) 59 | changes = tf.reduce_sum(tf.square(model.I_assign_1 - new_assign)) 60 | model.I_assign_1.assign(new_assign) 61 | return changes 62 | else: 63 | print("Warning: Model does not have a second layer!") 64 | return None 65 | def balancing_nonzero(model, k=1): # added feature: can balance to partially grow tree 66 | distribution = tf.cast(tf.reduce_sum(model.I_assign[0:model.N_C,:], 1), tf.int32) 67 | if tf.reduce_min(distribution)>0: 68 | return None 69 | print("Balancing node: ",end="") 70 | while tf.reduce_min(distribution)np.sum(data_count[i:]): 140 | med = dist[i] 141 | break 142 | dist = dist - med 143 | elif threshold=="median": 144 | median = np.median(dist) 145 | dist = dist - median 146 | signvec = tf.sign(dist) 147 | ############# adding one more centroid ############## 148 | signvec_positive = tf.sign(signvec + 1) 149 | signvec_negative = -tf.sign(signvec - 1) 150 | model.I_assign[chosen_centroid].assign(tf.einsum("a,ba->b",signvec_positive,reduced_mapping)) 151 | model.I_assign[model.N_C].assign(tf.einsum("a,ba->b", signvec_negative, reduced_mapping)) 152 | model.I_C[:,model.N_C].assign(model.I_C[:,chosen_centroid]) 153 | model.N_C += 1 154 | else: 155 | raise ValueError("split_mode not recognized.") 156 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Code for the paper 'Clustered Embedding Learning for Recommender Systems'. 2 | 3 | The code is implemented for the Movielens-1m dataset (https://grouplens.org/datasets/movielens/1m/). 4 | 5 | You may start with main.py. 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: linux-64 4 | _ipyw_jlab_nb_ext_conf=0.1.0=py39h06a4308_1 5 | _libgcc_mutex=0.1=main 6 | _openmp_mutex=4.5=1_gnu 7 | absl-py=1.3.0=pypi_0 8 | aiohttp=3.8.1=py39h7f8727e_1 9 | aiosignal=1.2.0=pyhd3eb1b0_0 10 | alabaster=0.7.12=pyhd3eb1b0_0 11 | anaconda=2022.05=py39_0 12 | anaconda-client=1.9.0=py39h06a4308_0 13 | anaconda-navigator=2.1.4=py39h06a4308_0 14 | anaconda-project=0.10.2=pyhd3eb1b0_0 15 | anyio=3.5.0=py39h06a4308_0 16 | appdirs=1.4.4=pyhd3eb1b0_0 17 | argon2-cffi=21.3.0=pyhd3eb1b0_0 18 | argon2-cffi-bindings=21.2.0=py39h7f8727e_0 19 | arrow=1.2.2=pyhd3eb1b0_0 20 | astroid=2.6.6=py39h06a4308_0 21 | astropy=5.0.4=py39hce1f21e_0 22 | asttokens=2.0.5=pyhd3eb1b0_0 23 | astunparse=1.6.3=pypi_0 24 | async-timeout=4.0.1=pyhd3eb1b0_0 25 | atomicwrites=1.4.0=py_0 26 | attrs=21.4.0=pyhd3eb1b0_0 27 | automat=20.2.0=py_0 28 | autopep8=1.6.0=pyhd3eb1b0_0 29 | babel=2.9.1=pyhd3eb1b0_0 30 | backcall=0.2.0=pyhd3eb1b0_0 31 | backports=1.1=pyhd3eb1b0_0 32 | backports.functools_lru_cache=1.6.4=pyhd3eb1b0_0 33 | backports.tempfile=1.0=pyhd3eb1b0_1 34 | backports.weakref=1.0.post1=py_1 35 | bcrypt=3.2.0=py39he8ac12f_0 36 | beautifulsoup4=4.11.1=py39h06a4308_0 37 | binaryornot=0.4.4=pyhd3eb1b0_1 38 | bitarray=2.4.1=py39h7f8727e_0 39 | bkcharts=0.2=py39h06a4308_0 40 | black=19.10b0=py_0 41 | blas=1.0=mkl 42 | bleach=4.1.0=pyhd3eb1b0_0 43 | blosc=1.21.0=h8c45485_0 44 | bokeh=2.4.2=py39h06a4308_0 45 | boto3=1.21.32=pyhd3eb1b0_0 46 | botocore=1.24.32=pyhd3eb1b0_0 47 | bottleneck=1.3.4=py39hce1f21e_0 48 | brotli=1.0.9=he6710b0_2 49 | brotlipy=0.7.0=py39h27cfd23_1003 50 | brunsli=0.1=h2531618_0 51 | bzip2=1.0.8=h7b6447c_0 52 | c-ares=1.18.1=h7f8727e_0 53 | ca-certificates=2022.3.29=h06a4308_1 54 | cachetools=4.2.2=pyhd3eb1b0_0 55 | certifi=2021.10.8=py39h06a4308_2 56 | cffi=1.15.0=py39hd667e15_1 57 | cfitsio=3.470=hf0d0db6_6 58 | chardet=4.0.0=py39h06a4308_1003 59 | charls=2.2.0=h2531618_0 60 | charset-normalizer=2.0.4=pyhd3eb1b0_0 61 | click=8.0.4=py39h06a4308_0 62 | cloudpickle=2.0.0=pyhd3eb1b0_0 63 | clyent=1.2.2=py39h06a4308_1 64 | colorama=0.4.4=pyhd3eb1b0_0 65 | colorcet=2.0.6=pyhd3eb1b0_0 66 | conda=4.12.0=py39h06a4308_0 67 | conda-build=3.21.8=py39h06a4308_2 68 | conda-content-trust=0.1.1=pyhd3eb1b0_0 69 | conda-env=2.6.0=1 70 | conda-pack=0.6.0=pyhd3eb1b0_0 71 | conda-package-handling=1.8.1=py39h7f8727e_0 72 | conda-repo-cli=1.0.4=pyhd3eb1b0_0 73 | conda-token=0.3.0=pyhd3eb1b0_0 74 | conda-verify=3.4.2=py_1 75 | constantly=15.1.0=pyh2b92418_0 76 | cookiecutter=1.7.3=pyhd3eb1b0_0 77 | cryptography=3.4.8=py39hd23ed53_0 78 | cssselect=1.1.0=pyhd3eb1b0_0 79 | curl=7.82.0=h7f8727e_0 80 | cycler=0.11.0=pyhd3eb1b0_0 81 | cython=0.29.28=py39h295c915_0 82 | cytoolz=0.11.0=py39h27cfd23_0 83 | daal4py=2021.5.0=py39h78b71dc_0 84 | dal=2021.5.1=h06a4308_803 85 | dask=2022.2.1=pyhd3eb1b0_0 86 | dask-core=2022.2.1=pyhd3eb1b0_0 87 | dataclasses=0.8=pyh6d0b6a4_7 88 | datashader=0.13.0=pyhd3eb1b0_1 89 | datashape=0.5.4=py39h06a4308_1 90 | dbus=1.13.18=hb2f20db_0 91 | debugpy=1.5.1=py39h295c915_0 92 | decorator=5.1.1=pyhd3eb1b0_0 93 | defusedxml=0.7.1=pyhd3eb1b0_0 94 | diff-match-patch=20200713=pyhd3eb1b0_0 95 | distributed=2022.2.1=pyhd3eb1b0_0 96 | docutils=0.17.1=py39h06a4308_1 97 | entrypoints=0.4=py39h06a4308_0 98 | et_xmlfile=1.1.0=py39h06a4308_0 99 | executing=0.8.3=pyhd3eb1b0_0 100 | expat=2.4.4=h295c915_0 101 | filelock=3.6.0=pyhd3eb1b0_0 102 | flake8=3.9.2=pyhd3eb1b0_0 103 | flask=1.1.2=pyhd3eb1b0_0 104 | flatbuffers=22.10.26=pypi_0 105 | fontconfig=2.13.1=h6c09931_0 106 | fonttools=4.25.0=pyhd3eb1b0_0 107 | freetype=2.11.0=h70c0345_0 108 | frozenlist=1.2.0=py39h7f8727e_0 109 | fsspec=2022.2.0=pyhd3eb1b0_0 110 | future=0.18.2=py39h06a4308_1 111 | gast=0.5.3=pypi_0 112 | gensim=4.1.2=py39h295c915_0 113 | giflib=5.2.1=h7b6447c_0 114 | glib=2.69.1=h4ff587b_1 115 | glob2=0.7=pyhd3eb1b0_0 116 | gmp=6.2.1=h2531618_2 117 | gmpy2=2.1.2=py39heeb90bb_0 118 | google-api-core=1.25.1=pyhd3eb1b0_0 119 | google-auth=1.33.0=pyhd3eb1b0_0 120 | google-auth-oauthlib=0.4.6=pypi_0 121 | google-cloud-core=1.7.1=pyhd3eb1b0_0 122 | google-cloud-storage=1.31.0=py_0 123 | google-crc32c=1.1.2=py39h27cfd23_0 124 | google-pasta=0.2.0=pypi_0 125 | google-resumable-media=1.3.1=pyhd3eb1b0_1 126 | googleapis-common-protos=1.53.0=py39h06a4308_0 127 | greenlet=1.1.1=py39h295c915_0 128 | grpcio=1.42.0=py39hce63b2e_0 129 | gst-plugins-base=1.14.0=h8213a91_2 130 | gstreamer=1.14.0=h28cd5cc_2 131 | h5py=3.6.0=py39ha0f2276_0 132 | hdf5=1.10.6=hb1b8bf9_0 133 | heapdict=1.0.1=pyhd3eb1b0_0 134 | holoviews=1.14.8=pyhd3eb1b0_0 135 | hvplot=0.7.3=pyhd3eb1b0_1 136 | hyperlink=21.0.0=pyhd3eb1b0_0 137 | icu=58.2=he6710b0_3 138 | idna=3.3=pyhd3eb1b0_0 139 | imagecodecs=2021.8.26=py39h4cda21f_0 140 | imageio=2.9.0=pyhd3eb1b0_0 141 | imagesize=1.3.0=pyhd3eb1b0_0 142 | importlib-metadata=4.11.3=py39h06a4308_0 143 | importlib_metadata=4.11.3=hd3eb1b0_0 144 | incremental=21.3.0=pyhd3eb1b0_0 145 | inflection=0.5.1=py39h06a4308_0 146 | iniconfig=1.1.1=pyhd3eb1b0_0 147 | intake=0.6.5=pyhd3eb1b0_0 148 | intel-openmp=2021.4.0=h06a4308_3561 149 | intervaltree=3.1.0=pyhd3eb1b0_0 150 | ipykernel=6.9.1=py39h06a4308_0 151 | ipython=8.2.0=py39h06a4308_0 152 | ipython_genutils=0.2.0=pyhd3eb1b0_1 153 | ipywidgets=7.6.5=pyhd3eb1b0_1 154 | isort=5.9.3=pyhd3eb1b0_0 155 | itemadapter=0.3.0=pyhd3eb1b0_0 156 | itemloaders=1.0.4=pyhd3eb1b0_1 157 | itsdangerous=2.0.1=pyhd3eb1b0_0 158 | jdcal=1.4.1=pyhd3eb1b0_0 159 | jedi=0.18.1=py39h06a4308_1 160 | jeepney=0.7.1=pyhd3eb1b0_0 161 | jinja2=2.11.3=pyhd3eb1b0_0 162 | jinja2-time=0.2.0=pyhd3eb1b0_3 163 | jmespath=0.10.0=pyhd3eb1b0_0 164 | joblib=1.1.0=pyhd3eb1b0_0 165 | jpeg=9e=h7f8727e_0 166 | jq=1.6=h27cfd23_1000 167 | json5=0.9.6=pyhd3eb1b0_0 168 | jsonschema=4.4.0=py39h06a4308_0 169 | jupyter=1.0.0=py39h06a4308_7 170 | jupyter_client=6.1.12=pyhd3eb1b0_0 171 | jupyter_console=6.4.0=pyhd3eb1b0_0 172 | jupyter_core=4.9.2=py39h06a4308_0 173 | jupyter_server=1.13.5=pyhd3eb1b0_0 174 | jupyterlab=3.3.2=pyhd3eb1b0_0 175 | jupyterlab_pygments=0.1.2=py_0 176 | jupyterlab_server=2.10.3=pyhd3eb1b0_1 177 | jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 178 | jxrlib=1.1=h7b6447c_2 179 | keras=2.8.0=pypi_0 180 | keras-preprocessing=1.1.2=pypi_0 181 | keyring=23.4.0=py39h06a4308_0 182 | kiwisolver=1.3.2=py39h295c915_0 183 | krb5=1.19.2=hac12032_0 184 | lazy-object-proxy=1.6.0=py39h27cfd23_0 185 | lcms2=2.12=h3be6417_0 186 | ld_impl_linux-64=2.35.1=h7274673_9 187 | lerc=3.0=h295c915_0 188 | libaec=1.0.4=he6710b0_1 189 | libarchive=3.4.2=h62408e4_0 190 | libclang=14.0.6=pypi_0 191 | libcrc32c=1.1.1=he6710b0_2 192 | libcurl=7.82.0=h0b77cf5_0 193 | libdeflate=1.8=h7f8727e_5 194 | libedit=3.1.20210910=h7f8727e_0 195 | libev=4.33=h7f8727e_1 196 | libffi=3.3=he6710b0_2 197 | libgcc-ng=9.3.0=h5101ec6_17 198 | libgfortran-ng=7.5.0=ha8ba4b0_17 199 | libgfortran4=7.5.0=ha8ba4b0_17 200 | libgomp=9.3.0=h5101ec6_17 201 | libidn2=2.3.2=h7f8727e_0 202 | liblief=0.11.5=h295c915_1 203 | libllvm11=11.1.0=h3826bc1_1 204 | libnghttp2=1.46.0=hce63b2e_0 205 | libpng=1.6.37=hbc83047_0 206 | libprotobuf=3.19.1=h4ff587b_0 207 | libsodium=1.0.18=h7b6447c_0 208 | libspatialindex=1.9.3=h2531618_0 209 | libssh2=1.10.0=h8f2d780_0 210 | libstdcxx-ng=9.3.0=hd4cf53a_17 211 | libtiff=4.2.0=h85742a9_0 212 | libunistring=0.9.10=h27cfd23_0 213 | libuuid=1.0.3=h7f8727e_2 214 | libwebp=1.2.2=h55f646e_0 215 | libwebp-base=1.2.2=h7f8727e_0 216 | libxcb=1.14=h7b6447c_0 217 | libxml2=2.9.12=h03d6c58_0 218 | libxslt=1.1.34=hc22bd24_0 219 | libzopfli=1.0.3=he6710b0_0 220 | llvmlite=0.38.0=py39h4ff587b_0 221 | locket=0.2.1=py39h06a4308_2 222 | lxml=4.8.0=py39h1f438cf_0 223 | lz4-c=1.9.3=h295c915_1 224 | lzo=2.10=h7b6447c_2 225 | markdown=3.3.4=py39h06a4308_0 226 | markupsafe=2.0.1=py39h27cfd23_0 227 | matplotlib=3.5.1=py39h06a4308_1 228 | matplotlib-base=3.5.1=py39ha18d171_1 229 | matplotlib-inline=0.1.2=pyhd3eb1b0_2 230 | mccabe=0.6.1=py39h06a4308_1 231 | mistune=0.8.4=py39h27cfd23_1000 232 | mkl=2021.4.0=h06a4308_640 233 | mkl-service=2.4.0=py39h7f8727e_0 234 | mkl_fft=1.3.1=py39hd3c417c_0 235 | mkl_random=1.2.2=py39h51133e4_0 236 | mock=4.0.3=pyhd3eb1b0_0 237 | mpc=1.1.0=h10f8cd9_1 238 | mpfr=4.0.2=hb69a4c5_1 239 | mpi=1.0=mpich 240 | mpich=3.3.2=hc856adb_0 241 | mpmath=1.2.1=py39h06a4308_0 242 | msgpack-python=1.0.2=py39hff7bd54_1 243 | multidict=5.2.0=py39h7f8727e_2 244 | multipledispatch=0.6.0=py39h06a4308_0 245 | munkres=1.1.4=py_0 246 | mypy_extensions=0.4.3=py39h06a4308_1 247 | navigator-updater=0.2.1=py39_1 248 | nbclassic=0.3.5=pyhd3eb1b0_0 249 | nbclient=0.5.13=py39h06a4308_0 250 | nbconvert=6.4.4=py39h06a4308_0 251 | nbformat=5.3.0=py39h06a4308_0 252 | ncurses=6.3=h7f8727e_2 253 | nest-asyncio=1.5.5=py39h06a4308_0 254 | networkx=2.7.1=pyhd3eb1b0_0 255 | nltk=3.7=pyhd3eb1b0_0 256 | nose=1.3.7=pyhd3eb1b0_1008 257 | notebook=6.4.8=py39h06a4308_0 258 | numba=0.55.1=py39h51133e4_0 259 | numexpr=2.8.1=py39h6abb31d_0 260 | numpy=1.21.5=py39he7a7128_1 261 | numpy-base=1.21.5=py39hf524024_1 262 | numpydoc=1.2=pyhd3eb1b0_0 263 | oauthlib=3.2.2=pypi_0 264 | olefile=0.46=pyhd3eb1b0_0 265 | oniguruma=6.9.7.1=h27cfd23_0 266 | openjpeg=2.4.0=h3ad879b_0 267 | openpyxl=3.0.9=pyhd3eb1b0_0 268 | openssl=1.1.1n=h7f8727e_0 269 | opt-einsum=3.3.0=pypi_0 270 | packaging=21.3=pyhd3eb1b0_0 271 | pandas=1.4.2=py39h295c915_0 272 | pandocfilters=1.5.0=pyhd3eb1b0_0 273 | panel=0.13.0=py39h06a4308_0 274 | param=1.12.0=pyhd3eb1b0_0 275 | parsel=1.6.0=py39h06a4308_0 276 | parso=0.8.3=pyhd3eb1b0_0 277 | partd=1.2.0=pyhd3eb1b0_1 278 | patchelf=0.13=h295c915_0 279 | pathspec=0.7.0=py_0 280 | patsy=0.5.2=py39h06a4308_1 281 | pcre=8.45=h295c915_0 282 | pep8=1.7.1=py39h06a4308_0 283 | pexpect=4.8.0=pyhd3eb1b0_3 284 | pickleshare=0.7.5=pyhd3eb1b0_1003 285 | pillow=9.0.1=py39h22f2fdc_0 286 | pip=21.2.4=py39h06a4308_0 287 | pkginfo=1.8.2=pyhd3eb1b0_0 288 | plotly=5.6.0=pyhd3eb1b0_0 289 | pluggy=1.0.0=py39h06a4308_1 290 | poyo=0.5.0=pyhd3eb1b0_0 291 | prometheus_client=0.13.1=pyhd3eb1b0_0 292 | prompt-toolkit=3.0.20=pyhd3eb1b0_0 293 | prompt_toolkit=3.0.20=hd3eb1b0_0 294 | protego=0.1.16=py_0 295 | protobuf=3.19.1=py39h295c915_0 296 | psutil=5.8.0=py39h27cfd23_1 297 | ptyprocess=0.7.0=pyhd3eb1b0_2 298 | pure_eval=0.2.2=pyhd3eb1b0_0 299 | py=1.11.0=pyhd3eb1b0_0 300 | py-lief=0.11.5=py39h295c915_1 301 | pyasn1=0.4.8=pyhd3eb1b0_0 302 | pyasn1-modules=0.2.8=py_0 303 | pycodestyle=2.7.0=pyhd3eb1b0_0 304 | pycosat=0.6.3=py39h27cfd23_0 305 | pycparser=2.21=pyhd3eb1b0_0 306 | pyct=0.4.6=py39h06a4308_0 307 | pycurl=7.44.1=py39h8f2d780_1 308 | pydispatcher=2.0.5=py39h06a4308_2 309 | pydocstyle=6.1.1=pyhd3eb1b0_0 310 | pyerfa=2.0.0=py39h27cfd23_0 311 | pyflakes=2.3.1=pyhd3eb1b0_0 312 | pygments=2.11.2=pyhd3eb1b0_0 313 | pyhamcrest=2.0.2=pyhd3eb1b0_2 314 | pyjwt=2.4.0=py39h06a4308_0 315 | pylint=2.9.6=py39h06a4308_1 316 | pyls-spyder=0.4.0=pyhd3eb1b0_0 317 | pyodbc=4.0.32=py39h295c915_1 318 | pyopenssl=21.0.0=pyhd3eb1b0_1 319 | pyparsing=3.0.4=pyhd3eb1b0_0 320 | pyqt=5.9.2=py39h2531618_6 321 | pyrsistent=0.18.0=py39heee7806_0 322 | pysocks=1.7.1=py39h06a4308_0 323 | pytables=3.6.1=py39h77479fe_1 324 | pytest=7.1.1=py39h06a4308_0 325 | python=3.9.12=h12debd9_0 326 | python-dateutil=2.8.2=pyhd3eb1b0_0 327 | python-fastjsonschema=2.15.1=pyhd3eb1b0_0 328 | python-libarchive-c=2.9=pyhd3eb1b0_1 329 | python-lsp-black=1.0.0=pyhd3eb1b0_0 330 | python-lsp-jsonrpc=1.0.0=pyhd3eb1b0_0 331 | python-lsp-server=1.2.4=pyhd3eb1b0_0 332 | python-slugify=5.0.2=pyhd3eb1b0_0 333 | python-snappy=0.6.0=py39h2531618_3 334 | pytz=2021.3=pyhd3eb1b0_0 335 | pyviz_comms=2.0.2=pyhd3eb1b0_0 336 | pywavelets=1.3.0=py39h7f8727e_0 337 | pyxdg=0.27=pyhd3eb1b0_0 338 | pyyaml=6.0=py39h7f8727e_1 339 | pyzmq=22.3.0=py39h295c915_2 340 | qdarkstyle=3.0.2=pyhd3eb1b0_0 341 | qstylizer=0.1.10=pyhd3eb1b0_0 342 | qt=5.9.7=h5867ecd_1 343 | qtawesome=1.0.3=pyhd3eb1b0_0 344 | qtconsole=5.3.0=pyhd3eb1b0_0 345 | qtpy=2.0.1=pyhd3eb1b0_0 346 | queuelib=1.5.0=py39h06a4308_0 347 | readline=8.1.2=h7f8727e_1 348 | regex=2022.3.15=py39h7f8727e_0 349 | requests=2.27.1=pyhd3eb1b0_0 350 | requests-file=1.5.1=pyhd3eb1b0_0 351 | requests-oauthlib=1.3.1=pypi_0 352 | ripgrep=12.1.1=0 353 | rope=0.22.0=pyhd3eb1b0_0 354 | rsa=4.7.2=pyhd3eb1b0_1 355 | rtree=0.9.7=py39h06a4308_1 356 | ruamel_yaml=0.15.100=py39h27cfd23_0 357 | s3transfer=0.5.0=pyhd3eb1b0_0 358 | scikit-image=0.19.2=py39h51133e4_0 359 | scikit-learn=1.0.2=py39h51133e4_1 360 | scikit-learn-intelex=2021.5.0=py39h06a4308_0 361 | scipy=1.7.3=py39hc147768_0 362 | scrapy=2.6.1=py39h06a4308_0 363 | seaborn=0.11.2=pyhd3eb1b0_0 364 | secretstorage=3.3.1=py39h06a4308_0 365 | send2trash=1.8.0=pyhd3eb1b0_1 366 | service_identity=18.1.0=pyhd3eb1b0_1 367 | setuptools=61.2.0=py39h06a4308_0 368 | sip=4.19.13=py39h295c915_0 369 | six=1.16.0=pyhd3eb1b0_1 370 | smart_open=5.1.0=pyhd3eb1b0_0 371 | snappy=1.1.9=h295c915_0 372 | sniffio=1.2.0=py39h06a4308_1 373 | snowballstemmer=2.2.0=pyhd3eb1b0_0 374 | sortedcollections=2.1.0=pyhd3eb1b0_0 375 | sortedcontainers=2.4.0=pyhd3eb1b0_0 376 | soupsieve=2.3.1=pyhd3eb1b0_0 377 | sphinx=4.4.0=pyhd3eb1b0_0 378 | sphinxcontrib-applehelp=1.0.2=pyhd3eb1b0_0 379 | sphinxcontrib-devhelp=1.0.2=pyhd3eb1b0_0 380 | sphinxcontrib-htmlhelp=2.0.0=pyhd3eb1b0_0 381 | sphinxcontrib-jsmath=1.0.1=pyhd3eb1b0_0 382 | sphinxcontrib-qthelp=1.0.3=pyhd3eb1b0_0 383 | sphinxcontrib-serializinghtml=1.1.5=pyhd3eb1b0_0 384 | spyder=5.1.5=py39h06a4308_1 385 | spyder-kernels=2.1.3=py39h06a4308_0 386 | sqlalchemy=1.4.32=py39h7f8727e_0 387 | sqlite=3.38.2=hc218d9a_0 388 | stack_data=0.2.0=pyhd3eb1b0_0 389 | statsmodels=0.13.2=py39h7f8727e_0 390 | sympy=1.10.1=py39h06a4308_0 391 | tabulate=0.8.9=py39h06a4308_0 392 | tbb=2021.5.0=hd09550d_0 393 | tbb4py=2021.5.0=py39hd09550d_0 394 | tblib=1.7.0=pyhd3eb1b0_0 395 | tenacity=8.0.1=py39h06a4308_0 396 | tensorboard=2.8.0=pypi_0 397 | tensorboard-data-server=0.6.1=pypi_0 398 | tensorboard-plugin-wit=1.8.1=pypi_0 399 | tensorflow=2.8.0=pypi_0 400 | tensorflow-io-gcs-filesystem=0.27.0=pypi_0 401 | termcolor=2.1.0=pypi_0 402 | terminado=0.13.1=py39h06a4308_0 403 | testpath=0.5.0=pyhd3eb1b0_0 404 | text-unidecode=1.3=pyhd3eb1b0_0 405 | textdistance=4.2.1=pyhd3eb1b0_0 406 | tf-estimator-nightly=2.8.0.dev2021122109=pypi_0 407 | threadpoolctl=2.2.0=pyh0d69192_0 408 | three-merge=0.1.1=pyhd3eb1b0_0 409 | tifffile=2021.7.2=pyhd3eb1b0_2 410 | tinycss=0.4=pyhd3eb1b0_1002 411 | tk=8.6.11=h1ccaba5_0 412 | tldextract=3.2.0=pyhd3eb1b0_0 413 | toml=0.10.2=pyhd3eb1b0_0 414 | tomli=1.2.2=pyhd3eb1b0_0 415 | toolz=0.11.2=pyhd3eb1b0_0 416 | tornado=6.1=py39h27cfd23_0 417 | tqdm=4.64.0=py39h06a4308_0 418 | traitlets=5.1.1=pyhd3eb1b0_0 419 | twisted=22.2.0=py39h7f8727e_0 420 | typed-ast=1.4.3=py39h7f8727e_1 421 | typing-extensions=4.1.1=hd3eb1b0_0 422 | typing_extensions=4.1.1=pyh06a4308_0 423 | tzdata=2022a=hda174b7_0 424 | ujson=5.1.0=py39h295c915_0 425 | unidecode=1.2.0=pyhd3eb1b0_0 426 | unixodbc=2.3.9=h7b6447c_0 427 | urllib3=1.26.9=py39h06a4308_0 428 | w3lib=1.21.0=pyhd3eb1b0_0 429 | watchdog=2.1.6=py39h06a4308_0 430 | wcwidth=0.2.5=pyhd3eb1b0_0 431 | webencodings=0.5.1=py39h06a4308_1 432 | websocket-client=0.58.0=py39h06a4308_4 433 | werkzeug=2.0.3=pyhd3eb1b0_0 434 | wget=1.21.3=h0b77cf5_0 435 | wheel=0.37.1=pyhd3eb1b0_0 436 | widgetsnbextension=3.5.2=py39h06a4308_0 437 | wrapt=1.12.1=py39he8ac12f_1 438 | wurlitzer=3.0.2=py39h06a4308_0 439 | xarray=0.20.1=pyhd3eb1b0_1 440 | xlrd=2.0.1=pyhd3eb1b0_0 441 | xlsxwriter=3.0.3=pyhd3eb1b0_0 442 | xz=5.2.5=h7b6447c_0 443 | yaml=0.2.5=h7b6447c_0 444 | yapf=0.31.0=pyhd3eb1b0_0 445 | yarl=1.6.3=py39h27cfd23_0 446 | zeromq=4.3.4=h2531618_0 447 | zfp=0.5.5=h295c915_6 448 | zict=2.0.0=pyhd3eb1b0_0 449 | zipp=3.7.0=pyhd3eb1b0_0 450 | zlib=1.2.12=h7f8727e_2 451 | zope=1.0=py39h06a4308_1 452 | zope.interface=5.4.0=py39h7f8727e_0 453 | zstd=1.4.9=haebb681_0 454 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tensorflow as tf 3 | import numpy as np 4 | import pandas as pd 5 | from loss import * 6 | ####################################################################################### 7 | # Loading the matrix to be decomposed 8 | def read_data_ml100k(): 9 | data_dir = '/movielens_100k/' 10 | names = ['user_id', 'item_id', 'rating', 'timestamp'] 11 | data = pd.read_csv(os.path.join(data_dir, 'u.data'), '\t', names=names, engine='python') 12 | return data 13 | def read_data_ml1m(): 14 | data_dir = '/movielens_1m/' 15 | names = ['user_id', 'item_id', 'rating', 'timestamp'] 16 | data = pd.read_csv(os.path.join(data_dir, 'ratings.dat'), '\::', names=names, engine='python') 17 | return data 18 | 19 | 20 | 21 | def PCA_1stVec_split(M, row_indices): 22 | M_reduced_T = tf.boolean_mask(tf.transpose(M), row_indices) 23 | normalized_data = normalize(M_reduced_T) 24 | eigen_values, eigen_vectors = tf.linalg.eigh(tf.matmul(tf.transpose(normalized_data), normalized_data)) 25 | PCA_Vec = eigen_vectors[:,-1] 26 | dist = tf.einsum("ab,b->a", normalized_data, PCA_Vec) 27 | return dist 28 | def normalize(M): 29 | X = tf.identity(M) 30 | X -=tf.reduce_mean(M, axis=0) 31 | return X 32 | def isprime(num): 33 | for n in range(2,int(num**0.5)+1): 34 | if num%n==0: 35 | return False 36 | return True 37 | 38 | 39 | 40 | def print_info(step, A, model, tf_mask_train, tf_mask_validation, train_frac, total_data): 41 | train_loss = lossFrobenius(A, model(), tf_mask_train) 42 | validation_loss = lossFrobenius(A, model(), tf_mask_validation) 43 | centroid_val_loss = lossFrobenius(A, model(pred_with_centroid=True), tf_mask_validation) 44 | tf.print("Step %2d: train_loss=%2.5f, val_loss=%2.5f, val_loss_with_centroid=%2.5f" 45 | % (step, train_loss/train_frac/total_data, 46 | validation_loss/(1-train_frac)/total_data, 47 | centroid_val_loss/(1-train_frac)/total_data)) 48 | # tf.print("Centroid distribution:", tf.reduce_sum(model.I_assign, 1)) 49 | if model.base_model == "NeuNMF": 50 | tf.print("Frac:", tf.math.sigmoid(model.frac)) 51 | return None --------------------------------------------------------------------------------