【计算机视觉】入门级VGG网络进行的物体识别与垃圾分类

举报
yd_211085766 发表于 2023/10/05 15:06:59 2023/10/05
【摘要】 本文为入门计算机视觉与人工智能和华为云平台的同学提供了一篇无门槛的完整项目流程,包括项目流程搭建,项目具体实现代码,还有华为云平台ModertArts的上手方法环境配置等等,以及如何将本地项目置于华为云平台进行训练。

本文为入门计算机视觉与人工智能和华为云平台的同学提供了一篇无门槛的完整项目流程,包括项目流程搭建,项目具体实现代码,还有华为云平台ModertArts的上手方法环境配置等等,以及如何将本地项目置于华为云平台进行训练。

1. 项目实施方案

数据集来源于和鲸数据集:垃圾分类“VOC2007.ZIP”。项目优化,我们将模型用VGG16代替,在华为平台进行训练。实验环境为python3.7.3,tensorflow2.10.0,平台主要为jupyter notebook进行模型训练与储存,pycharm进行完整项目的设计,ui平台为Qt designer。租用华为ModelArts开发环境conda3-cuda10.2-cudnn7-ubuntu18.04,规格为GPU: 1*V100(32GB)|CPU: 8核 64GB。

由于项目为物体识别中的图片识别,首先进行数据预处理,即图片尺寸归一化;为了加强模型鲁棒性,我们定义了平移与选择旋转函数适当对数据进行数据增强;之后进行卷积网络模型构建,动态学习率的选择,损失函数设计,保存最佳参数、设置过早停,反复调试drop层来得到较好的丢弃比例,之后进行模型训练与保存;用VGG16网络,手写网络并且添加l2正则化;之后设计准确度与损失的函数图像可视化;然后进行图片读取与识别;最后为了评测模型,我们引入混淆矩阵来计算准确率与召回率。最后进行UI设计。此为我们的项目设计流程。

算法原理:卷积神经网络VGG16网络

2. 项目基本原理

2.1卷积层

卷积层作为输入层后的第一层,主要目的是提取输入的特征表示。卷积层是由多个特征图组成,每个特征图由多个神经元组成,每个神经元通过卷积核与上一层特征图的局部区域相连。卷积核是一个带权值的矩阵,用于提取和计算不同的特征映射。

2.1.1卷积核

卷积核,又叫滤波器,给定输入图像,输入图像中一个小区域中像素,加权后成为输出图像中的每个对应像素,其中权值即为卷积核。也就是说,卷积核实际上可以理解为是一个权值矩阵。 卷积所得的输出的计算公式为:

式中:Xi为输入特征图,Yj为输出特征图,权值记为Wij,bj是其偏置参数

2.1.2 卷积运算

可以将卷积核作为一个权值矩阵,对图片不同位置进行运算时,共享权值。卷积神经网络每一层输出的特征图上的像素点在输入图片上映射的区域大小叫做感受野(绿色框)。

2.1.3 多通道卷积运算

2.1.3.1 彩色图片和灰度图像

灰度图:灰度图像只有一个通道,把白色与黑色之间按对数关系分为若干等级,称为灰度。灰度分为256阶(0-255),数字越大越接近白色,越小越接近黑色 。

RGB图:彩色图有三个通道,是通过对红R、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色。(每像素颜色16777216(256 * 256 * 256)种)其中R、G、B由不同的灰度级来描述,每个分量有256级灰度(0-255)

2.1.3.2 多通道卷积运算

多通道输入,单核卷积,卷出来之后相加

2.1.4 padding

随着卷积次数的增加,卷积后的矩阵会越变越小,而且图像的边缘计算次数会小于图像的内部。 所以进行padding操作,即边缘补0,得到的特征图大小个原来一样 这样解决了图像越卷越小和边缘计算次数少的问题

2.2池化层

2.2.1 原理和计算方法

基于局部相关性的思想,通过从局部相关的一组元素中进行采样或信息聚合,从而得到新的元素值。 平均池化层:从局部相关元素集中计算平均值 最大池化层:从局部相关元素集中选取最大的一个元素值

2.2.2 池化层选择

特征提取的误差主要来自两个方面:

(1)邻域大小受限造成的估计值方差增大;

(2)卷积层参数误差造成估计均值的偏移。

