深度学习_11_softmax_图片识别代码&原理解析

完整代码:

import torch
from d2l import torch as d2l

"创建训练集&创建检测集合"
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

"每个图片长度,以及图片种类"
num_inputs = 784
num_outputs = 10

"模型全局"
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)

"softmax全局"
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True) # 计算行的和
    return X_exp / partition  # 这里应用了广播机制

"输出,即传入图片输出"
def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

"交叉熵损失"
def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])

"显示预测与估计相对应下标数量"
def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # 确定长宽高都大于1
        y_hat = y_hat.argmax(axis=1) # 取出每行中最大值
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum()) # 返回对应下标数量


"利用优化后的模型计算精度"
def evaluate_accuracy(net, data_iter):  #@save

    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel()) # 下标相同数量 / 总下标
    return metric[0] / metric[1]


"加法器全局"
class Accumulator:  #@save

    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

"训练更新模型&返回训练损失与精度函数"
def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        # print(y)
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

lr = 0.1

"更新模型"
def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

if __name__ == '__main__':
    num_epochs = 10
    cnt = 1
    print(W)
    print(b)
    print(W.shape)
    for i in range(num_epochs):
        X, Y = train_epoch_ch3(net, train_iter, cross_entropy, updater)
        print("训练次数: " + str(cnt))
        cnt += 1
        print("训练损失: {:.4f}".format(X)) #训练损失是用训练数据集测得
        print("训练精度: {:.4f}".format(evaluate_accuracy(net, test_iter))) # 训练精度是用测试数据集测得
        print(".................................")
        print(W)
        print(b)

代码解析如下:

Fashion MNIST数据集中,每个样本都属于10个不同的类别,代表不同类型的服装(如鞋子、衬衫、裤子等)虽然只有10种类别但是每种类别有很多个样例,足够我们训练模型以及监测模型,所以这里输出为10,每次获取256个样例(获取的256个样例中有多种类别,鞋子,衬衫等,并不是单一类别)且返回的除了样例之外,还有该样例的标号(这个标号是为了区分该样例到底是什么)后续会讲到

还有一点就是获取的图片的像素是28*28大小,而且是二维,每个像素范围是0~255表示不同的颜色,这个数据迭代器load_data_fashion_mnist会将数据预处理成784,也就是一个维度

这种处理方式的优点就是更方便计算,但是缺点就是损失了一些空间信息,所以训练出来的模型算是低级模型吧,更高级的模型还是要保留图片二维空间训练出来

"创建训练集&创建检测集合"
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

"每个图片长度,以及图片种类"
num_inputs = 784
num_outputs = 10

这里sgd是库函数提供的更新梯度函数,由于梯度是由python自动帮你计算,他会在updater(X.shape[0])后自动获取计算的梯度

"更新模型"
def updater(batch_size):
   return d2l.sgd([W, b], lr, batch_size)

这个函数就是查看模型的识别情况,看识别对的数量有多少

"显示预测与估计相对应下标数量"
def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # 确定长宽高都大于1
        y_hat = y_hat.argmax(axis=1) # 取出每行中最大值
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum()) # 返回对应下标数量


这个函数就是对前面生成的数据以及函数的调用,并检测生成的模型的情况

"训练更新模型&返回训练损失与精度函数"
def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        # print(y)
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

至于损失函数以及softmax就不再赘述,前面的文章已经剖析得很清楚了

深度学习_8_对Softmax回归的理解


以下主要讲述对求y_hat以及梯度计算得理解:

由上述可知,获取的数据集的形状train_iter, test_iter它们的形状为(256, 784)

模型W的形状为(784, 10)

模型b的形状为(10, )

为了方便不考虑波动值b

为了模拟计算过程我们将数据进行相对缩小

784 ----> 4
256 ----> 3
10  ----> 2

那么获取的数据集合的shape就是(3, 4)设获取得数据集为X假设其具体值为

[
 [1, 2, 3, 4],
 [5, 6, 7, 8,],
 [9, 10, 11, 12]
]

那么模型W的形状就是(4, 2)其为

[
 [w1,w2],
 [w3,w4],
 [w5,w6],
 [w7,w8]
]

X * W是可行的其结果为:

[
  [(1*w1+2*w3+3*w5+4*w7), (1*w2+2*w4+3*w6+4*w8)],
  [(5*w1+6*w3+7*w5+8*w7), (5*w2+6*w4+7*w6+8*w8)],
   [(9*w1+10*w3+11*w5+12*w7), (9*w2+10*w4+11*w6+12*w8)],
]

其中w1,w2…有对应数值,并不是单纯未知数

这里X*W的每个元素值对应Oi,要将Oi转成y_hat,进行一次softmax操作即可

展示这个的原因只是想证实y_hat由W中的元素w1,w2,w3…构成,那么后面求得的损失函数也是由w1,w2,w3…构成

而求梯度的操作就是对损失函数求每个wi偏导的操作

W的shape是(4,2)其中的每个元素都是wi,所以最后的求得得梯度也是(4, 2),最后有W和梯度以及学习率就可以更新新的W了

最后理解一下偏导:

偏导将偏导得变量当作未知数,其他变量当作常数,是因为本次求导最关心得是本变量对整个函数的数值影响,而忽略其他变量对函数的影响

你可能感兴趣的:(深度学习,python,pytorch)