赞
踩
提示:文章用于学习记录
常见分类网络结构可以分为两部分,一部分是特征提取部分,另一部分是分类部分;特征提取部分一般由各类卷积组成,卷积拥有强大的特征提取能力;分类部分一般由全连接组成,特征提取部分获取到的特征一般是一维向量,可以直接进行全连接分类;VGG16 网络由三种不同的层组成,分别是卷积层、最大池化层、全连接层。
import os, shutil # c数据集解压缩所在目录的路径 original_dataset_dir = 'Cat&Dog' # 创建一个存储较小数据集的目录 base_dir = 'find_cats_and_dogs' os.mkdir(base_dir) # 培训、验证和测试拆分 train_dir = os.path.join(base_dir, 'train') os.mkdir(train_dir) validation_dir = os.path.join(base_dir, 'validation') os.mkdir(validation_dir) test_dir = os.path.join(base_dir, 'test') os.mkdir(test_dir) # 训练猫图片 train_cats_dir = os.path.join(train_dir, 'cats') os.mkdir(train_cats_dir) # 训练狗图片 train_dogs_dir = os.path.join(train_dir, 'dogs') os.mkdir(train_dogs_dir) # 验证猫图片 validation_cats_dir = os.path.join(validation_dir, 'cats') os.mkdir(validation_cats_dir) # 验证狗图片 validation_dogs_dir = os.path.join(validation_dir, 'dogs') os.mkdir(validation_dogs_dir) # 测试猫的图片 test_cats_dir = os.path.join(test_dir, 'cats') os.mkdir(test_cats_dir) # 测试猫的图片 test_dogs_dir = os.path.join(test_dir, 'dogs') os.mkdir(test_dogs_dir) # 复制前1000个cat图像以训练模型 fnames = ['cat.{}.jpg'.format(i) for i in range(1000)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(train_cats_dir, fname) shutil.copyfile(src, dst) # 将500个猫图像复制到验证 fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(validation_cats_dir, fname) shutil.copyfile(src, dst) # 复制500个猫图像到测试 fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(test_cats_dir, fname) shutil.copyfile(src, dst) # 复制1000个狗图片去训练模型 fnames = ['dog.{}.jpg'.format(i) for i in range(1000)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(train_dogs_dir, fname) shutil.copyfile(src, dst) # 复制500个狗图片去验证 fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(validation_dogs_dir, fname) shutil.copyfile(src, dst) # 复制500个狗图片去测试 fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)] for fname in fnames: src = os.path.join(original_dataset_dir, fname) dst = os.path.join(test_dogs_dir, fname) shutil.copyfile(src, dst)
# 打印新数据集的尺寸
print('total training cat images:', len(os.listdir(train_cats_dir)))
print('total training dog images:', len(os.listdir(train_dogs_dir)))
print('total validation cat images:', len(os.listdir(validation_cats_dir)))
print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
print('total test cat images:', len(os.listdir(test_cats_dir)))
print('total test dog images:', len(os.listdir(test_dogs_dir)))
# 构建小型卷积网络 import tensorflow as tf from keras import layers from keras import models model = models.Sequential() model.add(tf.keras.layers.Conv2D(32,(3,3),activation="relu", input_shape=(150,150,3))) model.add(tf.keras.layers.MaxPooling2D((2,2))) model.add(tf.keras.layers.Conv2D(64,(3,3),activation="relu")) model.add(tf.keras.layers.MaxPooling2D((2,2))) model.add(tf.keras.layers.Conv2D(128,(3,3),activation="relu")) model.add(tf.keras.layers.MaxPooling2D((2,2))) model.add(tf.keras.layers.Conv2D(128,(3,3),activation="relu")) model.add(tf.keras.layers.MaxPooling2D((2,2))) # model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(512, activation="relu")) model.add(tf.keras.layers.Dense(1, activation="sigmoid")) model.summary()
# 使用RMSprop优化器
from tensorflow.keras import optimizers
model.compile(loss="binary_crossentropy",
optimizer=optimizers.RMSprop(learning_rate=1e-4),
metrics=["acc"])
# 数据预处理 from keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator(rescale=1./255) # 进行缩放 test_datagen = ImageDataGenerator(rescale=1./255) # 进行缩放 train_generator = train_datagen.flow_from_directory( train_dir, # 待处理的目录 target_size=(150,150), # 图像大小设置 batch_size=20, class_mode="binary" # 损失函数是binary_crossentropy 所以使用二进制标签 ) validation_generator = test_datagen.flow_from_directory( validation_dir, # 待处理的目录 target_size=(150,150), # 图像大小设置 batch_size=20, class_mode="binary" # 损失函数是binary_crossentropy 所以使用二进制标签 )
# 查看生成器输出
for data_batch, labels_batch in train_generator:
print(data_batch.shape)
print(labels_batch.shape)
break
# 使用生成器使我们的模型适合于数据
history = model.fit(
train_generator, # 第一个参数必须是Python生成器
steps_per_epoch=100, # 2000 / 20
epochs=30, # 迭代次数
validation_data=validation_generator, # 待验证的数据集
validation_steps=50
)
# 保存模型
model.save("cats_and_dogs_small.h5")
# 在训练和验证数据上绘制模型的损失和准确性
import matplotlib.pyplot as plt
%matplotlib inline
history_dict = history.history # 字典形式
for key, _ in history_dict.items():
print(key)
acc = history_dict["acc"]
val_acc = history_dict["val_acc"]
loss = history_dict["loss"]
val_loss = history_dict["val_loss"]
epochs = range(1, len(acc)+1)
# acc
plt.plot(epochs, acc, "bo", label="Training acc")
plt.plot(epochs, val_acc, "b", label="Validation acc")
plt.title("Training and Validation acc")
plt.legend()
plt.figure()
# loss
plt.plot(epochs, loss, "bo", label="Training loss")
plt.plot(epochs, val_loss, "b", label="Validation loss")
plt.title("Training and Validation loss")
plt.legend()
这些图具有过拟合的特点,随着时间的增加,训练精度在不断增加,接近100%,而验证精度则停留在70-72%;原因是训练样本(2000)相对较少;
减轻过度拟合方法:dropout和重量衰减(L2正则化);这里使用的是在深度学习模型处理图像时几乎普遍使用的数据增强的方法。
文件夹中包含1,150张图片,包含4种天气下道路的图片,
分别为City_road(晴天下道路),fog(雾天道路),rain(雨天道路),snow(雪天道路),
构建一个多分类模型(4分类),对不同天气下的道路图片进行识别。
构建模型时请严格按照如下规则进行数值标签映射{‘City_road’: 0, ‘fog’: 1, ‘rain‘: 2, ‘snow‘: 3}
#相关包导入 import torch import numpy as np import pandas as pd import matplotlib.pyplot as plt import torch.nn as nn import torch.nn.functional as F import torchvision from torchvision import datasets, transforms import os import shutil %matplotlib inlineimport numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings('ignore') import ssl ssl._create_default_https_context = ssl._create_unverified_context
# 数据分区
# 首先我们需要做的是另外建立一个新的文件夹,这个文件夹下面有两个文件夹,分别是train和test,代码如下:
base_dir = r"./weatherRoad"
if not os.path.isdir(base_dir):
os.mkdir(base_dir)
train_dir = os.path.join(base_dir, 'train')
test_dir = os.path.join(base_dir, 'test')
os.mkdir(train_dir)
os.mkdir(test_dir)
# 然后,在train和test文件夹中分别建立以四种天气类型命名的文件夹,代码如下:
species = ['City_road', 'fog', 'rain', 'snow']
for train_or_test in ['train', 'test']:
for spec in species:
os.mkdir(os.path.join(base_dir, train_or_test, spec))
image_dir = r'./train'
for i, img in enumerate(os.listdir(image_dir)):
for spec in species:
if spec in img:
s = os.path.join(image_dir, img)
if i % 5 == 0:
d = os.path.join(base_dir, 'test', spec, img)
else:
d = os.path.join(base_dir, 'train', spec, img)
shutil.copy(s, d)
# 操作完之后,我们可以查看一下各个文件夹中各有多少数据
for train_or_test in ['train', 'test']:
for spec in species:
print(train_or_test, spec, len(os.listdir(os.path.join(base_dir, train_or_test, spec))))
# 加载数据及数据预处理 transformation = transforms.Compose([ transforms.Resize((96, 96)), # 改变图像大小 transforms.ToTensor(), transforms.Normalize(mean = [0.5, 0.5, 0.5], std = [0.5, 0.5, 0.5]) # 标准化 ]) train_ds = datasets.ImageFolder( train_dir, transform = transformation ) test_ds = datasets.ImageFolder( test_dir, transform = transformation ) train_dl = torch.utils.data.DataLoader(train_ds, batch_size = 16, shuffle = True) test_dl = torch.utils.data.DataLoader(test_ds, batch_size = 16)
train_ds.classes
train_ds.class_to_idx
len(train_ds),len(test_ds)
# 模型定义代码如下: class Model(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(3, 16, 3) self.bn1 = nn.BatchNorm2d(16) self.pool = nn.MaxPool2d((2, 2)) self.conv2 = nn.Conv2d(16, 32, 3) self.bn2 = nn.BatchNorm2d(32) self.conv3 = nn.Conv2d(32, 64, 3) self.bn3 = nn.BatchNorm2d(64) self.drop = nn.Dropout(0.5) self.linear_1 = nn.Linear(64 * 10 * 10, 1024) self.bn_l1 = nn.BatchNorm1d(1024) self.linear_2 = nn.Linear(1024, 256) self.bn_l2 = nn.BatchNorm1d(256) self.linear_3 = nn.Linear(256, 4) def forward(self, input): x = F.relu(self.conv1(input)) x = self.pool(x) x = self.bn1(x) x = F.relu(self.conv2(x)) x = self.pool(x) x = self.bn2(x) x = F.relu(self.conv3(x)) x = self.pool(x) x = self.bn3(x) # print(x.size()) x = x.view(-1, 64 * 10 * 10) x = F.relu(self.linear_1(x)) x = self.bn_l1(x) x = self.drop(x) x = F.relu(self.linear_2(x)) x = self.bn_l2(x) x = self.drop(x) x = self.linear_3(x) return x # 这里需要注意的是各个层的位置,BN层放在池化层后面,以激活层和Dropout层之间
# 模型训练 loss_func = torch.nn.CrossEntropyLoss() device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") def fit(epoch, model, trainloader, testloader): correct = 0 total = 0 running_loss = 0 model.train() # 训练阶段 for x, y in trainloader: x, y = x.to(device), y.to(device) y_pred = model(x) loss = loss_func(y_pred, y) optimizer.zero_grad() loss.backward() optimizer.step() with torch.no_grad(): y_pred = torch.argmax(y_pred, dim = 1) correct += (y_pred == y).sum().item() total += y.size(0) running_loss += loss.item() epoch_acc = correct / total epoch_loss = running_loss / len(trainloader.dataset) test_correct = 0 test_total = 0 test_running_loss = 0 model.eval() # 评价阶段,一般在有dropout层和BN层的时候使用 with torch.no_grad(): for x, y in testloader: x, y = x.to(device), y.to(device) y_pred = model(x) loss = loss_func(y_pred, y) y_pred = torch.argmax(y_pred, dim = 1) test_correct += (y_pred == y).sum().item() test_total += y.size(0) test_running_loss += loss.item() epoch_test_acc = test_correct / test_total epoch_test_loss = test_running_loss / len(testloader.dataset) print('epoch: ', epoch, 'loss: ', round(epoch_loss, 3), 'accuracy: ', round(epoch_acc, 3), 'test_loss: ', round(epoch_test_loss, 3), 'test_accuracy: ', round(epoch_test_acc, 3)) return epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc # 这里需要注意的是,要区分训练阶段和评价阶段,一般在有Dropout层和BN层的时候使用
model = Model()
model.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr = 0.001)
epochs = 30
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc = fit(epoch, model, train_dl, test_dl)
train_loss.append(epoch_loss)
train_acc.append(epoch_acc)
test_loss.append(epoch_test_loss)
test_acc.append(epoch_test_acc)
# 保存模型
torch.save(model.cpu().state_dict(), './modeldict.pth') # 只保留参数
torch.save(model, './model.pth') # 保留整个模型
plt.plot(range(1,epochs+1),train_loss,label='train_loss')
plt.plot(range(1,epochs+1),test_loss,label='test_loss')
plt.plot(range(1,epochs+1),train_acc,label='train_acc')
plt.plot(range(1,epochs+1),test_acc,label='test_acc')
plt.show()
# 这个顺序很重要,要和训练时候的类名顺序一致
class_names = ['City_road', 'fog', 'rain', 'snow']
# 载入模型并读取权重
model.load_state_dict(torch.load('./modeldict.pth'))
model.to(device)
model.eval() # 测试模式
# 图片地址列表
img_paths = ['weatherRoad/test/City_road/City_road_3.jpg', 'weatherRoad/test/fog/fog_56.jpg', 'weatherRoad/test/rain/rain_22.jpg',
'weatherRoad/test/snow/snow_13.jpg', 'weatherRoad/test/City_road/City_road_67.jpg']
from PIL import Image # 对图片地址列表中的每个地址 for path in img_paths: img = plt.imread(path) # 显示图片的第一种方法 plt.axis('off') plt.imshow(img) plt.show() img = Image.open(path) # 输入地址打开图片 # img.show() # 显示图片的第二种方法 # 拓张维度: # torch.nn只支持小批次的数据输入,不支持输入单个样本。比如nn.Conv2d接收4D # tensor作为输入:nSamples * nChannels * Height * Width, # 如果只有一个样本,那么使用input.unsqueeze(0)来增加一个批次维度。 img_ = transformation(img).unsqueeze(0) img_ = img_.to(device) # 是否使用GPU outputs = model(img_) # 得出11类概率 _, indice = torch.max(outputs, 1) # 输出概率最大的类别索引,前面是概率值,后面是索引 result = class_names[indice] # 得到类别名称 print('predicted:', result) # 输出类别名称
以上就是猫狗二分类与四种天气图片数据多分类(pytorch)方法实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。