平均池化层能减小第一种误差,更多的保留图像的背景信息,最大池化层能减小第二种误差,更多的保留纹理信息。

2.3 Flatten层

用于将输入层的数据压成一维的数据,因为卷积层处理的是二维数据,全连接层只能接收一维数据,所以用在卷积层和全连接层之间

2.4激活函数

激活函数的主要作用是提供网络的非线性建模能力。如果没有激活函数,那么该网络仅能够表达线性映射,此时即便有再多的隐藏层,其整个网络跟单层神经网络也是等价的。因此也可以认为,只有加入了激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。接收一维数据,所以用在卷积层和全连接层之间。相比起Sigmoid和tanh,ReLU 在SGD中能够快速收敛。 2.ReLU导数计算方便。 3.有效缓解了梯度消失的问题。 梯度消失: 在梯度下降中, 随着算法反向的反馈,预测值与真实值之间的误差每传播一层会衰减一次,将导致模型收敛停滞不前,最终没有变化,此时并没有收敛到比好的解。

2.5 Drop层

2.5.1 过拟合原因

神经网络层的神经元的参数代表了学到的特征,但是这些特征是被多个神经元共同表征的,当模型复杂度高(神经元过多或者层数过多)且数据集少,就会出现过拟合,即同一个特征被过多的神经元表示,出现了特征冗余。

2.5.2 Dropout的机制

在每次的神经网络的反向传播中,会随机选择一些神经元,设定其反向传播对应的参数为0,然后对于被改变后的神经网络进行反向传播,相当于在反向传播时有一些"神经元"被移除了(与L1正则化效果相同) 主要参数:dropout( rate=丢弃率) 设置为 0.1,则意味着会丢弃 10% 的神经元。

3.项目的设计过程

3.1数据预处理与数据增强

