pytorch_lesson11 梯度向量的方向和大小+反向传播的意义与实现+动量法及实现+batch_size和epoch+在MINST-FASHION上实现神经网络的学习流程

仅仅是学习记录笔记,搬运了学习课程的ppt内容,本意不是抄袭!望大家不要误解!纯属学习记录笔记!!!!!!


一、梯度下降中的两个关键问题

1 找出梯度向量的方向和大小

在这里插入图片描述

2 让坐标点移动起来(进行一次迭代)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、找出距离和方向:反向传播

1 反向传播的定义与价值

在这里插入图片描述
加粗样式
单层的反向传播计算已经很复杂了,双层的只会更复杂,所以我们通过链式法则来解决这个问题:
在这里插入图片描述
对于单层的神经网络来说,梯度计算过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于双层的神经网络来说,梯度计算过程如下:
在这里插入图片描述

在这里插入图片描述

2 PyTorch实现反向传播

import torch

x = torch.tensor(1., requires_grad=True) #requires_grad表示允许对x进行梯度计算
y = x ** 2

print(torch.autograd.grad(y, x))
#求y对x求导在x=1上的值
#(tensor(2.),)
#导入库、数据、定义神经网络类,完成正向传播
#继承nn.Module类完成正向传播
import torch
import torch.nn as nn
from torch.nn import functional as F
#确定数据
torch.manual_seed(420)
X = torch.rand((500,20),dtype=torch.float32) * 100
y = torch.randint(low=0,high=3,size=(500,1),dtype=torch.float32)
#定义神经网路的架构
"""
注意:这是一个三分类的神经网络,因此我们需要调用的损失函数多分类交叉熵函数CEL
CEL类已经内置了sigmoid功能,因此我们需要修改一下网络架构,删除forward函数中输出层上的sigmoid
函数,并将最终的输出修改为zhat
"""
class Model(nn.Module):
	def __init__(self,in_features=10,out_features=2):
		super(Model,self).__init__() #super(请查找这个类的父类,请使用找到的父类替换现在
的类)
		self.linear1 = nn.Linear(in_features,13,bias=True) #输入层不用写,这里是隐藏
层的第一层
		self.linear2 = nn.Linear(13,8,bias=True)
		self.output = nn.Linear(8,out_features,bias=True)

	def forward(self, x):
		z1 = self.linear1(x)
		sigma1 = torch.relu(z1)
		z2 = self.linear2(sigma1)
		sigma2 = torch.sigmoid(z2)
		z3 = self.output(sigma2)
		#sigma3 = F.softmax(z3,dim=1)
		return z3
		
input_ = X.shape[1] #特征的数目
output_ = len(y.unique()) #分类的数目
#实例化神经网络类
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)
#前向传播
zhat = net.forward(X)
#定义损失函数
criterion = nn.CrossEntropyLoss()
#对打包好的CorssEnrtopyLoss而言,只需要输入zhat
loss = criterion(zhat,y.reshape(500).long())
loss
net.linear1.weight.grad #不会返回任何值
#反向传播,backward是任意损失函数类都可以调用的方法,对任意损失函数,backward都会求解其中全部
w的梯度
loss.backward()
net.linear1.weight.grad #返回相应的梯度
#与可以重复进行的正向传播不同,一次正向传播后,反向传播只能进行一次
#如果希望能够重复进行反向传播,可以在进行第一次反向传播的时候加上参数retain_graph
loss.backward(retain_graph=True)
loss.backward()

三、移动坐标点

1 走出第一步

在这里插入图片描述

#在这里,我们的数据是生成的随机数,为了让大家看出效果,所以我才设置了步长为10,正常不会使用这么
大的步长
#步长、学习率的英文是learning rate,所以常常简写为lr
lr = 10
dw = net.linear1.weight.grad
w = net.linear1.weight.data
#对任意w可以有
w -= lr * dw

2 从第一步到第二步:动量法Momentum

在这里插入图片描述
在这里插入图片描述

#恢复小步长
lr = 0.1
gamma = 0.9
dw = net.linear1.weight.grad
w = net.linear1.weight.data
v = torch.zeros(dw.shape[0],dw.shape[1])
#v要能够跟dw相减,因此必须和dw保持相同的结构,初始v为0,但后续v会越来越大

#对任意w可以有
v = gamma * v - lr * dw
w -= v
w
#不难发现,当加入gamma之后,即便是较小的步长,也可以让w发生变化

