<filename>unlp/supervised/nlg/gmodels/gpt_model.py
# -*- coding: utf-8 -*-

"""
@Time    : 2022/3/1 1:58 下午
@Author  : hcai
@Email   : <EMAIL>
"""
import os

import torch
from torch.nn import functional as F
import numpy as np
from transformers import GPT2Config, GPT2LMHeadModel

class EarlyStopping:
    """Early stops the training if validation loss doesn't improve after a given patience."""
    def __init__(self, patience=7, verbose=False, delta=0):
        """
        Args:
            patience (int): How long to wait after last time validation loss improved.
                            上次验证集损失值改善后等待几个epoch
                            Default: 7
            verbose (bool): If True, prints a message for each validation loss improvement.
                            如果是True，为每个验证集损失值改善打印一条信息
                            Default: False
            delta (float): Minimum change in the monitored quantity to qualify as an improvement.
                            监测数量的最小变化，以符合改进的要求
                            Default: 0
        """
        self.patience = patience
        self.verbose = verbose
        self.counter = 0
        self.best_score = None
        self.early_stop = False
        self.val_loss_min = np.Inf
        self.delta = delta

    def __call__(self, val_loss, model):

        score = -val_loss

        if self.best_score is None:
            self.best_score = score
            self.save_checkpoint(val_loss, model)
        elif score < self.best_score + self.delta:
            self.counter += 1
            print(f'EarlyStopping counter: {self.counter} out of {self.patience}')
            if self.counter >= self.patience:
                self.early_stop = True
        else:
            self.best_score = score
            self.save_checkpoint(val_loss, model)
            self.counter = 0

    def save_checkpoint(self, val_loss, model):
        '''
        Saves model when validation loss decrease.
        验证损失减少时保存模型。
        '''
        if self.verbose:
            print(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}).  Saving model ...')
        # torch.save(model.state_dict(), 'checkpoint.pt')     # 这里会存储迄今最优模型的参数
        torch.save(model, 'finish_model.pkl')                 # 这里会存储迄今最优的模型
        self.val_loss_min = val_loss


def create_model(args, is_eval=False):
    """
    :param args:
    :param vocab_size:字典大小
    :return:
    """
    if args.model_path and os.path.exists(args.model_path):
        print('loading model from {}'.format(args.model_path))
        model = GPT2LMHeadModel.from_pretrained(args.model_path)
    else:  # 若没有指定预训练模型，则初始化模型
        print('training model from scratch!')
        model_config = GPT2Config.from_json_file(args.model_config)
        model = GPT2LMHeadModel(config=model_config)
    # # 根据tokenizer的vocabulary调整GPT2模型的vocab的大小
    # model.resize_token_embeddings(vocab_size)
    model = model.to(args.device)
    print('model config:\n{}'.format(model.config.to_json_string()))
    if is_eval:
        model = model.eval()
    return model

def caculate_loss(logit, target, pad_idx, smoothing=True):
    if smoothing:
        logit = logit[..., :-1, :].contiguous().view(-1, logit.size(2))
        target = target[..., 1:].contiguous().view(-1)

        eps = 0.1
        n_class = logit.size(-1)

        one_hot = torch.zeros_like(logit).scatter(1, target.view(-1, 1), 1)
        one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1)
        log_prb = F.log_softmax(logit, dim=1)

        non_pad_mask = target.ne(pad_idx)
        loss = -(one_hot * log_prb).sum(dim=1)
        loss = loss.masked_select(non_pad_mask).mean()  # average later
    else:
        # loss = F.cross_entropy(predict_logit, target, ignore_index=pad_idx)
        logit = logit[..., :-1, :].contiguous().view(-1, logit.size(-1))
        labels = target[..., 1:].contiguous().view(-1)
        loss = F.cross_entropy(logit, labels, ignore_index=pad_idx)
    return loss


def calculate_acc(logit, labels, ignore_index=-100):
    logit = logit[..., :-1, :].contiguous().view(-1, logit.size(-1))
    labels = labels[..., 1:].contiguous().view(-1)

    _, logit = logit.max(dim=-1)  # 对于每条数据，返回最大的index
    # 进行非运算，返回一个tensor，若labels的第i个位置为pad_id，则置为0，否则为1
    non_pad_mask = labels.ne(ignore_index)
    n_correct = logit.eq(labels).masked_select(non_pad_mask).sum().item()
    n_word = non_pad_mask.sum().item()
    return n_correct, n_word
<filename>unlp/supervised/nlg/gmodels/gpt_model.py
# -*- coding: utf-8 -*-

"""
@Time    : 2022/3/1 1:58 下午
@Author  : hcai
@Email   : <EMAIL>
"""
import os

import torch
from torch.nn import functional as F
import numpy as np
from transformers import GPT2Config, GPT2LMHeadModel

