赞
踩
从本篇博客开始介绍一系列经典的神经网络模型,比如CNN,RNN等等,大概对自己的要求初步就是理解每个模型的原理,然后能用keras实现即可!后续等kera较为熟练之后,再上手新的框架,框架不重要,最核心的其实还是理解这个模型本身。
今天主要介绍下卷积神经网络!
卷积神经网络的英文名称叫:Convolutional Neural Network(CNN) ,其中 Convolutional表示卷积的含义,为什么有这个奇怪的东东呢?那是因为卷积神经网络相比于普通的多层神经网络加入了2个新的结构:
正是由于有了这个卷积层才有了卷积神经网络这个称谓!
那相信大家肯定会问了,什么叫卷积和池化呢?
回答这个问题之前,我们首先来看下CNN整体的一个架构,看是如何运作的,具体见下图:
有了上图直观的认识之后,下面分几个问题来了解一下CNN的原理。
CNN的应用场景主要是在图像处理上,这时候很简单的一个问题来了:
1、为什么图像处理要用CNN 而不是之前普通的多层神经网络呢?
要回答上述问题,首先就需要了解图像是如何存储在计算机当中的!
为什么参数是117625呢?
图解见下方:
所以通过上述的解释,普通的多层神经网络无法用在图像处理上!那下一个问题:卷积神经网络CNN又是如何胜任这一项工作的呢?而这就需要从CNN的网络结构说起了!
CNN的网络结构通过博客一开始的图片就可以看到大概有三种不同的结构层:
那么这三种结构分别代表什么意思呢?继续往下。
首先说下最重要的卷积层,毕竟从CNN这个名称就可以看出来卷积层在CNN中的地位举足轻重了!
先上两个牛逼的动图:
上述很炫酷的操作展示的就是卷积的过程!
关于上述卷积首先就有这个问题了:
1、小滑窗是啥?为啥扫来扫去的?起到的目的是什么?
在上述通俗的解释之下,下面我们来看看一些学术名词和解释,卷积的过程大概涉及到3个参数:
2、为什么卷积核能起到效果?卷积核的权重是共享的吗?
上面的过程我们了解了大概卷积的操作,大家肯定会问,诶,为什么卷积会起到效果呢?
其实这个问题可以这么理解,如果对于设计不同的卷积核,比如有的是斜对角线全为1,那么这样扫描下来得到的结果中,如果哪一块斜着的元素值比较大就说明原始图片有着斜对角的相关特征,其余分析类似!所以卷积就是通过卷积核扫描矩阵自动提取特征而起到效果的!
故我们只需要把图片数据灌进去,设计好卷积核的尺寸、数量和滑动的步长就可以让自动提取出图片的某些特征,从而达到分类的效果!
小tips:一般情况下,根据实验得到的经验来看,会在越靠近输入层的卷积层设定少量的卷积核,越往后,卷积层设定的卷积核数目就越多
另外权重是共享的!上面动图的扫描过程就可以看到!
3、最右边得到的结果是怎么算的?
这个计算结果注意是矩阵的内积运算,而不是矩阵相乘运算即可,最后注意加上偏置项!
具体来看一个实例:为什么下图的卷积操作会得到1呢?
计算公式就是:
1
×
1
+
1
×
(
−
1
)
+
1
=
1
1×1+1×(-1)+1=1
1×1+1×(−1)+1=1 注意,任一相乘元素为0已经省略,计算就是各自两个矩阵内积,然后相加,最后加上偏置即可!
这样最复杂的卷积就讲完了!
其实关于池化层主要有两个问题:
1、什么叫池化层?分类是?
关于定义,具体见下图:
所以池化说白了就是一个降维的过程,如何降维呢?有两种方式:
2、为什么要搞这个池化层?
可以结合卷积核操作的本质来看!
一句话总结就是:池化操作保留下了卷积后有用的信息,同时还达到简化计算的目的!
在CNN中,做完了卷积和池化之后,接下来干吗呢?能直接把结果丢出来吗?做预测吗?显然是不行的,因为上述操作ok之后数据还是矩阵的形式,最后需要的是预测某一类的概率!所以我们怎么办呢?
这样就将CNN的三层结构给讲清楚了!
除了上述基本的问题之外,还有几个小的问题值得注意:
1、卷积核的尺寸必须为正方形吗?可以为长方形吗?如果是长方形应该怎么计算?
不是必须的。可以为长方形。
2、卷积核的个数如何确定?每一层的卷积核的个数都是相同的吗?
由经验确定。不一定,而且一开始设少一点,后面多一点效果会更好!为什么呢?如手写数字识别中第一层我们设定卷积核个数为5个,一般是找出诸如"横线"、“竖线”、“斜线”等共性特征,我们称之为basic feature,经过max pooling后,在第二层卷积层,设定卷积核个数为20个,可以找出一些相对复杂的特征,如“横折”、“左半圆”、“右半圆”等特征,越往后,卷积核设定的数目越多,越能体现label的特征就越细致,就越容易分类出来
3、步长的向右和向下移动的幅度必须是一样的吗?
keras是深度学习的框架之一,非常好用,和Python语法非常相似,下面就来看下如何用
keras实现简单的三层神经网络。
import keras
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
x = np.array([[0, 1, 0], [0, 0, 1], [1, 3, 2], [3, 2, 1]])
y = np.array([0, 0, 1, 1]).T
x
array([[0, 1, 0],
[0, 0, 1],
[1, 3, 2],
[3, 2, 1]])
y
array([0, 0, 1, 1])
结构
[外链图片转存失败(img-AIuPfvjt-1562771378758)(attachment:image.png)]
下面的例子就是上面的神经元个数可以依次换成:4,4,4,1
'''构建了一个三层的神经网络
- 第一个隐层有5个神经元 为relu激活函数
- 第二个隐层有4个 为relu激活函数
- 均为全连接层
- 然后输出 使用sigmoid激活函数
'''
simple_model = Sequential()
simple_model.add(Dense(5, input_shape=(x.shape[1],), activation='relu', name='layer1'))
simple_model.add(Dense(4, activation='relu', name='layer2'))
simple_model.add(Dense(1, activation='sigmoid', name='layer3'))
simple_model.compile(optimizer='sgd', loss='mean_squared_error')
simple_model.fit(x, y, epochs=20000) # 训练20000次模型
import matplotlib.pyplot as plt
plt.plot(simple_model.history.history['loss']) # 训练集的acc
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.show()
simple_model.predict(x[0:1]) # 预测第一个结果看和实际是否相符 相符!
array([[0.02791087]], dtype=float32)
CNN演变历史
import keras
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape)
(60000, 28, 28) (60000,) (10000, 28, 28) (10000,)
x_train
array([[[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], ..., [[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]]], dtype=uint8)
y_train
array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)
(60000, 28, 28)解释:
训练集有6万张图片,测试集有1万张图片
x_train = x_train.reshape(-1, 28, 28, 1)
x_test = x_test.reshape(-1, 28, 28, 1)
# 除以255是因为图片的最大像素就是255 进行归一化处理
x_train = x_train / 255.
x_test = x_test / 255.
y_train = keras.utils.to_categorical(y_train) # label做一个one-hot encoding处理
y_test = keras.utils.to_categorical(y_test)
x_train.shape # 要保证以这种形式输入到神经网络中
'''1表示图片: black/white 黑白!, 3表示图片: RGB,即彩色的'''
(60000, 28, 28, 1)
y_train # one-hot编码处理
array([[0., 0., 0., ..., 0., 0., 0.],
[1., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 1., 0.]], dtype=float32)
y_train.shape
(60000, 10)
from keras.layers import Conv2D, MaxPool2D, Dense, Flatten from keras.models import Sequential lenet = Sequential() # 表示我们的网络将学习6个滤波器 每个滤波器的大小都是3×3,步长为1 lenet.add(Conv2D(6, kernel_size=3, strides=1, padding='same', input_shape=(28, 28, 1))) # 2×2的最大池化层 步长为2 lenet.add(MaxPool2D(pool_size=2, strides=2)) # 表示我们的网络将学习16个滤波器 每个滤波器的大小都是5×5,步长为1 lenet.add(Conv2D(16, kernel_size=5, strides=1, padding='valid')) # 2×2的最大池化层 步长为2 lenet.add(MaxPool2D(pool_size=2, strides=2)) # 展开 lenet.add(Flatten()) # 接下来相当于有两层full-connected网络 # 120个神经元 全连接网络 lenet.add(Dense(120)) # 84个神经元 全连接网络 lenet.add(Dense(84)) lenet.add(Dense(10, activation='softmax')) # 10个类别的softmax分类器
疑问:上述代码没有加激活函数为啥也ok呢?
lenet.compile('sgd', loss='categorical_crossentropy', metrics=['accuracy'])
50次迭代
lenet.fit(x_train, y_train, batch_size=64, epochs=50, validation_split=0.2) # 即训练集再拿出0.2作为验证集
# 一开始测测试集用来做最后的模型评估!
Train on 48000 samples, validate on 12000 samples Epoch 1/50 48000/48000 [==============================] - 19s 390us/step - loss: 0.0660 - acc: 0.9804 - val_loss: 0.0602 - val_acc: 0.9808 Epoch 2/50 48000/48000 [==============================] - 20s 409us/step - loss: 0.0625 - acc: 0.9813 - val_loss: 0.0604 - val_acc: 0.9802 Epoch 3/50 48000/48000 [==============================] - 20s 416us/step - loss: 0.0591 - acc: 0.9823 - val_loss: 0.0621 - val_acc: 0.9803 Epoch 4/50 48000/48000 [==============================] - 21s 437us/step - loss: 0.0565 - acc: 0.9829 - val_loss: 0.0631 - val_acc: 0.9802 ...... Epoch 45/50 48000/48000 [==============================] - 21s 441us/step - loss: 0.0164 - acc: 0.9954 - val_loss: 0.0616 - val_acc: 0.9834 Epoch 46/50 48000/48000 [==============================] - 14s 293us/step - loss: 0.0164 - acc: 0.9950 - val_loss: 0.0652 - val_acc: 0.9832 Epoch 47/50 48000/48000 [==============================] - 12s 259us/step - loss: 0.0162 - acc: 0.9952 - val_loss: 0.0640 - val_acc: 0.9833 Epoch 48/50 48000/48000 [==============================] - 13s 274us/step - loss: 0.0151 - acc: 0.9956 - val_loss: 0.0687 - val_acc: 0.9818 Epoch 49/50 48000/48000 [==============================] - 14s 302us/step - loss: 0.0148 - acc: 0.9957 - val_loss: 0.0654 - val_acc: 0.9832 Epoch 50/50 48000/48000 [==============================] - 13s 268us/step - loss: 0.0142 - acc: 0.9958 - val_loss: 0.0670 - val_acc: 0.9828 <keras.callbacks.History at 0x1289d4e10>
score = lenet.evaluate(x_test, y_test)
print('test score:', score[0])
print('test accuracy:', score[1])
10000/10000 [==============================] - 2s 188us/step
test score: 0.05805485805499775
test accuracy: 0.9844
首先希望看到训练集和验证集上面损失函数同步变化的情况
import matplotlib.pyplot as plt
plt.plot(lenet.history.history['loss'])
plt.plot(lenet.history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc = 'lower left')
plt.show()
import matplotlib.pyplot as plt
plt.plot(lenet.history.history['acc'])
plt.plot(lenet.history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc = 'upper left')
plt.show()
更新的点:
from keras.layers import Conv2D, MaxPool2D, Dense, Flatten, Dropout from keras.models import Sequential from keras.layers.core import Activation from keras.optimizers import SGD, Adam, RMSprop '''搭建模型''' lenet = Sequential() # 表示我们的网络将学习6个滤波器 每个滤波器的大小都是3×3,步长为1 lenet.add(Conv2D(6, kernel_size=3, strides=1, padding='same', input_shape=(28, 28, 1))) # 2×2的最大池化层 步长为2 lenet.add(Activation("relu")) lenet.add(MaxPool2D(pool_size=2, strides=2)) # 表示我们的网络将学习16个滤波器 每个滤波器的大小都是5×5,步长为1 lenet.add(Conv2D(16, kernel_size=5, strides=1, padding='valid')) lenet.add(Activation("relu")) # 2×2的最大池化层 步长为2 lenet.add(MaxPool2D(pool_size=2, strides=2)) # 展开 lenet.add(Flatten()) # 接下来相当于有两层full-connected网络 # 120个神经元 全连接网络 lenet.add(Dense(120)) lenet.add(Activation("relu")) lenet.add(Dropout(0.25)) # 84个神经元 全连接网络 lenet.add(Dense(84)) lenet.add(Activation("relu")) lenet.add(Dense(10, activation='softmax')) # 10个类别的softmax分类器 '''编译''' lenet.compile('Adam', loss='categorical_crossentropy', metrics=['accuracy'])
'''训练模型'''
lenet.fit(x_train, y_train, batch_size=64, epochs=50, validation_split=0.2) # 即训练集再拿出0.2作为验证集
# 一开始测测试集用来做最后的模型评估!
Train on 48000 samples, validate on 12000 samples Epoch 1/50 48000/48000 [==============================] - 27s 569us/step - loss: 0.3039 - acc: 0.9047 - val_loss: 0.0934 - val_acc: 0.9722 Epoch 2/50 48000/48000 [==============================] - 26s 552us/step - loss: 0.0953 - acc: 0.9702 - val_loss: 0.0723 - val_acc: 0.9768 Epoch 3/50 48000/48000 [==============================] - 16s 333us/step - loss: 0.0718 - acc: 0.9777 - val_loss: 0.0540 - val_acc: 0.9824 Epoch 4/50 48000/48000 [==============================] - 15s 311us/step - loss: 0.0589 - acc: 0.9820 - val_loss: 0.0502 - val_acc: 0.9842 ...... Epoch 46/50 48000/48000 [==============================] - 15s 316us/step - loss: 0.0065 - acc: 0.9978 - val_loss: 0.0551 - val_acc: 0.9903 Epoch 47/50 48000/48000 [==============================] - 15s 309us/step - loss: 0.0054 - acc: 0.9982 - val_loss: 0.0636 - val_acc: 0.9890 Epoch 48/50 48000/48000 [==============================] - 15s 310us/step - loss: 0.0067 - acc: 0.9978 - val_loss: 0.0609 - val_acc: 0.9890 Epoch 49/50 48000/48000 [==============================] - 15s 309us/step - loss: 0.0057 - acc: 0.9980 - val_loss: 0.0653 - val_acc: 0.9895 Epoch 50/50 48000/48000 [==============================] - 15s 309us/step - loss: 0.0059 - acc: 0.9980 - val_loss: 0.0603 - val_acc: 0.9872 <keras.callbacks.History at 0x15d843048>
'''评估'''
score = lenet.evaluate(x_test, y_test)
print('test score:', score[0])
print('test accuracy:', score[1])
10000/10000 [==============================] - 1s 148us/step
test score: 0.06008354149081474
test accuracy: 0.9885
import matplotlib.pyplot as plt
plt.plot(lenet.history.history['loss'])
plt.plot(lenet.history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc = 'upper right')
plt.show()
import matplotlib.pyplot as plt
plt.plot(lenet.history.history['acc'])
plt.plot(lenet.history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc = 'lower right')
plt.show()
import numpy as np
np.set_printoptions(linewidth=100) # 设定print输出每行最大长度为100个字符防止自动换行
m = np.random.randn(5,5) * 10 + 1000
print('Origin:\n', m, '\n')
m_row_max = m.max(axis = 1)
print('Row max value:\n', m_row_max.reshape(5, 1), '\n')
m = m - m_row_max.reshape(5,1) # 每行元素减去本行的最大值(防止出现 INF)
m_exp = np.exp(m) # 求指数
m_exp_row_sum = m_exp.sum(axis=1).reshape(5,1)
softmax = m_exp / m_exp_row_sum
print('Softmax:\n', softmax, '\n') # 校验softmax数组是否正确的方法是查看每一行的和是否是1
print('Verification:\n', softmax.sum(axis=1).reshape(5, 1))
Origin: [[1004.52758237 995.19160091 1014.47409723 994.09021834 1016.73181843] [1026.35621051 995.54729275 995.78077014 1011.75856544 989.43945877] [1004.76643052 989.01982099 1001.7131216 988.0155224 993.99620217] [1015.41230066 980.8775022 1004.08270454 983.0149173 1004.06092301] [ 992.30890863 1001.69547553 1004.56735877 1015.58083086 1006.31579688]] Row max value: [[1016.73181843] [1026.35621051] [1004.76643052] [1015.41230066] [1015.58083086]] Softmax: [[4.53487284e-06 3.99944642e-10 9.46850978e-02 1.32946073e-10 9.05310367e-01] [9.99999543e-01 4.16731973e-14 5.26325929e-14 4.57428369e-07 9.27381078e-17] [9.54905823e-01 1.38450623e-07 4.50739196e-02 5.07146673e-08 2.00682639e-05] [9.99976235e-01 1.00396200e-15 1.20118141e-05 8.51108363e-15 1.17530073e-05] [7.81778848e-11 9.32434044e-07 1.64763560e-05 9.99887924e-01 9.46669077e-05]] Verification: [[1.] [1.] [1.] [1.] [1.]]
所以哪一列取值最大就属于哪一类!
import numpy as np
def logistic(x):
return 1/(1+np.exp(-x))
print(logistic(6.666666))
0.9987289828906512
所以sigmoid仅仅是二分类的问题,softmax可以解决多分类的问题!
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。