Ejemplo n.º 1
0
def evaluate_rating_error(model,logger,data_loader,device,epoch,user_weight_dict = None,batch_size=512):
    model.eval()
    test_total_loss = 0.0
    test_total_loss_2 = 0.0
    test_weight_sum = 0.0
    test_batch_num = int(np.ceil(float(data_loader.data_size()) / batch_size))
    all_pred = []
    for batch_idx in range(test_batch_num):
        batch_weight = []
        batch_data = data_loader.get_batch_data(batch_idx, batch_size)

        for u_idx in batch_data["user"]:
            if user_weight_dict is None:
                batch_weight.append(1.0)
            else:
                batch_weight.append(1 / user_weight_dict[u_idx])
        batch_weight = np.array(batch_weight)

        b_user = torch.tensor(np.array(batch_data["user"]))
        b_item = torch.tensor(np.array(batch_data["item"]))
        b_rate = np.array(batch_data["rate"]).astype(np.float32)
        b_user, b_item = b_user.to(device),b_item.to(device)
        b_scores = model.forward(b_user,b_item)
        b_scores = variable2numpy(b_scores,device)
        loss = (((b_rate - b_scores[:,0]) ** 2) * batch_weight).sum()
        loss_2 = (np.abs(b_rate - b_scores[:, 0]) * batch_weight).sum()
        all_pred.extend(b_scores[:,0].tolist())
        test_total_loss += loss
        test_total_loss_2 += loss_2
        test_weight_sum += batch_weight.sum()
    mse_loss = test_total_loss / test_weight_sum
    mae_loss = test_total_loss_2 / test_weight_sum
    # logger.info("test:%d:%f:%f:%f" % (epoch, np.array(all_pred).mean(),test_total_loss / test_weight_sum,test_total_loss_2 / test_weight_sum))

    return {"mse":mse_loss,"mae":mae_loss}
Ejemplo n.º 2
0
def evaluate_diversity(model,logger,device,movie_info,num_pick=10):
    model.eval()
    num_items = model.num_items
    item_cnt = np.zeros(num_items).astype(np.float)
    all_dis = []
    for u_idx in range(model.num_users):
        b_user = u_idx * np.ones(num_items).astype(np.int)
        b_item = np.array(range(num_items)).astype(np.int)
        b_user, b_item = torch.tensor(b_user), torch.tensor(b_item)

        b_scores = model.forward(b_user, b_item)
        b_scores = variable2numpy(b_scores,device)
        sort_idx = np.argsort(b_scores[:,0])[::-1]
        r_item_genres = []
        for s_item in sort_idx[:num_pick]:
            item_cnt[s_item] += 1
            r_item_genres.append(movie_info[s_item]['genre_array'][None,:])
        r_item_genres = np.concatenate(r_item_genres,0)
        all_dis.append((1 - cosine_similarity(r_item_genres)).sum() / (num_pick * (num_pick - 1)))
    item_cnt = item_cnt
    sort_item_idx = np.argsort(item_cnt)[::-1]
    gini_den,gini_sum = 0.0,0.0
    for (s_idx, item) in enumerate(sort_item_idx.tolist()):
        gini_den += 1.0 * (2 * s_idx - num_items + 1) * item_cnt[item]
        gini_sum += 1.0 * num_items * item_cnt[item]

    # logger.info(gini_den / gini_sum)
    # logger.info(np.array(all_lrs).mean())
    return gini_den / gini_sum,np.array(all_dis).mean()
