Deeplizard《Pytorch神经网络高效入门教程》笔记

机器学习:使用数据来确定函数的参数

深度学习:特殊的机器学习,形式为多层神经网络,常常用于处理图像、文本、声音等高纬度信号

#硬件基础:GPU、Cuda

GPU比起CPU更适合并行计算

机器学习中的计算多为可分解为并行计算的问题

Cuda是管理GPU的软件API

# 查看GPU的设备数量:
print(torch.cuda.device_count())
# 使用Cuda之前都需要检测一下cuda是否可用,没检测就默认没有
print(torch.cuda.is_available())

Pytorch可以使用多块GPU,Cuda可以指定GPU索引

为什么不在GPU上做所有的计算

数据从CPU转移到GPU上代价昂贵,将小型任务从CPU转移到GPU可能会更慢,GPU对于可以被分解成许多小任务的任务来说效果更好

张量(tensor)

张量是神经网络主要的数据结构,网络中的输入、输出、转换都是用张量表示的

张量的实例:(上下两两对应)

计算机术语:number、array、2d-array

数学术语:scalar、vector、matrix

综上,根据表示某个数据结构中的数据所需要的索引数量,将上述所有术语统一为:nd-array/nd-tensor,所以张量是多维数组

image-20210718203036956

例如:

import torch

dd = [
[1,2,3],
[4,5,6],
[7,8,9]
]

t = torch.tensor(dd)

# print(torch.cuda.is_available())
# 调用cuda函数会返回同样的张量,但是会运行在GPU上
# t = t.cuda()

print(type(t))
# 输出:<class 'torch.Tensor'>

张量的秩

张量的秩表示了张量中存在的维数,如:rank-2 tensor = matrix = 2d-array = 2d-tensor

张量的秩告诉我们要多少索引来访问数据结构中的值

# 张量的秩 = shape的长度
print(len(t.shape))
# 输出:2

张量的轴

张量的轴表示一个张量的特定维数**(有多少坐标轴)**,轴的长度表示沿轴有多少索引

张量的秩表示了张量中存在的轴数,轴数 = 维数

张量的形状

张量的形状由每个轴的长度决定,知道张量的形状,就知道了每个轴的长度

import torch

t = torch.tensor([
    [1, 1, 1, 1],
    [2, 2, 2, 2],
    [3, 3, 3, 3]
], dtype=torch.float32)

print(t.size)
# 输出:torch.Size([3, 4])
print(t.shape)
# 输出:torch.Size([3, 4])
print(len(t.shape))
# 输出:2
print(torch.tensor(t.shape).prod())
# 输出:tensor(12)
print(t.numel())
# 输出:12

张量的形状包含了关于张量的轴、秩、索引信息

重塑(reshpae)没有改变原来数据结构中的数据,但是改变了数据的存储方式,如:2 by 3变成3 by 2

重塑的分量值乘积必须等于张量中元素的个数

# 在pytorch中张量的size和张量的shape是一样的
print(t.shape)
# 输出:torch.Size([3, 3])

# 重塑t
t = t.reshape(1,9)
print(t)
# 输出:tensor([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
print(t.shape)
# 输出:torch.Size([1, 9])
print(len(t.shape))
# 输出:2

##Creating tensor from data

import torch
import numpy as np

t = torch.Tensor()
print(type(t))
# 输出:<class 'torch.Tensor'>

张量包含统一的数据类型

print(t.dtype)
# 输出:torch.float32

张量计算依赖于类型和设备

print(t.device)
# 输出:cpu
print(t.layout)
# 输出:torch.strided

创建张量

# 4种方法创建张量

# 类构造函数(Constructor)
data = np.array([1,2,3])
print(type(data))
# 输出:<class 'numpy.ndarray'>

# 工厂函数(Factories)
t1 = torch.Tensor(data)
print(type(t1))
# 输出:<class 'torch.Tensor'>

# 工厂函数(Factories)
t2 = torch.tensor(data)
print(type(t2))
# 输出:<class 'torch.Tensor'>

