本文根据小土堆教程撰写。
蚂蚁蜜蜂/练手数据集:链接: https://pan.baidu.com/s/1jZoTmoFzaTLWh4lKBHVbEA?密码: 5suq
这篇博客将以我自己的理解结合例子进行说明,示例中均有详细注释,建议自己尝试一下。
目录
0、环境说明
1、加载数据
2、tensorboard可视化工具
3、transform对图像操作
4、DataSet加载数据集
5、dataloader加载数据
6、卷积操作
7、卷积层
8、池化层
9、非线性激活层
10、线性层(全连接层)
11、简单的神经网络
12、损失函数
13、反向传播
0、环境说明
torch1.1+torchvision0.11.1+cudatoolkit10.0.130,其他的提示缺啥就下载啥
1、加载数据
? ? 首先是加载数据,这里是使用了一个类Dataset(数据集),在使用中会和下面的代码有一些出入,要视情况而定。
? ? 比如下面这个代码读取的数据集,其类名直接作为文件夹的名字,然后文件夹的内部就是一系列图片,所以root_dir指向数据集,label_dir说明类别,由root_dir+label_dir链接地址就能找到此类的图片,这就是MyData类所做的事情。
? ? main函数做的是将两个类图片分别读取然后合并得到总的数据集。
#加载数据#
import torch
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
#初始化#
def __init__(self,root_dir,label_dir):
#加self表示类变量#
self.root_dir=root_dir#基础路径(根目录)#
self.label_dir=label_dir#指向基础路径下的目标文件地址#label_dir是图片文件夹名称即图片标签
self.path=os.path.join(root_dir,label_dir)#将两个文件路径进行连接#
self.img_path=os.listdir(self.path)#读取path指向的文件目录下每一个图片名称#
#返回第idx张图片及其标签#
def __getitem__(self, idx):
img_name=self.img_path[idx]#获得第idx张图片名称#
img_item_path=os.path.join(self.path,img_name)#此路径指向该图片#
img=Image.open(img_item_path)#加载图片#
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
#label_dir是图片文件夹名称即图片标签
label=self.label_dir
return label,img#label是img的类别#
#返回图片列表长度即训练集/测试集大小#
def __len__(self):
return len(self.img_path)#图片数量#
'''
#单张图片的读取
img_path = "pct/2959730355_416a18c63c.jpg" # 图片路径并非图片文件夹“#
img = Image.open(img_path)
img.show()#显示图片#
'''
#利用Dataset读取
root_dir=""#基础路径(根目录)#
ants_dir="ant"
ants_dataset=MyData(root_dir,ants_dir)
label_0,img_0=ants_dataset[0]#第0号图片及其标签
#img_0.show()
print(label_0)
bees_dir="bee"
bees_dataset=MyData(root_dir,bees_dir)
label_0,img_0=bees_dataset[0]#第0号图片及其标签
#img_0.show()
print(label_0)
#将两个数据集进行合并#
all_dataset=ants_dataset+bees_dataset
print(len(all_dataset))
2、tensorboard可视化工具
? ? tensorboard是一个可视化的工具,重点要了解他怎么写和怎么读,这些都写在下面的代码里了,在使用的时候要记得在最后执行close
#tensorboard#
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
writer=SummaryWriter("logs")#把数据写入logs文件夹内
image_path="./ant/308196310_1db5ffa01b.jpg"
img=Image.open(image_path)#此时img类型为JpegImageFile
img_array=np.array(img)#将img转化为numpy型
writer.add_image("test",img_array,1,dataformats="HWC")
'''
add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats='CHW'):
tag:标题
img_tensor:图像(类型:torch.Tensor, numpy.array, or string/blobname)
global_step:步数
dataformats:图像类型HWC表示高度、宽度、通道(可以利用img_array.shape进行查看)
生成文件后放在前面在类中定义的logs文件夹中
阅读生成的文件:
控制台输入
tensorboard --logdir="logs" --port=6007
#logdir指定目录
#port指定接入端口
#点击TensorBoard 2.4.0 at http://localhost:6006/ (Press CTRL+C to quit)中的“http://localhost:6006/”
如果是在服务器下Tensorboard然后在本地查看:
可见https://zhuanlan.zhihu.com/p/231790992亲测有用
'''
for i in range(100):
writer.add_scalar("y=x",i,i)
'''
add_scalar(self, tag, scalar_value, global_step=None, walltime=None):
tag:图表标题
scalar_value:数值,与global_step对应,y轴
global_step:步数,训练多少步时对应数值是多少,x轴
生成文件后放在前面在类中定义的logs文件夹中
阅读生成的文件:
终端输入
tensorboard --logdir="logs" --port=6006
#logdir指定目录
#port指定接入端口
#点击TensorBoard 2.4.0 at http://localhost:6006/ (Press CTRL+C to quit)中的“http://localhost:6006/”
'''
writer.close()
? ? transform有一系列对图像的操作,比如类型转化、大小变化、裁剪等。
#transform对图像进行操作
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
img_path="pct/2959730355_416a18c63c.jpg"
img=Image.open(img_path)#打开图片为PIL数据类型
print(img)
"""
利用transform.ToTensor将PIL或numpy数据类型的img转化为Tensor数据类型的图片
tensor:包装了神经网络所需要的参数如梯度、反向传播等
"""
tensor_trans=transforms.ToTensor()
tensor_img=tensor_trans(img)#执行__call__将img转化为tensor类型
print(tensor_img)
#"""test2中所用到的SummaryWriter类"""
#writer=SummaryWriter("log")
#writer.add_image("Tensor_img",tensor_img)
#writer.close()
"""
Totensor的使用
Normalize归一化
resize调整大小
RandomCrop随机裁剪
"""
"""
Normalize(均值,方差)
output[channel] = (input[channel] - mean[channel]) / std[channel]
"""
trans_norm=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])#(output-0.5)/0.5
img_norm=trans_norm(tensor_img)
"""
resize调整大小
参数为大小(序列或整数):所需的输出大小。
如果大小是这样的序列(h,w),输出大小将与此匹配。如果大小是整数,图像的较小边缘将与此数字匹配。
"""
trans_resize=transforms.Resize((512,512))#调整到512X512的PIL类型img
img_resize=trans_resize(img)#这里输入为PIL,输出为PIL
print(img_resize)
trans_resize_2=transforms.Resize(1000)
#串联两个操作即先执行trans_resize_2再执行tensor_trans
trans_compose=transforms.Compose([trans_resize_2,tensor_trans])
img_resize_2=trans_compose(img)
"""
RandomCrop输入一个PIL类型的img
"""
trans_random=transforms.RandomCrop(20)#裁剪为512X512的,若指定(n,m)则裁剪大小为nXm
trans_compose_2=transforms.Compose([trans_random,tensor_trans])
for i in range(10):
img_crop=trans_compose_2(img)
4、DataSet加载数据集
? ? 注意这一节与第一节是不同的,这里包含有MNIST、FakeData、COCO、LSUN、ImageFolder、DatasetFolder、ImageNet、CIFAR等一些常用的数据集,可以直接下载使用。
"""
使用官方提供的数据集
CIFAR10:6万32X32的图片,5万训练,1万测试
"""
import torchvision
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transformsz.Compose([
torchvision.transforms.ToTensor()
])
#下载CIFAR10到root路径下,train=True表示下载的是训练集,False表示下载的是测试集,download表示要下载
#transform=dataset_transform转化成Tensor数据类型
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意修改该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
train_set=torchvision.datasets.CIFAR10(root="./DataSet",train=True,transform=dataset_transform,download=True)
test_set=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
# print(test_set.classes)
#
# print(test_set[0])#输出图片+标签信息
# img,target=test_set[0]#img存放图片信息,target存放标签信息
# print(img)
# print(target)
# print(test_set.classes[target])#输出标签类别
# img.show()#显示图片
writer=SummaryWriter("logs")
for i in range(10):
img,target=test_set[i]
writer.add_image("test_set",img,i)
writer.close()
5、dataloader加载数据
? ? 这里是读取第四步中下载的数据,dataloader本质是一个可迭代对象,将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。
#DataLoader加载数据
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
"""
dataset:数据集
batch_size:一次取多少数据
shuffle:是否按顺序读取,默认为false
num_workers:多进程,默认为0,用主进程加载,>0时windows下可能出错
drop_last:对余数的处理(除不尽)
"""
test_loader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)
writer=SummaryWriter("dataloader")
for epoch in range(2):
step=0
for data in test_loader:
img,targets=data
writer.add_images("epoch_{}".format(epoch),img,step)
step=step+1
writer.close()
6、卷积操作
? ? 这里是利用函数的卷积。
#conv2d卷积操作
import torch
from torch import nn
import torch.nn.functional as F
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]])
kernel=torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
input=torch.reshape(input,(1,1,5,5))#尺寸变化,Conv2d有要求
kernel=torch.reshape(kernel,(1,1,3,3))
"""
二维卷积
conv2d(input,weight,bias=None,stride=1,padding=0,dilation=1,groups=1):
input:输入要求是(batch,通道,高,宽)
weight:权重(卷积核),输入要求是(输出通道,输入通道,高,宽)
bias:偏置
stride:步长,单个数(横纵皆是),俩数(横步长,纵步长)
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
"""
output=F.conv2d(input,kernel,stride=1)
print(output)
output2=F.conv2d(input,kernel,stride=2)
print(output2)
output3=F.conv2d(input,kernel,stride=1,padding=1)
print(output3)
7、卷积层
? ? 与第6节不同,这里引入了model的概念,自己定义卷积核的到校、步长等通过正向传播的得到结果。卷积层也是未来常用的一个层
#nn.Conv2d卷积层
import torch
import torchvision
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch import nn
from torch.utils.tensorboard import SummaryWriter
#加载数据集
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
dataset=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset,batch_size=64)
"""
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,
padding=0,dilation=1,groups=1,bias=True,padding_mode='zeros'):
in_channels:输入通道数,RGB图像一般为3通道
out_channels:输出通道数,通道数1->2,两个卷积核
kernel_size:卷积核大小,数或者元组(行,列),卷积核中参数训练得到
stride:步长,单个数(横纵皆是),俩数(横步长,纵步长)
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
下面的不太常用一般设为默认值
dilation:卷积核对应位的距离
groups:分组卷积
bias:结果是否加减一个常数
padding_mode:按怎样模式填充
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, 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
myModule=MyModule()
writer=SummaryWriter("nn.conv")
step=0
for data in dataloader:
imgs,targets=data
output=myModule(imgs)
writer.add_images("input",imgs,step)
#变化成六通道后不能显示(报错),需要reshape转化为三通道
output=torch.reshape(output,(-1,3,30,30))#设-1会自动计算结果
writer.add_images("output",output,step)
step=step+1
writer.close()
8、池化层
? ? 池化操作,同7,也是module中的一个层次。
#pooling池化层
import torch
from torch import nn
from torch.nn import MaxPool2d
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)#将数字格式设置为浮点型
input=torch.reshape(input,(-1,1,5,5))#尺寸变化,MaxPool2d有要求
"""
nn.MaxPool2d(kernel_size, stride,padding, dilation, ceil_mode,return_indices)
kernel_size:窗口大小,数字或元组
stride:横向或纵向路径大小,默认值是窗口大小
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
dilation:窗口对应位的距离
ceil_mode:True则ceil模式(向下取整),False则floor模式(向上取整),设计到对边缘数的处理同理,默认为False
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.maxpool1=MaxPool2d(kernel_size=3,ceil_mode=True)
def forward(self, input):
output=self.maxpool1(input)
return output
MyModule=MyModule()
output=MyModule(input)
print(output)
9、非线性激活层
#非线性激活层
import torch
from torch import nn
from torch.nn import ReLU
input=torch.tensor([[1,-0.5],
[-1,3]])
output=torch.reshape(input,(-1,1,2,2))
"""
ReLU(inplace)
负数归0
inplace:是否覆盖input,true覆盖,False不覆盖,默认为False
Sigmoid()
归一化归至(-1,1)
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.relu1=ReLU()
def forward(self,input):
output=self.relu1(input)
return output
MyModule=MyModule()
output=MyModule(input)
print(output)
10、线性层(全连接层)
#线性层
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
#加载数据集
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
dataset=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset,batch_size=64,drop_last=True)
"""
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None):
in_features:输入大小
out_features:输出大小
bias:True则设偏置学习,False不设
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.linear1=Linear(196608,10)
def forward(self,input):
output=self.linear1(input)
return output
MyModule=MyModule()
for data in dataloader:
imgs,targets=data
print(imgs.shape)
"""
flatten将输入展成一行
[[1,2],[3,4]]->[1,2,3,4]
"""
input=torch.flatten(imgs)
print(input.shape)
output=MyModule(input)
11、简单的神经网络
? ? 这一节结合了7~10节,构成了一个简单的神经网络,当然,只有正向传播,这个代码的最后有一个tensorboard的黑科技,记得试一下。
#构建一个简单的神经网络
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__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)
"""
Sequential():
依次执行()中的操作
"""
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.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
MyModule=MyModule()
input=torch.ones((64,3,32,32))#定义大小,其中元素全部复制为1
output=MyModule(input)
writer=SummaryWriter("../compute")
writer.add_graph(MyModule,input)#生成计算图,很神奇,记得点几下
writer.close()
12、损失函数
要进行训练,那么损失函数就必不可少了,这里展示了几种常见的loss函数例如MSELoss、CrossEntropyLoss。
#loss实际与预测值的损失,用于更新(反向传播) 注意输入输出
import torch
from torch.nn import L1Loss
from torch import nn
input=torch.tensor([1,2,3],dtype=torch.float32)
target=torch.tensor([1,2,5],dtype=torch.float32)
input=torch.reshape(input,(1,1,1,3))
target=torch.reshape(target,(1,1,1,3))
"""
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean'):
reduction:mean:求均值;sum:求和
"""
loss=L1Loss()
result=loss(input,target)
print(result)
"""
nn.MSELoss()
先求平方差,再求均值
"""
loss_mse=nn.MSELoss()
result_mse=loss_mse(input,target)
print(result_mse)
"""
nn.CrossEntropyLoss():
交叉熵损失,常用于分类问题
input:(N,C) N batch size,C类
output:实际标签
"""
x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])
x=torch.reshape(x,(1,3))
loss_CE=nn.CrossEntropyLoss()
print(loss_CE(x,y))
13、反向传播
? ? 加上反向传播,可用pycharm的调试功能看到梯度的变化。
#Back propagation反向传播,在test14网络的基础上进行
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__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)
"""
Sequential():
依次执行()中的操作
"""
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.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
MyModule=MyModule()
loss=nn.CrossEntropyLoss()
for data in dataloader:
imgs,targets=data
# 向前传播 该网络输出为一个10维向量,每一维表示是此类的概率
outputs=MyModule(imgs)
#利用交叉熵计算损失
result_loss=loss(outputs,targets)
#反向传播,计算梯度
result_loss.backward()
|