Ejemplo n.º 3
0
def evaluate_obs_expo(model,logger,user_rating_list,test_user_obs_list,device):
    Ks = [1,5,10,20,50]
    model.eval()
    pos_prob = []
    item_ranks = []
    recalls = [[] for _ in range(len(Ks))]
    ndcgs = [[] for _ in range(len(Ks))]
    num_user = len(user_rating_list)
    num_item = model.num_items

    exc_idx = generate_exc_list(user_rating_list, num_item)
    for u_idx in range(num_user):
        if len(test_user_obs_list[u_idx]) > 0:
            input_user_ids = np.array([u_idx]).astype(np.int64)
            input_user_ids = torch.tensor(input_user_ids).to(device)
            pred_scores = model.evaluate(input_user_ids)
            all_scores = variable2numpy(pred_scores,device)
            all_scores = all_scores[0,:][:,None]
            rating_pos_list = [t_data[0] for t_data in user_rating_list[u_idx]]
            rating_pos_idx = np.array(rating_pos_list)

            t_pos_prob,t_item_ranks,t_recalls,t_ndcgs = evaluate_score(all_scores, exc_idx, rating_pos_idx, test_user_obs_list[u_idx], num_item, Ks)
            pos_prob.extend(t_pos_prob)
            item_ranks.extend(t_item_ranks)

            for k_idx in range(len(t_recalls)):
                recalls[k_idx].append(t_recalls[k_idx])
                ndcgs[k_idx].append(t_ndcgs[k_idx])

            if u_idx > 0 and u_idx % 3000 == 0:
                logger.info(str(u_idx) + "/" + str(num_user))

    nll = np.log(np.array(pos_prob)).mean()
    mar = np.array(item_ranks).mean()
    mre = [np.array(k_recalls).mean() for k_recalls in recalls]
    mndcg = [np.array(k_ndcgs).mean() for k_ndcgs in ndcgs]
    logger.info("Negative Log Likelihood:" + str(nll))
    logger.info("Mean Average Rank:" + str(mar))
    logger.info("Mean Recall:" + str(mre))
    logger.info("Mean NDCG:" + str(mndcg))
    result = {'nll':nll,'mar':mar,'mre':mre,'mndcg':mndcg}
    return result