# 工厂函数(Factories)
t3 = torch.as_tensor(data)
print(type(t3))
# 输出:<class 'torch.Tensor'>

# 工厂函数(Factories)
t4 = torch.from_numpy(data)
print(type(t4))
# 输出:<class 'torch.Tensor'>

print(t1.dtype)
# 输出:torch.float32
print(t2.dtype)
# 输出:torch.int32
print(t3.dtype)
# 输出:torch.int32
print(t4.dtype)
# 输出:torch.int32

# 分析原因:构造函数在构造张量时使用全局缺省值,工厂函数根据输入判断数据类型

print(t1)
print(t2)
print(t3)
print(t4)

# 输出:
# tensor([1., 2., 3.])
# tensor([1, 2, 3], dtype=torch.int32)
# tensor([1, 2, 3], dtype=torch.int32)
# tensor([1, 2, 3], dtype=torch.int32)

numpy与pytorch的转换关系:

import torch
import numpy as np

data = np.array([1,2,3])

t1 = torch.Tensor(data)
t2 = torch.tensor(data)     # 最常用
t3 = torch.as_tensor(data)  # 可以接受python中的任意数据类型
t4 = torch.from_numpy(data) # 只可以接受numpy中的数据类型

data[0] = 0
data[1] = 0
data[2] = 0

print(t1)
print(t2)
print(t3)
print(t4)

# 输出:
# tensor([1., 2., 3.])
# tensor([1, 2, 3], dtype=torch.int32)
# tensor([0, 0, 0], dtype=torch.int32)
# tensor([0, 0, 0], dtype=torch.int32)

# 分析原因:这种差异源于每个创建选项中分配内存的方式
# 前两个选项在内存中创建额外的输入数据副本(copy)
# 后两者用数组在内存中的共享,两者指针相同(share)

创建特殊张量:

# 创建特殊张量

# 创建2*2的单位张量(矩阵)
t5 = torch.eye(2)
print(t5)
# 输出:
# tensor([[1., 0.],
#         [0., 1.]])

# 创建2*2的0张量
t6 = torch.zeros(2,2)
print(t6)
# 输出:
# tensor([[0., 0.],
#         [0., 0.]])

# 创建全1张量
t7 = torch.ones(2,2)
print(t7)
# 输出:
# tensor([[1., 1.],
#         [1., 1.]])

# 创建随机张量
t8 = torch.rand(2,2)
print(t8)
# 输出:
# tensor([[0.9487, 0.1351],
#         [0.9750, 0.0050]])

张量运算

压缩(flatten)操作:

import torch

# 将下面3个张量看做3张4*4的图片,作为1个批次传入CNN(CNN输入一般长度为4,即有一个秩为4的张量,有 4个坐标轴)

t1 = torch.tensor([
    [1,1,1,1],
    [1,1,1,1],
    [1,1,1,1],
    [1,1,1,1],
])

t2 = torch.tensor([
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2],
])

t3 = torch.tensor([
    [3,3,3,3],
    [3,3,3,3],
    [3,3,3,3],
    [3,3,3,3],
])

# 用堆栈将3个张量连接到一个轴上
t = torch.stack((t1,t2,t3))
print(t.shape)
# 输出:torch.Size([3, 4, 4])
# 3:表示轴的长度(批大小);4:长和宽

# 重塑图像
t = t.reshape(3,1,4,4)
print(t)
# 输出:
# tensor([[[[1, 1, 1, 1],
#           [1, 1, 1, 1],
#           [1, 1, 1, 1],
#           [1, 1, 1, 1]]],
#
#
#         [[[2, 2, 2, 2],
#           [2, 2, 2, 2],
#           [2, 2, 2, 2],
#           [2, 2, 2, 2]]],
#
#
#         [[[3, 3, 3, 3],
#           [3, 3, 3, 3],
#           [3, 3, 3, 3],
#           [3, 3, 3, 3]]]])

用索引解释每个轴的意义:

