pytorch_小土堆听课笔记

官网教程:https://pytorch.org/docs/stable/index.html

Pytorch安装

https://zhuanlan.zhihu.com/p/629463313
[[01-study note/Python/安装Anaconda]]
安装目录:D:\ProgramData\anaconda3
安装Pytorch
https://pytorch.org/
安装cuda
cuda安装目录:D:CUDA_install
在这里插入图片描述

pytorch部署到jupyter notebook当中使用
https://blog.csdn.net/weixin_45775136/article/details/125800300

切换到Pytorch环境
在这里插入图片描述

检查Pytorch是否可用

import torch

torch

  • torch

    • utils

      • data

        • class Dataset():
          “”"
          数据集本身,并获取label
          “”

        • class Dataloader():
          “”"
          加载数据
          “”"

      • tensorboard

        • class SummaryWriter(存放日志的文件夹):
          “”"
          创建日志
          “”"
  • torchvision

    • transforms

张量(一阶、二阶)

一阶张量=一维数组(向量vector)
arr = [1, 2, 3, 4, 5]

二阶张量=二维数组(矩阵matrix)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

三阶张量=三维数组
arr = np.array([[[1, 2], [3, 4], [5, 6], [7, 8]]])

python中的库、包、class之间的关系是什么

模块就是一个.py文件

    • 模块

      • 函数

      • 类呢

创建模块

模块的调用路径

Python的模块搜索顺序:当前目录——>shell变量下的pythonpath目录——>默认路径

导入模块的过程,就是将模块文件下的函数列举在了本文件中,因此函数可以直接调用。

主程序调用

# 主程序
if __name__=='__main__':
	pass
	该代码块的内容如果作为主函数,则执行
	如果这个py文件被调用,则该代码块不会被调用执行

导入模块

import 模块名

import 模块名1,模块名2

from 模块名 import 功能1,功能2

from 模块名 import *(导入该模块的所有功能)

别名:as 别名

创建包

包 = 模块s+__init__.py文件+__pycache__(这个文件是自动生成的)
在这里插入图片描述

这个init文件的作用:__init__.py 文件的作用是将一个文件夹标记为 Python 包,使得该文件夹下的模块可以被导入和使用。同时,它还可以包含包的初始化代码,例如导入其他模块或者定义变量等。

导入包的方法和导入模块的方法一致

当调入的模块不在同一文件夹该如何处理?

创建子模块

先创建子包,然后在子包里面添加模块

包 = 模块s+__init__.py文件+__pycache__(这个文件是自动生成的)

模块组成包(库)

torch的层级关系

nn
	"""
	模型
	"""
	
torch.utils.data
	class Dataset():
		"""
		数据集本身,并获取label
		"""
		pass
	class Dataloader():
		"""
		加载数据
		"""
		
torch.utils.tensorboard
	class SummaryWriter(存放日志的文件夹):
		"""
		创建日志
		"""
from torchvision import transforms	

Dataset与Dataloader

以创建图片数据集为例,创建dataset的核心在于将图片文件夹转换为list类型,每个图片都是列表中的一个元素,因为转换为list类型,方便取值操作。同样的label文件夹也是如此

import os
import torch
from PIL import Image
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torchvision import transforms


class My_data(Dataset):
    """
    需要干以下几件事
    init:核心在与拿到image_list,label_list
    getitem:核心在与拿到每个图片对应的label,需要创建索引
    len:核心在于计算image_list的长度
    """
    def __init__(self,root_dir,img_dir,label_dir):
        self.root_dir=root_dir
        self.image_path=os.path.join(root_dir,img_dir)
        self.label_path=os.path.join(root_dir,label_dir)

        self.image_list=os.listdir(self.image_path)
        self.label_list=os.listdir(self.label_path)

        # 对image list与label list排序,才能对应
        self.image_list.sort()
        self.label_list.sort()

    def __getitem__(self, index):
        # 创建索引
        img_name=self.image_list[index]
        label_name=self.label_list[index]

        # 打开图片和她对应的label
        img_name_path=os.path.join(self.image_path,img_name)
        label_name_path=os.path.join(self.label_path,label_name)
        with open(img_name_path,'r') as f:
            label=f.readline()
        
        img = self.transform(img)
        sample = {'img': img, 'label': label}
        return sample

    def __len__(self):
        return len(self.image_list)
    