def train(args):
    device = args.device

    # Set up logger
    pre_fix = "%s_seqexpo" % (args.dataset)
    result_dir = "results/" + pre_fix
    if not osp.exists(result_dir):
        os.makedirs(result_dir)
    logger = Logger(result_dir)
    logger.info(str(args))

    # Read data
    data_dir = osp.join("data", args.dataset)
    num_user = 3000
    num_item = 1000

    num_train = args.num_train
    num_valid = 5
    num_test = 5

    user_rating_list_train = [[] for _ in range(num_user)]
    user_rating_list_trainval = [[] for _ in range(num_user)]
    train_item_idx = []
    p_train_count = np.zeros(num_item).astype(np.float32)

    valid_user_obs_list = [[] for _ in range(num_user)]
    test_user_obs_list = [[] for _ in range(num_user)]

    with open(osp.join(data_dir, "observation_data.txt"), 'r') as fin:
        for (line_idx, line) in enumerate(fin):
            line_info = line.replace("\n", "").split(",")

            user, item, rate, s_idx = int(line_info[0]), int(line_info[1]), float(line_info[2]), int(line_info[3])
            if s_idx < num_train:
                user_rating_list_train[user].append((item, rate, s_idx))
                user_rating_list_trainval[user].append((item, rate, s_idx))
                if item not in train_item_idx:
                    train_item_idx.append(item)
                p_train_count[item] += 1
            elif s_idx < num_train + num_valid:
                user_rating_list_trainval[user].append((item, rate, s_idx))
                valid_user_obs_list[user].append(item)
            elif s_idx < num_train + num_valid + num_test:
                test_user_obs_list[user].append(item)

    train_loader = SeqUserLoader(user_rating_list_train,num_user,num_item)
    train_loader.shuffle()

    # Calculate the popularity to initize the bias term for fast converge
    p_train_count = (p_train_count + 1e-10) / p_train_count[train_item_idx].sum()
    p_train_logp = np.log(p_train_count)[None,:]

    # Build the model
    model_config = {'device':device,'num_users':num_user,'num_items':num_item,\
                    'latent_dim':args.latent_dim, 'gru_input_dim':args.gru_input_dim,\
                    'gru_hidden_dim':args.gru_hidden_dim}
    model = EXPOSEQ(model_config,init_bias = torch.tensor(p_train_logp.astype(np.float32)).to(device))
    if len(args.init_model) > 0:
        model.load_state_dict(torch.load(args.init_model,map_location=device))

    if device == "cuda":
        model.cuda(args.gpu_id)
    optimizer = torch.optim.Adam(model.parameters(),lr=args.lr,weight_decay=args.weight_decay)

    b_valid_result = None
    b_test_result = None
    stop_cnt = 0
    # model.eval()
    # logger.info("Valid evaluation")
    # valid_result = evaluate_exposure(model, logger, args.gpu_id, user_rating_list_train, valid_user_obs_list, device)

    total_anneal_steps = 10000
    anneal = 0.0
    update_count = 0.0
    anneal_cap = 0.05
    for epoch in range(1, args.epochs):
        logger.info("anneal:%f" % (anneal))
        model.train()
        total_cls_loss = 0.0
        total_kld_loss = 0.0
        total_loss = 0.0
        batch_num = train_loader.data_size()
        for batch_idx in range(batch_num):
            user_item_list,user_rate_list = train_loader.get_batch_data(batch_idx)
            optimizer.zero_grad()
            user_item_list = torch.tensor(user_item_list)
            user_rate_list = torch.tensor(user_rate_list)
            user_item_list, user_rate_list = user_item_list.to(device),user_rate_list.to(device)
            item_logsoftmax,user_emb_p_mu_u, user_emb_p_sigma_u, user_emb_q_mu_u, user_emb_q_sigma_u, user_emb_mu_v, user_emb_sigma_v = model.forward(user_item_list,user_rate_list,1)

            cls_loss = F.nll_loss(item_logsoftmax,user_item_list) * args.batch_size
            kld_loss_1 = torch.sum(torch.sum(0.5 * (-torch.log(user_emb_q_sigma_u) + torch.log(user_emb_p_sigma_u) + (
                        (user_emb_q_mu_u - user_emb_p_mu_u) ** 2 + user_emb_q_sigma_u) / user_emb_p_sigma_u - 1), -1))
            kld_loss_2 = torch.sum(torch.sum(0.5 * (-torch.log(user_emb_sigma_v) + (user_emb_sigma_v + user_emb_mu_v ** 2) - 1), -1))

            # Anneal logic
            if total_anneal_steps > 0:
                anneal = min(anneal_cap, 1. * update_count / total_anneal_steps)
            else:
                anneal = anneal_cap
            update_count += 1.0

            loss = cls_loss + anneal * (kld_loss_1 + kld_loss_2)
            loss.backward()
            optimizer.step()
            total_cls_loss += variable2numpy(cls_loss, device)
            total_kld_loss += variable2numpy(kld_loss_1 + kld_loss_2, device)
            total_loss += variable2numpy(loss,device)

            # if batch_idx > 0 and batch_idx % args.out_interval == 0:
            #     logger.info("train:%d:%d:%f,%f,%f" % (epoch, batch_idx, total_cls_loss / ((batch_idx + 1) * args.batch_size), total_kld_loss / ((batch_idx + 1) * args.batch_size), total_loss / ((batch_idx + 1) * args.batch_size)))
        logger.info("train:%d:%f,%f,%f" % (epoch,total_cls_loss / batch_num,total_kld_loss / batch_num,total_loss / batch_num))
        train_loader.shuffle()

        if epoch > 0 and epoch % args.save_interval == 0:
            model.eval()
            logger.info("Valid evaluation")
            valid_result = evaluate_exposure(model, logger, args.gpu_id, user_rating_list_train, valid_user_obs_list, device)
            logger.info("Test evaluation")
            test_result = evaluate_exposure(model, logger, args.gpu_id, user_rating_list_trainval, test_user_obs_list, device)
            # torch.save(model.state_dict(), osp.join(result_dir, str(epoch) + ".pkl"))
            if b_valid_result is None or valid_result['nll'] > b_valid_result['nll']:
                b_valid_result = valid_result
                b_test_result = test_result
                torch.save(model.state_dict(), osp.join(result_dir,"final_model.pkl"))
                stop_cnt = 0
            else:
                stop_cnt += 1
            logger.info(b_valid_result)
            logger.info(b_test_result)

            if stop_cnt >= 3:
                logger.info("Stop Training.")
                break