3 torch.optim实现带动量的梯度下降

在这里插入图片描述

#导入库、数据、定义神经网络类,完成正向传播
import torch
import torch.nn as nn
from torch.nn import functional as F
from torch import optim

#确定数据
torch.manual_seed(420)

X = torch.rand((500, 20), dtype=torch.float32)
y = torch.randint(low=0, high=2, size=(500, 1), dtype=torch.float32)

#确定数据、确定优先需要设置的值
lr = 0.1
gamma = 0.9

#定义神经网络的架构
class Model(nn.Module):
    def __init__(self, input_features, output_features):
        super(Model, self).__init__()
        self.Linear1 = nn.Linear(input_features, 13, bias=True)
        self.Linear2 = nn.Linear(13, 8, bias=True)
        self.Output = nn.Linear(8, output_features)

    def forward(self, x):
        z1 = self.Linear1(x)
        sigma1 = torch.relu(z1)
        z2 = self.Linear2(sigma1)
        sigma2 = torch.sigmoid(z2)
        z3 = self.Output(sigma2)
        #因为后面要用到多分类交叉损失熵,所以最后一层输出之后没有经过激活函数,到时候直接使用log_softmax即可
        return z3

input_ = X.shape[1] #输入的是特征的数目,不是样本数量
#print(input_)
output_ = len(y.unique())

#实例化神经网络类
torch.manual_seed(420)
net = Model(input_features=input_, output_features=output_)
#print(net.parameters())
#查看神经网络的参数,包括多个神经网络层中的w和b


#定义损失函数
criterion = nn.CrossEntropyLoss()

#定义优化算法
opt = optim.SGD(net.parameters(), lr=lr, momentum=gamma)
#optim需要三个数据,我们要优化的参数,学习率和动量参数

#接下来我们进行一轮梯度下降
zhat = net.forward(X)      #向前传播
loss = criterion(zhat, y.reshape(500).long())  #损失函数值
loss.backward()            #向后传播
opt.step()                 #更新权重w,这一瞬间,坐标点就发生了变化,所有的梯度必须重新计算
opt.zero_grad()  

四、开始迭代:batch_size与epoches

1 为什么要有小批量?

在这里插入图片描述
在这里插入图片描述mini-batch SGD的优势是算法不会轻易陷入局部最优,由于每次梯度向量的方向都会发生巨大变化,因此一旦有机会,算法就能够跳出局部最优,走向全局最优(当然也有可能是跳出一个局部最优,走向另一个局部最优)。不过缺点是,需要的迭代次数变得不明。如果最开始就在全局最优的范围内,那可能只需要非常少的迭代次数就收敛,但是如果最开始落入了局部最优的范围,或全局最优与局部最优的差异很小,那可能需要花很长的时间、经过很多次迭代才能够收敛,毕竟不断改变的方向会让迭代的路线变得曲折。从整体来看,为了mini-batch SGD这“不会轻易被局部最优困住”的优点,我们在神经网络中使用它作为优化算法(或优化算法的基础)。当然,还有另一个流传更广、更为认知的理由支持我们使用minibatch SGD:mini-batch SGD可以提升神经网络的计算效率,让神经网络计算更快。

2 batch_size与epoches

在mini-batch SGD中,我们选择的批量batch含有的样本数被称为batch_size,批量尺寸,这个尺寸一定是小于数据量的某个正整数值。每次迭代之前,我们需要从数据集中抽取batch_size个数据用于训练。

重要概念:epoch,epoch是衡量训练数据被使用次数的单位,一个epoch表示优化算法将全部训练数据都使用了一次。它与梯度下降中的迭代次数有非常深的关系,我们常使用“完成1个epoch需要n次迭代“这样的语言。

在这里插入图片描述

3 TensorDataset与DataLoader

from torch.utils.data import TensorDataset

a = torch.randn(500, 2, 3)
b = torch.randn(500, 3, 4, 5)
c = torch.randn(500, 1)

#打包特征和标签的工具TensorDataset
print(TensorDataset(a, b, c)[0])#在第一个维度上将a,b,c张量进行拼接,拼接的一个准则是最外层的维度一定要一致
print(TensorDataset(a, c)[0])

当我们将数据打包成一个对象之后,我们需要使用划分小批量的功能DataLoader。DataLoader是处理训练前专用的功能,它可以接受任意形式的数组、张量作为输入,并把他们一次性转换为神经网络可以接入的tensor。