首先从和鲸社区下载垃圾分类数据集:(https://www.heywhale.com/mw/dataset/605e9402cb6d360015a471dc/file)。数据处理时本想采用文档名进行分类,但是发现图片名中居然有拼写错误或符号不符的问题,只好进行手动数据打包,按目录名分类,共分为9类,共7957个.jpg文件。

首先进行数据集加载,加载数据集的方法为tf.keras.preprocessing.image_dataset_from_directory()。directory, #数据所在目录,本项目labels按照默认,所以应该路径应该包含子目录,每个子目录包含一种类型的文件 label_mode, #标签从目录结构生成,或者是整数标签的列表/元组,本项目中是按照给出的目录结构生成,参数按照默认值,categorical:标签将被编码为分类向量,validation_split=None, #数据划分,0~1之间。

batch_size = 32

img_height = 112

img_width = 112

train_ds = tf.keras.preprocessing.image_dataset_from_directory(

r"D:\DATABASE\rubbish", #数据集目录

label_mode="categorical", #标签模式,根据目录生成

validation_split=0.2, # 验证集比例为20%

subset="training", #这是个训练集

seed=42 , #随机种子,保证划分一致

image_size=(img_height, img_width), #图像大小

batch_size=batch_size)

val_ds = tf.keras.preprocessing.image_dataset_from_directory(

r"D:\DATABASE\rubbish",

label_mode="categorical",

validation_split=0.2,

subset="validation",

seed=162,

image_size=(img_height, img_width),

batch_size=batch_size)

即划分训练集与测试集为4:1,数据归一大小为112X112。

def showself(self):

plt.figure(figsize=(20, 10))

for images, labels in self.train_ds.take(1):

labels = [tf.argmax(i) for i in labels]

for i in range(20):

ax = plt.subplot(5, 10, i + 1)

plt.imshow(images[i].numpy().astype("uint8"))

plt.title(self.class_names[labels[i]])

plt.axis("off")

plt.show()

数据集预览,展示二十张图片,以10*2的格式表现。

之后进行数据增强,加强模型鲁棒性。自定义平移函数与旋转函数,使用random函数随机50张图片进行数据增强。

def translate(img, x, y):

#获取图像尺寸

(h, w) = img.shape[:2]

#定义平移矩阵

M = np.float32([[1, 0, x], [0, 1, y]])

#使用OpenCV仿射变换函数实现平移操作

shifted = cv2.warpAffine(img, M, (w, h))

#返回转换后的图像

return shifted

img1 = cv2.imread(str(roses[1]))

shifted=translate(img1,-20,20)

cv2.imwrite(str(roses[1]),shifted)

print(img1)

# 定义旋转rotate函数

def rotate(img, angle, center = None, scale = 0.7):

# 获取图像尺寸

h=112

w=112

# 旋转中心的缺失值为图像中心

if center is None:

center = (w/2, h/2)

# 调用计算旋转矩阵函数

M = cv2.getRotationMatrix2D(center, angle, scale)

# 使用opencv仿射变换函数实现旋转操作

rotated = cv2.warpAffine(img, M, (w, h))

# 返回旋转后的图像

return rotated

#数据增强,随机翻转与随机平移,运行后注释掉

# i=0

# while i<50:

# x=np.random.randint(-20,20)

# y=np.random.randint(-20,20)

# angle=np.random.randint(-180,180)

# num1=np.random.randint(1,7957)

# num2=np.random.randint(1,7957)

# img1 = cv2.imread(str(roses[num1]))

# img2 = cv2.imread(str(roses[num2]))

# shifted=translate(img1,x,y)

# rotated=rotate(img2,angle)

# cv2.imwrite(str(roses[num1]),shifted)

# cv2.imwrite(str(roses[num2]),rotated)

# i=i+1

最后进行数据集浅缓存,使用cache()函数,cache( filename='', name=None ) filename=:一个 tf.string 标量 tf.张量,表示文件系统上用于缓存此数据集中的元素的目录的名称。如果未提供文件名,则数据集将缓存在内存中。 缓存此数据集中的元素。 首次迭代数据集时,其元素将缓存在指定的文件或内存中。后续迭代将使用缓存的数据

AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().shuffle(1200).prefetch(buffer_size=AUTOTUNE)

val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

3.2卷积神经网络

首先构造卷积神经网络模型lenet类,类内为models.Sequential(),

Sequential()方法是一个容器,描述了神经网络的网络结构,在Sequential()的输入参数中描述从输入层到输出层的网络结构,可以在()中自定义搭建神经网络的网络结构,类比在桌面上拼积木的话,Sequential()相当于放置积木的桌面。内有三层3*3卷积核的卷积层,激活函数ReLU,分段线性函数,单侧抑制;两层2*2采样的最大池化层;一层dropout层,经过多次尝试后将丢弃比例设置为0.3以缓解过拟合的现象;一层Flatten层用于将输入层的数据压成一维的数据,因为卷积层处理的是二维数据,全连接层只能接收一维数据。一层全连接层与一层输出层。

class lenet(nn.Module):


def __init__(self):

super(lenet, self).__init__()

self.feature = models.Sequential([

layers.experimental.preprocessing.Rescaling(1. / 255, input_shape=(img_height, img_width, 3)),

layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)), # 卷积层1,卷积核3*3

layers.MaxPooling2D((2, 2)), # 池化层1,2*2采样

layers.Conv2D(32, (3, 3), activation='relu'), # 卷积层2,卷积核3*3

layers.MaxPooling2D((2, 2)), # 池化层2,2*2采样

layers.Conv2D(64, (3, 3), activation='relu'), # 卷积层3,卷积核3*3

layers.Dropout(0.3),

# layers.AveragePooling2D(pool_size=(2,2),strides=(2,2)),

layers.Flatten(), # Flatten层,连接卷积层与全连接层

layers.Dense(128, activation='relu'), # 全连接层,特征进一步提取

layers.Dense(len(class_names)) # 输出层,输出预期结果

])

def forward(self, x):

x = self.model(x)

return x

之后进行动态学习率设计,采用tf.keras.optimizers.schedules.ExponentialDecay()函数,最初学习率为0.001,每隔64步衰减一次,衰减系数为0.96,非每一步更新学习速率。之后放入优化器进行编译。

initial_learning_rate = 1e-3

lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(

initial_learning_rate, #设置初始学习率

decay_steps=64, #每隔多少个step衰减一次

decay_rate=0.94, #衰减系数

staircase=True)

# 将指数衰减学习率送入优化器

optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)

model.compile(optimizer=optimizer,

# loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),

loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),

metrics=['accuracy'])

