目录
一. read_data
"""
Dataset 数据加载
就是说,要想获取自己电脑里的数据,读取它,那么就要遵守 pytorch 加载数据的规则。
他的规则就是 定义一个class类,继承 Dataset (from torch.utils.data import Dataset),并且,在类中,定义三个函数,
分别是:初始化 init、获得每一个数据 getitem、数据长度 len。
过程:
1、init:路径、合并路径、把文件夹中的每一个文件名称,做成一个列表init;
2、访问init中的列表,把列表的名称逐一传递给一个变量,命名为name,再次合并路径,并且把文件名连接在路径之后,
接下来,用PIL中的Image.open函数,读取(加载)上述路径的文件(命名为img)(这里肯定是图像了),返回 图像img和标签 label(这是getitem的工作);
3、最后用len()返回列表的长度。
定义好 类 以后,后面就可以实例化这个类,定义参数(本例其实是一个路径,一个夹名称了),名称可以和定义类中的不一样,但是位置要对应(奥,这可能是Python课程里说的位置参数?)。
引用之前定义的类,把上述参数,传递进去。
最后打印自定义数据列表的长度。
———————————————
4.无论在Windows还是Unix/Linux上,都可以使用正斜杠符(/)作为相对路径的分隔符,以保持跨平台的一致性。
绝对路径的是"\",再windows下需要写成“\\”,否则会报错。或者在原始字符串前面加上一个 r,以将其视为原始字符串(不进行转义处理)
相对路径的是“/”。不用修改
Dataset:
能把数据进行编号便于模型加载
提供一种方式,获取数据,及其label,实现两个功能:
1、如何获取每一个数据,及其label
2、告诉我们总共有多少个数据
数据集的组织形式,有两种方式:
1、文件夹的名字,就是数据的label
2、文件名和label,分别处在两个文件夹中,label可以用txt的格式进行存储
在jupyter中,可以查看,help,两个方式:
1、help(Dataset)
2、Dataset??
Dataloader:
为网络提供不同的数据形式,比如将0、1、2、3进行打包
————————————————
"""
# dataset有两个作用:1、加载每一个数据,并获取其label;2、用len()查看数据集的长度
from torch.utils.data import Dataset
import cv2
from PIL import Image
import os
class Mydata(Dataset):
def __init__(self,root_dir,label_dir): # 初始化,在实例化对象时会自动执行。为这个函数用来设置在类中的全局变量
self.root_dir = root_dir
self.label_dir = label_dir
self.dir_path = os.path.join(root_dir,label_dir) # 将路径连接起来,因为在win下和linux下的斜线方向不一样,所以用这个函数来连接路径
self.img_path = os.listdir(self.dir_path) # 利用listdir函数,将img_path文件夹下的内容返回成一个列表
def __getitem__(self, idx): # 内置的方法,获取数据以及对应的 label
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) # show 或者 操作图片之前,必须要把图片打开(读取)
label = self.label_dir
return img,label # img 是每一张图片的名称,根据这个名称,就可以使用查看(直接img)、print、size等功能
# label 是这个图片的标签,在当前这个项目中,标签,就是只文件夹名称
def __len__(self):
return len(self.img_path) #返回列表的长度
root_dir = "dataset/train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = Mydata(root_dir,ants_label_dir)
bees_dataset = Mydata(root_dir,bees_label_dir)
train_datase = ants_dataset + bees_dataset
'''
self 是一个对当前实例(对象)的引用。当你创建一个类的实例时,Python会自动将这个实例本身作为第一个参数传递给类的方法(除了静态方法和类方法,它们不使用self)
自定义的数据集都需要写__getitem__方法,定义了使用索引操作符 [] 时对象的行为,会返回相应的图片
'''
二. dataloader
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
#准备的测试集
test_data =torchvision.datasets.CIFAR10("./data",False,download=True,transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=True)
#debug发现samper(采样器)是randomsampler随机采样
#测试集中第一张图片及target
img,target = test_data[0]
print(img.shape)
print(target)
writer = SummaryWriter("dataloader")
step = 0
#遍历数据加载器 test_loader
for epoch in range(2): #运行for循环就会重新调用test_loader,如果shuffle是True的话,那么bactch_size中的图片都不一样
for data in test_loader: #data 是一个包含了两个元素的元组,第一个元素是当前批次的所有图片,第二个元素是这些图片对应的标签。这行代码将图片赋值给变量 imgs,将标签赋值给变量 target。
imgs,target = data
# print(imgs.shape) #由于 batch_size=4,imgs,(4, C, H, W) 的四维张量,
# print(target)
writer.add_images(f"epoch{epoch}",imgs,step) #注意这里是images,添加批量图片,不是image
step +=1
writer.close()
#format 方法是一个字符串方法,用于格式化字符串。它允许你插入变量或者值到字符串中的占位符位置。
#"{}年{}月{}日".format(2023, 4, 5)
#输出将会是:'2023年4月5日'。
三. transforms
"""
alt+enter 可以提示导入缺少的类
tranform是工具箱,class是工具模板,用来加工数据常用的有ToTensor、ToPILImage这些class,
tool = transformer.ToTensor(),创建具体的工具,即实例化。
result = tool(input) 根据工具手册选定输入
ctrl+p可以看见这个函数所需要的参数
cv2的imread无法读取中文路径的图片
./是返回shang
"""
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "dataset/train/ants/0013035.jpg"
img = Image.open(img_path)
print(img)
writer = SummaryWriter("logs")
tensor_tran = transforms.ToTensor() #先根据模板创建一个工具
tensor_img = tensor_tran(img) #再调用他内置的__call__函数
writer.add_image("Tupian",tensor_img)
writer.close()
#print(tensor_img)
四。dataset_transforms
'''
1."."表示当前目录,“..”表示上一级目录
2.选中之后,ctrl+/全部注释掉
3.下载较慢的话可以直接用迅雷下,完成之后在创一个同样的文件夹,放进去,运行后会自动解压
4.也可以ctrl+z,看他的源代码,找俩下载链接
'''
import torchvision
from torch.utils.tensorboard import SummaryWriter
dataset_transforms = torchvision.transforms.Compose([
torchvision.transforms.ToTensor() #不用实例化,直接使用
])
train_set = torchvision.datasets.CIFAR10("./data",True,download=True,transform=dataset_transforms)
test_set = torchvision.datasets.CIFAR10("./data",False,download=True,transform=dataset_transforms)
# print(test_set[0]) 是一个元组,包含图片以及他的target
# print(test_set.classes)
# img,target = train_set[0] train_set[0]是一个元组,包含图片以及他的target
# print(img)
# print(target)
# print(test_set.classes[target])
# img.show()
print(train_set[0])
writer = SummaryWriter("logs")
for i in range(10): #range从0开始
img, target = train_set[i]
writer.add_image("dataset",img,i)
writer.close()
五. conv
'''
torch.nn是封装好了的,相当于方向盘,torch.nn.functional相当于具体齿轮的运转,也就是底层的一些东西
'''
import torch
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需要一个(minibatch,in_channels,iH,iW)即(batchsize,channles图层数,H,W)所以使用reshape函数来调整大小。平面是一个通道
kernel = torch.reshape(kernel,[1,1,3,3]) #
print(input.shape)
print(kernel.shape)
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)
六.conv2d
'''
1.kernal size指定后不用具体写有哪些数,他会根据一定的方法自动采样,训练过程中kernal自动调整的越来越好
卷积核是用来提取图片特征的,不同的卷积核可以提取角度的特征,输出通道的数量就等于卷积核的数量
几个卷积核就是几通道的。一个卷积核作用完RGB三个通道后会把得到的三个矩阵的对应值相加,也就是说会合并,所以一个卷积核产生一个通道。
通道数就是提取特征图的层数
-1 是一个占位符,表示让 PyTorch 自动计算该维度的大小。
'''
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("./data",False,transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset,batch_size=64)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3,6,3,1,0)
def forward(self,x):
x = self.conv1(x)
return x
tudui = tudui()
print(tudui)
step = 0
writer = SummaryWriter("logs")
for data1 in dataloader: #遍历整个dataloader
imgs,target = data1
ouput = tudui(imgs)
print(imgs.shape )
print(ouput.shape)
#torch.Size([64, 3, 32, 32])
writer.add_images("input",imgs,global_step=step)
#torch.Size([64, 6, 30, 30])
ouput =torch.reshape(ouput,[-1,3,30,30]) #由于add_imagws函数的限制,必须改成3通道的,将6变成3,相当于将每一张图片的6个图层中的3个拿出来,重新组成一张图片,导致bach_size数量增加
writer.add_images("outpu",ouput,global_step=step)
step +=1
七.loss
import torch
from torch import nn
from torch.nn import L1Loss
input = torch.Tensor([1,2,3])
output = torch.Tensor([1,2,5])
# input = torch.reshape(input,[1,1,1,3]) #bath_size = 1,一维,一行三列,这里reshape是为了增加bathsize这一列,不过新版本不用这样了
# output = torch.reshape(output,[1,1,1,3])
loss = L1Loss(reduction="mean")
result = loss(input,output)
print(result)
loss_mse = nn.MSELoss(reduction="sum") #sum代表每个损失相加,mean代表损失相加后的平均值
result = loss_mse(input,output)
print(result)
x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,[1,3])
loss = nn.CrossEntropyLoss()
result = loss(x,y)
print(result)
#交叉熵这个内置函数先将x经过softmax处理之后,再计算误差的
八.linear
'''
1.
torch.nn.Linear 是一个用于定义线性变换的模块。具体而言,它表示一个全连接层(fully connected layer)或线性层,其数学表示为:
y=weight × x + bias
其中:
x 是输入张量(输入特征),
weight 是权重矩阵,
bias 是偏置(可选),
y 是输出张量。
在机器学习中,这个线性变换通常用于将输入特征映射到输出特征。这是神经网络中最基本的一种层,也称为全连接层或仿射层。
2.
输入特征维度: 这是指输入数据的特征的数量。假设你有一个数据集,每个数据点有若干特征,
比如一个图像分类任务中,每个图像的特征可能是像素值。输入特征维度就是描述每个数据点有多少个特征。
输出特征维度: 这是指模型输出的特征的数量。在神经网络中,通过层与层之间的权重矩阵相乘和激活函数的处理,输入的特征会经过一系列变换,最终得到输出。
输出特征维度描述了最终模型的输出具有多少个特征。
这两个参数定义了线性层中的权重矩阵的形状。当你在模型中使用线性层时,它决定了输入数据经过线性变换后的输出特征的数量。
举个例子,如果你有一个图像分类任务,每个图像有 784 个像素(输入特征维度为 784),而你想要输出 10 个类别的概率分布(输出特征维度为 10),
那么你可能会创建一个 torch.nn.Linear(784, 10) 的线性层,用于将输入特征映射到输出特征。
3.输入特征维度的计算方式可以通过以下公式表示:
Input Features = Height × Width × Channels
其中:
Height 是图像的高度(像素数),
Width 是图像的宽度(像素数),
Channels 是图像的通道数,比如在 RGB 彩色图像中通常是 3,表示红色、绿色、蓝色三个通道。
即只看一张图片的,而不是一整个batch,
举例来说,如果有一张高度为 224 像素、宽度为 224 像素、并且包含 3 个通道的 RGB 图像,则该图像的输入特征维度为 224×224×3。
在深度学习模型中,这些输入特征通常被扁平化(flattened)成一个一维向量,然后传递给全连接层或其他类型的层。这一维向量的长度就是输入特征的维度。
'''
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,64,drop_last=True)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.liinter=Linear(196008,10) #这里是因为以下的计算是1 *196608,而不是1*1*1*196008
def forward(self,x):
x =self.liinter(x)
return x
tudui = tudui()
for data in dataloader:
imgs,targets = data
print(imgs.shape)
imgs = torch.reshape(imgs,[1,1,1,-1]) #经过我将其改成【2,1,1,-1】来验证,Input Features不考虑batchsize是对的
#imgs = torch.flatten(imgs)
print(imgs.shape)
output = tudui(imgs)
print(output.shape)
九.loss-network
'''
交叉熵(Cross-Entropy)是一个用于衡量两个概率分布之间的差异的指标。在深度学习中,交叉熵常常用来衡量模型预测的概率分布与真实概率分布之间的相似程度。
在分类问题中,通常有一个模型的输出,表示每个可能类别的概率分布,而真实标签则表示样本的真实类别。
概率分布:在分类问题中,模型通常输出一个概率分布,其中每个类别都有一个相关的概率值。这些概率值表示模型对每个类别的预测置信度。
通常使用softmax函数将原始模型输出(通常称为"分数"或"logits")转化为概率分布,以确保所有概率值都在0到1之间且总和为1。
真实标签:对于每个样本,都有一个真实的类别标签,表示该样本的实际类别。
torch.nn.CrossEntropyLoss来计算交叉熵损失。你需要提供模型的输出和真实标签作为输入。该损失函数会自动将模型的输出转化为概率分布,
并计算与真实标签之间的交叉熵。
在训练深度学习模型时,通常的目标是最小化交叉熵损失。你可以使用优化算法(如随机梯度下降)来反向传播损失并更新模型的权重,以逐渐提高模型的性能。
'''
import torchvision.datasets
from torch import nn
from torch.nn import Flatten
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,1)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.module1 = nn.Sequential( #这个compose的功能差不多,更加简洁
nn.Conv2d(3, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding="same"),
nn.MaxPool2d(2),
Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
x = self.module1(x)
return x
tudui = tudui()
loss = nn.CrossEntropyLoss() #这个函数自带softmax功能,所以输入的x不是概率形式也行,所有下面output还有负数
for data in dataloader:
imgs,targets = data
output = tudui(imgs)
#print(output)
result = loss(output,targets)
#print(targets)
print(result)
result.backward() #所以是反向传播来计算梯度,根据梯度来更新参数,实现loss最小化
'''
softmax函数是一个常用的数学函数,通常用于多类别分类问题中,用来将一组原始分数(也称为logits或未归一化的分数)转换为概率分布。
softmax函数的主要作用是将输入值映射到0到1之间,并且确保所有输出值的总和为1,使其可以被解释为各个类别的概率。
归一性: softmax函数确保输出的所有元素之和等于1,因此可以被解释为概率分布。
增强差异性: 它会放大输入中的大值,抑制小值,从而增强了模型对最大值的敏感性,有助于区分不同类别。
用于多类别分类: softmax通常用于多类别分类问题,其中每个类别都有一个与之相关的输出节点。
softmax将模型的原始输出转换为每个类别的概率,最终将样本分类到概率最高的类别中
工作流程:
1.多类别分类问题:在多类别分类任务中,你有多个类别需要对输入样本进行分类。每个类别都有一个与之相关的输出节点。神经网络的最后一层通常输出未经归一化的分数(logits),这些分数需要转化为类别概率以进行分类。
2.Softmax函数:为了将这些未经归一化的分数转化为类别概率,通常使用softmax函数。softmax函数将logits映射为0到1之间的概率分布,并确保所有类别的概率之和为1。这使得模型的输出可以被解释为每个类别的概率。
3.交叉熵损失:交叉熵损失是一种用来衡量模型输出的概率分布与真实标签之间的差异的损失函数。
在分类问题中,交叉熵损失通常与softmax函数一起使用,以衡量模型的预测概率与真实标签之间的差距。
公式中的$p(x)$表示真实的标签分布(通常是一个独热编码,其中一个类别的概率为1,其余为0)。
公式中的$q(x)$表示模型的输出概率分布,通过softmax函数获得。
交叉熵损失越小,表示模型的预测与真实标签越相似,模型性能越好。
独热编码(One-Hot Encoding)是一种常用的编码方式,通常在多类别分类问题中用于表示类别标签。
独热编码是一种将类别标签表示为二进制向量的方法,其中只有一个元素为1,其余元素都为0。每个1的位置表示类别的标签,这种编码方式使得每个类别都具有唯一性,并且相互之间没有大小关系。例如,对于一个有三个类别的问题,独热编码可能如下所示:
类别A:[1, 0, 0]
类别B:[0, 1, 0]
类别C:[0, 0, 1]
在多类别分类问题中,每个样本的真实标签通常以独热编码的形式表示,以便将其与模型的输出进行比较。
'''
十.pool
'''
1.最大池化的默认stride是kernal_size
2.ceil即向上取整,保留,当池化超过边界时,保留那部分,floor反之,默认为falese
池化函数使用某一位置的相邻输出的总体统计特征来代替网络在该位置的输出。本质是 降采样,可以大幅减少网络的参数量。
1、./是当前目录 2、../是父级目录 3、/是根目录 根目录指逻辑驱动器的最上一级目录
'''
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,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],
])
input = torch.reshape(input,[1,5,5])
print(input.shape)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.pool = torch.nn.MaxPool2d(3,stride=3,ceil_mode=True)
def forward(self,x):
output = self.pool(x)
return output
tudui = tudui()
output =tudui(input)
print(output)
print(output.shape)
step = 0
writer = SummaryWriter("logs")
for data in dataloader:
imgs,targets = data
output = tudui(imgs)
writer.add_images("pool",output,step)
step +=1
writer.close()
十一.RELU
'''
ReLU激活函数的功能,小于0的数返回0,不小于0的数返回原值
没有激活函数的神经网络实际上是线性可加的,那么多线性层其实可以归为一层。只具有线性的神经网络表达能力极其有限。
所以增加非线性的激活函数实际上是给模型增加非线性的表达能力或者因素,有了非线性函数模型的表达能力就会更强。
整个模型就像活了一样,而不是想机器只会做单一的线性操作。
'''
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
input = torch.Tensor([[1,-0.5],
[-1,3]])
# input = torch.reshape(input,[-1,1,2,2])
print(input.shape)
dataset = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,64)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.relu = nn.ReLU() #让ReLU选择:修改引用实参 | 返回新变量,inplace替换,即当为true时,直接原位替换,默认为false,必须创建一个新的变量来接收值
self.sigmoid = nn.Sigmoid()
def forward(self,x):
output = self.sigmoid(x)
return output
tudui = tudui()
output = tudui(input)
print(output)
step = 0
writer = SummaryWriter("logs")
for data in dataloader:
imgs,targets =data
output = tudui(imgs)
writer.add_images("relu_sigmoid",output,step)
step+=1
writer.close()
十二.sqc
'''
输出通道数等于卷积核个数,与输入通道数无关
不需要公式,奇数卷积核把中心格子对准图片第一个格子,卷积核在格子外有两层就padding=2呗
当设置 padding="same" 时,卷积层会在输入的周围添加适当数量的零填充,以确保输出的尺寸与输入的尺寸相同。
same padding时,p=(f-1)\2 f是过滤器的尺寸,也是卷积核的尺寸,也就是kernel的大小,为5,所以5-1除以2等于2
'''
import torch
from torch import nn
from torch.nn import Flatten
from torch.utils.tensorboard import SummaryWriter
class tudui(nn.Module):
def __init__(self):
super().__init__()
# self.conv1 = nn.Conv2d(3,32,5,padding="same")
# self.maxpoll1 = nn.MaxPool2d(2)
# self.conv2 = nn.Conv2d(32,32,5,padding="same")
# self.maxpoll2 = nn.MaxPool2d(2)
# self.conv3 = nn.Conv2d(32,64,5,padding="same")
# self.maxpoll3 = nn.MaxPool2d(2)
# self.linear1 = nn.Linear(1024,64)
# self.fattern = Flatten()
# self.linear2 = nn.Linear(64,10)
self.module1 = nn.Sequential( #这个compose的功能差不多,更加简洁
nn.Conv2d(3, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding="same"),
nn.MaxPool2d(2),
Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
# x = self.conv1(x)
# x = self.maxpoll1(x)
# x = self.conv2(x)
# x = self.maxpoll2(x)
# x = self.conv3(x)
# x = self.maxpoll3(x)
# x = self.fattern(x)
# x = self.linear1(x)
# x = self.linear2(x)
x = self.module1(x)
return x
tudui = tudui()
print(tudui)
input = torch.ones(64,3,32,32)
output = tudui(input)
print(output.shape)
writer = SummaryWriter("logs")
writer.add_graph(tudui,input) #可以视化模型的结构,功能非常强大
writer.close()
十三.test-tensorboard
from torch.utils.tensorboard import SummaryWriter #tensorboard主要的库
import numpy as np
from PIL import Image
writer = SummaryWriter("logs") #实例化对象,并创造一个名字为“logs”的文件,用来存放数据
img_path = "dataset/train/bees/39672681_1302d204d1.jpg"
img_PIL =Image.open(img_path)
img_array = np.array(img_PIL) #由于add_image函数的要求,必须要将jpg文件转为np
print(type(img_array))
print(img_array.shape) #打印类型,用来调试,可知图片类型为“HWC”
writer.add_image("test",img_array,2,dataformats='HWC') #文档要求为“CHW”,解决办法就是指定数据格式为“HWC”
for i in range(100):
writer.add_scalar("y =2x",global_step=i,scalar_value=2*i) #标题不同,图不同,标题一样而改了ster或者value会导致
#图拟合,解决办法是删去文件“logs”里的事件。 事件不删会一直存在
writer.close()
#终端命令是: tensorboard --logdir=logs --port=6007 logs取决于你取得文件名字
十四.optim
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Flatten
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,1)
class tudui(nn.Module):
def __init__(self):
super().__init__()
self.module1 = nn.Sequential( #这个compose的功能差不多,更加简洁
nn.Conv2d(3, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding="same"),
nn.MaxPool2d(2),
Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
x = self.module1(x)
return x
tudui = tudui()
loss = nn.CrossEntropyLoss() #这个函数自带softmax功能,所以输入的x不是概率形式也行,所有下面output还有负数
optim = torch.optim.SGD(tudui.parameters(),lr=0.01) #学习率设置得太大,模型很不稳定,太小的话,又很慢,一般来说,先大后小
for epoch in range(20): #相当于每个图片只看了一遍,所以需要epoch套个for循环多看几遍
running_loss = 0.0
for data in dataloader:
imgs,targets = data
output = tudui(imgs)
result = loss(output,targets)
optim.zero_grad() #将梯度归零,上一个循环中每个参数对应的梯度清零。 不清零 叠加 weight直接起飞
result.backward() #计算梯度 ,优化器根据梯度进行的优化,梯度是通过损伤函数得来的
optim.step() #调用优化器,对每个参数进行调优. 这里其实是后传播更新了参数,参数变化所以导致权重的data发生变化
running_loss = running_loss +result #每一轮所有的loss的和
print(running_loss)
十五.model
'''
1.pytorch中的官方文档torch.nn,containers(容器)即里面是神经网络的骨架,其他的都是填充进去的,如池化等
2.这里用的是单步执行我的代码
3.采用序列来传参,也就是[]
'''
import torch
from torch import nn
class tudui(nn.Module):
def __init__(self): #__init__子类的方法中,可以指定如何初始化属性或执行特定于子类的任何其他设置。
super().__init__() #调用super().__init__(),可以确保父类nn.Module已正确初始化。这很重要,因为nn.Module提供了定义神经网络层和管理神经网络状态的基本功能。
def forward(self,input):
output = input +1
return output
Tudui = tudui()
x = torch.Tensor([1.0]) #采用序列来传参,也就是[]
output = Tudui(x) #因为父类中nn.Module中forword方法是__call__()方法的实现,当继承后,可调用对象会调用__call__()方法,进而forward所以可以直接传参
print(output)
'''
在 nn.Module 中,__call__ 方法被实现为调用 forward 方法。
当你写下 output = Tudui(x) 这行代码时,Python 会自动调用 Tudui 这个实例对象的 __call__ 方法,
而 __call__ 方法实际上又会调用 forward 方法,并把 x 作为参数传递给 forward 方法。
'''
十六.test-modle
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.modle = 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(1024,64),
nn.Linear(64,10)
)
def forward(self,x):
x = self.modle(x)
return x
if __name__ == '__main__': #if __name__ == 'main': 下的代码只有在文件作为脚本直接执行时才会被执行,而 该py脚本import 到其他脚本中时,if__name__==.....后的代码不会被执行。
tudui = Tudui()
input = torch.ones([64,3,32,32]) #利用这个来检查我们写的是否正确
output = tudui(input)
print(output.shape)
#Python脚本既可以作为主程序直接运行,也可以作为模块被其他Python脚本导入和重用。
# if __name__ == '__main__':允许你区分脚本是被直接运行还是被导入为模块使用。
十七.model-pretained
import torchvision
from torch import nn
# train_data = torchvision.datasets.ImageNet("./data_imagenet","train",transform=torchvision.transforms
# .ToTensor())
vgg16_false = torchvision.models.vgg16(weights=None) #没训练的也不可能为0,网络参数的初始化全0会导致无法打破对称性,一般都是随机初始化的数
vgg16_true = torchvision.models.vgg16(weights="DEFAULT")
vgg16_true.classifier.add_module("add_linear",nn.Linear(1000,10)) #在现有的网络加些东西,加外面(vgg16_true.add_module)或者里面
print(vgg16_true) #print可以看见网络结构
print(vgg16_false)
vgg16_false.classifier[6] = nn.Linear(4096,10) #修改特定的层
print(vgg16_false)
十八.model-save
#终端输ls看文件
import torch
import torchvision.models
from torch import nn
from torch.nn import Flatten
vgg16 = torchvision.models.vgg16()
#保存方式1 保存的模型的结构和模型的参数
torch.save(vgg16,"./vgg16_model1.pth") #既保存了网络模型结构,也保存了网络模型参数
#保存方式2 模型参数(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_model2.pth") #把vgg16的状态也就是参数保存成一个字典
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.module1 = nn.Sequential( #这个compose的功能差不多,更加简洁
nn.Conv2d(3, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding="same"),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding="same"),
nn.MaxPool2d(2),
Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
x = self.module1(x)
return x
tudui = Tudui()
torch.save(tudui,"tudui_model1.pth")
十九.model-load
import torch
import torchvision
from torch import nn
from torch.nn import Flatten
from model_save import *
#保存方式1 加载模型
model = torch.load("./vgg16_model1.pth")
# print(model)
#方式2 加载模型
vgg16 = torchvision.models.vgg16()
vgg16.load_state_dict(torch.load("./vgg16_model2.pth")) #网络结构加参数组成一个完整的模型
# model = torch.load("./vgg16_model2.pth") #加载了一个字典的参数
# print(model)
# print(vgg16)
#陷阱
# class Tudui(nn.Module):
# def __init__(self):
# super().__init__()
#
# self.module1 = nn.Sequential( #这个compose的功能差不多,更加简洁
# nn.Conv2d(3, 32, 5, padding="same"),
# nn.MaxPool2d(2),
# nn.Conv2d(32, 32, 5, padding="same"),
# nn.MaxPool2d(2),
# nn.Conv2d(32, 64, 5, padding="same"),
# nn.MaxPool2d(2),
# Flatten(),
# nn.Linear(1024, 64),
# nn.Linear(64, 10)
# )
#
# def forward(self,x):
# x = self.module1(x)
# return x
model = torch.load("tudui_model1.pth") #仅针对,自己的模型,要让这个程序能够访问到tudui这个类,所有最前面要impot那个文件,或者把网络结构复制过来复制
print(model) #要让程序访问到model的类
#import的是类,我们保存的实例化的模型是有参数的
二十.train
import torch
import torchvision.datasets
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
from model import * #使用*来导入模块中的所有内容,包括函数、类、变量等.表示从指定的模块中导入所有的内容,而不需要显式地指定导入哪些具体的项。
#尽管这种导入方式可能会方便,但通常不是一个良好的做法,特别是在大型项目中,因为它可能会导致命名冲突和不可维护的代码。
#注意导入的一定要在同一文件夹下
#准备数据集
train_data = torchvision.datasets.CIFAR10("data",True,transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),
download=True)
#数据集长度
test_data_size = len(test_data)
train_data_size = len(train_data)
print(f"训练集的长度为:{test_data_size}") #ctrl +d 复制
print(f"测试集的长度为:{train_data_size}")
#利用Dataloader来加载数据集
train_dataloader = DataLoader(train_data,64)
test_dataloader = DataLoader(test_data,64)
#搭建神经网络 一般喜欢将神经网络单独放在一个model文件夹内,然后import进来
# class Tudui(nn.module):
# def __init__(self):
# super().__init__()
# self.modle = 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*64*4,64),
# nn.Linear(64,10)
#
# )
# def forward(self,x):
# x = self.modle(x)
# return x
#创建网络模型
tudui = Tudui()
#定义损失函数
loss_fn = nn.CrossEntropyLoss()
#优化器
learning_rate = 0.01 #建议单独把参数拿出来,方便改进
#0.01 = 1e-2=1 * 10^(-2)
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate)
#设置训练网络的一些参数
#记录训练的次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#训练的轮数
epoch = 10
#添加tensorboard
writer = SummaryWriter("logs") #深色线:平滑处理;浅色线:真实曲线
# 训练步骤开始
start_time = time.time()
tudui.train() #这个只对Dropout, BatchNorm, etc.等网络层调用的时候才有用,没有这些层不调用也可
for i in range(epoch):
print(f"----------第{i+1}轮训练开始------------")
for data in train_dataloader:
imgs,targets = data
output = tudui(imgs)
loss = loss_fn(output,targets)
#优化器调优
optimizer.zero_grad()
loss.backward()
optimizer.step()
#记录训练次数
total_train_step +=1
if total_train_step%100==0 : #训练次数太多了,根本看不清,所以100个再打印
end_time = time.time()
print(end_time - start_time)
print(f"训练次数: {total_train_step},loss: {loss.item()}") #loss加上.item(),可以将tensor数据类型改成数字输出,为后面的loss可视化做准备
writer.add_scalar("train_loss",loss,total_train_step)
#测试步骤开始 可能应该是验证集,测试集是最后训练好了之后测试模型效果
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #每训练完一轮,将模型放在测试集上验证一下效果,nograd意思是没有梯度,所以也就没有调优
for data in test_dataloader:
imgs,targets = data
output = tudui(imgs)
loss = loss_fn(output,targets)
total_test_loss =loss + total_test_loss
accuracy = (output.argmax(1)==targets).sum()
total_accuracy = total_accuracy +accuracy
print(f"整体的testloss:{total_test_loss}")
print(f"整体测试集上的正确率: {total_accuracy/test_data_size}")
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("测试的正确率",total_accuracy/test_data_size,total_test_step)
total_test_step += 1
torch.save(tudui,f"tudui_{i}.pth")
#torch.save(tudui.state_dict(),"tudui_{i}.pth") #另一种保存方式
print("模型已保存")
writer.close()
二十一.train-gpu1
#网络模型
#数据的输入和标注
#损失函数
#if torch.cuda.is_available():
# .cuda()
'''
在谷歌上colab可以使用gpu,加!相当去运行终端命令
nvidia-smi可以看显卡配置
例子:if torch.cuda.is_available():
tudui = tudui.cuda()
'''
import time
import torch
import torchvision.datasets
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import * #使用*来导入模块中的所有内容,包括函数、类、变量等.表示从指定的模块中导入所有的内容,而不需要显式地指定导入哪些具体的项。
#尽管这种导入方式可能会方便,但通常不是一个良好的做法,特别是在大型项目中,因为它可能会导致命名冲突和不可维护的代码。
#注意导入的一定要在同一文件夹下
#准备数据集
train_data = torchvision.datasets.CIFAR10("data",True,transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),
download=True)
#数据集长度
test_data_size = len(test_data)
train_data_size = len(train_data)
print(f"训练集的长度为:{test_data_size}") #ctrl +d 复制
print(f"测试集的长度为:{train_data_size}")
#利用Dataloader来加载数据集
train_dataloader = DataLoader(train_data,64)
test_dataloader = DataLoader(test_data,64)
#搭建神经网络 一般喜欢将神经网络单独放在一个model文件夹内,然后import进来
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.modle = 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(1024,64),
nn.Linear(64,10)
)
def forward(self,x):
x = self.modle(x)
return x
#创建网络模型
tudui = Tudui()
if torch.cuda.is_available():
tudui = tudui.cuda()
#定义损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.cuda()
#优化器
learning_rate = 0.01 #建议单独把参数拿出来,方便改进
#0.01 = 1e-2=1 * 10^(-2)
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate)
#设置训练网络的一些参数
#记录训练的次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#训练的轮数
epoch = 10
#添加tensorboard
writer = SummaryWriter("logs") #深色线:平滑处理;浅色线:真实曲线
# 训练步骤开始
start_time = time.time()
tudui.train() #这个只对Dropout, BatchNorm, etc.等网络层调用的时候才有用,没有这些层不调用也可
for i in range(epoch):
print(f"----------第{i+1}轮训练开始------------")
for data in train_dataloader:
imgs,targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
output = tudui(imgs)
loss = loss_fn(output,targets)
#优化器调优
optimizer.zero_grad()
loss.backward()
optimizer.step()
#记录训练次数
total_train_step +=1
if total_train_step%100==0 : #训练次数太多了,根本看不清,所以100个再打印
end_time = time.time()
print(end_time-start_time)
print(f"训练次数: {total_train_step},loss: {loss.item()}") #loss加上.item(),可以将tensor数据类型改成数字输出,为后面的loss可视化做准备
writer.add_scalar("train_loss",loss,total_train_step)
#测试步骤开始 可能应该是验证集,测试集是最后训练好了之后测试模型效果
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #每训练完一轮,将模型放在测试集上验证一下效果,nograd意思是没有梯度,所以也就没有调优
for data in test_dataloader:
imgs,targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
output = tudui(imgs)
loss = loss_fn(output,targets)
total_test_loss =loss + total_test_loss
accuracy = (output.argmax(1)==targets).sum()
total_accuracy = total_accuracy +accuracy
print(f"整体的testloss:{total_test_loss}")
print(f"整体测试集上的正确率: {total_accuracy/test_data_size}")
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("测试的正确率",total_accuracy/test_data_size,total_test_step)
total_test_step += 1
torch.save(tudui,f"tudui_{i}.pth")
#torch.save(tudui.state_dict(),"tudui_{i}.pth") #另一种保存方式
print("模型已保存")
writer.close()
'''
在谷歌上colab可以使用gpu,加!相当去运行终端命令
nvidia-smi可以看显卡配置
#模型和损失函数都可以不用赋值,但是数据需要
网络模型
数据的输入和标注
损失函数
第二种方式:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 这是一种语法糖
'''
import time
import torch
import torchvision.datasets
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import * #使用*来导入模块中的所有内容,包括函数、类、变量等.表示从指定的模块中导入所有的内容,而不需要显式地指定导入哪些具体的项。
#尽管这种导入方式可能会方便,但通常不是一个良好的做法,特别是在大型项目中,因为它可能会导致命名冲突和不可维护的代码。
#注意导入的一定要在同一文件夹下
#定义训练的设备
# device = torch.device("cpu")
device = torch.device("cuda:0")
#准备数据集
train_data = torchvision.datasets.CIFAR10("data",True,transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10("data",False,transform=torchvision.transforms.ToTensor(),
download=True)
#数据集长度
test_data_size = len(test_data)
train_data_size = len(train_data)
print(f"训练集的长度为:{test_data_size}") #ctrl +d 复制
print(f"测试集的长度为:{train_data_size}")
#利用Dataloader来加载数据集
train_dataloader = DataLoader(train_data,64)
test_dataloader = DataLoader(test_data,64)
#搭建神经网络 一般喜欢将神经网络单独放在一个model文件夹内,然后import进来
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.modle = 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(1024,64),
nn.Linear(64,10)
)
def forward(self,x):
x = self.modle(x)
return x
#创建网络模型
tudui = Tudui()
# if torch.cuda.is_available():
# tudui = tudui.cuda()
tudui = tudui.to(device) #模型和损失函数都可以不用赋值,但是数据需要
#定义损失函数
loss_fn = nn.CrossEntropyLoss()
# if torch.cuda.is_available():
# loss_fn = loss_fn.cuda()
loss_fn = loss_fn.to(device)
#优化器
learning_rate = 0.01 #建议单独把参数拿出来,方便改进
#0.01 = 1e-2=1 * 10^(-2)
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate)
#设置训练网络的一些参数
#记录训练的次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#训练的轮数
epoch = 10
#添加tensorboard
writer = SummaryWriter("logs") #深色线:平滑处理;浅色线:真实曲线
# 训练步骤开始
start_time = time.time()
tudui.train() #这个只对Dropout, BatchNorm, etc.等网络层调用的时候才有用,没有这些层不调用也可
for i in range(epoch):
print(f"----------第{i+1}轮训练开始------------")
for data in train_dataloader:
imgs,targets = data
# if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
output = tudui(imgs)
loss = loss_fn(output,targets)
#优化器调优
optimizer.zero_grad()
loss.backward()
optimizer.step()
#记录训练次数
total_train_step +=1
if total_train_step%100==0 : #训练次数太多了,根本看不清,所以100个再打印
end_time = time.time()
print(end_time-start_time)
print(f"训练次数: {total_train_step},loss: {loss.item()}") #loss加上.item(),可以将tensor数据类型改成数字输出,为后面的loss可视化做准备
writer.add_scalar("train_loss",loss,total_train_step)
#测试步骤开始 可能应该是验证集,测试集是最后训练好了之后测试模型效果
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #每训练完一轮,将模型放在测试集上验证一下效果,nograd意思是没有梯度,所以也就没有调优
for data in test_dataloader:
imgs,targets = data
# if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
output = tudui(imgs)
loss = loss_fn(output,targets)
total_test_loss =loss + total_test_loss
accuracy = (output.argmax(1)==targets).sum()
total_accuracy = total_accuracy +accuracy
print(f"整体的testloss:{total_test_loss}")
print(f"整体测试集上的正确率: {total_accuracy/test_data_size}")
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("测试的正确率",total_accuracy/test_data_size,total_test_step)
total_test_step += 1
torch.save(tudui,f"tudui_{i}.pth")
#torch.save(tudui.state_dict(),"tudui_{i}.pth") #另一种保存方式
print("模型已保存")
writer.close()
二十二.useful-transforms
'''
1.使用transforms要多关注输入输出的类型
2.官方文档很重要
3.关注方法需要什么参数
4.不知道返回值时,可以print,debug,type等
sequence序列也就是(H,W)类似的
'''
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
img = Image.open("dataset/train/ants/0013035.jpg")
print(img)
#Totensor
trans_totensor = transforms.ToTensor()
tensor_img = trans_totensor(img)
writer.add_image("图片",tensor_img)
#normalize 归一化,将数据缩放为均值0,标准差为1,作用是旨在使神经网络的训练更加稳定和有效,梯度消失和梯度爆炸.
# 归一化是为了消除奇异值,及样本数据中与其他数据相比特别大或特别小的数据 这样可以加快训练速度
print(tensor_img[0][0][0])
trans_norm = transforms.Normalize([4,5,0.5],[0.5,0.5,0.5])
img_norm = trans_norm(tensor_img)
print(img_norm[0][0][0])
writer.add_image("图片1",img_norm,1)
#Resize 单是一个int的话,代表最小的边符合
print(img.size)
resize = transforms.Resize((512,512))
resize_img = resize(img)
resize_img = trans_totensor(resize_img)
writer.add_image("resize",resize_img,0)
print(resize_img)
#compose 将多个transforms合在一起
resize_img_2 = transforms.Resize(218)
print(resize_img_2.size)
compose = transforms.Compose([resize_img_2,trans_totensor])#compose中需要一个列表的参数,参数就是transforms,不用提前在前面实例化
img1 = compose(img)
print(img1.size)
writer.add_image("compose",img1,0)
#randomcrop,随机裁剪
randomcrop = transforms.RandomCrop((218,512))
compose2 = transforms.Compose([randomcrop,trans_totensor]) #compose要求输入和输出必须相匹配
for i in range(10):
img_crop = compose2(img)
writer.add_image("cropHW",img_crop,i)
writer.close()
二十三.正确率的计算
import torch
#0 1
output = torch.tensor([[0.1,0.2], #0
[0.3,0.4]]) #1
print(output.argmax(1)) #0.1,0.2是模型对于0,1两个类别预测的结果,选大的那个为结果,argmax函数1代表横向看,则0.2大输出1,第二行0.4大,输出1,所以结果【1,1】
preds = output.argmax(1)
targets = torch.tensor([0,1]) #这里的0,1是图片的真实标签
print(preds==targets) #相等则为true即预测正确,反之
print((output==targets).sum()) #其和就等于预测正确的个数