关于卷积神经网络(CNN)的深入理解与代码实现

如下来大部分内容来着于书《深度学习入门-基于Python的理论与实现》:博主手敲和总结

神经网络的深入理解

6.1参数的更新

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个的过程称为最优化。

 文中提到了四种参数更新的方法:SGD、Momentum、AdaGrad、Adam四种方法。

6.2 权重的初始值

在神经网络中,权重的初始值特别重要。实际上,设定什么样的权重初始值,经常关系到神经网络的学习是否能成功。

6.2.1权值衰减

权值衰减---抑制过拟合、提高模型泛化能力的技巧

权值衰减就是一种以减少权重参数的值为目的进行学习的方法。通过减少权重参数的值为目的进行学习的方法。通过建设权重的参数的值一直过拟合的发生。

如果想减小权重的值,一开始就将初始值设置为较小的值才是正途。(但权重初始值不能设置为0)

现在,在一般的深度学习框架中,Xavier初始值已被当做标准使用。比如,Caffe框架中,通过设定权重初始值赋予xavier参数,就可以使用Xavier初始值。

 6.2.3ReLU的权重初始值

总结一下,当激活函数使用ReLU时,权重初始值使用He初始值,当激活函数为sigmoid或者tanh等S型曲线函数时,初始值使用Xavier初始值。这是目前的最佳实线。

6.3 Batch normalization

看一下机器学习竞赛的结果发现,很多通过使用Batch Normalization方法获得了优异的结果。

Batch Norm 有如下优点:

1.可以使学习快速进行(可以增加学习效率)

2不那么依赖初始值(对于初始值不用那么神经质)

3.抑制过拟合(降低Dropout等的必要性)

Batch Norm的思路是调整各层的激活值分布使其拥有适当的广度。为此,要向神经网络中插入对数据分布进行正规化的层,即Batch Normalization层。

6.4 正则化

在机器学习的问题中,过拟合是很常见的问题。过拟合是只能拟合训练数据,不能很好地拟合不包含在训练集中的其他数据的状态。机器学习的目标是提高泛化能力,即便是没包含在训练数据中的未观测数据,也希望模型可以进行正确的识别。我们可以制作复杂的、表现力强的模型,但是相应地,抑制过拟合的技巧也很重要。

6.4.1 过拟合

发生过拟合的原因,主要有如下两个。

1.模型拥有大量参数、表现力强。

2.训练数据少

6.4.2 权重衰减

权值衰减是一直以来经常被使用的一种一直过拟合的方法。该方法通过在学习过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。

复习一下,神经网络的学习目的是减少损失函数的值。

6.4.3 Dropout

作为抑制过拟合的方法,前面我们介绍了为损失函数加上权重的L2范数的权值衰减方法。该方法可以简单的实现,在某种程度上能够抑制过拟合。但是,如果网络的模型变得复杂,只用权值衰减就难以应付了。在这种情况下,我们经常会使用Dropout方法。

Dropout是一种在学习过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。

6. 5 超参数的验证

超参数是指,比如各层的神经元数量、batch大小,参数更新时的学习率活权值衰减等。如果这些超参数没有设置合适的值,模型的性能会很差。虽然超参数的取值非常重要,但在决定超参数的过程中一般伴随着很多的试错。本书讲介绍尽可能高效的寻找超参数的值的方法。

6.5.1 验证数据

之前我们使用的数据集分成了训练数据和测试数据,训练数学用于学习,测试数据用于评估泛化能力。由此,就可以评估是否过度拟合了训练数据(是否发生了过拟合),以及泛化能力如何等等。

下面我们要对超参数设置各种各样的值进行验证。這里注意不能使用测试数据评估超参数的性能。这点很重要,但也容易被忽视。

为什么不用用测试数据评估超参数的性能呢?这里因为如果使用测试数据调整超参数,超参数的值会对测试数据发生过拟合。换句话说,用测试数据确认超参数的值的好坏,就会导致超参数的值被调整只拟合测试数据。这样的话,可能就会得到不能拟合其他数据、泛化能力低的模型。

因此,调整超参数时,必须使用超参数专用的确认数据。用于调整超参数的数据,一般称为验证数据(validation data)。我们使用这个验证数据来评估超参数的好坏。

根据不同的数据集,有的会事先分成训练数据、验证数据、测试数据三部分,有的只会分成训练数据和测试数据两部分,有的则不进行分割。在这种情况下,用户需要自行进行分割。如MNIST数据集,获得验证数据最简单的办法就是从训练数据中事先分割20%作为验证数据。

代码案例如下:

(x_train,t_train),(x_test,t_test)=load_mnist()

#打乱训练数据
x_train,t_train=shuffle_dataset(x_train,t_train)

