diff --git a/DKT/config/config_lgcntrans.json b/DKT/config/config_lgcnlstmattn.json similarity index 92% rename from DKT/config/config_lgcntrans.json rename to DKT/config/config_lgcnlstmattn.json index bd2da21..39957ae 100644 --- a/DKT/config/config_lgcntrans.json +++ b/DKT/config/config_lgcnlstmattn.json @@ -1,6 +1,7 @@ { "name": "lgcnLSTMattn", "n_gpu": 1, + "seed": 42, "arch": { "type": "lgcnLSTMattn", @@ -19,7 +20,8 @@ "batch_size": 512, "shuffle": true, "num_workers": 2, - "validation_split": 0.2 + "validation_split": 0.2, + "asset_dir": "/opt/ml/level2_dkt-recsys-09/DKT/asset" } }, "optimizer": { @@ -43,6 +45,7 @@ } }, "model": { + "name": "geslstmattn", "max_seq_len": 200, "hidden_dim": 256, "n_layers": 2, diff --git a/DKT/data_loader/dataloader_lgcnlstmattn.py b/DKT/data_loader/dataloader_lgcnlstmattn.py index af574db..2c06c6e 100644 --- a/DKT/data_loader/dataloader_lgcnlstmattn.py +++ b/DKT/data_loader/dataloader_lgcnlstmattn.py @@ -9,12 +9,13 @@ import tqdm from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import KFold -from src.feature_engine import fe +from .feature_engine_lgcnlstmattn import fe import warnings warnings.simplefilter(action='ignore', category=FutureWarning) + class Preprocess: - def __init__(self, args): + def __init__(self, **args): self.args = args self.train_data = None self.test_data = None @@ -41,15 +42,15 @@ def split_data(self, data, ratio=0.8, shuffle=True, seed=0): return data_1, data_2 def __save_labels(self, encoder, name): - le_path = os.path.join(self.args.asset_dir, name + "_classes.npy") + le_path = os.path.join(self.args['asset_dir'], name + "_classes.npy") np.save(le_path, encoder.classes_) def __preprocessing(self, df, is_train=True): cate_cols = ["assessmentItemID", "testId", "KnowledgeTag"] - if not os.path.exists(self.args.asset_dir): - os.makedirs(self.args.asset_dir) + if not os.path.exists(self.args['asset_dir']): + os.makedirs(self.args['asset_dir']) for col in cate_cols: @@ -61,7 +62,7 @@ def __preprocessing(self, df, is_train=True): le.fit(a) self.__save_labels(le, col) else: - label_path = os.path.join(self.args.asset_dir, col + "_classes.npy") + label_path = os.path.join(self.args['asset_dir'], col + "_classes.npy") le.classes_ = np.load(label_path) df[col] = df[col].apply( @@ -96,21 +97,21 @@ def __feature_engineering(self, df, is_train): return df def load_data_from_file(self, file_name, is_train=True): - csv_file_path = os.path.join(self.args.data_dir, file_name) + csv_file_path = os.path.join(self.args['data_dir'], file_name) df = pd.read_csv(csv_file_path, parse_dates=['Timestamp']) # , nrows=100000) df = self.__feature_engineering(df, is_train) df = self.__preprocessing(df, is_train) # 추후 feature를 embedding할 시에 embedding_layer의 input 크기를 결정할때 사용 - self.args.n_questions = len( - np.load(os.path.join(self.args.asset_dir, "assessmentItemID_classes.npy")) + self.args['n_questions'] = len( + np.load(os.path.join(self.args['asset_dir'], "assessmentItemID_classes.npy")) ) - self.args.n_test = len( - np.load(os.path.join(self.args.asset_dir, "testId_classes.npy")) + self.args['n_test'] = len( + np.load(os.path.join(self.args['asset_dir'], "testId_classes.npy")) ) - self.args.n_tag = len( - np.load(os.path.join(self.args.asset_dir, "KnowledgeTag_classes.npy")) + self.args['n_tag'] = len( + np.load(os.path.join(self.args['asset_dir'], "KnowledgeTag_classes.npy")) ) df = df.sort_values(by=["userID", "Timestamp"], axis=0) @@ -146,43 +147,6 @@ def load_train_data(self, file_name): def load_test_data(self, file_name): self.test_data = self.load_data_from_file(file_name, is_train=False) - -class DKTDataset(torch.utils.data.Dataset): - def __init__(self, data, args): - self.data = data - self.args = args - - def __getitem__(self, index): - row = self.data[index] - - # 각 data의 sequence length - seq_len = len(row[0]) - - test, question, tag, correct = row[0], row[1], row[2], row[3] - - cate_cols = [test, question, tag, correct] - - # max seq len을 고려하여서 이보다 길면 자르고 아닐 경우 그대로 냅둔다 - if seq_len > self.args.max_seq_len: - for i, col in enumerate(cate_cols): - cate_cols[i] = col[-self.args.max_seq_len :] - mask = np.ones(self.args.max_seq_len, dtype=np.int16) - else: - mask = np.zeros(self.args.max_seq_len, dtype=np.int16) - mask[-seq_len:] = 1 - - # mask도 columns 목록에 포함시킴 - cate_cols.append(mask) - - # np.array -> torch.tensor 형변환 - for i, col in enumerate(cate_cols): - cate_cols[i] = torch.tensor(col) - - return cate_cols - - def __len__(self): - return len(self.data) - class GESDataset(torch.utils.data.Dataset): @@ -208,12 +172,12 @@ def __getitem__(self, index): total_cols = cate_cols + cont_columns # max seq len을 고려하여서 이보다 길면 자르고 아닐 경우 그대로 냅둔다 - if seq_len > self.args.max_seq_len: + if seq_len > self.args['max_seq_len']: for i, col in enumerate(total_cols): - total_cols[i] = col[-self.args.max_seq_len :] - mask = np.ones(self.args.max_seq_len, dtype=np.int16) + total_cols[i] = col[-self.args['max_seq_len'] :] + mask = np.ones(self.args['max_seq_len'], dtype=np.int16) else: - mask = np.zeros(self.args.max_seq_len, dtype=np.int16) + mask = np.zeros(self.args['max_seq_len'], dtype=np.int16) mask[-seq_len:] = 1 # mask도 columns 목록에 포함시킴 @@ -250,34 +214,6 @@ def collate(batch): return tuple(col_list) -def get_loaders(args, train, valid): - - pin_memory = False - train_loader, valid_loader = None, None - - if train is not None: - trainset = DKTDataset(train, args) - train_loader = torch.utils.data.DataLoader( - trainset, - num_workers=args.num_workers, - shuffle=True, - batch_size=args.batch_size, - pin_memory=pin_memory, - collate_fn=collate, - ) - if valid is not None: - valset = DKTDataset(valid, args) - valid_loader = torch.utils.data.DataLoader( - valset, - num_workers=args.num_workers, - shuffle=False, - batch_size=args.batch_size, - pin_memory=pin_memory, - collate_fn=collate, - ) - - return train_loader, valid_loader - def get_GES_loaders(args, train, valid): pin_memory = False @@ -287,9 +223,9 @@ def get_GES_loaders(args, train, valid): trainset = GESDataset(train, args) train_loader = torch.utils.data.DataLoader( trainset, - num_workers=args.num_workers, + num_workers=args['num_workers'], shuffle=True, - batch_size=args.batch_size, + batch_size=args['batch_size'], pin_memory=pin_memory, collate_fn=collate, ) @@ -297,9 +233,9 @@ def get_GES_loaders(args, train, valid): valset = GESDataset(valid, args) valid_loader = torch.utils.data.DataLoader( valset, - num_workers=args.num_workers, + num_workers=args['num_workers'], shuffle=False, - batch_size=args.batch_size, + batch_size=args['batch_size'], pin_memory=pin_memory, collate_fn=collate, ) diff --git a/DKT/data_loader/feature_engine_lgcnlstmattn.py b/DKT/data_loader/feature_engine_lgcnlstmattn.py index aa15e3e..b427da9 100644 --- a/DKT/data_loader/feature_engine_lgcnlstmattn.py +++ b/DKT/data_loader/feature_engine_lgcnlstmattn.py @@ -17,61 +17,17 @@ def fe(df): ## col_name를 기준으로 mean, std, sum을 추가하는 함수. def new_feature_answer(df, col_name:str, new_feature_name:str): - - grouped_df = df.groupby(col_name) - - mean_series = grouped_df.mean()['answerCode'] - std_series = grouped_df.std()['answerCode'] - sum_series = grouped_df.sum()['answerCode'] - - series2mean = dict() - for i, v in zip(mean_series.keys(), mean_series.values): - series2mean[i] = v - - series2std = dict() - for i, v in zip(std_series.keys(), std_series.values): - series2std[i] = v - - series2sum = dict() - for i, v in zip(sum_series.keys(), sum_series.values): - series2sum[i] = v + mean_series = df.groupby(col_name).agg({'answerCode':'mean'}).to_dict()['answerCode'] + std_series = df.groupby(col_name).agg({'answerCode':'std'}).to_dict()['answerCode'] + sum_series = df.groupby(col_name).agg({'answerCode':'sum'}).to_dict()['answerCode'] - df[f'{new_feature_name}_ans_mean'] = df[col_name].map(series2mean) - df[f'{new_feature_name}_ans_std'] = df[col_name].map(series2std) - df[f'{new_feature_name}_ans_sum'] = df[col_name].map(series2sum) + df[f'{new_feature_name}_ans_mean'] = df[col_name].map(mean_series) + df[f'{new_feature_name}_ans_std'] = df[col_name].map(std_series) + df[f'{new_feature_name}_ans_sum'] = df[col_name].map(sum_series) return df - - ## col_name를 기준으로 mean, std, sum을 추가하는 함수. - def new_feature_answer(df, col_name:str, new_feature_name:str): - - grouped_df = df.groupby(col_name) - - mean_series = grouped_df.mean()['answerCode'] - std_series = grouped_df.std()['answerCode'] - sum_series = grouped_df.sum()['answerCode'] - - - series2mean = dict() - for i, v in zip(mean_series.keys(), mean_series.values): - series2mean[i] = v - - series2std = dict() - for i, v in zip(std_series.keys(), std_series.values): - series2std[i] = v - - series2sum = dict() - for i, v in zip(sum_series.keys(), sum_series.values): - series2sum[i] = v - - df[f'{new_feature_name}_ans_mean'] = df[col_name].map(series2mean) - df[f'{new_feature_name}_ans_std'] = df[col_name].map(series2std) - df[f'{new_feature_name}_ans_sum'] = df[col_name].map(series2sum) - - return df - # 난이도 설정을 위한 ELO 사용 def get_ELO_function(df): @@ -226,21 +182,6 @@ def get_user_mean(df): df['recent3_elap_time'] = df.groupby(['userID'])['elap_time'].rolling(3).mean().fillna(0).values - - # time_df = df[["userID", "prefix", "Timestamp"]].sort_values(by=["userID", "prefix", "Timestamp"]) - # time_df["first"] = time_df[["userID_reset", "prefix_reset"]].any(axis=1).apply(lambda x: 1 - int(x)) - # time_df["reset_time"] = time_df["Timestamp"].diff().fillna(pd.Timedelta(seconds=0)) - # time_df["reset_time"] = ( - # time_df["reset_time"].apply(lambda x: x.total_seconds()) * time_df["first"] - # ) - # df["reset_time"] = time_df["reset_time"]#.apply(lambda x: math.log(x + 1)) - - # time_df["reset_time"] = time_df["Timestamp"].diff().fillna(pd.Timedelta(seconds=0)) - # time_df["reset_time"] = ( - # time_df["reset_time"].apply(lambda x: x.total_seconds()) * time_df["first"] - # ) - # df["reset_time"] = time_df["reset_time"]#.apply(lambda x: math.log(x + 1)) - return df diff --git a/DKT/data_loader/make_user_item_interaction.py b/DKT/data_loader/make_user_item_interaction.py index 7cfcbca..4b6f767 100644 --- a/DKT/data_loader/make_user_item_interaction.py +++ b/DKT/data_loader/make_user_item_interaction.py @@ -73,7 +73,7 @@ def __make_user_item_interaction(config, train_df, test_df): print('preprocessed data save') - data_dir = config['data_loader']['data_dir'] + data_dir = config['data_loader']['args']['data_dir'] np.save(os.path.join(data_dir, 'preprocessed_data'), np.array([train_dict, max(users) + 1, max(items) + 1])) tag_df_sorted = all_df.sort_values(by=['KnowledgeTag_new', 'iid_new']) grouped_tag = tag_df_sorted.groupby('KnowledgeTag_new').apply(lambda r: list(set(r['iid_new'].values))) diff --git a/DKT/model/criterion_lgcnlstmattn.py b/DKT/model/criterion_lgcnlstmattn.py new file mode 100644 index 0000000..285908a --- /dev/null +++ b/DKT/model/criterion_lgcnlstmattn.py @@ -0,0 +1,6 @@ +import torch.nn as nn + + +def get_criterion(pred, target): + loss = nn.BCEWithLogitsLoss(reduction="none") + return loss(pred, target) \ No newline at end of file diff --git a/DKT/model/metric_lgcnlstmattn.py b/DKT/model/metric_lgcnlstmattn.py new file mode 100644 index 0000000..ea28c44 --- /dev/null +++ b/DKT/model/metric_lgcnlstmattn.py @@ -0,0 +1,9 @@ +import numpy as np +from sklearn.metrics import accuracy_score, roc_auc_score + + +def get_metric(targets, preds): + auc = roc_auc_score(targets, preds) + acc = accuracy_score(targets, np.where(preds >= 0.5, 1, 0)) + + return auc, acc \ No newline at end of file diff --git a/DKT/model/model_lgcnlstmattn.py b/DKT/model/model_lgcnlstmattn.py index d7e6aca..0d8f90e 100644 --- a/DKT/model/model_lgcnlstmattn.py +++ b/DKT/model/model_lgcnlstmattn.py @@ -15,34 +15,34 @@ class GESLSTMATTN(nn.Module): - def __init__(self, args, adj_matrix): + def __init__(self, adj_matrix, **args): super(GESLSTMATTN, self).__init__() self.args = args + self.device = self.args.device # Set Parameter self.CONTISIZE = 6 - self.hidden_dim = self.args.hidden_dim - self.n_layers = self.args.n_layers - self.n_heads = self.args.n_heads - self.drop_out = self.args.drop_out + self.hidden_dim = self.args['hidden_dim'] + self.n_layers = self.args['n_layers'] + self.n_heads = self.args['n_heads'] + self.drop_out = self.args['drop_out'] # Embedding # interaction은 현재 correct로 구성되어있다. correct(1, 2) + padding(0) self.embedding_interaction = nn.Embedding(3, self.hidden_dim // 3) - self.embedding_test = nn.Embedding(self.args.n_test + 1, self.hidden_dim // 3) - self.embedding_tag = nn.Embedding(self.args.n_tag + 1, self.hidden_dim // 3) - + self.embedding_test = nn.Embedding(self.args['n_test'] + 1, self.hidden_dim // 3) + self.embedding_tag = nn.Embedding(self.args['n_tag'] + 1, self.hidden_dim // 3) # =============== GCN embedding, embedding_question=================================================== - self.indices = torch.tensor(adj_matrix[0]).type(torch.int64).to(self.args.device) - self.values = torch.tensor(adj_matrix[1]).to(self.args.device) + self.indices = torch.tensor(adj_matrix[0]).type(torch.int64).to(self.device) + self.values = torch.tensor(adj_matrix[1]).to(self.args['device']) self.shape = adj_matrix[2] self.SparseL = torch.sparse.FloatTensor(self.indices, self.values, self.shape) - self.gcn_n_item = int(self.args.gcn_n_items) - self.gcn_n_layes = int(self.args.gcn_n_layes) + self.gcn_n_item = int(self.args['gcn_n_items']) + self.gcn_n_layes = int(self.args['gcn_n_layes']) - self.gcn_embedding = nn.Embedding(self.gcn_n_item, self.hidden_dim // 3).to(self.args.device) + self.gcn_embedding = nn.Embedding(self.gcn_n_item, self.hidden_dim // 3).to(self.device) self.out = self.get_GES_embedding() self.embedding_question = nn.Parameter(self.out) diff --git a/DKT/model/optimizer_lgcnlstmattn.py b/DKT/model/optimizer_lgcnlstmattn.py new file mode 100644 index 0000000..0a49e90 --- /dev/null +++ b/DKT/model/optimizer_lgcnlstmattn.py @@ -0,0 +1,13 @@ +from torch.optim import Adam, AdamW + + +def get_optimizer(model, args): + if args.optimizer == "adam": + optimizer = Adam(model.parameters(), lr=args.lr, weight_decay=0.01) + if args.optimizer == "adamW": + optimizer = AdamW(model.parameters(), lr=args.lr, weight_decay=0.01) + + # 모든 parameter들의 grad값을 0으로 초기화 + optimizer.zero_grad() + + return optimizer \ No newline at end of file diff --git a/DKT/model/scheduler_lgcnlstmattn.py b/DKT/model/scheduler_lgcnlstmattn.py new file mode 100644 index 0000000..859d09f --- /dev/null +++ b/DKT/model/scheduler_lgcnlstmattn.py @@ -0,0 +1,16 @@ +from torch.optim.lr_scheduler import ReduceLROnPlateau +from transformers import get_linear_schedule_with_warmup + + +def get_scheduler(optimizer, args): + if args.scheduler == "plateau": + scheduler = ReduceLROnPlateau( + optimizer, patience=10, factor=0.5, mode="max", verbose=True + ) + elif args.scheduler == "linear_warmup": + scheduler = get_linear_schedule_with_warmup( + optimizer, + num_warmup_steps=args.warmup_steps, + num_training_steps=args.total_steps, + ) + return scheduler \ No newline at end of file diff --git a/DKT/test/test_lgcnlstmattn.py b/DKT/test/test_lgcnlstmattn.py index bc40703..0863df0 100644 --- a/DKT/test/test_lgcnlstmattn.py +++ b/DKT/test/test_lgcnlstmattn.py @@ -1,9 +1,12 @@ import os import torch -from args import parse_args +import sys + +sys.path.append('/opt/ml/level2_dkt-recsys-09/DKT') + from trainer import trainer_lgcnlstmattn from data_loader.dataloader_lgcnlstmattn import Preprocess -from src.utils import get_adj_matrix +from utils.util_lgcnlstmattn import get_adj_matrix import numpy as np from args import parse_args import argparse diff --git a/DKT/train/train_lgcnlstmattn.py b/DKT/train/train_lgcnlstmattn.py index 39f8d80..d02a757 100644 --- a/DKT/train/train_lgcnlstmattn.py +++ b/DKT/train/train_lgcnlstmattn.py @@ -1,52 +1,63 @@ import os import numpy as np +import pandas as pd import torch import wandb -from args import parse_args +import sys + +sys.path.append('/opt/ml/level2_dkt-recsys-09/DKT') + from trainer import trainer_lgcnlstmattn +from data_loader.make_user_item_interaction import __make_user_item_interaction from data_loader.dataloader_lgcnlstmattn import Preprocess -from src.utils import setSeeds, get_adj_matrix +from utils.util_lgcnlstmattn import setSeeds, get_adj_matrix import random from parse_config import ConfigParser import argparse import collections +import os def main(args): wandb.login() - setSeeds(args.seed) + setSeeds(args['seed']) args.device = "cuda" if torch.cuda.is_available() else "cpu" - - + if not os.path.exists('/opt/ml/input/data/preprocessed_data.npy'): + train_df = pd.read_csv("/opt/ml/input/data/train_data.csv") + test_df = pd.read_csv("/opt/ml/input/data/test_data.csv") + __make_user_item_interaction(args, train_df, test_df) + [train_dict, num_user, num_item] = np.load('/opt/ml/input/data/preprocessed_data.npy', allow_pickle=True) - rel_dict = np.load('/opt/ml/input/data/preprocessed_data_rel.npy', allow_pickle=True)[0] + rel_dict = np.load('/opt/ml/input/data/preprocessed_data_rel.npy', allow_pickle=True)[0] + print('num_user:%d, num_item:%d' % (num_user, num_item)) args.gcn_n_items = num_item train_dict_len = [len(train_dict[u]) for u in train_dict] print('max len: %d, min len:%d, avg len:%.2f' % (np.max(train_dict_len), np.min(train_dict_len), np.mean(train_dict_len))) - + model_params = args['model'] # adj_matrix_wo_normarlize = get_adj_matrix_wo_normarlize(train_dict, num_item, args.max_seq_len) - adj_matrix = get_adj_matrix(train_dict, rel_dict, num_item, args.alpha, args.beta, args.max_seq_len) - - + adj_matrix = get_adj_matrix(train_dict, rel_dict, num_item, model_params['alpha'], model_params['beta'], model_params['max_seq_len']) print('Model preparing...') - preprocess = Preprocess(args=args) - preprocess.load_train_data(args.file_name) + preprocess = Preprocess(**args['data_loader']['args']) + preprocess.load_train_data('train_data.csv') train_data = preprocess.get_train_data() train_data, valid_data = preprocess.split_data(train_data) + + trainer_params = args['trainer'] + name_dict = { - 'model': args.model, - 'n_epochs': args.n_epochs, - 'batch_size': args.batch_size, - 'lr': args.lr, - 'max_seq_len': args.max_seq_len, - 'hidden_dim': args.hidden_dim, + 'model': model_params['name'], + 'n_epochs': trainer_params['n_epochs'], + 'batch_size': trainer_params['batch_size'], + 'lr': trainer_params['lr'], + 'max_seq_len': model_params['max_seq_len'], + 'hidden_dim': model_params['hidden_dim'], } name = '' @@ -54,8 +65,7 @@ def main(args): name += f'{key}_{value}, ' wandb.init(project="LGCNtrans", config=vars(args), name=name, entity="ffm") - model = trainer_lgcnlstmattn.get_model(args, adj_matrix).to(args.device) - # trainer.run(args, train_data, valid_data, model) + model = trainer_lgcnlstmattn.get_model(adj_matrix, **model_params).to(args.device) trainer_lgcnlstmattn.run_with_vaild_loss(args, train_data, valid_data, model) diff --git a/DKT/trainer/trainer_lgcnlstmattn.py b/DKT/trainer/trainer_lgcnlstmattn.py index 4fe9f65..b38878e 100644 --- a/DKT/trainer/trainer_lgcnlstmattn.py +++ b/DKT/trainer/trainer_lgcnlstmattn.py @@ -4,85 +4,22 @@ import torch import wandb -from src.criterion import get_criterion -from data_loader.dataloader_lgcnlstmattn import get_loaders, get_GES_loaders +from model.criterion_lgcnlstmattn import get_criterion +from data_loader.dataloader_lgcnlstmattn import get_GES_loaders -from src.metric import get_metric -from src.optimizer import get_optimizer -from src.scheduler import get_scheduler +from model.metric_lgcnlstmattn import get_metric +from model.optimizer_lgcnlstmattn import get_optimizer +from model.scheduler_lgcnlstmattn import get_scheduler from datetime import datetime from model import model_lgcnlstmattn #GESLSTMATTN -def get_model(args, adj_matrix): +def get_model(adj_matrix, **args): - model = model_lgcnlstmattn.GESLSTMATTN(args, adj_matrix) + model = model_lgcnlstmattn.GESLSTMATTN(adj_matrix, **args) return model - -def run(args, train_data, valid_data, model): - train_loader, valid_loader = get_loaders(args, train_data, valid_data) - - - # only when using warmup scheduler - args.total_steps = int(math.ceil(len(train_loader.dataset) / args.batch_size)) * ( - args.n_epochs - ) - args.warmup_steps = args.total_steps // 10 - - optimizer = get_optimizer(model, args) - scheduler = get_scheduler(optimizer, args) - - best_auc = -1 - early_stopping_counter = 0 - for epoch in range(args.n_epochs): - - print(f"Start Training: Epoch {epoch + 1}") - - ### TRAIN - train_auc, train_acc, train_loss = train( - train_loader, model, optimizer, scheduler, args - ) - - ### VALID - auc, acc = validate(valid_loader, model, args) - - ### TODO: model save or early stopping - wandb.log( - { - "epoch": epoch, - "train_loss_epoch": train_loss, - "train_auc_epoch": train_auc, - "train_acc_epoch": train_acc, - "valid_auc_epoch": auc, - "valid_acc_epoch": acc, - } - ) - if auc > best_auc: - best_auc = auc - # torch.nn.DataParallel로 감싸진 경우 원래의 model을 가져옵니다. - model_to_save = model.module if hasattr(model, "module") else model - save_checkpoint( - { - "epoch": epoch + 1, - "state_dict": model_to_save.state_dict(), - }, - args.model_dir, - "model.pt", - ) - early_stopping_counter = 0 - else: - early_stopping_counter += 1 - if early_stopping_counter >= args.patience: - print( - f"EarlyStopping counter: {early_stopping_counter} out of {args.patience}" - ) - break - - # scheduler - if args.scheduler == "plateau": - scheduler.step(best_auc) def run_with_vaild_loss(args, train_data, valid_data, model): diff --git a/DKT/utils/util_lgcnlstmattn.py b/DKT/utils/util_lgcnlstmattn.py new file mode 100644 index 0000000..49e9fb7 --- /dev/null +++ b/DKT/utils/util_lgcnlstmattn.py @@ -0,0 +1,78 @@ +import os +import random + +import numpy as np +import torch +import scipy.sparse as sp + + + +def setSeeds(seed=42): + + # 랜덤 시드를 설정하여 매 코드를 실행할 때마다 동일한 결과를 얻게 합니다. + os.environ["PYTHONHASHSEED"] = str(seed) + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.backends.cudnn.deterministic = True + + + +def get_adj_matrix(train_dict, rel_dict, num_item, alpha, beta, max_len): + row_seq = [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + col_seq = [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + + row_sem = [i for i in rel_dict for j in rel_dict[i]] + [j for i in rel_dict for j in rel_dict[i]] + col_sem = [j for i in rel_dict for j in rel_dict[i]] + [i for i in rel_dict for j in rel_dict[i]] + + rel_matrix = sp.coo_matrix(([alpha]*len(row_seq)+[beta]*len(row_sem), (row_seq+row_sem, col_seq+col_sem)), (num_item, num_item)).astype(np.float32) + sp.eye(num_item) + + row_sum = np.array(rel_matrix.sum(1)) + 1e-24 + degree_mat_inv_sqrt = sp.diags(np.power(row_sum, -0.5).flatten()) + rel_matrix_normalized = degree_mat_inv_sqrt.dot(rel_matrix.dot(degree_mat_inv_sqrt)).tocoo() + + + indices = np.vstack((rel_matrix_normalized.row, rel_matrix_normalized.col)) + values = rel_matrix_normalized.data.astype(np.float32) + shape = rel_matrix_normalized.shape + + return indices, values, shape + +def get_adj_matrix_wo_rel(train_dict, num_item, alpha=1, max_len=20): + row_seq = [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + col_seq = [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + + rel_matrix = sp.coo_matrix(([alpha]*len(row_seq), (row_seq, col_seq)), (num_item, num_item)).astype(np.float32) + sp.eye(num_item) + + row_sum = np.array(rel_matrix.sum(1)) + 1e-24 + + degree_mat_inv_sqrt = sp.diags(np.power(row_sum, -0.5).flatten()) + + rel_matrix_normalized = degree_mat_inv_sqrt.dot(rel_matrix.dot(degree_mat_inv_sqrt)).tocoo() + + indices = np.vstack((rel_matrix_normalized.row, rel_matrix_normalized.col)) + + values = rel_matrix_normalized.data.astype(np.float32) + + shape = rel_matrix_normalized.shape + + return indices, values, shape + + +def get_adj_matrix_wo_normarlize(train_dict, num_item, alpha=1, max_len=20): + + row_seq = [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + col_seq = [train_dict[u][-max_len:][n+1] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + [train_dict[u][-max_len:][n] for u in train_dict for n in range(len(train_dict[u][-max_len:])-1)] + + rel_matrix = sp.coo_matrix(([alpha]*len(row_seq), (row_seq, col_seq)), (num_item, num_item)).astype(np.float32) + sp.eye(num_item) + + rel_matrix = rel_matrix.tocoo() + + indices = np.vstack((rel_matrix.row, rel_matrix.col)) + + values = rel_matrix.data.astype(np.float32) + + shape = rel_matrix.shape + + return indices, values, shape \ No newline at end of file