from torch.utils.data import DataLoader
import numpy as np

DataLoader(np.random.randn(5, 2))

for i in DataLoader(np.random.randn(5, 2)):
    print(i)
    break

#tensor([[-0.5758,  1.0728]], dtype=torch.float64) 因为numpy生成array数组的时候,对于浮点型来说,优先生成float64

for i in DataLoader(torch.randn(5, 2, dtype=torch.float32)):
    print(i.dtype)
    break
#torch.float32

data = DataLoader(torch.randn(500, 2), batch_size=120, shuffle=True, drop_last=True)
#drop_last如果设置为true的话,当最后一个batch样本不够组成一个batch_size时,就会抛掉最后一个batch
#设置为False,则会保留一个较小的batch,默认情况下是False

#查看一下生成的对象
for i in data:
    print(i.shape)

'''
torch.Size([120, 2])
torch.Size([120, 2])
torch.Size([120, 2])
torch.Size([120, 2])
'''

对于小批量随机梯度下降而言,我们一般这样使用TensorDataset与DataLoader:

from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader

torch.manual_seed(420)
X = torch.rand((50000, 20), dtype=torch.float32)
y = torch.randint(low=0, high=3, size=(50000, 1), dtype=torch.float32)

epochs = 4
bs = 4000

data = TensorDataset(X, y)
batchdata = DataLoader(data, batch_size=bs, shuffle=True)
print(len(batchdata))  #13

#可以使用.datasets查看数据集相关的属性
print(batchdata.dataset)
#查看不到详细的内容,正常来说,batchdata会包括一个索引值,20个x特征值和1个y标签值
#<torch.utils.data.dataset.TensorDataset object at 0x0000022D23C1FA08>
print(enumerate(batchdata.dataset))


'''
(tensor([0.8054, 0.1990, 0.9759, 0.1028, 0.3475, 0.1554, 0.8856, 0.6876, 0.2506,
        0.1133, 0.2105, 0.4035, 0.2448, 0.8644, 0.2896, 0.1729, 0.3458, 0.0117,
        0.2572, 0.2272]), 
tensor([2.]))
'''
#batchdata中第一个样本是20个特征及其标签值

#查看第一个样本的特征
#print(batchdata.dataset[0][0])
'''
tensor([0.8054, 0.1990, 0.9759, 0.1028, 0.3475, 0.1554, 0.8856, 0.6876, 0.2506,
        0.1133, 0.2105, 0.4035, 0.2448, 0.8644, 0.2896, 0.1729, 0.3458, 0.0117,
        0.2572, 0.2272])
'''
print(batchdata.dataset[0][1])
#tensor([2.])

#属性batch_size,查看batch_size是多少
print(batchdata.batch_size)
#4000

for batch_idx, (x,y) in enumerate(batchdata):
	#sigma = net(x)
	#loss = lossfn(sigma, y)
	#loss.backward()
	#opt.step()
	#opt.zero_grad()
	print(x.shape)
	print(y.shape)
	print(x,y)
	if batch_idx == 2:
		break #为了演示用,所以打断,在正常的循环里是不会打断的

除了自己生成数据之外,我们还可以将外部导入的数据放到TensorDataset与DataLoader里来使用:

#导入sklearn中的数据
from sklearn.datasets import load_breast_cancer as LBC

data = LBC()

X = torch.tensor(data.data, dtype=torch.float32)
y = torch.tensor(data.target, dtype=torch.float32)

data = TensorDataset(X, y)
batchdata = DataLoader(data, batch_size=5, shuffle=True)
'''
for x, y in batchdata:
    print(x)
    break
'''
#验证有效

导入外部数据,通过pandas导入

import pandas as pd
import numpy as np

data = pd.read_csv("creditcard.csv")
print(data.shape)
#(284807, 31)

x = torch.tensor(np.array(data.iloc[:, :-1]), dtype=torch.float32)
y = torch.tensor(np.array(data.iloc[:, -1]), dtype=torch.float32)

data = TensorDataset(x, y)

batchdata = DataLoader(data, batch_size=1000, shuffle=True)

for x, y in batchdata:
    print(x)
    break

#实验验证是可以的

五、在MINST-FASHION上实现神经网络的学习流程

导库+设置各种初始值

#1、导库,设置各种初始值
import torch
import torch.nn
from torch import optim
from torch import functional as F
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset

lr = 0.1
gamma = 0
epochs = 10
batch_size = 128