然后进行设置早停与保存最佳模型参数。训练次数为20次(在试验中发现20次一般就会过早停)使用早停可以节约训练时间、防止训练次数过多拟合到噪声而发生过拟合。设置min_delta=0.001即测试集上准确率变化小于0.001则认为无提升,patience=5为5轮未提升后跳出训练。保存最优模型的权重且仅保存权重。

checkpointer = ModelCheckpoint('best_model.h5',

monitor='val_accuracy',

verbose=1,

save_best_only=True,

save_weights_only=True)

earlystopper = EarlyStopping(monitor='val_accuracy',

min_delta=0.001,

patience=5,

verbose=1)

进行模型训练,callback可在每次训练后返回特定值如准确度,损失还有是否过早停。将fit后的model赋给history。由于本机不支持GPU加速,训练时间长,故将history保存到txt文档中,下次执行可直接调用。

#保留每次训练的loss和acc,训练,执行一次后注释掉

history = model.fit(train_ds,

validation_data=val_ds,

epochs=epochs,

callbacks=[checkpointer, earlystopper])

model.save('train.model.pth')

# 将history保存于txt文件中,避免多次训练耗费时间

with open('history.txt','wb') as file_txt:

pickle.dump(history.history,file_txt)

with open('history.txt','rb') as file_txt:

history=pickle.load(file_txt)

3.3数据可视化

数据可视化的acc与loss来源于history的callback,读入history文档后使用matplotlib.pyplot进行可视化。

acc = history['accuracy']

val_acc = history['val_accuracy']

loss = history['loss']

val_loss = history['val_loss']

epochs_range = range(len(loss))

plt.figure(figsize=(12, 4))

plt.subplot(1, 2, 1)

plt.plot(epochs_range, acc, label='Training Accuracy')

plt.plot(epochs_range, val_acc, label='Validation Accuracy')

plt.legend(loc='lower right')

plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)

plt.plot(epochs_range, loss, label='Training Loss')

plt.plot(epochs_range, val_loss, label='Validation Loss')

plt.legend(loc='upper right')

plt.title('Training and Validation Loss')

plt.show()

3.4垃圾识别与模型评测

垃圾识别是从文件夹中读取.jpg文件并预测其类别,先将其归一化为训练集相同的尺寸,再使用model.predict()函数计算其在各个类别上的概率,之后使用np.argmax()取出最大概率的下标,从而输出最大概率下的类别作为其预测的结果。

def pre_pic(self):

filePath, filetype = QtWidgets.QFileDialog.getOpenFileName(self, "选择图片", "*","Images (*.jpg)")

self.model.load_weights('best_model.h5')

# img = Image.open(r"D:\DATABASE\rubbish\K-FruitSkin\K-FruitSkin0.jpg") # 这里选择你需要预测的图片

img = Image.open(str(filePath)) # 这里选择你需要预测的图片

plt.imshow(img)

plt.show()

img = np.array(img)

image = tf.image.resize(img, [self.img_height, self.img_width])

img_array = tf.expand_dims(image, 0) # /255.0 # 记得做归一化处理(与训练集处理方式保持一致)

predictions = self.model.predict(img_array) # 这里选用你已经训练好的模型

a=str(self.class_names[np.argmax(predictions)])

self.lineEdit.setText( a)

模型评测主要使用混淆矩阵的方法。confusion_matrix(labels, predictions)函数可直接表示出标签与对应概率,之后便是借用混淆矩阵计算准确率,召回率。我们令TP为混淆矩阵对角线的元素(预测与真实相同的元素),FP为每一行中除TP外其他数之和(预测是但真实值非),FN同理为每一列除TP外其他数之和(真实是而预测非),TN则为全部数之和减掉TP,FP,FN的和。则模型全部种类的总识别率为TP之和除以全部数之和,平均精确率为TP / (TP + FP)之和除以类别数,平均召回率为TP / (TP + FN)除以类别数。

def plot_cm(self,labels, predictions):

# 生成混淆矩阵

conf_numpy = confusion_matrix(labels, predictions)

# 将矩阵转化为 DataFrame

conf_df = pd.DataFrame(conf_numpy, index=self.class_names, columns=self.class_names)

plt.figure(figsize=(8, 7))

sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")

plt.rcParams['font.sans-serif'] = ['SimHei']

plt.title('混淆矩阵', fontsize=15)

plt.ylabel('真实值', fontsize=14)

plt.xlabel('预测值', fontsize=14)