# 输出第一个图像
print(t[0])
# 输出:
# tensor([[[1, 1, 1, 1],
#          [1, 1, 1, 1],
#          [1, 1, 1, 1],
#          [1, 1, 1, 1]]])

# 输出第一个图像里第一个彩色通道
print(t[0][0])
# 输出:
# tensor([[1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1]])

# 输出第一个图像里第一个彩色通道的第一行像素
print(t[0][0][0])
# 输出:
# tensor([1, 1, 1, 1])

# 输出第一个图像里第一个彩色通道的第一行像素的第一个像素值
print(t[0][0][0][0])
# 输出:
# tensor(1)

元素操作:两个张量必须有相同的形状才能执行一个元素操作

import numpy as np
import torch

t1 = torch.tensor([
    [1,2],
    [3,4]
],dtype=torch.float32)

t2 = torch.tensor([
    [9,8],
    [7,6]
],dtype=torch.float32)

t3 = torch.tensor([
    [9,8]
],dtype=torch.float32)

print(t1+t2)
# 输出:
# tensor([[10., 10.],
#         [10., 10.]])

# 张量广播:低秩的张量会通过广播转换,复制张量的一个或多个轴,使它与另一个张量形状相同

print(t1+2)
# 输出:
# tensor([[3., 4.],
#         [5., 6.]])
print(t3+t2)
# 输出:
# tensor([[18., 16.],
#         [16., 14.]])

#CNN实例

CNN输入一般长度为4,即有一个秩为4的张量,有 4个坐标轴

分4个步骤:

  1. 准备数据
  2. 建立模型
  3. 训练模型
  4. 分析模型结果

预备知识:面向对象

实参self是指向实例本身的引用,让实例可以访问类中的属性和方法

我们将方法__init__()定义成了包含三个形参:self、name和age。在这个方法的定义中,形参self必不可少,还必须位于其他形参的前面。为何必须在方法定义中包含形参self呢?

因为Python调用这个__init__()方法来创建Dog实例时,将自动传入实参self。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。

我们创建Dog实例时,Python将调用Dog类的方法__init__()。我们将通过实参向Dog()传递名字和年龄;self会自动传递,因此我们不需要传递它。每当我们根据Dog类创建实例时,都只需给最后两个形参(name和age)提供值。

两个变量都有前缀self。以self为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。self.age = age的作用与此类似。

创建类:

class Dog(): 
 """一次模拟小狗的简单尝试""" 
 
 def __init__(self, name, age): 
     """初始化属性name和age""" 
     self.name = name 
     self.age = age 
 
 def sit(self): 
     """模拟小狗被命令时蹲下""" 
     print(self.name.title() + " is now sitting.") 
 
 def roll_over(self): 
     """模拟小狗被命令时打滚""" 
     print(self.name.title() + " rolled over!")

创建实例:

 --snip--
 
my_dog = Dog('willie', 6) 
your_dog = Dog('lucy', 3) 
print("My dog's name is " + my_dog.name.title() + ".") 
print("My dog is " + str(my_dog.age) + " years old.")

my_dog.sit() 
print("\nYour dog's name is " + your_dog.name.title() + ".") 
print("Your dog is " + str(your_dog.age) + " years old.") 
your_dog.sit()

"""
输出:
My dog's name is Willie. 
My dog is 6 years old. 
Willie is now sitting. 
Your dog's name is Lucy. 
Your dog is 3 years old. 
Lucy is now sitting.
"""

创建子类:

super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名。

class Car():
     """一次模拟汽车的简单尝试"""
     def __init__(self, make, model, year):
         self.make = make
         self.model = model
         self.year = year
         self.odometer_reading = 0
 
     def get_descriptive_name(self):
         long_name = str(self.year) + ' ' + self.make + ' ' + self.model
         return long_name.title()

     def read_odometer(self):
         print("This car has " + str(self.odometer_reading) + " miles on it.")

     def update_odometer(self, mileage):
         if mileage >= self.odometer_reading:
         self.odometer_reading = mileage
         else:
         print("You can't roll back an odometer!")

     def increment_odometer(self, miles):
         self.odometer_reading += miles
    
