1.AlexNet的结构![](https://i-blog.csdnimg.cn/blog_migrate/d8a30c6a64be1624de468f19011bde17.png)
1.卷积层
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]
nn.ReLU(inplace=True),#inplace的意思是 :若inplace=F,则在计算完之后返回一个新创建的对象,属于值传递。inplace=T计算完之后会修改输入对象的值,即在原来数据的地址上直接修改数据,不需要返回数据。节省内存空间
nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
采用
公式来算feature map:
输入:为3*224*224通道(maxpool)
第一层:48*27*27 卷积核:11
第二层:128*27*27
第三层:128*13*13(maxpool)
第四层:192*13*13
第五层:192*13*13
第六层:128*13*13(maxpool)
第七层:128*6*6
2.全连接层
nn.Dropout(p=0.5),#Dropout的概率为50%,50%的神经元会被消除
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),#初始化类别的个数。其实只有5个
采用dropout随机失活神经元来提高网络的泛化性。
3.前向传播
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1)#展平处理,从通道维度开始,可以用view函数展平。
x = self.classifier(x)#分类处理
return x
附带完整代码:
import torch.nn as nn
import torch
class AlexNet(nn.Module):
def __init__(self, num_classes=5, init_weights=False):
super(AlexNet, self).__init__()
self.features = nn.Sequential( #Sequential 是一个容器,可以将卷积网络打包到features之中,features意思是提取图片特征的功能
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]
nn.ReLU(inplace=True),#inplace的意思是 :若inplace=F,则在计算完之后返回一个新创建的对象,属于值传递。inplace=T计算完之后会修改输入对象的值,即在原来数据的地址上直接修改数据,不需要返回数据。节省内存空间
nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
)
self.classifier = nn.Sequential(#分类器,内容是全连接层
nn.Dropout(p=0.5),#Dropout的概率为50%,50%的神经元会被消除
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),#初始化类别的个数。其实只有5个
)
if init_weights:
self._initialize_weights()#初始化权重函数
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1)#展平处理,从通道维度开始,可以用view函数展平。
x = self.classifier(x)#分类处理
return x
def _initialize_weights(self):
for m in self.modules():#返回一个迭代器,会遍历网络中所有的模块,即迭代每一个层结构
if isinstance(m, nn.Conv2d):#判断给的输入是不是属于给定(卷积层)的类型
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')#当层结构为卷积层,我们用kaiming——normal对m.weight权重进行遍历初始化
if m.bias is not None:#如果偏置不为空,则设定为空(初始化)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):#如果是一个全连接层,
nn.init.normal_(m.weight, 0, 0.01)#则用正态分布来初始化权重
nn.init.constant_(m.bias, 0)#和偏置
2.训练模型
1.数据集预处理
data_transform = {
"train": transforms.Compose([transforms.RandomResizedCrop(224),#随机裁剪224*224
transforms.RandomHorizontalFlip(),#随机水平反转
transforms.ToTensor(),#转换成张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),#标准化处理
"val": transforms.Compose([transforms.Resize((224, 224)), # cannot 224, must (224, 224)
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}#归一化
data_root = os.path.abspath(os.path.join(os.getcwd(), "../")) # get data root path os.getcwd() 将两个路径连接在一起
image_path = os.path.join(data_root, "flower_data") # flower data set path
assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
transform=data_transform["train"])
train_num = len(train_dataset)
# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
cla_dict = dict((val, key) for key, val in flower_list.items()) #将val与key反
# write dict into json file
json_str = json.dumps(cla_dict, indent=4)#编码
with open('class_indices.json', 'w') as json_file:
json_file.write(json_str)#保存
batch_size = 32
nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # number of workers
print('Using {} dataloader workers every process'.format(nw))
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size, shuffle=True,#随机洗牌
num_workers=0)
validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
transform=data_transform["val"])
val_num = len(validate_dataset)
validate_loader = torch.utils.data.DataLoader(validate_dataset,
batch_size=batch_size, shuffle=False,
num_workers=0)
print("using {} images for training, {} images for validation.".format(train_num,
val_num))
2.训练模型
net = AlexNet(num_classes=5, init_weights=True) #实例化
net.to(device)#用gpu
loss_function = nn.CrossEntropyLoss()#交叉熵损失函数
# pata = list(net.parameters())
optimizer = optim.Adam(net.parameters(), lr=0.0002)#adam优化器。
epochs = 10
save_path = './AlexNet.pth'
best_acc = 0.0
train_steps = len(train_loader)
for epoch in range(epochs):
# train
net.train()#dropout 在训练中使用
running_loss = 0.0#初始化
train_bar = tqdm(train_loader, file=sys.stdout)#进度条的库
for step, data in enumerate(train_bar):
images, labels = data
optimizer.zero_grad()
outputs = net(images.to(device))
loss = loss_function(outputs, labels.to(device))
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,#进度条库
epochs,
loss)
# validate
net.eval()#验证时不用dropout
acc = 0.0 # accumulate accurate number / epoch
with torch.no_grad():#验证时不会计算损失梯度,因为不需要权重更新
val_bar = tqdm(validate_loader, file=sys.stdout)
for val_data in val_bar:
val_images, val_labels = val_data
outputs = net(val_images.to(device))
predict_y = torch.max(outputs, dim=1)[1]
acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
val_accurate = acc / val_num
print('[epoch %d] train_loss: %.3f val_accuracy: %.3f' %
(epoch + 1, running_loss / train_steps, val_accurate))
if val_accurate > best_acc:
best_acc = val_accurate
torch.save(net.state_dict(), save_path)
print('Finished Training')
if __name__ == '__main__':
main()
附带完整代码:
import os
import sys
import json
import torch
import torch.nn as nn
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
from tqdm import tqdm
from model import AlexNet
def main():
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("using {} device.".format(device))
data_transform = {
"train": transforms.Compose([transforms.RandomResizedCrop(224),#随机裁剪224*224
transforms.RandomHorizontalFlip(),#随机水平反转
transforms.ToTensor(),#转换成张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),#标准化处理
"val": transforms.Compose([transforms.Resize((224, 224)), # cannot 224, must (224, 224)
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}#归一化
data_root = os.path.abspath(os.path.join(os.getcwd(), "../")) # get data root path os.getcwd() 将两个路径连接在一起
image_path = os.path.join(data_root, "flower_data") # flower data set path
assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
transform=data_transform["train"])
train_num = len(train_dataset)
# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
cla_dict = dict((val, key) for key, val in flower_list.items()) #将val与key反
# write dict into json file
json_str = json.dumps(cla_dict, indent=4)#编码
with open('class_indices.json', 'w') as json_file:
json_file.write(json_str)#保存
batch_size = 32
nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # number of workers
print('Using {} dataloader workers every process'.format(nw))
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size, shuffle=True,#随机洗牌
num_workers=0)
validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
transform=data_transform["val"])
val_num = len(validate_dataset)
validate_loader = torch.utils.data.DataLoader(validate_dataset,
batch_size=batch_size, shuffle=False,
num_workers=0)
print("using {} images for training, {} images for validation.".format(train_num,
val_num))
# test_data_iter = iter(validate_loader)
# test_image, test_label = test_data_iter.next()
#
# def imshow(img):
# img = img / 2 + 0.5 # unnormalize
# npimg = img.numpy()
# plt.imshow(np.transpose(npimg, (1, 2, 0)))
# plt.show()
#
# print(' '.join('%5s' % cla_dict[test_label[j].item()] for j in range(4)))
# imshow(utils.make_grid(test_image))
net = AlexNet(num_classes=5, init_weights=True) #实例化
net.to(device)#用gpu
loss_function = nn.CrossEntropyLoss()#交叉熵损失函数
# pata = list(net.parameters())
optimizer = optim.Adam(net.parameters(), lr=0.0002)#adam优化器。
epochs = 10
save_path = './AlexNet.pth'
best_acc = 0.0
train_steps = len(train_loader)
for epoch in range(epochs):
# train
net.train()#dropout 在训练中使用
running_loss = 0.0#初始化
train_bar = tqdm(train_loader, file=sys.stdout)#进度条的库
for step, data in enumerate(train_bar):
images, labels = data
optimizer.zero_grad()
outputs = net(images.to(device))
loss = loss_function(outputs, labels.to(device))
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,#进度条库
epochs,
loss)
# validate
net.eval()#验证时不用dropout
acc = 0.0 # accumulate accurate number / epoch
with torch.no_grad():#验证时不会计算损失梯度,因为不需要权重更新
val_bar = tqdm(validate_loader, file=sys.stdout)
for val_data in val_bar:
val_images, val_labels = val_data
outputs = net(val_images.to(device))
predict_y = torch.max(outputs, dim=1)[1]
acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
val_accurate = acc / val_num
print('[epoch %d] train_loss: %.3f val_accuracy: %.3f' %
(epoch + 1, running_loss / train_steps, val_accurate))
if val_accurate > best_acc:
best_acc = val_accurate
torch.save(net.state_dict(), save_path)
print('Finished Training')
if __name__ == '__main__':
main()
3.验证训练结果
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))])
# load image
img_path = "../tulip.jpg"
assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
img = Image.open(img_path)
plt.imshow(img)
# [N, C, H, W]
img = data_transform(img)
# expand batch dimension
img = torch.unsqueeze(img, dim=0)#扩充一个batch维度
# read class_indict
json_path = './class_indices.json'
assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)#读取类别名称
with open(json_path, "r") as f:
class_indict = json.load(f)
# create model
model = AlexNet(num_classes=5).to(device)
# load model weights
weights_path = "./AlexNet.pth"
assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
model.load_state_dict(torch.load(weights_path))
model.eval()#关闭dropout
with torch.no_grad():
# predict class
output = torch.squeeze(model(img.to(device))).cpu()
predict = torch.softmax(output, dim=0)
predict_cla = torch.argmax(predict).numpy()
print_res = "class: {} prob: {:.3}".format(class_indict[str(predict_cla)],
predict[predict_cla].numpy())
plt.title(print_res)
for i in range(len(predict)):
print("class: {:10} prob: {:.3}".format(class_indict[str(i)],
predict[i].numpy()))
plt.show()
if __name__ == '__main__':
main()