if __name__=='__main__':
    transform = transforms.Compose([transforms.Resize((256, 256)), transforms.ToTensor()]) # compose 将这两个操作变成一个,totensor,变成tensor张量的形式
    ant_datasets=My_data('C:\\Users\\x1352\\Desktop\\tudui_data\\练手数据集\\train','ants_image','ants_label')
    my_dataloader=Dataloader(ant_datasets,batch_size=1,num_workers=2)
    # ant_datasets是数据集,batch_size是批次(也就是说将一个数据集分为几批),num_workers:用于并行加载数据的工作线程数。设置为0表示在主进程中加载数据,设置为大于0的值表示在后台使用多个线程加载数据。默认值为0。多线程可以并行执行任务,提高速度
    for i,j in enumerate(my_dataloader):
        print(i, j['img'].shape)

Tensorboard

add_scalar:

from torch.utils.tensorboard import SummaryWriter

my_writer=SummaryWriter('my_log3')

for a in range(100):
    my_writer.add_scalar('title',6*a+5,a)
my_writer.close()

运行my_logk日志文件夹:tensorboard --logdir=my_log --port=6007

add_image:

import numpy as np
import torch
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
my_writer=SummaryWriter(log_dir='my_logs_dir')

imag_PIL=Image.open('20230815170426.jpg')
# img_PIL.show()

# 将img_PIL转换为tensor格式
imag_tensor=torch.tensor(np.array(imag_PIL))
print(imag_tensor.shape)

my_writer.add_image('tagtitle4',imag_tensor,global_step=4,dataformats='HWC')
my_writer.close()

torchvision

transform

from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
import torch
import numpy as np

# 创建一个writer实例
writer=SummaryWriter('loggg')

img=Image.open('20230815170426.jpg')
print(type(img))

# 创建to tensor工具(实例化)
tensor_trans=transforms.ToTensor()
img_tensor1=tensor_trans(img)
writer.add_image('source_img',img_tensor1)
# print(type(img_tensor1))
# print(img_tensor1)

# 其实也可以直接torch.tensor(np array)也能实现同样的效果
# img_tensor2=torch.tensor(np.array(img))
# print(type(img_tensor2))

norm_tool=transforms.Normalize([0.5,0.6,0.7],[0.8,0.8,0.8])
img_tensor1_norm=norm_tool.forward(img_tensor1)
writer.add_image('source_img_norm',img_tensor1_norm)
# print(img_tensor1_norm)
writer.close()

下载CIFAR10数据集

# 下载CIFAR10数据集
import torchvision.datasets as datasets

train_data=datasets.CIFAR10('CIFAR_train',train=True,download=True)
test_data=datasets.CIFAR10('CIFAR_test',train=False,download=True)

# print(train_data[0])
# print(train_data.classes)
img,target=train_data[0]
print(target)
print(train_data.classes[target])
print(img)
img.show()

model

import torch
from torch import nn

class my_Model(nn.Module):
    def __init__(self,name):
        super().__init__()
        self.name=name
        
    def forward(self,input):
        output=3*input+2
        return output

model1=my_Model('Tom')
print(model1.name)

result=model1.forward(1)
print(f'前向传播的结果是:{result}')

forward函数的调用

直接:对象(传入forward函数的参数)==对象.forward(传入forward函数的参数)

为什么x = self.conv1(x)可以省略.forward?

  • 在Python编程语言中,x = self.conv1(x)可以省略.forward()方法,因为在定义类时,如果继承了torch.nn.Module,那么该类就会自动拥有__call__()方法。__call__()方法会将self.conv1(x)的计算过程封装起来,当调用该类的实例时,就会执行__call__()方法。因此,x = self.conv1(x)相当于调用了__call__()方法,从而实现了前向传播的过程。
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)  

    def forward(self, x):
        x = self.conv1(x)  
        return x