class ElectricCar(Car): 
 """电动汽车的独特之处""" 

     def __init__(self, make, model, year): 
     """初始化父类的属性""" 
    	 super().__init__(make, model, year) 
 
my_tesla = ElectricCar('tesla', 'model s', 2016) 
print(my_tesla.get_descriptive_name())

准备数据

下载数据集(下载到了F:\SCIENCE_AND_MATH\python&机器学习立项\a python学习代码)

import numpy as np
import torch
import torchvision
import torchvision.transforms as transforms
import numpy
import matplotlib.pyplot as plt
import os

os.environ['KMP_DUPLICATE_LIB_OK']='True'

# 下载数据集
train_set = torchvision.datasets.FashionMNIST(
    root='./data/FashionMNIST'
    ,train=True
    ,download=True
    ,transform=transforms.ToTensor()
)

train_loader = torch.utils.data.DataLoader(
    train_set, batch_size = 100
)

batch = next(iter(train_loader))
images, labels = batch

grid = torchvision.utils.make_grid(images,nrow=10)

plt.figure(figsize=(15,15))
plt.imshow(np.transpose(grid,(1,2,0)))

print('labels',labels)
# 输出:labels tensor([9, 0, 0, 3, 0, 2, 7, 2, 5, 5, 0, 9, 5, 5, 7, 9, 1, 0, 6, 4, 3, 1, 4, 8,
#         4, 3, 0, 2, 4, 4, 5, 3, 6, 6, 0, 8, 5, 2, 1, 6, 6, 7, 9, 5, 9, 2, 7, 3,
#         0, 3, 3, 3, 7, 2, 2, 6, 6, 8, 3, 3, 5, 0, 5, 5, 0, 2, 0, 0, 4, 1, 3, 1,
#         6, 3, 1, 4, 4, 6, 1, 9, 1, 3, 5, 7, 9, 7, 1, 7, 9, 9, 9, 3, 2, 9, 3, 6,
#         4, 1, 1, 8])

建立模型

创建一个神经网络:

  1. Extend the nn.Module base class
  2. Define layers as class attributes
  3. Implement the forward() method
import torch.nn as nn

class Network(nn.Module):
    def __init__(self):
        super(Network, self).__init__()
        # 下一层的输入是上一层的输出
        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

    def forward(self, t):
        return t

network = Network()
print(network)
# 输出:Network(
#   (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
#   (conv2): Conv2d(6, 12, kernel_size=(5, 5), stride=(1, 1))
#   (fc1): Linear(in_features=192, out_features=120, bias=True)
#   (fc2): Linear(in_features=120, out_features=60, bias=True)
#   (out): Linear(in_features=60, out_features=10, bias=True)
# )

预测结果

import torch
import torch.nn as nn
import torch.nn.functional as F

import torchvision
import torchvision.transforms as transforms

# 下载数据集
train_set = torchvision.datasets.FashionMNIST(
    root='./data/FashionMNIST'
    ,train=True
    ,download=True
    ,transform=transforms.ToTensor()
)

# 搭建数据集
class Network(nn.Module):
    def __init__(self):
        super(Network, self).__init__()
        # 下一层的输入是上一层的输出
        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

    def forward(self, t):
        # 1. input layer
        t = t

        # 2. hidden conv layer
        t = self.conv1(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size = 2, stride = 2)

        # 3. hidden conv layer
        t = self.conv2(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size = 2, stride = 2)
        return t

        # 4. hidden linear layer
        t = t.reshape(-1, 12*4*4)
        t = self.fc1(t)
        t = F.relu(t)

        # 5.hidden linear layer
        t = self.fc2(t)
        t = F.relu(t)

        # 6.output layer
        t = self.out(t)
        # t = F.softmax(t,dim=1)

# 关闭梯度跟踪功能
torch.set_grad_enabled(False)

network = Network()
sample = next(iter(train_set))
image, label = sample
image.unsqueeze(0).shape
pred = network(image.unsqueeze(0))

