深度学习入门(自用)

环境搭建记录

# 已经配置conda24 python3.10 创建新的深度学习环境 conda create -n d2l-zh python=3.10 pip -y conda activate d2l-zh # 注意后面都是在这环境执行的 python --version which python gcc --version nvidia-smi free -h df -h 安装jupyter、d2l和pytorch python -m pip install jupyter python -m pip install d2l -i https://pypi.tuna.tsinghua.edu.cn/simple 检查显卡驱动版本并安装torch nvidia-smi python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# ubuntu20.04 sudo nvidia-modprobe -u -c=0 nvidia-smi

Jupyter

shift 回车 = 运行

conda activate d2l-zh jupyter notebook

鱼书基础知识

训练集 → 被切成很多个 batch
训练 1 个 batch → 叫 1 次 iteration
iteration 总次数 → 叫 iters_num
完整看完一遍训练集 → 叫 1 个 epoch

Affine层
激活函数

sigmoid

ReLU

输出层

softmax

...懒得写了看书吧

小土堆教程

一、基础教程

1. pytorch加载数据

from torch.utils.data import Dataset from PIL import Image import os # Python 标准库,用于操作系统相关功能 # 自定义数据集类需要重写 getitem、len 和 add 方法 class MyData(Dataset): # 继承Dataset类,语法: class 子类名(父类名) # 自定义数据集类示例 # 这里的数据标签格式是文件夹名字为标签,文件夹里面放待识别的图,图的名字差不多随便命名的 def __init__(self, root_dir, label_dir): """初始化方法""" self.root_dir = root_dir self.label_dir = label_dir # 为了让linux和windows都可以用(linux是/,而windows是//) self.path = os.path.join(self.root_dir, self.label_dir) self.img_path = os.listdir(self.path) # 列出某个文件夹下面有哪些文件和文件夹,并且是列表格式的 def __getitem__(self, idx): # idx 为索引参数 """根据索引获取数据项""" img_name = self.img_path[idx] img_item_path = os.path.join(self.root_dir, self.label_dir, img_name) img = Image.open(img_item_path) label = self.label_dir return img, label def __len__(self): """返回数据集大小""" return len(self.img_path) root_dir = "dataset/train" ants_label_dir = "ants" ants_dataset = MyData(root_dir, ants_label_dir) img, label = ants_dataset[0] # 这个类里的方法比较特殊,是双下划线的 len(ants_dataset) # 用同样的方法可以创建bees_dataset,还可以合并两个 train_dataset = ants_dataset + bees_dataset

2. TensorBoard

使用时要在终端输入:

tensorboard --logdir=logs
from torch.utils.tensorboard import SummaryWriter # SummaryWriter是一个类 import numpy as np from PIL import Image # SummaryWriter是直接向log_dir文件夹写事件文件的类,这个事件文件可以被tensoroboard解析 writer = SummaryWriter("logs") # 需要输入的核心参数就是文件夹名称,这里就是“log” # 常用的两个方法:writer.add_image()和writer.add_scalar() image_path = "" img_PIL = Image.open(image_path) img_array = np.array(img_PIL) # 1.writer.add_image() # def add_image( # self, tag, img_tensor, # global_step=None, walltime=None, dataformats="CHW" # ): # tag (string): Data identifier # img_tensor (torch.Tensor, numpy.array, or string/blobname): Image data # global_step (int): Global step value to record # walltime (float): Optional override default walltime (time.time()) # seconds after epoch of event # dataformats (string): Image data format specification of the form # CHW, HWC, HW, WH, etc. writer.add_image("test", img_array, 1, dataformats="HWC") #高 宽 通道数 # 这里可以通过1,2,3滑动那个滑杆看到不同阶段的图片,1表示第一阶段 # 2.writer.add_scalar() # def add_scalar( # self, # tag, 就是图标的标题 # scalar_value, 需要保存的数值(y) # global_step=None, 训练的次数(x) # walltime=None, # new_style=False, # double_precision=False, # ): for i in range(100): writer.add_scalar("y=x", i, i) writer.close() writer.close()

3. Transforms