这段代码是不是和以下代码等效?x传递给self.conv1,我们实际上是在调用该层的前向传播方法,以便对输入数据进行处理并生成输出结果。这个输出结果将作为下一层或最终的预测结果使用。
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)  

    def forward(self, x):
        x = self.conv1.forward(x)  
        return x

卷积操作实例

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from PIL import Image


# 定义模型
class my_model(nn.Module):
    def __init__(self):
        super(my_model, self).__init__()
        # 卷积层实例,输入channel=3,输出channel=6,卷积核:3x3,step=1,padding=0
        # 这段代码中,卷积核的9个元素没有明确定义。在PyTorch中,卷积层的权重是通过nn.Parameter类自动初始化的。默认情况下,这些权重是随机初始化的,范围在-1到1之间。如果你想自定义权重,可以在__init__方法中使用nn.init模块进行初始化。所以这段代码的img3的张量输出结果是不一致的
        self.conv_1=Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=1)

    def forward(self,input):
        x=self.conv_1(input)
        return x

my_CNN=my_model()

# 准备数据
img=Image.open('6aa3baf260a16ee94c11dab0d498a0c0.jpg')
trans_tensor=torchvision.transforms.ToTensor()
img2=trans_tensor(img)
print(img2.shape)  # torch.Size([3, 1280, 2048])

# 传入模型
img3=my_CNN(img2)

# 输出结果
print(img3.shape)  # torch.Size([6, 1280, 2048])
print(img3)

图片相关操作

from PIL import Image
img=Image.open('6aa3baf260a16ee94c11dab0d498a0c0.jpg')
img.show()
也可以将图片转换为tensor方便后续操作

卷积函数

F是面向过程的写法,class是面向过程的写法

需要自定义卷积核

F.conv2d(input,weight,bias…)
在这里插入图片描述

import torch
import torch.nn.functional as F
from PIL import Image
from torchvision import transforms
img = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])
my_kernel=torch.tensor([[1,2,1],[0,1,0],[2,1,0]])

img=torch.reshape(img,(1,1,5,5))
my_kernel=torch.reshape(my_kernel,(1,1,3,3))

output=F.conv2d(img,my_kernel)
print(output)

关于class的传参问题

class lei(a):
	def __init__(self,c,d):
        super(lei,self).__init__()
    def forward(e,f)
    	pass

a不是参数,而是继承的类
c,d的参数,会在创建对象的时候,调用这个方法.同时也调用了父类a的init方法,所以父类a 的init方法中参数也要传递
dog=lei(c=1,d=2)

池化层

import torch
from torch import nn

class my_model(nn.Module):
    def __init__(self):
        super(my_model, self).__init__()
        self.pooling1=nn.MaxPool2d(kernel_size=3,stride=1,padding=0)
    
    def forward(self,input):
        # 或者output=self.pooling1.forward(input)
        output=self.pooling1(input)
        return output

polpol=my_model()
x=torch.tensor([[[10, 12, 12],
                [18, 16, 16],
                [13,  9,  3]]]).float()

result=polpol.forward(input=x)
print(result)

线性层

import torch
from torch import nn

class my_model(nn.Module):
    def __init__(self):
        super().__init__()
        self.def_linar=nn.Linear(20,30)
    def forward(self,x):
        out=self.def_linar(x)
        return out
    
x = torch.randn(128, 20)
model1=my_model()
my_out=model1.forward(x=x)
print(my_out.shape)

非线性层(激活函数层)

激活函数理论汇总

Multi-Head-Attention层

初始化

 def __init__(self):
        super().__init__()
        再iniit中用class创造工具,使得对象创建后可以直接调用函数,而无需重新创造工具
        但事实上,真正的对象其实用到比较少,往往再forward里面的对象直接用这个def工具的情况较多

Sequential

在这里插入图片描述

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

优化器

优化器的作用是根据梯度信息与损失函数,来更新参数
在这里插入图片描述

class(参数1,参数2)

参数指的是init传递的参数

__init__表示在创建对象时候传递的参数

损失函数

L1Loss

import torch
import torch.nn as nn

# 创建L1Loss实例
l1_loss_mean = nn.L1Loss(reduction='mean')
l1_loss_sum = nn.L1Loss(reduction='sum')
l1_loss_none = nn.L1Loss(reduction='none')