# 输出预测值
print(pred)

# 输出:tensor([[[[0.0122, 0.0374, 0.0500, 0.0419],
#           [0.0109, 0.1017, 0.0652, 0.0656],
#           [0.0115, 0.1175, 0.0642, 0.0195],
#           [0.0563, 0.0306, 0.0447, 0.0357]],
#
#          [[0.1596, 0.1338, 0.1603, 0.1478],
#           [0.1149, 0.1281, 0.1538, 0.1335],
#           [0.1243, 0.1449, 0.1948, 0.0838],
#           [0.2023, 0.1793, 0.2004, 0.1619]],
#
#          [[0.0000, 0.0081, 0.0052, 0.0616],
#           [0.0000, 0.0596, 0.0379, 0.1111],
#           [0.0076, 0.0378, 0.0019, 0.0365],
#           [0.0137, 0.0084, 0.0570, 0.0394]],
#
#          [[0.1156, 0.1432, 0.2106, 0.1228],
#           [0.1699, 0.2335, 0.2120, 0.1105],
#           [0.2533, 0.2374, 0.2159, 0.1524],
#           [0.2252, 0.1840, 0.1605, 0.1354]],
#
#          [[0.0391, 0.0791, 0.0161, 0.0003],
#           [0.0366, 0.0812, 0.0221, 0.0225],
#           [0.0000, 0.0000, 0.0340, 0.0000],
#           [0.0584, 0.0000, 0.0000, 0.0000]],
#
#          [[0.0590, 0.1055, 0.0919, 0.1235],
#           [0.0653, 0.0763, 0.0357, 0.0265],
#           [0.0823, 0.1065, 0.0143, 0.0241],
#           [0.0000, 0.0000, 0.0216, 0.0913]],
#
#          [[0.0283, 0.0078, 0.0398, 0.0609],
#           [0.0435, 0.0399, 0.0299, 0.0426],
#           [0.0492, 0.0101, 0.0000, 0.0017],
#           [0.0340, 0.0527, 0.0299, 0.0552]],
#
#          [[0.0000, 0.1243, 0.1174, 0.1353],
#           [0.0455, 0.1736, 0.1031, 0.0733],
#           [0.1540, 0.1384, 0.1157, 0.0918],
#           [0.1010, 0.1247, 0.1199, 0.0988]],
#
#          [[0.0041, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0000, 0.0120, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0314],
#           [0.0000, 0.0000, 0.0271, 0.0316]],
#
#          [[0.0000, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0040, 0.0078, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0023]],
#
#          [[0.0000, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0000],
#           [0.0000, 0.0000, 0.0000, 0.0000]],
#
#          [[0.1166, 0.1134, 0.0761, 0.0594],
#           [0.0650, 0.0755, 0.0413, 0.0509],
#           [0.0327, 0.0827, 0.0767, 0.0920],
#           [0.0784, 0.1132, 0.0870, 0.1121]]]])

循环训练:

import torch
import numpy
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import torchvision
import torchvision.transforms as transforms

torch.set_printoptions(linewidth=120)
torch.set_grad_enabled(True)

def get_num_correct(preds, labers):
    return preds.argmax(dim=1).eq(labers).sum().item()

# 下载数据集
train_set = torchvision.datasets.FashionMNIST(
    root='./data/FashionMNIST'
    ,train=True
    ,download=True
    ,transform=transforms.ToTensor()
)

# 搭建数据集
class Network(nn.Module):
    def __init__(self):
        super(Network, self).__init__()
        # 下一层的输入是上一层的输出
        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

    def forward(self, t):

        t = F.relu(self.conv1(t))
        t = F.max_pool2d(t,kernel_size=2,stride=2)

        t = F.relu(self.conv2(t))
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        t = t.reshape(-1,12*4*4)
        t = F.relu(self.fc1(t))

        t = F.relu(self.fc2(t))

        t = self.out(t)
        return t


network = Network()

# 循环训练:
train_loader = torch.utils.data.DataLoader(train_set, batch_size=100)
optimizer = optim.Adam(network.parameters(), lr=0.01)