ToTensor:
from torchvision import transforms from PIL import Image import cv2 from torch.utils.tensorboard import SummaryWriter # 最常用的有ToTesor, Compose, Normalize, To PILImage # tensor 数据类型 # class ToTensor: # """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. This transform does not support torchscript. # Converts a PIL Image or numpy.ndarray (H x W x C) in the range # [0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0] # if the PIL Image belongs to one of the modes (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1) # or if the numpy.ndarray has dtype = np.uint8 # In the other cases, tensors are returned without scaling. # .. note:: # Because the input image is scaled to [0.0, 1.0], this transformation should not be used when # transforming target image masks. See the `references`_ for implementing the transforms for image masks. # .. _references: https://github.com/pytorch/vision/tree/main/references/segmentation # """ # def __init__(self) -> None: # _log_api_usage_once(self) # def __call__(self, pic): # """ # Args: # pic (PIL Image or numpy.ndarray): Image to be converted to tensor. # Returns: # Tensor: Converted image. # """ # return F.to_tensor(pic) # def __repr__(self) -> str: # return f"{self.__class__.__name__}()" img_path = "/home/ljy/Projects/DDPM/hymenoptera_data/train/ants/0013035.jpg" img = Image.open(img_path) # 这就是PIL数据类型 <class 'PIL.JpegImagePlugin.JpegImageFile'> cv_img = cv2.imread(img_path) # <class 'numpy.ndarray'> writer = SummaryWriter("logs") tensor_trans = transforms.ToTensor() # 可以实现对Image和Opencv读取的图片进行格式转换 tensor_img = tensor_trans(img) # <class 'torch.Tensor'> writer.add_image("Tensor_img", tensor_img) # torch.tensor本来就是[3, H, W]格式了,所以没有写 writer.close()
包含totensor在内的常用transfrom功能:
# 补充一点__call__/class知识 # class Person: # def __call__(self, name): # print("__call__" + name) # def call(self, name): # print("call" + name) # Li = Person() # 因为没有__init__所以不能接受参数,不可以写Li = Person("LiSi") # Li("LiSi") # 用__call__调用可以直接括号内调用,而不用.方法 # Li.call("Lisi") # class Person_with_init: # def __init__(self, name): # print("Init" + name) # def __call__(self, name): # print("__call__" + name) # Wa = Person_with_init("XiaoWang") # Wa("XiaoWang") # # 输出结果 # # __call__LiSi # # callLisi # # InitXiaoWang # # __call__XiaoWang from torch.utils.tensorboard import SummaryWriter from PIL import Image from torchvision import transforms writer = SummaryWriter("logs") img_path = "/home/ljy/Projects/DDPM/hymenoptera_data/train/ants/0013035.jpg" img_PIL = Image.open(img_path) # to tensor to_tensor = transforms.ToTensor() img_tensor = to_tensor(img_PIL) writer.add_image("norm_test", img_tensor, 1) # normalize trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) img_norm = trans_norm(img_tensor) writer.add_image("norm_test", img_norm, 2) # resize print(img_PIL.size) trans_resize = transforms.Resize((512, 512)) img_resize = trans_resize(img_PIL) # 也可以用img_tensor,但是输出的img_resize还是PIL格式的 print(img_resize.size) img_resize_tensor = to_tensor(img_resize) # add_image需要是np.array或者tensor格式的 writer.add_image("norm_test", img_resize_tensor, 3) # compose - resize 即利用compose的resieze的实现方法 trans_resize_2 = transforms.Resize(256) # 整体按比例缩放,使短边为256 trans_compose = transforms.Compose([trans_resize_2, to_tensor]) # 需要给一个列表,里面都是transform格式,而且要注意前后格式是否匹配 # 常见顺序 # transforms.Compose([ # transforms.Resize(...), # 可以处理 PIL # transforms.ToTensor(), # PIL -> Tensor # transforms.Normalize(...) # 只能处理 Tensor # ]) img_compose_resize = trans_compose(img_PIL) writer.add_image("norm_test", img_compose_resize, 4) # print(img_PIL.size) (768, 512) # print(img_compose_resize.shape) torch.Size([3, 256, 384]) writer.close()

4. DataLoader

# 取出64个,打乱,最后不整的丢掉 test_data = DataLoader(dataset=test_set, batch_size=64, shuffle=True, num_workers=0, drop_last=True)

二、神经网络搭建

1. nn.Model

from torch import nn import torch class MYNN(nn.Module): def __init__(self): super().__init__() def forward(self, input): output = input + 1 return output jjj = MYNN() x = torch.tensor(1.0) output = jjj(x) print(output)

2. conv2d.function

import torch import torch.nn.functional as F input = torch.tensor([ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]) kernal = torch.tensor([[1, 2, 1], [0, 1, 0], [2, 1, 0]]) print(input.shape) # conv2d 要求格式是 [batch_size, channel, height, width] input = torch.reshape(input, (1, 1, 5, 5)) kernal = torch.reshape(kernal, (1, 1, 3, 3)) print(input.shape) print(kernal.shape) output = F.conv2d(input, kernal, stride=2) #padding表示在外围扩充 print(output)
# 输出结果 torch.Size([5, 5]) torch.Size([1, 1, 5, 5]) torch.Size([1, 1, 3, 3]) tensor([[[[ 49, 57, 65], [ 89, 97, 105], [129, 137, 145]]]])

3. conv2d

卷积核数量是与out_channel有关的

