栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

为了小论文之李沐带你学AI(三)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为了小论文之李沐带你学AI(三)

今天来测试softmax分类图片问题

!!!!!我省略掉了对于计算准确率,动画展示的部分
torchvision.transforms是pytorch中的图像预处理包,包含了很多种对图像数据进行变换的函数,这些都是在我们进行图像数据读入步骤中必不可少的.

  • torchvision.transforms.Compose():将多种变换组合在一起

  • transforms.ToTensor() 是将一个image格式转换成torch.FloatTensor的数据形式

  • transforms.Resize(resize)调整图片的风变绿

  • 记录keepdim的作用

import numpy as np

a = torch.ones((2,2))
 
b = np.array([[1,2,3],[1,1,1]])
c = torch.from_numpy(b)
 
interval_0 = torch.sum(c, dim=0, keepdim=True)

interval_1 = torch.sum(c, dim=1)

keepdim的作用可以防止计算完之后的数据变成一个一行的数据,第二个是防止维数被挤压

batch_size = 256

def get_dataloader_workers():  
    """使用4个进程来读取数据。"""
    return 2

#ok,现在我们整合所有的组件,并且定义函数用来获得数据集
def load_data_fashion_mnist(batch_size,resize=None):
  #下载Fashion-Mnist数据集,然后将其加载到内存中
  trans = [transforms.ToTensor()] #定义一个列表,这个列表中包含了想要对plmimage类型数据做的数据类型变换
  if resize:
    trans.insert(0,transforms.Resize(resize))#如果我们设定了resize,这里会重置它的分辨率
  trans = transforms.Compose(trans)#组合这么这些个操作
  mnist_train=torchvision.datasets.FashionMNIST(root="./data",train=True,transform=trans,download=True)#载入数据到内存
  mnist_test=torchvision.datasets.FashionMNIST(root="./data",train=False,transform=trans,download=True)#载入数据到内存
  return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),data.DataLoader
          (mnist_test,batch_size,shuffle=False,num_workers=get_dataloader_workers()))


batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)

#初始化模型的参数
num_inputs = 784	#我们需要把一个28*28的图片拉长到1*784的向量,虽然这样会损失空间信息
num_outputs = 10	#最后我们的预测是只有10个分类的
W = torch.normal(0,0.01,size=(num_inputs,num_outputs),requires_grad=True)#定义我们的层,记录梯度
b = torch.zeros(num_outputs,requires_grad=True)#定义我们的bias,记录梯度

#定义softmax的操作
def softmax(X):
  X_exp = torch.exp(X)  #计算每一个参数求幂
  partition = X_exp.sum(1,keepdim=True)
  return X_exp/partition  #注意这里应用了广播机制

对于这里的Softmax我有话要说
X = torch.normal(0,1,(2,5))#创建了一个均值为0,方差为1的,2行5列的矩阵
tensor([[ 1.3066, 0.0417, 0.6489, -0.0553, -0.9866],[-1.7921, -0.4884, 1.7815, 2.2112, -0.6010]])
X_exp = torch.exp(X),在这里,我们是对每一个元素,进行了以e为底的操作
partition = X_exp.sum(1,keepdim=True)
dim=1说明,最后我们的数据会变成一列数据,为什么要变成一列的数据,比如在这里就变成
partition = X_exp.sum(1,keepdim=True)
partition.shape
torch.Size([2, 1])
因为对于我们来说一行代表一个样本,我们需要再一行上进行分布的预测,tip在最后return的时候应用了广播操作,最后partition会变成[2*5]的矩阵,每一列的数值与第一列的数值相同

补充
tensor.reshape(-1)的意思。我们都知道,在我们进行矩阵的变换的时候,需要保持元素的数量是一致的。-1的意思就是定义了一个未知数,只需要会根据其他维数的信息,来计算这个维度

我在这里简单的形容一下交叉熵是如何计算的吧

重新起个头
%matplotlib inline
import torch
import torchvision
from IPython import display
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

这一部分是我们需要导入的库,我现在才发现,其实d2l库是李老师专门写出来的一个库,方便我们学习,同志们把泪目打在公屏上。

def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签。"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

这个函数,我们会传入一个int类型的Labels,比如labels = [1,2,3],返回对应的文本标签

def get_dataloader_workers():
    """使用4个进程来读取数据。"""
    return 4

并行的读取数据,这个部分可以根据自己的电脑情况调大调小

