【注意事项总结】
全部来源:https://blog.csdn.net/qq_43665602?type=blog感谢大佬提供灵感,大家快去关注!!!
训练神经网络时,一个epoch还没跑完,报错:
RuntimeError: stack expects each tensor to be equal size, but got [3, 224, 224] at entry 0 and [1, 224, 224] at entry 6
读入了通道数为1的图片。
解决方式:
① 检查数据集,有一个无关的黑白图片,删掉。
② 黑白图片有用的话,将黑白图片的通道数改成3通道。
在制作数据集的getitem方法里,将获取的img转成3通道。
img=img.convert("RGB")
class MyData(Dataset):
def __init__(self,root_dir,label_dir,transform=None):
'''初始化类:根据一个类创建实例。为整个class/后面的函数提供全局变量'''
'''获取图片的步骤
1、创建图片的列表:获取文件夹;获取文件夹下所有的图片root_dir
2、label:这里的label是图片上一级的名称(文件夹的名字)
'''
# self:指定了类里面的一个全局变量
#获取图片的列表和label
#a_base/dataset/train
self.root_dir=root_dir
#ants或bees,注意:文件名就是label
self.label_dir=label_dir
#将路径连接起来,就是ants或bees的路径
self.path=os.path.join(self.root_dir,self.label_dir)
#获取所有图片的地址,放在一个list里面(只是获取了所有图片的名字)
self.img_path=os.listdir(self.path)
self.transform=transform
def __getitem__(self, idx):
'''从路径获取每一张图片'''
#根据索引获取图片的名称
img_name=self.img_path[idx]
#上一步只是知道了名称。加上相对路径:加上每一张图片的路径
img_item_path=os.path.join(self.root_dir,self.label_dir,img_name) #这一步把每一个图片的路径都获取到了
#获取img:从路径打开图片
img=Image.open(img_item_path)
#img=img.convert("RGB")
#获取label
label=self.label_dir
if self.transform:
img=self.transform(img)
if label=="ants":
label=torch.tensor(1)
else:label=torch.tensor(0)
return img,label
def __len__(self):
'''返回数据集的长度:图片的数量'''
return len(self.img_path)
'''创建数据集'''
#获取蚂蚁的训练数据集
root_dir_train= "dataset/train"
ants_label_dir_train="ants"
ants_dataset=MyData(root_dir_train,ants_label_dir_train,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(),]
))
#查看第一张图片的信息
print(ants_dataset[0])
img,label=ants_dataset[0]
print("图片的信息:{},图片的label:{}".format(img,label))
print('图片的数据类型:{}'.format(type(img)))
print('label的数据类型:{}'.format(type(label)))
#查看第一张图片
#img.show()
#获取蜜蜂的训练数据集
bees_label_dir_train="bees"
bees_dataset=MyData(root_dir_train,bees_label_dir_train,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#获取测试集
#蚂蚁
root_dir_test="dataset/val"
ants_label_dir_test="ants"
ants_dataset_test=MyData(root_dir_test,ants_label_dir_test,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#蜜蜂
bees_label_dir_test="bees"
bees_dataset_test=MyData(root_dir_test,bees_label_dir_test,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#整个训练数据集:对象可以相加
train_dataset=ants_dataset+bees_dataset #训练集的图片
#整个测试数据集:
test_dataset=ants_dataset_test+bees_dataset_test #测试集的图片
#定义训练的设备
device=torch.device("cuda:0")
'''查看数据集的长度'''
train_data_size=len(train_dataset)
test_data_size=len(test_dataset)
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))
'''利用dataloader加载数据集'''
train_dataloader=DataLoader(train_dataset,batch_size=10)
test_dataloader=DataLoader(test_dataset,batch_size=10)
输出一下结果对不对:
可以看到img和label的数据类型均为tensor数据类型。
'''测试一下数据的形状'''
for (data,label) in train_dataloader:
print('数据:{}'.format(data))
print('数据的形状:{}'.format(data.shape))
print('数据类型:{}'.format(type(data)))
print('label:{}'.format(label))
print('label的形状:{}'.format(label.shape))
print('label类型:{}'.format(type(label)))
break
结果:为什么label的Size是10,因为此时batch_size是10。
使用ResNet34预训练网络。测试的时候发现修改的网络和之前修改的网络(Resnet34)分类数都有问题!!!如何修改正确的网络看(三)修改网络。这部分只写如何测试网络。
'''3创建网络模型 resnet34 预训练的模型'''
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
resnet34.fc=nn.Sequential(
nn.Linear(resnet34.fc.in_features,resnet34.fc.out_features),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(resnet34.fc.out_features,2)
)
resnet34.add_module("Softmax",nn.Softmax(dim=None))
resnet34=resnet34.cuda()
print(resnet34)
inp_tensor=torch.randint(10,size=(1,3,244,244),dtype=torch.float32)
inp_tensor=inp_tensor.cuda()
print('输入图片的形状:{}'.format(inp_tensor))
out=resnet34(inp_tensor)
print('测试网络的输出结果:{}'.format(out))
print('测试网络输出结果的形状:{}'.format(out.shape))
此处以ResNet34为例,将网络修改成分为两类的分类器。
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
resnet34=resnet34.cuda()
print(resnet34)
'''3创建网络模型 resnet34 预训练的模型'''
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
resnet34.fc=nn.Linear(in_features=512,out_features=128)
resnet34.add_module("fc2",nn.Linear(in_features=128,out_features=2))
resnet34.add_module("softmax",nn.Softmax())
resnet34=resnet34.cuda()
print(resnet34)
结果:分类的结果还是128!!!并没有分成2。原因是必须在fc这个模块里进行修改。
网络模型:
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
#resnet34.fc=nn.Linear(in_features=512,out_features=2)
resnet34.add_module("fc_add",nn.Linear(in_features=1000,out_features=2))
resnet34.add_module("softmax",nn.Softmax())
resnet34=resnet34.cuda()
测试案例:
inp_tensor=torch.randint(10,size=(1,3,244,244),dtype=torch.float32)
inp_tensor=inp_tensor.cuda()
print('输入图片的形状:{}'.format(inp_tensor))
out=resnet34(inp_tensor)
print('测试网络的输出结果:{}'.format(out))
print('测试网络输出结果的形状:{}'.format(out.shape))
结果:输出的结果还是1000!!!加的out_feature=1根本没用!!!
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
resnet34.fc=nn.Sequential(
nn.Linear(resnet34.fc.in_features,resnet34.fc.out_features),
nn.ReLU(),
nn.Dropout(0.4),
nn.Linear(resnet34.fc.out_features,2)
)
resnet34.add_module("Softmax",nn.Softmax(dim=None))
resnet34=resnet34.cuda()
print(resnet34)
inp_tensor=torch.randint(10,size=(1,3,244,244),dtype=torch.float32)
inp_tensor=inp_tensor.cuda()
print('输入图片的形状:{}'.format(inp_tensor))
out=resnet34(inp_tensor)
print('测试网络的输出结果:{}'.format(out))
print('测试网络输出结果的形状:{}'.format(out.shape))
'''3创建网络模型 resnet34 预训练的模型'''
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
#直接修改全连接层
resnet34.fc=nn.Linear(in_features=512,out_features=2)
#加入softmax层输出的就是一个概率值
#resnet34.add_module("softmax",nn.Softmax())
resnet34=resnet34.cuda()
print(resnet34)
效果一般般。。。
要点就是要将线性层加到模块里面去!!!
vgg16_t=torchvision.models.vgg16(pretrained=True,progress=True)
vgg16_t.classifier.add_module("7",nn.Linear(1000,10))
vgg16_t=vgg16_t.to(device)
print(vgg16_t)
原因可能是过拟合(在训练集上的效果好,在测试集上的效果差,泛化能力差)了,或者参数选择,模型太大等等。。。
参见:https://blog.csdn.net/shanglianlm/article/details/85019633
网络训练初期希望学习率大一点,后期希望学习率小一点,可以使用学习率衰减策略。
参见:https://blog.csdn.net/qq_43665602/article/details/126981783?spm=1001.2014.3001.5502
完整代码:
# function:将数据加载进来后进行分类
from torch.utils.data import Dataset
from PIL import Image
import os
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
#继承Dataset类
#在Dataset里就要进行图片数据类型的转换
class MyData(Dataset):
def __init__(self,root_dir,label_dir,transform=None):
'''初始化类:根据一个类创建实例。为整个class/后面的函数提供全局变量'''
'''获取图片的步骤
1、创建图片的列表:获取文件夹;获取文件夹下所有的图片root_dir
2、label:这里的label是图片上一级的名称(文件夹的名字)
'''
# self:指定了类里面的一个全局变量
#获取图片的列表和label
#a_base/dataset/train
self.root_dir=root_dir
#ants或bees,注意:文件名就是label
self.label_dir=label_dir
#将路径连接起来,就是ants或bees的路径
self.path=os.path.join(self.root_dir,self.label_dir)
#获取所有图片的地址,放在一个list里面(只是获取了所有图片的名字)
self.img_path=os.listdir(self.path)
self.transform=transform
def __getitem__(self, idx):
'''从路径获取每一张图片'''
#根据索引获取图片的名称
img_name=self.img_path[idx]
#上一步只是知道了名称。加上相对路径:加上每一张图片的路径
img_item_path=os.path.join(self.root_dir,self.label_dir,img_name) #这一步把每一个图片的路径都获取到了
#获取img:从路径打开图片
img=Image.open(img_item_path)
#img=img.convert("RGB")
#获取label
label=self.label_dir
if self.transform:
img=self.transform(img)
if label=="ants":
label=torch.tensor(1)
else:label=torch.tensor(0)
return img,label
def __len__(self):
'''返回数据集的长度:图片的数量'''
return len(self.img_path)
'''创建数据集'''
#获取蚂蚁的训练数据集
root_dir_train= "dataset/train"
ants_label_dir_train="ants"
ants_dataset=MyData(root_dir_train,ants_label_dir_train,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(),]
))
#查看第一张图片的信息
print(ants_dataset[0])
img,label=ants_dataset[0]
print("图片的信息:{},图片的label:{}".format(img,label))
print('图片的数据类型:{}'.format(type(img)))
print('label的数据类型:{}'.format(type(label)))
#查看第一张图片
#img.show()
#获取蜜蜂的训练数据集
bees_label_dir_train="bees"
bees_dataset=MyData(root_dir_train,bees_label_dir_train,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#获取测试集
#蚂蚁
root_dir_test="dataset/val"
ants_label_dir_test="ants"
ants_dataset_test=MyData(root_dir_test,ants_label_dir_test,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#蜜蜂
bees_label_dir_test="bees"
bees_dataset_test=MyData(root_dir_test,bees_label_dir_test,
transform=torchvision.transforms.Compose(
[torchvision.transforms.Resize((224,224)),
torchvision.transforms.ToTensor(), ]
))
#整个训练数据集:对象可以相加
train_dataset=ants_dataset+bees_dataset #训练集的图片
#整个测试数据集:
test_dataset=ants_dataset_test+bees_dataset_test #测试集的图片
#定义训练的设备
device=torch.device("cuda:0")
'''查看数据集的长度'''
train_data_size=len(train_dataset)
test_data_size=len(test_dataset)
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_size))
'''利用dataloader加载数据集'''
train_dataloader=DataLoader(train_dataset,batch_size=10,shuffle=True)
test_dataloader=DataLoader(test_dataset,batch_size=10,shuffle=True)
# for batch,(img,label) in enumerate(train_dataloader):
# channels=img.shape[1]
# if channels==1:
# print(batch)
# img=torch.permute(img[0],dims=(1,2,0))
# plt.imshow(img,cmap='gray')
# plt.show()
'''3创建网络模型 resnet34 预训练的模型'''
resnet34=torchvision.models.resnet34(pretrained=True,progress=True)
resnet34.fc=nn.Sequential(
nn.Linear(resnet34.fc.in_features,resnet34.fc.out_features),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(resnet34.fc.out_features,2)
)
resnet34.add_module("Softmax",nn.Softmax(dim=None))
resnet34=resnet34.cuda()
print(resnet34)
'''4创建损失函数'''
loss_fn = nn.CrossEntropyLoss()
loss_fn=loss_fn.cuda()
'''5优化器'''
learning_rate = 1e-4 # 学习率
optimizer = torch.optim.Adam(resnet34.parameters(), learning_rate, )
'''6设置训练啊网络的一些参数'''
total_train_step = 0 # 记录训练的次数
total_test_step = 0 # 记录测试的次数
epoch = 10 # 记录训练的轮数 第七轮开始往后精度不在上升
for i in range(epoch):
print("-----------------第{}轮训练开始:------------------".format(i))
'''训练步骤开始'''
resnet34.train()
total_train_accuracy=0 #训练集的正确个数
for data in train_dataloader:
imgs, targets = data # 取数据
imgs=imgs.cuda()
targets=targets.cuda()
outputs = resnet34(imgs) # 送入神经网络模型
loss = loss_fn(outputs, targets) # 计算误差
'''训练一次:优化器优化模型'''
optimizer.zero_grad() # 梯度清零
loss.backward() # 反向传播
optimizer.step() # 对参数进行优化
total_train_step = total_train_step + 1
print("在训练集上的训练次数:{},loss:{}".format(total_train_step, loss.item())) # 例如.item()可以把一个tensor数据类型转换成真实的数字
train_accuracy = (outputs.argmax(1) == targets).sum() # outputs.argmax(1)==targets 预测结果和标签值相同的求和
total_train_accuracy = total_train_accuracy + train_accuracy
#整体训练集的准确率
print("整体训练集上的准确率:{}".format(total_train_accuracy/train_data_size))
'''训练完一轮后,在测试数据集上进行测试,看是否需要终止训练'''
'''测试步骤'''
resnet34.eval()
total_test_loss = 0
total_accuracy = 0 # 整体正确的个数
with torch.no_grad(): # 没有梯度
for data in test_dataloader:
imgs, targets = data
imgs=imgs.cuda()
targets=targets.cuda()
outputs = resnet34(imgs)
loss = loss_fn(outputs, targets) # loss是一部分loss
total_test_loss = loss.item() + total_test_loss # 整体loss
# 指标
accuracy = (outputs.argmax(1) == targets).sum() # outputs.argmax(1)==targets 预测结果和标签值相同的求和
total_accuracy = total_accuracy + accuracy
print("整体测试集上的loss:{}".format(total_test_loss))
print("整体测试集上的正确率:{}".format(total_accuracy / test_data_size))
total_test_step = total_test_step + 1
'''保存模型'''
#torch.save(resnet34, "resnet34_t_{}.pth".format(i)) # 将每一轮训练的结果都保存一下
#print("模型已经保存")
CIFAR10模型上效果较差,使用学习率衰减策略试一下
前期的图像处理问题
冻结一部分参数,修改网络的一部分参数