import torch import torchvision from torch.utils.data import DataLoader from torch import nn from torch.nn import Conv2d from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) # class Conv2d( # in_channels: int, # out_channels: int, # kernel_size: _size_2_t, # stride: _size_2_t = 1, # padding: _size_2_t | str = 0, # dilation: _size_2_t = 1, # groups: int = 1, # bias: bool = True, # padding_mode: str = 'zeros', # device: Any | None = None, # dtype: Any | None = None # ) class MyC2(nn.Module): def __init__(self): super(MyC2, self).__init__() self.conv1 = Conv2d(3, 6, 3, stride=1, padding=0) def forward(self, x): x = self.conv1(x) return x c1 = MyC2() writer = SummaryWriter("logs") step = 0 for data in dataloader: imgs, targets = data # imgs:[64, 3, 32, 32] 3个channel是rgb output = c1(imgs) # output:[64, 6, 30, 30] 没有padding所以像素值变小了 # print(output.shape) # tensor的shape :[batch_size, channel, height, width] output = torch.reshape(output, (-1, 3, 30, 30)) # 6个channel会不知道怎么显示,-1表示适应后面的变化 writer.add_images("inputs", imgs, step) # 注意是imgs [NCHW] writer.add_images("onputs", output, step) step += 1 writer.close()

4. maxpool

卷积是卷积核求和,池化是池化核选最大。

池化:每个 channel 独立使用同一种池化规则;卷积:每个输出卷积核生成一个输出 channel

import torch import torchvision from torch import nn from torch.nn import MaxPool2d from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10("./data", train=False, download=True, transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=64) # 一种方式 # input = 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]], dtype=torch.float32) # kernal = torch.tensor([[1, 2, 1], # [0, 1, 0], # [2, 1, 0]]) # input = torch.reshape(input, (-1, 1, 5, 5)) # print(input.shape) class Maxp2(nn.Module): def __init__(self): super().__init__() self.mp = MaxPool2d(kernel_size=3, ceil_mode=True) def forward(self, x): return self.mp(x) maxpool = Maxp2() writer = SummaryWriter("logs") step = 0 for data in dataloader: imgs, targets = data output = maxpool(imgs) writer.add_images("before_mp", imgs, step) # 用了batch_size一定要记得s writer.add_images("after_mp", output, step) step += 1 writer.close()

5. 激活

relu和sigmoid

import torch from torch import nn from torch.nn import ReLU from torch.nn import Sigmoid import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10("./data", False, torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) writer = SummaryWriter("logs") # 矩阵输入 # input = torch.tensor([[1, -0.5], # [-1, 3]]) # output = torch.reshape(input,(-1, 1, 2, 2)) # print(input.shape) # print(output.shape) # inplace表示是否替代input class sigd(nn.Module): def __init__(self): super().__init__() # self.relul1 = ReLU() self.sigmoid = Sigmoid() def forward(self, x): return self.sigmoid(x) # relu_my = relu() s = sigd() step = 0 for data in dataloader: imgs, targets = data writer.add_images("before", imgs, step) out_imgs = s(imgs) writer.add_images("after", out_imgs, step) step +=1 # output = relu_my(imgs) # print(output)

6. 线性层

from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter import torch from torch import nn from torch.nn import Linear import torchvision class my_linear(nn.Module): def __init__(self): super().__init__() self.linear = Linear(196608, 10) # 转变列数 def forward(self, input): return self.linear(input) dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) writer = SummaryWriter("logs") step = 0 my = my_linear() # 要有含义的才可以在add_image里面用 # C=1:灰度图 # C=3:RGB 图 # C=4:RGBA 图 for data in dataloader: imgs, targets = data print(imgs.shape) output = torch.reshape(imgs, (1, 1, 1, -1)) # output = torch.flatten(imgs) # writer.add_images("before", imgs, step) # writer.add_images("after", output, step) # step += 1 out_imgs = my(output) print(out_imgs.shape)

7. 网络搭建实战

import torch from torch import nn from torch.utils.tensorboard import SummaryWriter class classify_10(nn.Module): def __init__(self): super().__init__() # self.conv1 = nn.Conv2d(3, 32, 5, padding=2) # self.maxpool1 = nn.MaxPool2d(2) # self.conv2 = nn.Conv2d(32, 32, 5, padding=2) # self.maxpool2 = nn.MaxPool2d(2) # self.conv3 = nn.Conv2d(32, 64, 5, padding=2) # self.maxpool3 = nn.MaxPool2d(2) # self.flattern = nn.Flatten() # self.linear1 = nn.Linear(1024, 64) # self.linear2 = nn.Linear(64, 10) self.model1 = nn.Sequential( nn.Conv2d(3, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, padding=2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(1024, 64), nn.Linear(64, 10) ) def forward(self, x): # x = self.conv1(x) # x = self.maxpool1(x) # x = self.conv2(x) # x = self.maxpool2(x) # x = self.conv3(x) # x = self.maxpool3(x) # x = self.flattern(x) # x = self.linear1(x) # x = self.linear2(x) x = self.model1(x) return x my_model = classify_10() input = torch.ones((64, 3, 32, 32)) # print(input.shape) output = my_model(input) # print(output.shape) writer = SummaryWriter("logs") writer.add_graph(my_model, input) writer.close()