total_loss = 0
total_correct = 0

for batch in train_loader:
    images, labels = batch

    preds = network(images)
    loss = F.cross_entropy(preds, labels)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    total_loss += loss.item()
    total_correct += get_num_correct(preds,labels)

print("epoch:",0,"total_correct:",total_correct,"loss:",total_loss)

# 输出:epoch: 0 total_correct: 47265 loss: 333.8074033111334

(重点)简化版模型代码模板

import torch
from torch import nn
from torch.utils.data import DataLoader
import torchvision
import torchvision.transforms as transforms
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt


"1.读取数据"
# 导入torchvision里面的datasets里面的FashionMNNIST
# 导入训练集
training_data = torchvision.datasets.FashionMNIST(
    root='./data/FashionMNIST'
    ,train=True
    ,download=True
    ,transform=transforms.ToTensor()    #数据预处理
)

# 导入torchvision里面的datasets里面的FashionMNNIST
# 导入训练集
test_data = torchvision.datasets.FashionMNIST(
    root='./data/FashionMNIST'
    ,train=True
    ,download=True
    ,transform=transforms.ToTensor()
)

# 一次性传入64张图片
batch_size = 64


"2.给训练集测试集分别创建一个数据集加载器"
train_dataloader = DataLoader(training_data, batch_size = batch_size)
test_dataloader = DataLoader(test_data, batch_size = batch_size)

# 测试test_dataloader的读取情况
for X, y in test_dataloader:
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

# 使用Cuda之前都需要检测一下cuda是否可用,没检测就默认没有
print(torch.cuda.is_available())

# 如果显卡可用,则使用显卡训练
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))


"3.定义网络模型"
class NerualNetwork(nn.Module):
    def __init__(self):
        super(NerualNetwork, self).__init__()
        # 将数据展为1维
        self.flatten = nn.Flatten()
        # 定义linear_relu_stack,由众多的层构成
        self.linear_relu_stack = nn.Sequential(
            # 全连接层
            nn.Linear(28*28, 512),
            # ReLU()激活函数
            nn.ReLU(),
            # 全链接层
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    # x为传入数据
    def forward(self, x):
        # x先经过展平变成1维
        x = self.flatten(x)
        # 随后x经过linear_relu_stack
        logits = self.linear_relu_stack(x)
        return logits

# 调用刚定义的模型,将模型转为GPU(如果可用)
# 使用Cuda之前都需要检测一下cuda是否可用,没检测就默认没有
print(torch.cuda.is_available())
model = NerualNetwork().to(device)
print(model)

# 定义损失函数,计算相差多少,使用交叉熵损失
loss_fn = nn.CrossEntropyLoss()

# 定义优化器,用来训练时调参优化,使用随机梯度下降
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)


"4.训练模型"
# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    # 从数据加载中读取batch(一次读取多少张,即读取的批数),X(图片数据), y(图片真实标签)
    for batch, (X, y) in enumerate(dataloader):
        # 将数据存到显卡
        X, y = X.to(device), y.to(device)

        # 得到预测的结果pred
        pred = model(X)

        # 计算预测的误差
        loss= loss_fn(pred, y)

        # 反向传播,更新模型参数
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # 每训练100次,输出一次当前信息
        if batch%100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss:{loss:>7f}  [{current:>5d}/{size:>5d}]")


"5.测试模型"
def test(dataloder, model):
    size = len(dataloder.dataset)
    # 将模式转为验证模式
    model.eval()
    # 初始化test_loss 和 correct,用来统计每次的误差
    test_loss, correct = 0, 0
    # 测试时模型参数不需要更新,所以no_grad()
    with torch.no_grad():
        # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
        for X,y in dataloder:
            # 将数据存到显卡
            X, y = X.to(device), y.to(device)
            # 将图片传入到模型中,得到预测的结果pred
            pred = model(X)
            # 计算预测值[red和真实值y之间的差距
            test_loss += loss_fn(pred, y).item()
            # 统计预测正确的个数
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= size
    correct /= size
    print(f"Test Error: \n Accracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")