def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中。"""
    trans = [transforms.ToTensor()] # 在这里,我们规定了数据会进行什么变换
    if resize:  #如果resize这个参数不为0我们就会重置它的分辨率
        trans.insert(0, transforms.Resize(resize))  #进行Resize函数,重置分辨力
    trans = transforms.Compose(trans) #把这一系列的操作组合在一起
    mnist_train = torchvision.datasets.FashionMNIST(    #获取训练数据集    
        root="./data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(     #获取测试数据集
        root="./data", train=False, transform=trans, download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),    # 在pytorch中,使用DataLoader是一件毋庸置疑的事情
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

注意,我们需要对数据进行一系列的操作,trans就可以理解为这些操作的一个集合
resize就是重新定义它的分辨率
在pytorch中,我们的一切数据都需要分装成dataLoader,给定一个数据集,指定batchsize,在指定是否打乱,指定几个进程去读。这个都是用的很多,就不多说了

def softmax(X):
    X_exp = torch.exp(X)  #首先对于输入的的X,可能是一个向量,可能是一个矩阵,进行指数的求解
    partition = X_exp.sum(1, keepdim=True)  #我们现在是消灭列这个维数,为什么,因为对我们的设定,每一行是一个样本,所以要求的是行和而不是列和
    return X_exp / partition  # 这里应用了广播机制

就举个我们本例来说
我们的输入是
X是一个[256784]的矩阵W是一个[78410]的一个矩阵,就是一个[25610]的矩阵,我们是在一个[25610]的矩阵上进行一个softmax操作。
在我们本例上,我们的一行代表我们的一个样本,所以我们需要的是一个行和,而不是列和。
最后应用到了广播机制,当我们的使用完求和之后,获得的是一个[256
1]的矩阵,需要扩展到
一个[256*10]的矩阵。

def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)    #reshape -1参数的意思是,这个位置的大小是根据其他维数算出来的
    #在这里我们提前把X的维度从一个256*1*28*28的维度拉长到了一个256*784的维度

在这里定义我们的网络层,我们的网络层可以简单理解一个一个线性映射加上一个softmax

def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])   #在这里定义我们的损失函数,我们只需要知道,对于分类问题,我们大部分时间都是使用交叉熵这个损失函数的```
定义我们的损失函数,使用的是交叉熵,具体公式以后自己去理解。
但是我只能说

y_hat是一个256 10的矩阵
y的维度是一个256
1的矩阵

def train_epoch_ch3(net, train_iter, loss, updater):#真正开始训练的函数
    """训练模型一个迭代周期(定义见第3章)。"""
    # 将模型设置为训练模式
    #net就是我们创建好的网络,在这里,就是一个softmax的全连接层
    #train_iter就是我们的训练数据集
    #loss就是我们定义的损失函数
    #updater就是sgd优化函数,很简单,就是在梯度方向每一个参数走一个lr学习率的值
    if isinstance(net, torch.nn.Module):#首先将这个网络的模式,设置成训练模式,就是开启了梯度记录,梯度的计算
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3) #创建一个累加器
    for X, y in train_iter:
    #根据我们的定义,每一次我们都会从数据中读取一个256张图片已经对应的label
    # X的维度是 256*1*28*28
    # y的维度是 256*1

        # 计算梯度并更新参数
        y_hat = net(X)  #计算预测值,其实这里出来的还是一个256*10的一个矩阵
        l = loss(y_hat, y)  #计算损失,这里使用的是交叉熵来当损失函数
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数,这是使用了pytorch带的api的情况
            updater.zero_grad() #清空梯度,不然会在原来的梯度上累计叠加
            l.backward()#计算梯度
            updater.step()#拿计算出来的梯度更新模型
            metric.add(float(l) * len(y), accuracy(y_hat, y),
                       y.size().numel())#开始add
        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]   #返回损失和准去率

记住我们的步骤,一定是先计算损失,然后根据损失计算出来的损失调用 backwar()方向传播函数,然后再更新模型一定是这么一个操作

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    """训练模型(定义见第3章)。"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc']) #这一部分是动画函数,我们可以暂且放一放
    for epoch in range(num_epochs): #根据我们规定的训练轮回,进行一个迭代
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        #net就是我们创建好的网络,在这里,就是一个softmax的全连接层
        #train_iter就是我们的训练数据集
        #loss就是我们定义的损失函数
        #updater就是sgd优化函数,很简单,就是在梯度方向每一个参数走一个lr学习率的值
        
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc
def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)
#这个sgd就是一个优化更新的方法,对于每一个参数,进行一个 学习率lr与梯度方向上的优化

batch_size = 256    #在这里我们初始化一个batch_size的大小
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) #在这里我们先获得我们的训练集,和我们的测试集
for X, y in train_iter:
  print(X.reshape(-1, 784).shape)
  break


num_inputs = 784
num_outputs = 10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) #创建一个均值为0,方差为0.01的数据集,可以理解为他是一个输入784维,输出是10维的神经网络,并且开启梯度记录
b = torch.zeros(num_outputs, requires_grad=True)    #创建我们的bias,并且开启梯度记录


num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) #理论上这样就可以开始跑了
如果使用pytorch的api呢?
batch_size = 256
train_iter,test_iter = d2l.load_data_fashion_mnist(batch_size)  #别的不多说了,这个数据初始化,还是要自己初始化的呀宝儿


net = nn.Sequential(nn.Flatten(),nn.Linear(784,10))# 在这里nn.Flatten就是把数据磨平,默认从维度1还是这个和torch.flatten()不一样

def init_weight(m):
  if type(m) == nn.Linear:
    nn.init.normal_(m.weight,std=0.01)  #我们开始定义我们的参数是如何呗初始化的
net.apply(init_weights)#初始化我们的函数

loss = nn.CrossEntropyLoss()#以后直接调用我们的api,这就是我们的损失函数

trainer = torch.optim.SGD(net.parameters(), lr=0.1)#这个就是我们的优化函数

num_epochs = 10#定义优化的论述
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)#开始训练,就是如此的简单优雅

从这里我们能看到所谓的 LInear本质是就是矩阵的相乘,所谓的神经网络就是这么一个一个的线性变化并没有什么难度。
并且在这里我也要提出,优化函数是决定我们那里更新的梯度和学习率之后应该做的实行,backward()是反向传播,计算梯度,而loss是规定我们的损失函数的。这是一个逆向的过程。正向的过程是,先计算损失函数,因为开启了训练模式,会记录梯度,然后根据backward()计算梯度,并且记录,然后用优化函数去更新参数,以上!!!!很经典的一套过程

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/488554.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号