# 准备输入数据
input = torch.tensor([1.0, 2.0, 3.0])
target = torch.tensor([2.0, 2.0, 2.0])

# 计算损失值
loss_mean = l1_loss_mean(input, target)
loss_sum = l1_loss_sum(input, target)
loss_none = l1_loss_none(input, target)

print("Mean reduction:", loss_mean)
print("Sum reduction:", loss_sum)
print("None reduction:", loss_none)
"""
Mean reduction: tensor(0.6667)
Sum reduction: tensor(2.)
None reduction: tensor([1., 0., 1.])
"""

MSELoss

import torch
import torch.nn as nn

# 创建L1Loss实例
l1_loss_mean = nn.L1Loss(reduction='mean')
l1_loss_sum = nn.L1Loss(reduction='sum')
l1_loss_none = nn.L1Loss(reduction='none')

# 准备输入数据
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([2.0, 2.0, 2.0])

# 计算损失值
my_loss=nn.MSELoss(reduction='mean')
output=my_loss(input=x,target=y)

print(output)

CrossEntropyLoss

import torch
import torch.nn as nn

# 创建L1Loss实例
l1_loss_mean = nn.L1Loss(reduction='mean')
l1_loss_sum = nn.L1Loss(reduction='sum')
l1_loss_none = nn.L1Loss(reduction='none')

# 准备输入数据
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([2.0, 2.0, 2.0])

# 计算损失值
my_loss=nn.CrossEntropyLoss(reduction='mean')
output=my_loss(input=x,target=y)

print(output)

loss与优化器训练

在这里插入图片描述

现有模型的使用和修改

模型的保存

保存方式一

torch.save(vgg16,"vgg16_method1.pth")

vgg16是模型,vgg16_method1.pth是保存的模型文件(一般常见的是以.pth结尾的)

模型加载一

model=torch.load("vgg16_method1.pth")

保存方式二

torch.save(vgg16.state_dict(),"vgg16_method2.pth")

这种是只保存了模型参数,而且是官方推荐的

模型加载二

model=torch.load("vgg16_method2.pth") # 只加载了参数

vgg16=torchvision.models.vgg16(pretrained=False)  # 载入了没有训练参数的vgg16模型
vgg16_load_state_dict(torch.load("vgg16"))
import torch
import torchvision
from torch import nn

# 陷阱
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)

    def forward(self, x):
        x = self.conv1(x)
        return x


tudui = Tudui()
# torch.save(tudui, "tudui_method1.pth")  # 10kb模型
torch.save(tudui.state_dict(), "tudui_method2.pth")  # 9kb模型,只保存参数,字典类型

# 大小可能与导包有关系,少import,或者多使用from import,使用啥,导入啥

不管使用的保存模型的方式是方式一还是方式二,我们通常都会将模型架构class(不需要实例化)单独放在一个文件中,然后在训练时,from import

相对路径

根目录是项目根目录,而不是当前文件所在的目录

模型的创建、保存、加载实例

model.py

from torch import nn

class my_model(nn.Module):
    def __init__(self):
        super(my_model, self).__init__()

    def forward(self, x):
        y = 2*x+3
        return y

duixiang=my_model()

my_save.py

import torch
from torch import nn
from model import *

# 保存模型方式一,不用实例化,直接保存class
torch.save(my_model,"./save_and_load_example/my_model_v1.pth")

# 保存模型方式二,必须实例化
torch.save(duixiang.state_dict(),"./save_and_load_example/my_model_v2.pth")

my_load.py

import torch
from model import *

# 加载方式一保存
moxing1=torch.load('my_model_v1.pth')

# 加载方式二保存
# moxing2=torch.load('C:\\Users\\x1352\\Desktop\\tudui\\save_and_load_example\\my_model_v2.pth')  # 加载参数(好像这一行也可以不要)
duixiang.load_state_dict(torch.load('C:\\Users\\x1352\\Desktop\\tudui\\save_and_load_example\\my_model_v2.pth'))

在实际的使用中,是用到别人的class模型还是pth模型

测试时间

start_time=time.time()

代码块