# 一共训练5次
epochs = 5
for t in range(epochs):
    print(f"Epoch  {t+1}\n----------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model)
print("Done!")


# 保存训练好的模型
torch.save(model.state_dict(), "model.pth")
print("Saved Pytooch Model State to model.pth")


# 读取训练好的模型,加载训练好的参数
model = NerualNetwork()
model.load_state_dict(torch.load("model.pth"))


# 定义所有类别
classes = [
    "T-shirt/top(T恤)" ,
    "Trouser(裤子)" ,
    "Pullover(套衫)" ,
    "Dress(裙子)" ,
    "Coat(外套)" ,
    "Sandal(凉鞋)" ,
    "Shirt(汗衫)" ,
    "Sneaker(运动鞋)" ,
    "Bag(包)" ,
    "Ankle boot(踝靴)",
]


# 模型进入验证阶段
model.eval()

x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual:"{actual}"')

输出:

Shape of X [N, C, H, W]:  torch.Size([64, 1, 28, 28])
Shape of y:  torch.Size([64]) torch.int64
False
Using cpu device
False
NerualNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
    (5): ReLU()
  )
)
Epoch  1
----------------------
loss:2.299116  [    0/60000]
loss:2.295744  [ 6400/60000]
loss:2.285498  [12800/60000]
loss:2.287443  [19200/60000]
loss:2.266481  [25600/60000]
loss:2.254971  [32000/60000]
loss:2.263029  [38400/60000]
loss:2.245441  [44800/60000]
loss:2.246299  [51200/60000]
loss:2.222791  [57600/60000]
Test Error: 
 Accracy: 42.6%, Avg loss: 0.034767 

Epoch  2
----------------------
loss:2.231426  [    0/60000]
loss:2.234812  [ 6400/60000]
loss:2.205376  [12800/60000]
loss:2.218831  [19200/60000]
loss:2.155249  [25600/60000]
loss:2.147582  [32000/60000]
loss:2.170043  [38400/60000]
loss:2.128498  [44800/60000]
loss:2.155777  [51200/60000]
loss:2.095586  [57600/60000]
Test Error: 
 Accracy: 44.3%, Avg loss: 0.032670 

Epoch  3
----------------------
loss:2.129048  [    0/60000]
loss:2.119061  [ 6400/60000]
loss:2.058100  [12800/60000]
loss:2.088668  [19200/60000]
loss:1.939575  [25600/60000]
loss:1.963350  [32000/60000]
loss:2.007608  [38400/60000]
loss:1.931761  [44800/60000]
loss:2.017244  [51200/60000]
loss:1.897059  [57600/60000]
Test Error: 
 Accracy: 47.5%, Avg loss: 0.029400 

Epoch  4
----------------------
loss:1.975267  [    0/60000]
loss:1.941367  [ 6400/60000]
loss:1.849616  [12800/60000]
loss:1.908329  [19200/60000]
loss:1.659535  [25600/60000]
loss:1.753111  [32000/60000]
loss:1.826492  [38400/60000]
loss:1.730239  [44800/60000]
loss:1.884581  [51200/60000]
loss:1.723741  [57600/60000]
Test Error: 
 Accracy: 52.8%, Avg loss: 0.026347 

Epoch  5
----------------------
loss:1.837500  [    0/60000]
loss:1.783477  [ 6400/60000]
loss:1.677731  [12800/60000]
loss:1.761615  [19200/60000]
loss:1.448221  [25600/60000]
loss:1.611489  [32000/60000]
loss:1.702321  [38400/60000]
loss:1.598439  [44800/60000]
loss:1.782659  [51200/60000]
loss:1.620998  [57600/60000]
Test Error: 
 Accracy: 55.1%, Avg loss: 0.024255 

Done!
Saved Pytooch Model State to model.pth
Predicted: "Ankle boot(踝靴)", Actual:"Ankle boot(踝靴)"
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