#分割验证数据
validation_rate=0.20
validation_num=int(x_train.shape[0]*validation_rate)

x_val=x_train[:validation_num]
t_val=t_train[:validation_num]
x_train=x_train[:validation_num]
t_train=t_train[:validation_num]

6.5.2 超参数的最优化

进行过超参数的最优化时,逐渐缩小超参数的‘‘好值’’的存在范围非常重要。所谓逐渐缩小范围,是指一开始先大致设定一个范围,从这个范围中随机选出一个超参数,用真让搞采样到的值进行识别精度的评估。然后多次重复该操作,观察识别精度的结果,根据这个结果缩小超参数的范围的‘‘好值’’的范围。通过重复这一操作,就可以逐渐确定超参数的合适范围。

超参数的范围只要‘大致地指定’就可以了。

在超参数的最优化中,要注意点是深度学习需要很长时间(比如几天或者几周)。因此,在超参数的探索中,需要尽早放弃那些不符合逻辑的超参数。于是,在超参数的最优化中,减少学习的epoch,缩短一次评估所需的时间是一个不错的方法。

总结:

步骤0    设定超参数的范围

步骤1  从设定的超参数中随机采样

步骤2  使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置的非常小)

步骤3 重复步骤1和步骤2(100次等),根据它们识别精度的结果,缩小超参数的范围。

反复进行上诉操作,不断缩小超参数的范围,在缩小到一定程度时,从该范围中选出一个超参数的值。这就是进行超参数的最优化的一种方法。

 如下是代码实现部分:

Python深度学习03——Keras卷积神经网络(CNN)实现_keras cnn_阡之尘埃的博客-CSDN博客

卷积神经网络
卷积神经网络其实是做互相关运算,和概率论里面的卷积公式不是一个东西。理论就不过多介绍了,记住卷积神经网络是用于图像处理的,并且接受的输入不再是机器学习或者多层感知机里面的二维输入,由于图片本身就是二维数据了,再加上样本的个数这个维度,图片起码都是三维数据,并且考虑颜色通道不一样,四维的数据也很常见。

Keras里面的卷积层主要是Conv1(一维卷积层),Conv2(二维卷积层),UpSampling2D(1D)上采样层,MaxPooling2D(1D)最大池化层,AveragePooling2D(1D)平均池化层。

一般的卷积案例都采用MNIST手写数字集,这个数据集是黑白的,而且太简单了,MLP就能取得很好的分类效果。因此本次案例采用Cifar-10彩色数据集,其官网为:https://www.cs.toronto.edu/~kriz/cifar.html

该数据集为10分类的问题,物品为飞机汽车鸟狗青蛙等.......下面开始案例

卷积神经网络Python案例

Keras内置数据集,导入包导入数据:(若是有同学想学怎么把图片变成数据,可以看后面章节)

from keras.datasets import cifar10
import matplotlib.pyplot as plt
# 载入 Cifar10 数据集, 如果是第一次载入会自行下载数据集  
(X_train, Y_train), (X_test, Y_test) = cifar10.load_data()
print("X_train.shape: ", X_train.shape)
print("Y_train.shape: ", Y_train.shape)
print("X_test.shape: ", X_test.shape)
print("Y_test.shape: ", Y_test.shape)

展示结果为训练数据为5w张,测试集数据为1w。每个数据都是32*32的图片,并且*3表示是三种颜色,下面打印前9张图片看看

#绘出9张图片  
sub_plot= 330
for i in range(0, 9):
    ax = plt.subplot(sub_plot+i+1)
    ax.imshow(X_train[i], cmap="binary")
    ax.set_title("Label: " + str(Y_train[i]))
    ax.axis("off")
 
plt.subplots_adjust(hspace = .5)
# 显示数字图片  
plt.show()

下面开始构建神经网络,首先将数据归一化,y是10分类问题,所以要独热编码

import numpy as np
import pandas as pd 
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Dropout
from tensorflow.keras.utils import to_categorical
 
# 指定随机数种子  
seed = 10
np.random.seed(seed)
 
# 因为是固定范围, 所以执行正规化, 从 0-255 至 0-1 
X_train = X_train.astype("float32") / 255
X_test = X_test.astype("float32") / 255
# One-hot编码
Y_train = to_categorical(Y_train)
Y_test = to_categorical(Y_test)

定义卷积模型

# 定义模型
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), padding="same",input_shape=X_train.shape[1:], activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, kernel_size=(3, 3), padding="same",activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512, activation="relu"))
model.add(Dropout(0.5))
model.add(Dense(10, activation="softmax"))
model.summary()   #显示模型摘要资讯  

