import torch
import torch.nn as nn


def dm01():
    # 真实分布
    P = torch.tensor([[0.7, 0.2, 0.1]], dtype=torch.float)
    # 模型分布
    Q = torch.tensor([[0.5, 0.3, 0.2]], dtype=torch.float)
    # 定义KL散度损失，则聚合方式应该是sum求和
    criterion = nn.KLDivLoss(reduction='sum')
    # 注意：如果希望手算和PyTorch的结果对应上
    # # 则input应该是：【log(Q)，target是P】，即：【预测值，真实值】
    kl_loss_class = criterion(torch.log(Q), P)
    print("kl_loss_class-->", kl_loss_class)


def dm02():
    # 创建KL散度对象，reduction=batchmean是PyTorch推荐使用方式，一般也使用这种方式
    kl_loss = nn.KLDivLoss(reduction='batchmean')

    # 学生模型需要是log_softmax，教室模型需要softmax
    input_log_probs = torch.log_softmax(torch.tensor([[0.7, 0.2, 0.1]]), dim=1)
    target_probs = torch.softmax(torch.tensor([[0.5, 0.3, 0.2]]), dim=1)

    loss = kl_loss(input_log_probs, target_probs)
    print("loss-->", loss)



def dm03():
    # 创建KL散度对象，reduction=batchmean是推荐使用方式
    kl_loss = nn.KLDivLoss(reduction='batchmean')

    # 学生模型需要是log_softmax，教室模型需要softmax
    # 定义温度T，经验值：2或者4，这里我们依次试验【0.5, 1, 2, 4, 10, 100】
    T = 100
    input_log_probs = torch.log_softmax(torch.tensor([[0.7, 0.2, 0.1]]) / T, dim=1)
    print("input_log_probs-->", input_log_probs)
    target_probs = torch.softmax(torch.tensor([[0.5, 0.3, 0.2]]) / T, dim=1)
    print("target_probs-->", target_probs)
    loss = kl_loss(input_log_probs, target_probs)
    print("loss-->", loss)


if __name__ == '__main__':
    # dm01()
    # dm02()
    dm03()