#2、导入数据,分割小批量
import torchvision
import torchvision.transforms as transforms

minst = torchvision.datasets.FashionMNIST(
    root='/Users/gaoyuxing/Desktop/MINST-FASHION数据集',
    train=True,
    download=True,  #如果电脑上不存在这个数据集可以允许下载
    transform=transforms.ToTensor())

print(len(minst))
#60000 数据样本有6万个
#查看特征张量
print(minst.data)
#查看标签值
print(minst.targets)
#查看标签的类别
print(minst.classes)
#['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']一共九种类别

查看图像的模样

#查看图像的模样
import matplotlib.pyplot as plt
import numpy
#print(minst)  #查看数据的介绍
'''
Dataset FashionMNIST
    Number of datapoints: 60000
    Root location: /Users/gaoyuxing/Desktop/MINST-FASHION数据集
    Split: Train
    StandardTransform
Transform: ToTensor()
'''
#print(minst[0])
#查看的结果显示每个样本包括两个内容,第一是特征张量,第二个是对应的标签
#print(minst[0][0])  #绘制图像的时候我们只选取其特征向量进行读取
#print(minst[0][0].shape)
#torch.Size([1, 28, 28])  代表这个数据是有通道的,显示图像的时候我们需要改变其形状,转变为长和宽即可

plt.imshow(minst[0][0].view(28, 28).numpy()) #转变为array数组
plt.show()

请添加图片描述
第一个样本长得像是一只鞋,然后我们继续查看第二个样本

plt.imshow(minst[1][0].view(28, 28).numpy()) #转变为array数组
plt.show()

请添加图片描述
看起来是一个T恤

分割batch_size

#分割batch_size
batchdata = DataLoader(minst, batch_size=batch_size, shuffle=True)
#print(len(batchdata))
#60000/128=468.75  469

#查看会放入进行迭代的数据结构
for x, y in batchdata:
    print(x.shape)  #torch.Size([128, 1, 28, 28])
    print(y.shape)  #torch.Size([128])
    break
   
#查看了数据的结构之后我们需要思考为了满足神经网络需要的数据形状,时候需要对现有的数据形状进行处理

#目前的数据是一个思维结构,128是索引,1,28,28特征的形状,接下来我们需要将特征拉平成128,1*28*28的结构,则输入层的神经元就是28*28个

print(minst.data[0]) #可以读取第一个样本的特征值
print(minst.data[0].shape) #torch.Size([28, 28]) #第一个样本的特征值形状
print(minst.data[0].numel()) #784可以得到这个特征张量中一共有多少元素,这个数量就是我们输入层的神经元的个数

定义神经网络的架构

#定义网络结构
class Model(nn.Module):
    def __init__(self, input_feature, output_feature):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(input_feature, 128, bias=True)
        self.Output = nn.Linear(128, output_feature, bias=True)


    def forward(self, x):
        #外部输入的数据,我们一定要把特征拉平,每个样本的第2个维度是必须是28*28才对
        x = x.view(-1, 28*28)
        #-1是占位符,意思是要求pytorch自己计算这个位置上的维度值
        z1 = self.linear1(x)
        sigma1 = torch.relu(z1)
        z2 = self.Output(sigma1)
        sigma2 = F.log_softmax(z2, dim=1)
        return sigma2

在这里介绍一下view(-1,*)的功能,当我们定义好一个维度的大小时候,为了简便,可以要求pytorch自动计算另一个维度的大小

x = torch.rand(30, 40)
x = x.view(-1, 20)
print(x.shape)
#torch.Size([60, 20])

定义训练函数

