近期准备开始做毕设,因为要用来实现一些算法的功能,因此在对比了各种框架后还是选择了pytorch。相较于其他的深度学习框架,pytorch的使用更加的简洁,也易于理解,并且,还有一个选择它的原因在于Github上有很多的开源代码都是使用PyTorch进行开发的。而且Pytorch也有着越来越完善的扩展库,可以说正处于当打之年。
Pytorch加载数据 如何使用pytorch加载读取数据,主要涉及到两个类 Dataset 和 Dataloader 。
Dataset 对数据进行加载时,例如对一堆数据,例如此时图中的”垃圾“,dataset主要是告诉我们如何获取数据,例如提取可回收数据,并对其进行一个编号。同时还会获取数据相应的label,因此dataset主要是提供一种方式来获取数据及其真实的label 。
Dataloader 可用来对dataset整理出来的数据进行打包,主要是为了为后面的网络提供不同的数据形式。
对Dataset来说,如何获取每一个数据及其label、告诉我们总共有多少个数据,是它主要实现的功能。
以下是一个读取数据示例:
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 26 27 28 29 30 31 32 from torch.utils.data import Datasetfrom PIL import Imageimport osclass Mydata (Dataset ): def __init__ (self, root_dir, label_dir ): self.root_dir = root_dir self.labek_dir = label_dir self.path = os.path.join(self.root_dir, self.labek_dir) self.img_path = os.listdir(self.path) def __getitem__ (self, idx ): img_name = self.img_path[idx] img_item_path = os.path.join(self.root_dir, self.labek_dir, img_name) img = Image.open (img_item_path) label = self.labek_dir return img, label def __len__ (self ): return len (self.img_path) root_dir = "..\data\\train" ants_label_dir ="ants_image" bees_label_dir ="bees_image" ants_dataset =Mydata(root_dir, ants_label_dir) bees_dataset =Mydata(root_dir, bees_label_dir) train_dataset = ants_dataset + bees_dataset
TensorBoard的使用 TensorBoard是一个可视化工具,它可以用来展示网络图、张量的指标变化、张量的分布情况等。特别是在训练网络的时候,我们可以设置不同的参数(比如:权重W、偏置B、卷积层数、全连接层数等),使用TensorBoader可以很直观的帮我们进行参数的选择。它通过运行一个本地服务器,来监听6006端口。在浏览器发出请求时,分析训练时记录的数据,绘制训练过程中的图像。
通过SummaryWriter类,创建一个该类的对象,使用add_scalar方法即可绘制图像
1 2 3 4 5 6 7 8 9 10 11 from torch.utils.tensorboard import SummaryWriterwriter = SummaryWriter("logs" ) for i in range (100 ): writer.add_scalar("y=x" , i, i) writer.close()
再尝试:
1 2 3 4 5 6 7 8 9 from torch.utils.tensorboard import SummaryWriterwriter = SummaryWriter("logs" ) for i in range (100 ): writer.add_scalar("y=2x" , 2 *i, i) writer.close()
再每次想要得到一个新的图像时,需要对tag参数进行改变,pytorch会自动进行拟合。例如我在tag为y=2x的图像上绘制一个y=3x的数据:
1 writer.add_scalar("y=2x" , 3 *i, i)
示例 以下是一个用于区分蚂蚁和蜜蜂进行二分类的例子,其中有训练数据集和验证数据集,对于训练数据集其中一种组织形式是会指定告诉我们每个数据集的label:
此时我们要通过add_image方法对一组图像进行研究,对于这个方法的参数,有如下必须的:
1 2 3 4 Args: tag (str): Data identifier img_tensor (torch.Tensor, numpy.ndarray, or string/blobname): Image data global_step (int): Global step value to record
纵坐标相比add_scalar多了很多类型,此时我们如果使用Image库的open方法读取图片的话,返回的类型是不满足要求的,我们需要读取numpy类型的图像数据。
转换方式:img_array = np.array(img)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from torch.utils.tensorboard import SummaryWriterimport numpy as npfrom PIL import Imagewriter = SummaryWriter("logs" ) image_path = "../data/train/ants_image/0013035.jpg" img_PIL = Image.open (image_path) img_array = np.array(img_PIL) writer.add_image('test' , img_array, 1 , dataformats='HWC' ) writer.close()
不仅需要进行数据类型的转换,在add_image中还有一些shape的要求: Shape: img_tensor: Default is :math:(3, H, W)
. You can use torchvision.utils.make_grid()
to convert a batch of tensor into 3xHxW format or call add_images
and let us do the job. Tensor with :math:(1, H, W)
, :math:(H, W)
, :math:(H, W, 3)
is also suitable as long as corresponding dataformats
argument is passed, e.g. CHW
, HWC
, HW
.
当前我们数据的格式为(512, 768, 3) :print(img_array.shape)
,但是由于不是add_image方法默认的(3, H, W)
的形式,因此需要使用dataformats进行定义:
1 writer.add_image('test', img_array, 1, dataformats='HWC')
然后在进行运行就可正常执行。
Tips:从PIL到numpy,需要在add_image()中指定shape中每一个数字/维度表示的含义。
我们再查看一下SummaryWriter类绘制出的图像结果:
然后,再读取一张照片,将step参数改为2:
1 2 3 4 5 6 7 8 9 from torch.utils.tensorboard import SummaryWriterimport numpy as npfrom PIL import Imagewriter = SummaryWriter("logs" ) image_path = "../data/train/ants_image/5650366_e22b7e1065.jpg" img_PIL = Image.open (image_path) img_array = np.array(img_PIL) writer.add_image('test' , img_array, 2 , dataformats='HWC' ) writer.close()
此时就可以拖拽切换step查看每次读取的图片。
transforms是 PyTorch 中提供的一个图像预处理 模块,可以方便地对图像进行各种变换操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from PIL import Imagefrom torch.utils.tensorboard import SummaryWriterfrom torchvision import transformsimg_path = "../data/train/ants_image/0013035.jpg" img = Image.open (img_path) writer = SummaryWriter('logs_tf' ) tensor_trans = transforms.ToTensor() tensor_img = tensor_trans(img) writer.add_image("Tensor_img" , tensor_img) print (tensor_img)
主要就是使用transform类中的各种方法,包括各种输入、输出、作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from PIL import Imagefrom torch.utils.tensorboard import SummaryWriterfrom torchvision import transformswriter = SummaryWriter("logs_trsf" ) img = Image.open ("../data/train/ants_image/0013035.jpg" ) print (img)trans_totensor = transforms.ToTensor() img_tensor = trans_totensor(img) writer.add_image("ToTensor" , img_tensor) print (img_tensor[0 ][0 ][0 ])trans_norm = transforms.Normalize([0.5 , 0.5 , 0.5 ], [0.5 , 0.5 , 0.5 ]) img_norm = trans_norm(img_tensor) print (img_norm[0 ][0 ][0 ])writer.add_image("Normalize" , img_norm) writer.close()
以上代码主要做了什么呢?首先将读入的数据转换为tensor格式,然后将其进行归一化,最后输出归一化结果:
1 2 3 <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x27AB5995D60> tensor(0.3137) # 归一化前原始数据 tensor(-0.3725) # 归一化结果数据
归一化后的图片结果:
1 2 3 4 5 6 7 8 9 10 print (img.size)trans_resize = transforms.Resize((512 , 512 )) img_resize = trans_resize(img) img_resize = trans_totensor(img_resize) writer.add_image("Resize" , img_resize, 0 ) print (img_resize)
1 2 3 4 5 6 7 8 9 trans_resize_2 = transforms.Resize(512 ) trans_compose = transforms.Compose([trans_resize_2, trans_totensor]) img_resize_2 = trans_compose(img) writer.add_image("Resize" , img_resize_2, 1 )
1 2 3 4 5 6 7 trans_random = transforms.RandomCrop(512 ) trans_compose_2 = transforms.Compose([trans_random, trans_totensor]) for i in range (10 ): img_crop = trans_compose_2(img) writer.add_image("RandomCrop" , img_crop, i)
进行了十次随机裁剪:
在类的使用中,一般主要注意以下几点:
关注输入输出类型
多看官方文档
关注方法需要什么参数
不知道返回值的时候,使用print()、print(type())、debug……
Torchvision中的数据集使用 在pytorch中可以看到很多已有的可下载的数据集。通过TorchVision可以帮助我们快速的远程下载数据集。
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 import torchvisionfrom torch.utils.tensorboard import SummaryWriterdataset_transform = torchvision.transforms.Compose([ torchvision.transforms.ToTensor() ]) train_set = torchvision.datasets.CIFAR10(root="./dataset" , train=True , download=True , transform=dataset_transform) test_set = torchvision.datasets.CIFAR10(root="./dataset" , train=False , download=True , transform=dataset_transform) writer = SummaryWriter("log_dataset" ) for i in range (10 ): img, target = test_set[i] writer.add_image("test_set" , img, i) writer.close()
Dataloader的使用 Dataset是指用于存储和管理数据的类,而Dataloader用于从Dataset中按照指定方式读取数据。Dataloader的官方文档
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 26 27 import torchvisionfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWritertest_data = torchvision.datasets.CIFAR10(root="./dataset" , train=False , transform=torchvision.transforms.ToTensor()) test_loader = DataLoader(dataset=test_data, batch_size=4 , shuffle=True , num_workers=0 , drop_last=False ) img, target = test_data[0 ] print (img.shape)print (target)writer = SummaryWriter("dataloader" ) step = 0 for data in test_loader: imgs, targets = data writer.add_images("test_data" , imgs, step) step += 1 writer.close()
神经网络的搭建 通过pytorch搭建神经网络主要用到的是torch.nn模块。所有定义的神经网络都应从torch.nn.Module类中继承。
以下是创建了一个最简单的神经网络,输入为一个数字,经过前进函数后,可以将输入加一后输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import torchfrom torch import nnclass Yosheep (nn.Module): def __init__ (self ): super ().__init__() def forward (self, input ): output = input + 1 return output yosheep = Yosheep() x = torch.tensor(1.0 ) output = yosheep(x) print (output)
卷积操作 例子:
例如有一个5X5的输入图像,其中的每一块都表示这个位置的显色,并且还有一个卷积核。
在进行卷积的过程中,就是将卷积核与输入图的前三行三列进行匹配,然后进行相乘相加,最终就输出一个10:
当Stride为1时,卷积核在下一次会在图像中移动一步:
然后到换行时就往下一格,依次进行计算即可,最终计算出结果:
若Stride=2,与等于1不同的是,就是每次会走两步,移动路径如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import torchimport torch.nn.functional as Finput = 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 ]]) kernel = torch.tensor([[1 , 2 , 1 ], [0 , 1 , 0 ], [2 , 1 , 0 ]]) input = torch.reshape(input , (1 , 1 , 5 , 5 ))kernel = torch.reshape(kernel, (1 , 1 , 3 , 3 )) print (input .shape)print (kernel.shape)output = F.conv2d(input , kernel, stride=1 ) print (output)
1 2 3 4 5 6 # 当Stride为1时 torch.Size([1, 1, 5, 5]) torch.Size([1, 1, 3, 3]) tensor([[[[10, 12, 12], [18, 16, 16], [13, 9, 3]]]])
1 2 3 4 5 # 当Stride为2时 torch.Size([1, 1, 5, 5]) torch.Size([1, 1, 3, 3]) tensor([[[[10, 12], [13, 3]]]])
填充,会在图像的四周都填充指定数量的列,一般padding的值为0,当padding为1:
1 2 3 4 5 6 # 当padding=1,Stride=1,此时再进行卷积的结果 tensor([[[[ 1, 3, 4, 10, 8], [ 5, 10, 12, 12, 6], [ 7, 18, 16, 16, 8], [11, 13, 9, 3, 4], [14, 13, 9, 7, 4]]]])
卷积层使用 再pytorch中,卷积操作有一维、二维、三维的操作方法,一般我们对图片使用最多的是二维,也就是其中的conv2d方法。
在conv2d的参数中,outchannel参数的设置,也就是输出通道数,当outchannel为2时,则会有两个卷积核,最终的输出是两个结果的叠加:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import torchimport torchvisionfrom torch import nnfrom torch.nn import Conv2dfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , transform=torchvision.transforms.ToTensor(), download=True ) dataloader = DataLoader(dataset=dataset, batch_size=64 ) class Yosheep (nn.Module): def __init__ (self ): super ().__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 yosheep = Yosheep() writer = SummaryWriter("./nn_logs" ) step = 0 for data in dataloader: imgs, targets = data output = yosheep(imgs) writer.add_images("input" , imgs, step) output = torch.reshape(output, (-1 , 3 , 30 , 30 )) writer.add_images("output" , output, step) step += 1
卷积后得到的输出:
最大池化使用 最大池化使用的最多的方法还是maxpool2d。池化也是应用池化核,然后通过池化核去输入中进行匹配,不过此时的输出结果是最大的值(池化层默认步长是池化核的大小):
如果匹配到了边缘,则就要看Ceil_module的设置,如果为true,则保留,否则不保留:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import torchimport torchvision.datasetsfrom torch import nnfrom torch.nn import MaxPool2dfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , download=True , transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=64 ) class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.maxpool1 = MaxPool2d(kernel_size=3 , ceil_mode=True ) def forward (self, input ): output = self.maxpool1(input ) return output yosheep = Yosheep() writer = SummaryWriter("nn_maxpool_logs" ) step = 0 for data in dataloader: imgs, targets = data writer.add_images("input" , imgs, step) output = yosheep(imgs) writer.add_images("output" , output, step) step += 1 writer.close()
最大池化的应用可以想象,将1080p的视频转换为720p,会对画质减小,但是同时视频大小也会大大减小。
非线性激活 引入非线性关系: 如果在神经网络中只使用线性操作(如线性加权和),整个网络就会变成一个大的线性函数,多个线性层的组合依然是一个线性变换。非线性激活函数(例如sigmoid、tanh、ReLU等)引入了非线性关系,允许网络学习和表示非线性的模式,这对于解决复杂任务非常关键。
以下使用ReLU演示:
输入经过ReLU处理后,会进行简单的改变,当输入为负数时,则会被变为0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import torchfrom torch import nnfrom torch.nn import ReLUinput = torch.tensor([[1 , -0.5 ], [-1 , 3 ]]) output = torch.reshape(input , (-1 , 1 , 2 , 2 )) print (output.shape)class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.relu1 = ReLU() def forward (self, input ): output = self.relu1(input ) return output yosheep = Yosheep() output = yosheep(input ) print (output)
1 2 3 torch.Size([1, 1, 2, 2]) tensor([[1., 0.], [0., 3.]])
对图片进行操作,此处使用sigmoid:
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 26 27 28 29 30 31 32 33 34 35 36 37 import torchimport torchvision.datasetsfrom torch import nnfrom torch.nn import ReLU, Sigmoidfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterinput = torch.tensor([[1 , -0.5 ], [-1 , 3 ]]) output = torch.reshape(input , (-1 , 1 , 2 , 2 )) print (output.shape)dataset = torchvision.datasets.CIFAR10("./dataset" , train=False , download=True , transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=64 ) class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.relu1 = ReLU() self.sigmoid1 = Sigmoid() def forward (self, input ): output = self.sigmoid1(input ) return output yosheep = Yosheep() step = 0 writer = SummaryWriter("./nn_relu_logs" ) for data in dataloader: imgs, targets = data writer.add_images("input" , imgs, global_step=step) output = yosheep(imgs) writer.add_images("output" , output, global_step=step) step += 1 writer.close()
正则化层 通过正则化,可以加快神经网络的速度,也可以解决过拟合的问题。(BatchNorm2d)
线性层 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 26 27 28 29 30 31 32 33 34 35 import torchimport torchvisionfrom torch import nnfrom torch.nn import Linearfrom torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , download=True , transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=64 , drop_last=True ) class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.linear1 = Linear(196608 , 10 ) def forward (self, input ): output = self.linear1(input ) return output yosheep = Yosheep() for data in dataloader: imgs, targets = data print (imgs.shape) output = torch.flatten(imgs) print (output.shape) output = yosheep(output) print (output.shape)
1 2 3 4 5 6 torch.Size([64 , 3 , 32 , 32 ]) torch.Size([196608 ]) torch.Size([10 ])
其余在神经网络中还有很多的层,可以在官方文档中查看:https://pytorch.org/docs/stable/nn.html
Sequential的使用与搭建一个小神经网络
根据以上结构,构造对应的神经网络:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from torch import nnfrom torch.nn import Conv2d, MaxPool2d, Flatten, Linearclass Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.conv1 = Conv2d(3 , 32 , 5 , padding=2 ) self.maxpool1 = MaxPool2d(2 ) self.conv2 = Conv2d(32 , 32 , 5 , padding=2 ) self.maxpool2 = MaxPool2d(2 ) self.conv3 = Conv2d(32 , 64 , 5 , padding=2 ) self.maxpool3 = MaxPool2d(2 ) self.flatten = Flatten() self.linear1 = Linear(1024 , 64 ) self.linear2 = 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.flatten(x) x = self.linear1(x) x = self.linear2(x) return x yosheep = Yosheep() print (yosheep)
1 2 3 4 5 6 7 8 9 10 11 12 # 输出结构: Yosheep( (conv1): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (maxpool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (maxpool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv3): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (maxpool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (flatten): Flatten(start_dim=1, end_dim=-1) (linear1): Linear(in_features=1024, out_features=64, bias=True) (linear2): Linear(in_features=64, out_features=10, bias=True) )
此时用一个全1的数据对建立好的模型进行一个测试:
1 2 3 4 5 input = torch.ones((64 , 3 , 32 , 32 ))output = yosheep(input ) print (output.shape)
1 2 # 输出 torch.Size([64, 10])
此时对于以上的神经网络init函数中的内容,也可以使用Sequential方法来对其简化:
1 2 3 4 5 6 7 8 9 10 11 self.model1 = Sequential( Conv2d(3 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 64 , 5 , padding=2 ), MaxPool2d(2 ), Flatten(), Linear(1024 , 64 ), Linear(64 , 10 ) )
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 26 import torchfrom torch import nnfrom torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequentialclass Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.model1 = Sequential( Conv2d(3 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 64 , 5 , padding=2 ), MaxPool2d(2 ), Flatten(), Linear(1024 , 64 ), Linear(64 , 10 ) ) def forward (self, x ): x = self.model1(x) return x yosheep = Yosheep() print (yosheep)
也可以通过SummaryWriter来直接通过模型显示模型:
1 2 3 writer = SummaryWriter("logs_nn_sequential" ) writer.add_graph(yosheep, input ) writer.close()
通过双击即可详细查看内部的操作:
损失函数与反向传播 损失函数的计算方法,可以看出loss对于模型来说是越小越好的:
loss的作用:
计算实际输出和目标之间的差距
为我们更新输出提供一定的依据(反向传播)
1 2 3 4 5 6 7 8 9 10 11 12 13 import torchfrom torch.nn import L1Lossinputs = 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 )) loss = L1Loss() result = loss(inputs, targets) print (result)
平方差MSELoss
1 2 loss_mse = nn.MSELoss() result_mse = loss_mse(inputs, targets)
交叉熵
比如此时有一个三分类问题。此时有一个图片,一个神经网络,以及获取的一些数据:
1 2 3 4 5 6 x = torch.tensor([0.1 , 0.2 , 0.3 ]) y = torch.tensor([1 ]) x = torch.reshape(x, (1 , 3 )) loss_cross = nn.CrossEntropyLoss() result_cross = loss_cross(x, y) print (result_cross)
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 26 27 28 29 30 31 32 33 34 35 36 37 import torchimport torchvisionfrom torch.nn import L1Loss, Conv2d, MaxPool2d, Flatten, Linear, Sequentialfrom torch import nnfrom torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , download=True , transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=1 ) class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.model1 = Sequential( Conv2d(3 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 64 , 5 , padding=2 ), MaxPool2d(2 ), Flatten(), Linear(1024 , 64 ), Linear(64 , 10 ) ) def forward (self, x ): x = self.model1(x) return x loss = nn.CrossEntropyLoss() yosheep = Yosheep() for data in dataloader: imgs, target = data outputs = yosheep(imgs) result_loss = loss(outputs, target) result_loss.backward()
以上就可以求出每个数据的loss值,并且可以得到损失函数的一个梯度,进而可以通过这个方向进行梯度下降。
优化器 通过反向传播,可以计算出需要调节的参数和其对应的梯度,进而可以用优化器根据梯度来进行调整。
优化器:optim,可以使用其中的算法
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import torchimport torchvisionfrom torch.nn import L1Loss, Conv2d, MaxPool2d, Flatten, Linear, Sequentialfrom torch import nnfrom torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , download=True , transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=1 ) class Yosheep (nn.Module): def __init__ (self ): super ().__init__() self.model1 = Sequential( Conv2d(3 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 32 , 5 , padding=2 ), MaxPool2d(2 ), Conv2d(32 , 64 , 5 , padding=2 ), MaxPool2d(2 ), Flatten(), Linear(1024 , 64 ), Linear(64 , 10 ) ) def forward (self, x ): x = self.model1(x) return x loss = nn.CrossEntropyLoss() yosheep = Yosheep() optim = torch.optim.SGD(yosheep.parameters(), lr=0.01 ) for epoch in range (20 ): running_loss = 0.0 for data in dataloader: imgs, target = data outputs = yosheep(imgs) result_loss = loss(outputs, target) optim.zero_grad() result_loss.backward() optim.step() running_loss = running_loss + result_loss print (running_loss)
1 2 3 4 5 # loss下降的过程 tensor(18641.3711, grad_fn=<AddBackward0>) tensor(16151.2676, grad_fn=<AddBackward0>) tensor(15427.0596, grad_fn=<AddBackward0>) ......