plt.legend()

plt.show()

avaerage_Precision = []

avaerage_Recall = []

sum_TP = 0

conf_Mat = np.mat(conf_df.values)

for i in range(len(self.class_names)):

TP = conf_Mat[i, i]

FP = np.sum(conf_Mat[i, :]) - TP

FN = np.sum(conf_Mat[:, i]) - TP

TN = np.sum(conf_Mat) - TP - FP - FN

Precision = round(TP / (TP + FP), 3) if TP + FP != 0 else 0.

avaerage_Precision.append(Precision)

Recall = round(TP / (TP + FN), 3) if TP + FN != 0 else 0.

avaerage_Recall.append(Recall)

Specificity = round(TN / (TN + FP), 3) if TN + FP != 0 else 0.

sum_TP += TP

accuracy = round(sum_TP / np.sum(conf_Mat),5)

print("模型全部种类的总体识别率: ", accuracy)

self.lineEdit_2.setText(str(accuracy))

print('平均精确率: ', sum(avaerage_Precision) / len(self.class_names))

b=round(sum(avaerage_Precision) / len(self.class_names),5)

self.lineEdit_3.setText(str(b))

print('平均召回率: ', sum(avaerage_Recall) / len(self.class_names))

c=round(sum(avaerage_Recall) / len(self.class_names),5)

self.lineEdit_4.setText(str(c))










3.5 UI设计

pycharm中文件包括run.py(主程序),main.py(jupyter程序),heart.py(ui调用程序),poc与poc文件为调用背景所需文件。如上的点击按钮功能函数位于run.py文件中用于封装。UI设计的内容主要是将以上函数分装将其打包为MainWindow类,首先要确定成员变量,然后确定GPU使用情况,全部放置与def __init__(self):下,之后使用pyqt5库对于qt designer设计界面,将准确度曲线,损失曲线,数据集预览,混淆矩阵,垃圾识别的函数分别以按钮控件对应,并点击混淆矩阵显示识别率,精确率与召回率;点击物体识别可打开文件夹选择文件并将图片展示出来,在lineEdit中显示识别结果。主要困难为控件与控件间的调用导致出现的一些bug,不过此在调整__main__后解决,具体原因现在仍未知。最后插入背景,美化界

图片2.png

面。


4.项目优化

由于训练模型为标准卷积神经网络模型,为优化类似于此的多分类问题,我们将神经网络模型优化为VGG16模型。VGG16的全称 VGG是Visual Geometry Group Network的缩写,视觉几何群网络。

4.1 VGG16模型网络结构

卷积-卷积-池化-卷积-卷积-池化-卷积-卷积-卷积-池化-卷积-卷积-卷积-池化-卷积-卷积-卷积-池化-全连接-全连接-全连接 。

通道数分别为64,128,512,512,512,256,128,9。卷积层通道数翻倍,直到512时不再增加。通道数的增加,使更多的信息被提取出来。9为最终分类数目。

所有的激活单元都是Relu 。

用池化层作为分界,VGG16共有6个块结构,每个块结构中的通道数相同。因为卷积层和全连接层都有权重系数,也被称为权重层,其中卷积层13层,全连接3层,池化层不涉及权重。所以共有13+3=16权重层。

对于VGG16卷积神经网络而言,其13层卷积层和5层池化层负责进行特征的提取,最后的3层全连接层负责完成分类任务。

vgg16总共有16层,13个卷积层和3个全连接层,第一次经过64个卷积核的两次卷积后,采用一次pooling,第二次经过两次128个卷积核卷积后,再采用pooling,再重复两次三个512个卷积核卷积后,再pooling,最后经过三次全连接。

4.1.1 VGG16原理

VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野,采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。

简单来说,在VGG中,使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5*5卷积核,这样做的主要目的是在保证具有相同感知野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。而为什么可以这样代替呢:5x5卷积看做一个小的全连接网络在5x5区域滑动,我们可以先用一个3x3的卷积滤波器卷积,然后再用一个全连接层连接这个3x3卷积输出,这个全连接层我们也可以看做一个3x3卷积层。这样我们就可以用两个3x3卷积级联(叠加)起来代替一个 5x5卷积。

4.1.2 VGG16代码