class EarlyStopping:
    """Early stops the training if validation loss doesn't improve after a given patience."""
    def __init__(self, patience=7, verbose=False, delta=0):
        """
        Args:
            patience (int): How long to wait after last time validation loss improved.
                            上次验证集损失值改善后等待几个epoch
                            Default: 7
            verbose (bool): If True, prints a message for each validation loss improvement.
                            如果是True，为每个验证集损失值改善打印一条信息
                            Default: False
            delta (float): Minimum change in the monitored quantity to qualify as an improvement.
                            监测数量的最小变化，以符合改进的要求
                            Default: 0
        """
        self.patience = patience
        self.verbose = verbose
        self.counter = 0
        self.best_score = None
        self.early_stop = False
        self.val_loss_min = np.Inf
        self.delta = delta

    def __call__(self, val_loss, model):

        score = -val_loss

        if self.best_score is None:
            self.best_score = score
            self.save_checkpoint(val_loss, model)
        elif score < self.best_score + self.delta:
            self.counter += 1
            print(f'EarlyStopping counter: {self.counter} out of {self.patience}')
            if self.counter >= self.patience:
                self.early_stop = True
        else:
            self.best_score = score
            self.save_checkpoint(val_loss, model)
            self.counter = 0

    def save_checkpoint(self, val_loss, model):
        '''
        Saves model when validation loss decrease.
        验证损失减少时保存模型。
        '''
        if self.verbose:
            print(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}).  Saving model ...')
        # torch.save(model.state_dict(), 'checkpoint.pt')     # 这里会存储迄今最优模型的参数
        torch.save(model, 'finish_model.pkl')                 # 这里会存储迄今最优的模型
        self.val_loss_min = val_loss


def create_model(args, is_eval=False):
    """
    :param args:
    :param vocab_size:字典大小
    :return:
    """
    if args.model_path and os.path.exists(args.model_path):
        print('loading model from {}'.format(args.model_path))
        model = GPT2LMHeadModel.from_pretrained(args.model_path)
    else:  # 若没有指定预训练模型，则初始化模型
        print('training model from scratch!')
        model_config = GPT2Config.from_json_file(args.model_config)
        model = GPT2LMHeadModel(config=model_config)
    # # 根据tokenizer的vocabulary调整GPT2模型的vocab的大小
    # model.resize_token_embeddings(vocab_size)
    model = model.to(args.device)
    print('model config:\n{}'.format(model.config.to_json_string()))
    if is_eval:
        model = model.eval()
    return model

def caculate_loss(logit, target, pad_idx, smoothing=True):
    if smoothing:
        logit = logit[..., :-1, :].contiguous().view(-1, logit.size(2))
        target = target[..., 1:].contiguous().view(-1)

        eps = 0.1
        n_class = logit.size(-1)

        one_hot = torch.zeros_like(logit).scatter(1, target.view(-1, 1), 1)
        one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1)
        log_prb = F.log_softmax(logit, dim=1)

        non_pad_mask = target.ne(pad_idx)
        loss = -(one_hot * log_prb).sum(dim=1)
        loss = loss.masked_select(non_pad_mask).mean()  # average later
    else:
        # loss = F.cross_entropy(predict_logit, target, ignore_index=pad_idx)
        logit = logit[..., :-1, :].contiguous().view(-1, logit.size(-1))
        labels = target[..., 1:].contiguous().view(-1)
        loss = F.cross_entropy(logit, labels, ignore_index=pad_idx)
    return loss


def calculate_acc(logit, labels, ignore_index=-100):
    logit = logit[..., :-1, :].contiguous().view(-1, logit.size(-1))
    labels = labels[..., 1:].contiguous().view(-1)

    _, logit = logit.max(dim=-1)  # 对于每条数据，返回最大的index
    # 进行非运算，返回一个tensor，若labels的第i个位置为pad_id，则置为0，否则为1
    non_pad_mask = labels.ne(ignore_index)
    n_correct = logit.eq(labels).masked_select(non_pad_mask).sum().item()
    n_word = non_pad_mask.sum().item()
    return n_correct, n_word
# -*- coding: utf-8 -*- @Time    : 2022/3/1 1:58 下午 @Author  : hcai @Email   : <EMAIL> Early stops the training if validation loss doesn't improve after a given patience. Args:             patience (int): How long to wait after last time validation loss improved.                             上次验证集损失值改善后等待几个epoch                             Default: 7             verbose (bool): If True, prints a message for each validation loss improvement.                             如果是True，为每个验证集损失值改善打印一条信息                             Default: False             delta (float): Minimum change in the monitored quantity to qualify as an improvement.                             监测数量的最小变化，以符合改进的要求                             Default: 0 Saves model when validation loss decrease.         验证损失减少时保存模型。 # torch.save(model.state_dict(), 'checkpoint.pt')     # 这里会存储迄今最优模型的参数 # 这里会存储迄今最优的模型 :param args:     :param vocab_size:字典大小     :return: # 若没有指定预训练模型，则初始化模型 # # 根据tokenizer的vocabulary调整GPT2模型的vocab的大小 # model.resize_token_embeddings(vocab_size) # average later # loss = F.cross_entropy(predict_logit, target, ignore_index=pad_idx) # 对于每条数据，返回最大的index # 进行非运算，返回一个tensor，若labels的第i个位置为pad_id，则置为0，否则为1