#定义训练函数
def fit(net, batchdata, lr, epochs, gamma):
    criterion = nn.NLLLoss() #实力化损失函数
    #有了损失函数之后就要进行梯度下降求解,我们这里用到的是小批量随机梯度下降
    opt = optim.SGD(net.parameters(), lr=lr, momentum=gamma) #由于我们是对权重进行优化,所以我们当然需要网络参数,学习率和动量参数
    samples = 0
    correct = 0
    '''
    对于每一个epoch里面的每一个batch_size都要进行小批量梯度下降解
	首先重新定义了y的形状
	定义损失函数里面输入的预测值,这里根据损失函数使用的不同,输入的参数也不同
	定义具体的损失函数
	对损失函数进行反向传播
	走出一步动量梯度
	对原来的梯度值进行情况
    '''
    for epoch in range(epochs):
        for batch_idx, (x, y) in enumerate(batchdata):
            y = y.view(x.shape[0]) #我们在做交叉熵计算的时候y一定是一维张量
            sigma = net.forward(x)
            loss = criterion(sigma, y)
            loss.backward()  #反向传播
            opt.step()
            opt.zero_grad()

            yhat = torch.max(sigma, 1)[1]
            samples += x.shape[0]
            correct += torch.sum(yhat == y)

            if (batch_idx + 1) % 125 == 0 or batch_idx == len(batchdata) - 1:
                print('Epoch{}:[{}/{}({:.0f}%)]\tLoss:{:.6f}\t Accuracy:{:.3f}'.format(epoch + 1,
                                                                                      samples,
                                                                                      len(batchdata.dataset) * epochs,
                                                                                      100 * samples / (len(batchdata.dataset) * epochs),
                                                                                      loss.data.item(),
                                                                                      float(correct * 100) / samples))

#进行训练与评估
torch.manual_seed(420)
net = Model(input_feature=input_, output_feature=output_)
fit(net, batchdata, lr=lr, epochs=epochs, gamma=gamma)

对torch.max()详细使用一下

import torch
a = torch.tensor([[.1, .4, .3, .5], [.2, .3, .6, .8]])
print(torch.max(a))
#tensor(0.8000)
#可以给出整个张量的最大值,但我们需要的内含的一维张量上的最大值

print(torch.max(a, 1))
'''
torch.return_types.max(
values=tensor([0.5000, 0.8000]),
indices=tensor([3, 3]))
'''
#然后我们可以看出来,这里会给出每个样本中的最大值
print(torch.max(a, 1)[1])
#得到每个最大值的索引——也就是对应着的标签值
#tensor([3, 3])
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 附件中的脚本和训练数据提供了一个基于PyTorch的手写数字识别模型的实现。该模型使用一个卷积神经网络(Convolutional Neural Network,CNN)来进行训练和预测。 首先,pytorch_mnist.py是一个Python脚本,包含了模型的网络结构、损失函数、优化器以及训练、验证和测试的流程。它通过加载mnist.npz中的数据集,对模型进行训练,并评估其在测试集上的性能。 mnist.npz是一个Numpy数组文件,其中包含了手写数字MNIST数据集。MNIST数据集是一个常用的机器学习数据集,包含了60000个用于训练的手写数字图像和10000个用于测试的手写数字图像。每个图像都是28x28像素大小的灰度图像,表示了0到9之间的一个数字。mnist.npz文件将数据集分为了训练集、验证集和测试集,并存储为Numpy数组的形式。 脚本pytorch_mnist.py使用了PyTorch框架来定义了一个具有两个卷积层和三个全连接层的CNN模型。训练过程中,脚本使用了随机梯度下降(Stochastic Gradient Descent,SGD)算来优化模型的权重参数,并使用交叉熵损失函数来度量模型的性能。脚本还实现了训练集上的批次循环、验证集上的性能评估和在测试集上的预测。 下载并运行这些脚本和数据,你将能够训练一个基于CNN的手写数字识别模型,并使用该模型对新的手写数字图像进行识别。这个模型可以作为一个简单但有效的数字识别工具,有助于学习和理解深度学习和计算机视觉领域的相关概念和技术。 ### 回答2: 附件提供了两个文件,分别是脚本文件pytorch_mnist.py和训练数据文件mnist.npz。 脚本文件pytorch_mnist.py是使用PyTorch框架编写的一个用于识别手写数字的神经网络模型。它通过卷积神经网络的方对输入的手写数字图像进行分析和识别。脚本首先加载训练数据,然后定义了一个包含卷积层、池化层和全连接层的神经网络模型。接着使用随机梯度下降算对模型进行训练,并实现了损失函数和优化器。最后,在一定的迭代次数下,保存了训练好的模型,在测试集上进行准确率的评估。 训练数据文件mnist.npz包含了用于训练和测试的手写数字图像数据集,其中包括了60,000个训练样本和10,000个测试样本。这些图像数据已经被处理成灰度图像,并存储在一个numpy数组中。可以通过读取这些数据,并分为训练集和测试集,用于模型的训练和评估。 总结起来,这个附件提供了一个使用PyTorch框架编写的手写数字识别模型的实现脚本以及相应的训练数据。通过使用这些资源,我们可以训练一个卷积神经网络模型来对手写数字图像进行识别,并通过测试数据评估该模型的准确率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值