VGG耗费更多计算资源,并且使用了更多的参数,导致更多的内存占用(140M)。所以我们使用华为ModelArts创建环境加速训练,不幸的是,我们从华为ModelArts的开发环境中只能找到小于cuda10.2的版本,而cuda10.2版本对应的tensorflow-gpu是<2.4.0的,而项目所需要的环境为keras>=2.4.0,导致我们只能申请多核cpu进行训练。租用华为ModelArts开发环境conda3-cuda10.2-cudnn7-ubuntu18.04,规格为GPU: 1*V100(32GB)|CPU: 8核 64GB。

在前几次运行时发现有过拟合的出现,然后我们添加了L2正则化来减小过拟合。L2约束通常对稀疏的有尖峰的权重向量施加大的惩罚,而偏好于均匀的参数。这样的效果是鼓励神经单元利用上层的所有输入,而不是部分输入。所以L2正则项加入之后,权重的绝对值大小就会整体倾向于减少,尤其不会出现特别大的值(比如噪声),即网络偏向于学习比较小的权重。所以L2正则化在深度学习中还有个名字叫做“权重衰减”(weight decay),也有一种理解这种衰减是对权值的一种惩罚,所以有些书里把L2正则化的这一项叫做惩罚项(penalty)。

from tensorflow.keras.regularizers import l2

weight_decay=0.005

