softmax多分类学习

'''
softmax回归从零开始实现
'''
import torch
import torchvision
import numpy as np
import sys
sys.path.append('..')
import d2lzh_pytorch as d2l

'''
获取数据
'''
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
print(train_iter, test_iter)

'''
初始化模型参数
参数选择原因
每个样本输入的是长和宽(均为28),所以模型的输入向量长度就是28*28=784
一共有十个图像类别,所以单层神经网络有10个输出
因此softmax的权重跟偏差数分别为784*10 和 1*10
'''
num_inputs = 784
num_output = 10
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_output)), dtype=torch.float)
b = torch.zeros(num_output, dtype=torch.float)
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)

'''
实现softma运算
tensor 对多维度操作
我们需要实现对其中的一行或者一列进行求和,并在结果中保留行(dim=0)or列(dim=1)这两个维度(keepdim=True)
求和不影响原来的张量
X = torch.tensor([[1,2,3],[4,5,6]])
print(X.sum(dim=0, keepdim=True))
print(X)
tensor([[5, 7, 9]])
tensor([[1, 2, 3],
        [4, 5, 6]])
'''

def softmax(X):
    X_exp = X.exp()
    partition = X_exp.sum(dim=1, keepdim=True)
    '''
        这个函数先对每个元素进行指数运算(exp),
        然后对exp矩阵同行元素进行求和, 
        最后领矩阵每行各元素与该行元素之和相除
        得到矩阵每行元素和为1,即每行代表了该样本在不用类目下的预测概率
        :param X: 
        :return: 返回的矩阵是一个预测矩阵,任何一个元素代表一个样本在格式输出类别的预测概率
        '''
    return X_exp / partition

'''
定义模型
'''
def net(x):
    return softmax(torch.mm(x.view((-1, num_inputs)),w)+b)

'''
定义损失函数
softmax回归使用交叉熵损失函数,为了得到标签的预测概率使用gather函数
torch.gather(input, dim, index, out=None) 
维度dim按照索引列表index从input中选取指定元素
y_hat = torch.tensor([[0.1, 0.3, 0.6],[0.3, 0.2, 0.5]])
print(y_hat)
y = torch.LongTensor([0, 2])
print(y_hat.gather(1, y.view(-1, 1)))
tensor([[0.1000, 0.3000, 0.6000],
        [0.3000, 0.2000, 0.5000]])
tensor([[0.1000],
        [0.5000]])
gather函数会按照index的torch的形状进行选取input里面的元素
dim=1 则按照行进行选择
即本例子选择的是第一行的第一个(索引为0的元素),第二行的第三个(索引为2的元素)
y_hat = torch.tensor([[0.1, 0.3, 0.6],[0.3, 0.2, 0.5]])
y = torch.LongTensor([[0, 0, 1],[1, 0, 1]])
print(y_hat.gather(0, y))
tensor([[0.1000, 0.3000, 0.6000],
        [0.3000, 0.2000, 0.5000]])
tensor([[0.1000, 0.3000, 0.5000],
        [0.3000, 0.3000, 0.5000]])
dim为0的 按列进行选择 
所以得到的结果应该是
第一列的第一个 第二列的第一个 第三列的第二个
第一列的第二个 第二列的第一个 第三列的第二个
# 使用小技巧 y.view(-1, 1) 会将y转换成总长度除以1得到的行数,1为列的张量
# 举例说明 
# y = torch.arange(15)
# y.view(5,3)
# y.view(-1,3)
# y.view(5,-1)
# 以上三个都是一样的效果
'''

def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1,y.view(-1,1)))


'''
计算分类准确率 (分类预测正确的数量占总预测数量的比)
使用y_hat.argmax(dim=1)函数 返回的是矩阵y_hat每行最大的索引,
且返回结果的形状跟y_hat形状一样
'''
def accuracy(y_hat, y):
    '''
    :param y_hat: 预测值
    :param y: 真实值
    :return:
    '''
    return (y_hat.argmax(dim=1) == y).float().mean().item()

'''
0.5
通例子可以看出来
预测值中第一行预测值为0.6 索引为 2
实际值中正确值为0.1 索引为0
预测值中第一行预测值为0.5 索引为 2
实际值中正确值为0.5 索引为2
'''
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for x, y in data_iter:
        acc_sum += (net(x).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum/n

print(evaluate_accuracy(test_iter, net))

'''
训练模型
使用小批量随机梯度下降来优化模型的损失函数
'''

def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, optimizer=None):

    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()

            l.backward()
            if optimizer is None:
                d2l.sgd(params, lr, batch_size)
            else:
                optimizer.step()
            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print(f'epoch {epoch + 1}, loss {train_l_sum / n}, train acc {train_acc_sum / n}, test acc {test_acc}')

num_epochs, lr = 5, 0.1

train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [w, b], lr)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值