目录
一:建立AlexNet模型(在model文件中写)
1.构造5层卷积层
2.构造3层神经网络层
3.forward函数
4.模型最终代码
二:训练数据(在train中写)
1.读出数据
2.训练
3. 测试模型更新参数
4.完整的训练代码:
三:预测和模型评分(在predict文件中写)
四:代码使用:
点个赞呗!!!!!!
一:建立AlexNet模型(在model文件中写)
AlexNet网络结构相对简单,使用了8层卷积神经网络,前5层是卷积层,剩下的3层是全连接层
1.构造5层卷积层
Conv2d:构造卷积层,参数:(输入通道数,输出通道数等价于卷积核个数,卷积核大小,步长,加0)
ReLU:激活函数,inplace设置是否改变数据
MaxPool2d:池化操作,参数:(核大小,步长)
import torch
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self,num_classes=1000):super().__init__()self.features = nn.Sequential(nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(48, 128, kernel_size=5, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(128, 192, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(192, 192, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(192, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2) # output [128, 6, 6])
2.构造3层神经网络层
Dropout:将比例数据置空,比如数据为(1,2,3,4,5,6),当参数p=0.5时,数据会变成:(1,0,3,0,5,0)。p代表置空的比例,当然这个置空是随机的
Linear:线性变换,参数:(输入数据的通道数,输出数据的通道数)
ReLu:和上面一样
self.classifier = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(128 * 6 * 6, 2048),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(2048, 2048),nn.ReLU(inplace=True),nn.Linear(2048, num_classes),)
3.forward函数
torch.nn.Flatten(start_dim=1, end_dim=-1)
start_dim与end_dim代表合并的维度,开始的默认值为1,结束的默认值为 - 1,因此常被使用在神经网络当中,将每个batch的数据拉伸成一维
forward的作用:先让数据经过5层卷积,在经过3层全连接层
def forward(self, x):x = self.features(x)x = torch.flatten(x, start_dim=1)x = self.classifier(x)return x
4.模型最终代码
import torch
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self, num_classes=1000):super().__init__()self.features = nn.Sequential(nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(48, 128, kernel_size=5, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(128, 192, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(192, 192, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(192, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2) # output [128, 6, 6])self.classifier = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(128 * 6 * 6, 2048),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(2048, 2048),nn.ReLU(inplace=True),nn.Linear(2048, num_classes),)def forward(self, x):torch.nn.Flatten(start_dim=1, end_dim=-1)x = self.features(x)x = torch.flatten(x, start_dim=1)x = self.classifier(x)return x
二:训练数据(在train中写)
1.读出数据
RandomResizedCrop:对图片的区域随机改变图片的大小
RandomHorizontalFlip:对图片进行随机翻转
ToTensor:将图片转为Tensor数据类型
Normalize:对数据进行标准化,参数:(标准化的平均值元组,方差元组)
datasets.ImageFolder:读数据类别返回一个字典{0:类别一,1:类别二}, 这行代码可以获取数据的类别数以及对应的类别标签。以字典的形式保存
参数:(root:读取文件的路径(注意:路径文件中不能直接放图片,应该放各个图片类别的文件),transfrom:对图片进行预处理函数)
torch.utils.data.DataLoader:读取数据:参数(dataset:数据加载的数据集,batch_size:每次加载多少样本数,suffle:是否打乱数据,num_workers:最多并行加载数量)
import os
import sys
import jsonfrom tqdm import tqdm
import torch
import torch.nn as nn
from torchvision import transforms,datasetsfrom model import AlexNetdef main():# 看看是否使用cpudevice=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')print(f'using:',{device})# 要读入图片的目录路径,以下代码假设你这个路径下有文件(train训练集, val测试集)image_path=os.path.join('./','训练集和测试集的图片路径')# print(image_path)# 判断这个路径是否存在,若不存在则报错image path done nit existassert os.path.exists(image_path),'image path done ont exist'# 创建读入数据后对数据处理的方法集合data_transform={# 将训练数据和测试数据的处理集使用对象的方法,以便后面使用'train':transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406),(0.229, 0.224, 0.225))]),'val':transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}train_dataset=datasets.ImageFolder(root=os.path.join(image_path,'train'),transform=data_transform['train'])train_num=len(train_dataset)# print(train_num)flower_list=train_dataset.class_to_idx# print(flower_list)cla_dict=dict((val,key) for key,val in flower_list.items())# print(cla_dict)json_str=json.dumps(cla_dict,indent=4)with open('class_indices.json','w') as json_file:json_file.write(json_str)# print(json_str)batch_size=32# 这行代码可以得到你电脑的cpu最大进程数量,如果大于16那么就按照16来nw=min([os.cpu_count(),batch_size if batch_size>1 else 0,16])print(f'using {nw} dataloader workers every process')# 读取训练集数据train_loader=torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,num_workers=nw)validata_dataset=datasets.ImageFolder(root=os.path.join(image_path,'val'),transform=data_transform['val'])val_num = len(validata_dataset)# 读取测试集数据validata_loader=torch.utils.data.DataLoader(validata_dataset,batch_size=batch_size,shuffle=False,num_workers=nw)
2.训练
每行代码都要注释哦!!!
net.to(device)# 使用交叉熵损失函数loss_fn=nn.CrossEntropyLoss()# 使用优化器类,这里使用Adam优化器optimizer=torch.optim.Adam(net.parameters(),lr=0.0002)# 迭代数量epochs=10# 训练后的参数保存地址save_path='./AlexNet.pth'best_acc=0.0# 训练样本数train_step=len(train_loader)for epoch in range(epochs):# 开启训练模式net.train()# 初始化每次迭代的总损失running_loss=0.0# tqdm是一个进度条,将要迭代的数据放入,可以查看迭代的进度# stdout :它使用其参数直接显示在控制台窗口上。train_bar=tqdm(train_loader,file=sys.stdout)for step,data in enumerate(train_bar):# images是要训练的图片,labels是这张图片的类别images,labels=data# 将优化器的梯度置零optimizer.zero_grad()# 将图片加入到cpu中训练后返回outputsoutputs=net(images.to(device))# 计算损失loss=loss_fn(outputs,labels.to(device))# 反向传播计算参数loss.backward()# 跟新优化器中的参数optimizer.step()# 累加损失running_loss+=loss.item()# 输出语句train_bar.desc = f'train epoch {epoch + 1}/ {epochs} loss: {loss:.3f}'
3. 测试模型更新参数
# 开启预测模型net.eval()acc=0.0# 关闭torch中的梯度记录with torch.no_grad():# 开启进度条val_bar=tqdm(validata_loader,file=sys.stdout)# 开始迭代for val_data in val_bar:# 测试集图片,测试集目标值val_images,val_labels=val_data# 开始预测outputs=net(val_images.to(device))# 获取预测的结果集,因为用的是softmax,所以取没张图片的结果集的最大值就是这张图片的预测结果predict_y=torch.max(outputs,dim=1)[1]# 计算总损失acc+=torch.eq(predict_y,val_labels.to(device)).sum().item()# 计算平均损失val_accuracy=acc/val_num# 打印结果print(f'[epoch {epoch + 1}] train_loss: {running_loss / train_step:.3f}, val_accuracy:{val_accuracy:.3f}')# 如果这次的结果比上次的好,就更新参数,否则不变if val_accuracy>best_acc:best_acc=val_accuracytorch.save(net.state_dict(),save_path)
4.完整的训练代码:
import os
import sys
import jsonfrom tqdm import tqdm
import torch
import torch.nn as nn
from torchvision import transforms,datasetsfrom model import AlexNetdef main():# 看看是否使用cpudevice=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')print(f'using:',{device})# 要读入图片的目录路径,以下代码假设你这个路径下有文件(train训练集, val测试集)image_path=os.path.join('./','训练集和测试集的图片路径')# print(image_path)# 判断这个路径是否存在,若不存在则报错image path done nit existassert os.path.exists(image_path),'image path done ont exist'# 创建读入数据后对数据处理的方法集合data_transform={# 将训练数据和测试数据的处理集使用对象的方法,以便后面使用'train':transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406),(0.229, 0.224, 0.225))]),'val':transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}train_dataset=datasets.ImageFolder(root=os.path.join(image_path,'train'),transform=data_transform['train'])train_num=len(train_dataset)# print(train_num)flower_list=train_dataset.class_to_idx# print(flower_list)cla_dict=dict((val,key) for key,val in flower_list.items())# print(cla_dict)json_str=json.dumps(cla_dict,indent=4)with open('class_indices.json','w') as json_file:json_file.write(json_str)# print(json_str)batch_size=32nw=min([os.cpu_count(),batch_size if batch_size>1 else 0,16])print(f'using {nw} dataloader workers every process')train_loader=torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,num_workers=nw)validata_dataset=datasets.ImageFolder(root=os.path.join(image_path,'val'),transform=data_transform['val'])val_num = len(validata_dataset)validata_loader=torch.utils.data.DataLoader(validata_dataset,batch_size=batch_size,shuffle=False,num_workers=nw)net=AlexNet(num_classes=5)net.to(device)# 使用交叉熵损失函数loss_fn = nn.CrossEntropyLoss()# 使用优化器类,这里使用Adam优化器optimizer = torch.optim.Adam(net.parameters(), lr=0.0002)# 迭代数量epochs = 10# 训练后的参数保存地址save_path = './AlexNet.pth'best_acc = 0.0# 训练样本数train_step = len(train_loader)for epoch in range(epochs):# 开启训练模式net.train()# 初始化每次迭代的总损失running_loss = 0.0# tqdm是一个进度条,将要迭代的数据放入,可以查看迭代的进度# stdout :它使用其参数直接显示在控制台窗口上。train_bar = tqdm(train_loader, file=sys.stdout)for step, data in enumerate(train_bar):# images是要训练的图片,labels是这张图片的类别images, labels = data# 将优化器的梯度置零optimizer.zero_grad()# 将图片加入到cpu中训练后返回outputsoutputs = net(images.to(device))# 计算损失loss = loss_fn(outputs, labels.to(device))# 反向传播计算参数loss.backward()# 跟新优化器中的参数optimizer.step()# 累加损失running_loss += loss.item()# 输出语句train_bar.desc = f'train epoch {epoch + 1}/ {epochs} loss: {loss:.3f}'# 开启预测模型net.eval()acc = 0.0# 关闭torch中的梯度记录with torch.no_grad():# 开启进度条val_bar = tqdm(validata_loader, file=sys.stdout)# 开始迭代for val_data in val_bar:# 测试集图片,测试集目标值val_images, val_labels = val_data# 开始预测outputs = net(val_images.to(device))# 获取预测的结果集,因为用的是softmax,所以取没张图片的结果集的最大值就是这张图片的预测结果predict_y = torch.max(outputs, dim=1)[1]# 计算总损失acc += torch.eq(predict_y, val_labels.to(device)).sum().item()# 计算平均损失val_accuracy = acc / val_num# 打印结果print(f'[epoch {epoch + 1}] train_loss: {running_loss / train_step:.3f}, val_accuracy:{val_accuracy:.3f}')# 如果这次的结果比上次的好,就更新参数,否则不变if val_accuracy > best_acc:best_acc = val_accuracytorch.save(net.state_dict(), save_path)
三:预测和模型评分(在predict文件中写)
import os
import jsonimport torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as pltfrom model import AlexNetdef main():device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')# 处理图片函数data_transform=transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])# 测试图片的路径img_path='./1.jpeg'# 判断这张图片是否存在assert os.path.exists(img_path),f'{img_path} does not exist'# 读取图片img=Image.open(img_path)# 将图片展示出来plt.imshow(img)# 使用上面的函数处理图片img=data_transform(img)# print(img.shape)# 对图片维度进行扩充img=torch.unsqueeze(img,dim=0)print(img.shape)# 训练的时候生成的图片类别文件json_path='./class_indices.json'# 判断类别文件是否存在assert os.path.exists(json_path),f'{json_path} done not exist'# 读取文件with open(json_path,'r') as f:class_dict=json.load(f)print(class_dict)# 建立模型model=AlexNet(num_classes=5).to(device)# 训练后的参数文件weights_path='./AlexNet.pth'# 判断参数文件是否存在assert os.path.exists(weights_path),f'file {weights_path} does not exist'# 模型加载参数model.load_state_dict(torch.load(weights_path))# 开启预测模式model.eval()# 关闭梯度with torch.no_grad():# 预测output=model(img.to(device))print(output)# 对图片维度压缩回来output=torch.squeeze(output).cpu()# 使用softmax函数分类predict=torch.softmax(output,dim=0)# 获得图片的预测概率最大的那个就是这张图片的类别predict_class=torch.argmax(predict).numpy()# 完成打印print_res = f"class: {class_dict[str(predict_class)]}, prob: {predict[predict_class].numpy():.3f}"# 将图片类别写在图片上plt.title(print_res)# 展示图片plt.show()if __name__ == '__main__':main()
四:代码使用:
准备好需要训练和预测的数据集比如:
文件中有训练集和测试集
将flower_data这个路径写到image_path这个变量中就可以