VGG16_model_con = models.Sequential([

#两次使用64个3*3的卷积核,池化后维度(112,112,64)

layers.Conv2D(64,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block1_conv1', input_shape=(img_height, img_width, 3)),

layers.Conv2D(64,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block1_conv2'),

layers.AveragePooling2D(pool_size=(2,2),strides=(2,2), name = 'block1_pool'),

#两次使用128个3*3的卷积核,池化后维度(56,56,128)

layers.Conv2D(128,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block2_conv1'),

layers.Conv2D(128,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block2_conv2'),

layers.AveragePooling2D(pool_size=(2,2),strides=(2,2), name = 'block2_pool'),

#三次使用256个3*3的卷积核,池化后维度(28,28,256)

layers.Conv2D(256,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block3_conv1'),

layers.Conv2D(256,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block3_conv2'),

layers.Conv2D(256,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block3_conv3'),

layers.AveragePooling2D(pool_size=(2,2),strides=(2,2), name = 'block3_pool'),

#三次使用512个3*3的卷积核,池化后维度(14,14,512)

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block4_conv1'),

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block4_conv2'),

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block4_conv3'),

layers.AveragePooling2D(pool_size=(2,2),strides=(2,2), name = 'block4_pool'),

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block5_conv1'),

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block5_conv2'),

layers.Conv2D(512,(3,3),padding='same',kernel_regularizer=l2(weight_decay),activation='relu',name='block5_conv3'),

layers.AveragePooling2D(pool_size=(2,2),strides=(2,2), name = 'block5_pool'),

])

# 加载模型参数

VGG16_model_con.load_weights('vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5')

# 冻结前13层网络参数 保证加载的预训练参数不被改变

for layer in VGG16_model_con.layers[:13]:

layer.trainable = False

# 添加模型分类层(顶层)

VGG16_model_all = models.Sequential([

VGG16_model_con,

layers.Flatten(),

layers.Dense(256, activation='relu'),

layers.Dense(128, activation='relu'),

layers.Dense(len(class_names), activation="softmax")

])






4.2 训练结果展示

4.3可视化结果展示

4.4 VGG16优点

1.采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式

2.VGG网络的一层的信息数为64,后面的每层增加一倍,最多变为512个信息,信息数增加了,可以提取更多的信息。

3.VGG16具有如此之大的参数数目,可以预期它具有很高的拟合能力,更适合难度较大的分类

4.5 VGG16缺点

1.即训练时间过长,调参难度大。

2.需要的存储容量大,不利于部署。

5.华为云平台训练本地数据

5.1进入华为云开发平台创建开发环境

在华为云平台搜索ModelArts,点击管理控制台。下拉开发环境:Notebook,点击创建;

根据本地代码中的环境选择对应公共镜像,以及根据GPU或CPU训练要求在类型中选择,规格可根据自身需求创建,点击立即创建。

例如若用GPU训练,可使用NVIDIA V100 GPU单卡规格,32GB显存,适合深度学习场景下的算法训练和调测。

创建后可点击“启动”选择运行时间,结束项目后记得点击“停止”。点击“打开”进入环境。

5.2配置环境,导入文件

先点击环境终端配置环境,注意安装的库的版本。华为现最高cuda为10.2,对应tensorflow-gpu只有2.3,如果使用GPU训练要注意保证库直接的版本对应。

导入本地文件,点击左上角ModelArts Upload Files将本地文件上传,当文件大小超过100MB时,点击OBS中转,使用默认路径。之后就可以运行了。

5.2上手ModelArts可能会遇到的问题

1. 将文件保存到OBS桶中,再使ModelArts开发环境与OBS桶文件传输

要先创建OBS桶,由于实验所需要使用的Modelarts中的Notebook需要关联到OBS桶,因此需要先创建OBS桶。在华为云主页搜索OBS,或者进入以下网址,点击右上角“创建桶”按钮。


Https://console.huaweicloud.com/console/?region=cn-north-4#/obs/manager/buckets区域选择“北京四”,桶名称是不能和其他桶重名的,因此需要取一个相对复杂的桶名称避免重复。其他配置按默认即可。完成配置后点击“立即创建”。创建完成后进入桶,点击左侧导航栏的“对象”,选择“新建文件夹”。然后可将本地的文件或文件夹上传到obs桶中。

之后我们回到ModelArts的开发环境创建,此时我们需要存储配置改为云硬盘EVS。使用云硬盘evs可使用moxing进行文件传输,且文件传输效率快。

创建实例后,接下来将演示如何将obs桶中的文件传输到notebook中。如图,要将名为ton中的文件夹data中的文件train.txt传输到notebook中需要在notebook中输入以下代码即可。



import moxing as mox

mox.file.copy('obs://ton/data/train.txt','./data/train.txt')

2. 每次重启环境后库消失

由于notebook实例停止运行时除了work工作路径下的文件会被清空,因此每次进行训练时都需要重新安装依赖库,建议将所有需要安装的库写在一个txt文件中,这样可通过pip一键安装,节约时间。如requirements.txt文件中记录了以下需要安装的库。







接下来需要进入requirements.txt所在路径(本文中该文件位于根目录work下)

cd work

pip install -U -r requirements.txt

执行完即可。

6.项目的结果分析


从曲线上看在训练集上的准确率趋于100%,损失趋于0,在测试集上的准确度也可以保持在90%以上,损失也趋于平稳。几乎无过拟合现象。

点击垃圾识别后弹出图片并显示预测结果为K-Fruit。识别正确。


点击混淆矩阵弹出混淆矩阵图像,并显示模型总体识别率,平均精确率和平均召回率,由于原小数位数过多,所以使用round函数保留5位小数。如此看来模型的总体识别率,精确率还有召回率都保持在90%以上,模型准确度较好。

7. 项目总结

1.项目优势

1.运用pycharm强大的项目集功能,将不同功能模块的代码进行封装,在调用与修改代码时简单明了易操作,且run.py(主函数)文件代码简短,理解容易。

2.在交互界面上,采用QT designer进行简洁美观的界面设计,界面美观,控件设计合理,交互友好。并完成UI控制的数据可视化,完成评分标准:功能实现。

3.使用卷积神经网络算法3卷积层,2池化层,1全联接层,1drop层实现评分标准:理论水平。

4.人工手动多次调整超参数获得较好的几乎无过拟合、无欠拟合的模型,模型准确度在90%以上。实现项目评分指标:性能指标。

5.模型兼容度高可解释度高,由于数据集以目录分类,只要更换数据集,项目模型可进行另外数据集的训练预测。

6.运用华为ModelArts平台开发环境训练,提高效率。加强了对华为平台的认知。


2.项目缺点

1.优化模型VGG16出现过拟合现象且准确度不高,可能与参数或接口有关。

2.模型的训练数据采用非垃圾的水果来强化垃圾水果皮的训练不太合适,之后会将水果蔬菜从垃圾分类中去除,或者采用新的分类标准改进。

3.优化UI界面,优化模型,达成更好的其他分类问题的要求。

3.项目展望

项目基于传统卷积神经网络构建了完全由数据集决定的自主分类系统,参数调整由数据集所决定,可以做到切换数据集便可达到新的分类,采用界面广,根据对VGG16接口参数的调整可对更难区分的数据集的分类。



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。