32表示卷积核的格式,kernel_size表示卷积核大小,padding表示补零为相同尺寸(填充),strides表示填充步幅。最大池化层里面的pool_size=(2, 2)表示缩小比例。

编译模型,然后开始训练拟合,因为数据太大,只训练个20轮吧

# 编译模型
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
# 训练模型
history = model.fit(X_train, Y_train, validation_split=0.2, epochs=20, batch_size=128, verbose=2)

在训练集和测试集上评估模型

# 评估模型
print("\nTesting ...")
loss, accuracy = model.evaluate(X_train, Y_train)
print("训练数据集的准确度  = {:.2f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, Y_test)
print("测试数据集的准确度   = {:.2f}".format(accuracy))

储存模型

#存储Keras模型
print("Saving Model: cifar10.h5 ...")
model.save("cifar10.h5")

 画出损失变化

# 显示训练和验证损失
loss = history.history["loss"]
epochs = range(1, len(loss)+1)
val_loss = history.history["val_loss"]
plt.plot(epochs, loss, "bo-", label="Training Loss")
plt.plot(epochs, val_loss, "ro--", label="Validation Loss")
plt.title("Training and Validation Loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()
plt.show()

 画出准确率变化

# 显示训练和验证准确度  
acc = history.history["accuracy"]
epochs = range(1, len(acc)+1)
val_acc = history.history["val_accuracy"]
plt.plot(epochs, acc, "bo-", label="Training Acc")
plt.plot(epochs, val_acc, "ro--", label="Validation Acc")
plt.title("Training and Validation Accuracy")
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
plt.legend()
plt.show()

 计算预测结果的评价

# 计算分类的预测值
print("\nPredicting ...")
predict=model.predict(X_test) 
Y_pred=np.argmax(predict,axis=1)
#重新加载Y_test
(X_train, Y_train), (X_test, Y_test) = cifar10.load_data()
 
#计算混淆矩阵
#显示混淆矩阵
tb = pd.crosstab(Y_test.astype(int).flatten(), 
                 Y_pred.astype(int),
                 rownames=["label"], colnames=["predict"])
print(tb)
 

画热力图

import seaborn as sns
sns.heatmap(tb,cmap='Blues',fmt='.20g',annot=True)
#fmt='.20g'不使用科学计数法,annot=True表示增加注释
plt.tight_layout()

 

 随便选一个图片,看模型预测其概率准不准 

#重新归一化
X_train = X_train.astype("float32") / 255
X_test = X_test.astype("float32") / 255
 
#选第10张图片
i = 10
img = X_test[i]
# 将图片转换成 4D 张量  
X_test_img = img.reshape(1, 32, 32, 3).astype("float32")
 
# 绘出图表的预测结果
plt.figure()
plt.subplot(1,2,1)
plt.title("Example of Image:" + str(Y_test[i]))
plt.imshow(img, cmap="binary")
plt.axis("off")

显然这是飞机

看模型预测的种类概率

#预测结果的概率
print("Predicting ...")
probs = model.predict(X_test_img, batch_size=1)
plt.subplot(1,2,2)
plt.title("Probabilities for Each Image Class")
plt.bar(np.arange(10), probs.reshape(10), align="center")
plt.xticks(np.arange(10),np.arange(10).astype(str))
plt.show()

模型几乎38%认为是飞机,35%认为是鹿....(差点就错了)

下面查看分类错误对象的记录

Y_probs=model.predict(X_test) # 概率
Y_pred=np.argmax(predict,axis=1)# 分类
# 建立分类错误的 DataFrame 物件
Y_test = Y_test.flatten()
df = pd.DataFrame({"label":Y_test, "predict":Y_pred})
df = df[Y_test!=Y_pred]  # 筛选出分类错误的资料 
print(df.shape)
print(df.head())

随机选一张看看

#随机选 1 个错误分类的数字索引
i = df.sample(n=1).index.values.astype(int)[0]
print("Index: ", i)
img = X_test[i] 
 
#  绘出图表的预测结果
plt.figure()
plt.subplot(1,2,1)
plt.title("Example of Image:" + str(Y_test[i]))
plt.imshow(img, cmap="binary")
plt.axis("off")
plt.subplot(1,2,2)
plt.title("Probabilities for Each Image Class")
plt.bar(np.arange(10), Y_probs[i].reshape(10), align="center")
plt.xticks(np.arange(10),np.arange(10).astype(str))
plt.show()

这是一个船,但是模型几乎90%认为它是一个汽车....

再选一个看看

 这是一个猫,但是模型几乎60%认为它是一只马....

还是很有意思的,同学们可以调整模型参数让其准确率更高,再来进行测评

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DJ.马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值