end_time=time.time()

print(end_time-start_time)

GPU训练

  1. 实例化模型、损失函数
if torch.cuda.is_available():
    duixiang=duixiang.cuda()
    loss_func=loss_func.cuda()
  1. 数据(输入、标注):两个for循环下的(训练+测试,imgs,label)
for data in train_loader:
        imgs,label=data
        if torch.cuda.is_available():
            imgs=imgs.cuda()
            label=label.cuda()

for data in test_loader:
            imgs,label=data
            if torch.cuda.is_available():
                imgs=imgs.cuda()
                label=label.cuda()

完整的项目流程

model.py

import torch
from torch import nn

class my_model(nn.Module):
    def __init__(self):
        super(my_model, self).__init__()
        self.model=nn.Sequential(
            nn.Conv2d(3,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64*4*4,64),
            nn.Linear(64,10)
        )

    def forward(self,x):
        x=self.model(x)
        return x

train-test.py

import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
import time

start_time=time.time()
# ==================01、准备数据====================================
train_data=torchvision.datasets.CIFAR10(root="my_dataset",
                                        train=True,
                                        transform=torchvision.transforms.ToTensor(),
                                        download=True)
test_data=torchvision.datasets.CIFAR10(root="my_dataset",
                                        train=False,
                                        transform=torchvision.transforms.ToTensor(),
                                        download=True)

print(f'训练集的长度:{len(train_data)},测试集的长度:{len(test_data)}')  # 训练集的长度50000,测试集的长度10000

train_loader=DataLoader(dataset=train_data,batch_size=64)   #一共有64批,每批长度len=782;782x64=50048  
test_loader=DataLoader(dataset=test_data,batch_size=64)  #一共有64批,每批长度len=157;157x64=10048 

# ===============02、创建实例模型,损失函数,优化器==================================
duixiang=my_model()

loss_func=nn.CrossEntropyLoss()
# if torch.cuda.is_available():
#     duixiang=duixiang.cuda()
#     loss_func=loss_func.cuda()

optimizer=torch.optim.SGD(params=duixiang.parameters(),lr=0.01)  # 优化器传入实例模型的所有参数,学习率

# ===============03、设置训练初始参数================================================
epoch=10  # 训练的轮数
sum_train_step=0 # 训练集训练的总次数
sum_test_step=0 # 测试集训练的总次数

# ===============04、开始训练========================================================
for i in range(epoch):  # 训练步骤包括训练和测试,分别用训练集和测试集

    duixiang.train()  # 训练========================================================
    for data in train_loader:
        imgs,label=data
        # if torch.cuda.is_available():
        #     imgs=imgs.cuda()
        #     label=label.cuda()
        output=duixiang(imgs)
        loss=loss_func(output,label)

        # 优化器设置
        optimizer.zero_grad()  # 梯度清零
        loss.backward()   # 反向传播
        optimizer.step()  # 更新参数

        sum_train_step=sum_train_step+1 # 记录训练次数
        if sum_train_step % 100 ==0:
            print(f'已训练{sum_train_step}次')

    print(f'这是第{i+1}轮训练,此时训练集loss值为:{loss}')

    duixiang.eval() # 测试(每执行一轮测试一次)=============================================
    with torch.no_grad():
        sum_test_loss=0
        for data in test_loader:
            imgs,label=data
            # if torch.cuda.is_available():
            #     imgs=imgs.cuda()
            #     label=label.cuda()
            output=duixiang(imgs)
            loss=loss_func(output,label)

            sum_test_step=sum_test_step+1
            if sum_test_step % 10 ==0:
                print(f'已测试{sum_test_step}次,此时的loss值为:{loss}')

            sum_test_loss=sum_test_loss+loss  # 计算这一轮次的总loss值
    print(f'这是第{i+1}轮测试,此时测试集loss值之和为:{sum_test_loss}')

# ========================保存模型==================================================
torch.save(duixiang,"moxing")

end_time=time.time()
print(end_time-start_time)

声明

本笔记是根据b站up“小土堆”的pytorch教程所作,笔记是根据自身的理解做的,并没有完全照搬教程内容,因此可能会与原教程有所出入。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值