目录
一:建立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 nn
class 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 nn
class 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 json
from tqdm import tqdm
import torch
import torch.nn as nn
from torchvision import transforms,datasets
from model import AlexNet
def main():
# 看看是否使用cpu
device=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 exist
assert 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中训练后返回outputs
outputs=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_accuracy
torch.save(net.state_dict(),save_path)
4.完整的训练代码:
import os
import sys
import json
from tqdm import tqdm
import torch
import torch.nn as nn
from torchvision import transforms,datasets
from model import AlexNet
def main():
# 看看是否使用cpu
device=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 exist
assert 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
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)
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中训练后返回outputs
outputs = 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_accuracy
torch.save(net.state_dict(), save_path)
三:预测和模型评分(在predict文件中写)
import os
import json
import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
from model import AlexNet
def 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这个变量中就可以