8. 损失函数

loss求解方法:
import torch from torch import nn inputs = torch.tensor([1, 2, 3], dtype = torch.float32) targets = torch.tensor([1, 2 ,5], dtype = torch.float32) inputs = torch.reshape(inputs, (1, 1, 1, 3)) targets = torch.reshape(targets, (1, 1, 1, 3)) # class L1Loss( # size_average: Any | None = None, # reduce: Any | None = None, # reduction: str = 'mean' # ) # 可以是平均也可以是求和,默认是求和模式 # L1 loss loss = nn.L1Loss() # loss = nn.L1Loss(reduction="sum") result = loss(inputs, targets) # MSE 均方误差 loss_mse = nn.MSELoss() result_mse = loss_mse(inputs, targets) # 交叉熵 分类问题用的比较多,这个数据格式要注意 x = torch.tensor([0.1, 0.2, 0.3]) # torch.Size([3]) x = torch.reshape(x, (1, 3)) # torch.Size([1, 3]) # print(x.shape) y = torch.tensor([1]) loss_cross = nn.CrossEntropyLoss() result_corss = loss_cross(x, y) print(result) print(result_mse) print(result_corss)
比较完整的流程:
import torch import torchvision from torch.utils.data import DataLoader from torch import nn from torch.nn import Conv2d from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) class classify_10(nn.Module): def __init__(self): super().__init__() self.model1 = nn.Sequential( nn.Conv2d(3, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, padding=2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(1024, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model1(x) return x my_model = classify_10() loss_c = nn.CrossEntropyLoss() for data in dataloader: imgs, targets = data outputs = my_model(imgs) result_loss = loss_c(outputs, targets) # print(result_loss) result_loss.backward() # 求梯度 # print(outputs) # print(targets) # writer = SummaryWriter("logs") # writer.add_graph(my_model, input) # writer.close()

9. 优化器

主要的步骤在里面用序号标出来了

import torch import torchvision from torch.utils.data import DataLoader from torch import nn from torch.nn import Conv2d from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) class classify_10(nn.Module): def __init__(self): super().__init__() self.model1 = nn.Sequential( nn.Conv2d(3, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, padding=2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(1024, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model1(x) return x my_model = classify_10() loss_c = nn.CrossEntropyLoss() optim = torch.optim.SGD(my_model.parameters(), lr=0.01) # 1.首先设置优化器 for epoch in range(20): running_loss = 0.0 for data in dataloader: imgs, targets = data outputs = my_model(imgs) result_loss = loss_c(outputs, targets) # print(result_loss) optim.zero_grad() # 2.梯度清除 result_loss.backward() # 3.反向传播求梯度 optim.step() # 4.模型参数进行调节 running_loss += result_loss print(running_loss) # print(outputs) # print(targets)

三、其他补充知识

1. 对现成模型进行微调

import torchvision from torch import nn from torchvision.models import vgg16, VGG16_Weights # 这个是已经训练好的一个网络,后续要调整他应用到cifar10数据集 vgg16_true = torchvision.models.vgg16(weights=VGG16_Weights.DEFAULT ) train_data = torchvision.datasets.CIFAR10("./data", train=True, transform=torchvision.transforms.ToTensor()) # 模型微调 # VGG16后面输出是1000,现在加一个线性层调整成10 # vgg16_true.add_module("add_linear", nn.Linear(1000, 10)) # 替换最后一层的写法(最后一层是classifier[6]) vgg16_true.classifier[6] = nn.Linear(4096, 10) print(vgg16_true)

2. 模型保存与读取

保存:
import torch import torchvision vgg16 = torchvision.models.vgg16(weights=None) # 不写就是none # 保存方式 1:包含模型结构和参数 torch.save(vgg16, "./models/vgg16_method1.pth") # pth是pytorch后缀名 # 保存方式 2:仅保存模型参数(官方更推荐的,占用空间更小) torch.save(vgg16.state_dict(), "./models/vgg16_method2.pth")
读取:
import torch import torchvision # 加载保存的 1 model1 = torch.load("/home/ljy/Projects/DDPM/models/vgg16_method1.pth") # print(model1) # 加载保存的 2 # 里面是字典形式的参数 # model2_param_dict = torch.load("/home/ljy/Projects/DDPM/models/vgg16_method2.pth") model2 = torchvision.models.vgg16() model2.load_state_dict(torch.load("/home/ljy/Projects/DDPM/models/vgg16_method2.pth")) # print(model2)