Ejemplo n.º 5
0
if len(init_model) > 0:
    model.load_state_dict(torch.load(init_model, map_location=device))
if device == "cuda":
    model.cuda(args.gpu_id)

# Calcualate the propensity scores and save
batch_num = train_loader.data_size()
model.eval()
out_file = open(
    "propensity_scores/scores_seq_%s_%d" % (args.dataset, num_train), "w")
for batch_idx in range(batch_num):
    user_item_list_raw, user_rate_list = train_loader.get_batch_data(batch_idx)
    user_item_list = torch.tensor(user_item_list_raw)
    user_rate_list = torch.tensor(user_rate_list)
    user_item_list, user_rate_list = user_item_list.to(
        device), user_rate_list.to(device)

    b_scores, b_scores_bias = model.forward_evaluate(user_item_list,
                                                     user_rate_list)
    b_scores_bias = variable2numpy(b_scores_bias, device)
    b_scores = variable2numpy(b_scores, device)

    for (item_idx, item) in enumerate(user_item_list_raw.tolist()):
        item_scores_idx = b_scores[item_idx, :]
        item_prob = np.exp(item_scores_idx)
        item_prob = item_prob / item_prob.sum()
        out_line = str(batch_idx) + "," + str(item) + "," + str(
            item_prob[item]) + "\n"
        out_file.write(out_line)
    if batch_idx % 100 == 0:
        print("%d/%d" % (batch_idx, batch_num))
Ejemplo n.º 6
0
def train(args):
    device = args.device

    num_train = 20
    num_valid = 5
    num_user = 3000
    num_item = 1000

    # Read the calculated propensity scores if necessary
    if args.weight_type == 1:
        weight_dict = {}
        with open(
                "propensity_scores/scores_hpf_%s_%d" %
            (args.dataset, num_train), 'r') as fin:
            for (line_idx, line) in enumerate(fin):
                line_info = line.replace("\n", "").split(",")
                weight_dict[(int(line_info[0]),
                             int(line_info[1]))] = float(line_info[2])
    elif args.weight_type == 2:
        weight_dict = {}
        with open(
                "propensity_scores/scores_seq_%s_%d" %
            (args.dataset, num_train), 'r') as fin:
            for (line_idx, line) in enumerate(fin):
                line_info = line.replace("\n", "").split(",")
                weight_dict[(int(line_info[0]),
                             int(line_info[1]))] = float(line_info[2])

    # Set up logger
    pre_fix = "rate_prediction_%s_%d_%d_%d" % (args.dataset, args.if_weight,
                                               args.weight_type, num_train)
    result_dir = "results/" + pre_fix
    if not osp.exists(result_dir):
        os.makedirs(result_dir)
    logger = Logger(result_dir)
    logger.info(str(args))

    # Read data
    data_dir = osp.join("data", args.dataset)
    train_user_col_list = [[] for _ in range(num_user)]
    valid_rating_list = []
    with open(osp.join(data_dir, "observation_data.txt"), 'r') as fin:
        for (line_idx, line) in enumerate(fin):
            line_info = line.replace("\n", "").split(",")

            user, item, rate, t_idx, ideal_weight = int(line_info[0]), int(
                line_info[1]), float(line_info[2]), int(line_info[3]), float(
                    line_info[4])
            if len(train_user_col_list[user]) < num_train:
                train_user_col_list[user].append([item, rate])
            elif len(train_user_col_list[user]) < num_train + num_valid:
                valid_rating_list.append((user, item, rate))
    test_rating_list = []
    with open(osp.join(data_dir, "random_test_data.txt"), 'r') as fin:
        for (line_idx, line) in enumerate(fin):
            line_info = line.replace("\n", "").split(",")

            user, item, rate = int(line_info[0]), int(line_info[1]), float(
                line_info[2])
            test_rating_list.append((user, item, rate))

    # Calculate popularity
    p_count = np.zeros(num_item).astype(np.float32)
    for u_idx in range(len(train_user_col_list)):
        for t_data in train_user_col_list[u_idx]:
            p_count[t_data[0]] += 1
    p_count = p_count / p_count.sum()

    # Pair each training example with its propensity scores
    train_rating_list = []
    # t_rates = []
    # t_weights = []
    for user in range(len(train_user_col_list)):
        user_train_list = train_user_col_list[user]
        for (t_idx, t_data) in enumerate(user_train_list):
            item = t_data[0]
            rate = t_data[1]
            if args.weight_type == 0:
                weight = p_count[item] * num_item
            else:
                weight = weight_dict[(user, item)] * num_item

            train_rating_list.append((user, item, rate, 1 / weight))

    random.shuffle(train_rating_list)
    train_loader = UserRatingLoader(train_rating_list)
    valid_loader = UserRatingLoader(valid_rating_list)
    test_loader = UserRatingLoader(test_rating_list)

    model_config = {
        'num_users': num_user,
        'num_items': num_item,
        'latent_dim': args.num_dim
    }
    all_valid_results = []
    all_test_results = []

    # Repeat training for 10 times
    for t_idx in range(10):
        logger.info("Trial %d" % t_idx)

        # Create model
        model = GMF(model_config)
        if device == "cuda":
            model.cuda(args.gpu_id)
        optimizer = torch.optim.Adam(model.parameters(),
                                     lr=args.lr,
                                     weight_decay=args.weight_decay)
        model.eval()
        b_valid_loss = 9999.0
        b_valid_result, b_test_result = None, None
        stop_cnt = 0
        for epoch in range(1, args.epochs):
            model.train()
            total_loss = 0.0
            batch_num = int(
                np.ceil(float(train_loader.data_size()) / args.batch_size))
            for batch_idx in range(batch_num):
                batch_data = train_loader.get_batch_data(
                    batch_idx, args.batch_size, args.if_weight)
                optimizer.zero_grad()
                b_user = torch.tensor(np.array(batch_data["user"]))
                b_item = torch.tensor(np.array(batch_data["item"]))
                b_rate = torch.tensor(
                    np.array(batch_data["rate"]).astype(np.float32))
                b_weight = torch.tensor(
                    np.array(batch_data["weight"]).astype(np.float32))
                b_user, b_item, b_rate, b_weight = b_user.to(
                    device), b_item.to(device), b_rate.to(device), b_weight.to(
                        device)
                b_scores = model.forward(b_user, b_item)
                loss = ((((b_rate - b_scores[:, 0])**2) *
                         b_weight).sum()) / (b_weight.mean())
                loss.backward()
                optimizer.step()
                total_loss += variable2numpy(loss, device)

            logger.info("train:%d:%f" %
                        (epoch, total_loss / len(train_rating_list)))
            train_loader.shuffle()
            if epoch > 0 and epoch % args.evaluate_interval == 0:
                model.eval()
                valid_result = evaluate_rating_error(model, logger,
                                                     valid_loader, device,
                                                     epoch)
                test_result = evaluate_rating_error(model, logger, test_loader,
                                                    device, epoch)

                # Decide when to stop
                if valid_result["mse"] < b_valid_loss:
                    b_valid_loss = valid_result["mse"]
                    b_valid_result = valid_result
                    b_test_result = test_result
                    stop_cnt = 0
                    logger.info("The best valid loss is : " +
                                str(b_valid_loss))
                else:
                    stop_cnt += 1
                if stop_cnt >= 3:
                    break

        logger.info(b_valid_result)
        logger.info(b_test_result)
        all_valid_results.append(b_valid_result)
        all_test_results.append(b_test_result)

    # Report the average results
    for key in all_valid_results[0].keys():
        all_valid_values = np.array(
            [t_result[key] for t_result in all_valid_results])
        all_test_values = np.array(
            [t_result[key] for t_result in all_test_results])
        logger.info("%s in valid,%f,%f" %
                    (key, all_valid_values.mean(), all_valid_values.std()))
        logger.info("%s in test,%f,%f" %
                    (key, all_test_values.mean(), all_test_values.std()))
        logger.info("Per result valid:")
        for t_result in all_valid_results:
            logger.info(t_result[key])
        logger.info("Per result test:")
        for t_result in all_test_results:
            logger.info(t_result[key])