机器学习平凡之路六

机器学习平凡之路六

  1. 卷积层神经网络
  2. 图像识别

卷积层神经网络

卷积层神经网络称作为卷积网络,与普通的神经网络是,卷积层的神经元中只覆盖输入特征局部范围的单元,其中的过滤器可以做到对图像关键特征的提取。卷积层神经网络在图像识别上有很好的效果

图像识别

猫狗分类本质就是二元分类的问题。对单一种类细分就是多元分类问题。

回顾卷积网络代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential, load_model
model = tf.keras.Sequential() # 序贯模型
model.add(layers.Conv2D(
filters=32, # 添加Conv2D层,指定过滤器的个数,即通道数
kernel_size=(3,3), # 指定卷积核的大小
activation='relu', # 指定激活函数
input_shape=(28,28,1))) # 指定输入数据样本张量的类型
model.add(layers.MaxPooling2D(pool_size=(2,2))) # 添加2D层
model.add(layers.Conv2D(64,(3,3),activation='relu')) # 添加Conv2D层
model.add(layers.MaxPooling2D(pool_size=(2,2))) # 再次添2D层
model.add(layers.Dropout(0.25)) # 添加Dropout层
model.add(layers.Flatten()) # 添加展平层
model.add(layers.Dense(128,activation='relu'))
model.add(layers.Dropout(0.5)) # 添加Dropout层
model.add(layers.Dense(10,activation='softmax'))
model.compile(optimizer = 'adam', # 优化器
loss = 'categorical_crossentropy', #损失函数
metrics = ['acc']) # 评估指标
model.summary()

image-20210331093529751

同样的生成图形

1
2
3
from IPython.display import SVG # 实现神经网络结构的图形化显示
from tensorflow.keras.utils import model_to_dot
SVG(model_to_dot(model, show_shapes=True,show_layer_names=True, dpi=65).create(prog='dot', format='svg'))

image-20210331093816250

image-20210331093835633

image-20210331093928589

  1. 分为了三大层次构建
  2. 输入层,特层学习(卷积和最大池化),分类学习(展平层,全连接,softmax)

卷积层的原理

卷积网络是通过Conv2D层中的过滤器用卷积计算对图像的核心特性进行抽取,提高图像处理的效率和准确率

其余原理可以自行百度学习

利用卷积网络给天气进行分类

数据说明:天气分为4种多云,雨天,晴天,日落

  1. 准备数据集,把不同目录中图像全部整理到同一个特征张量数组中,进行乱序排列。输入结构应该是4D张量(样本,图像高度,图像宽度,颜色深度)
  2. 对应的获取目录名整理到一个同样长度的1D标签张量中,次序与特征张量一致

准备图像数据

1
2
3
4
import numpy as np
import pandas as pd
import os
print(os.listdir('dataset2')) # 显示数据集的目录

image-20210331102747618

处理图像数据到X和y中(pip install opencv-python)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
dir = 'dataset2/'
cloudy_dir = dir+'cloudy'
rain_dir = dir+'rain'
shine_dir = dir+'shine'
sunrise_dir = dir+'sunrise'
import cv2
X = []
y_label = []
imgsize = 150
# 定义一个函数读入图像
def training_data(label, data_dir):
for img in os.listdir(data_dir):
print('正在读入:',data_dir)
try:
path = os.path.join(data_dir, img)
img = cv2.imread(path, cv2.IMREAD_COLOR)
img = cv2.resize(img, (imgsize, imgsize))
X.append(np.array(img))
y_label.append(str(label))
except Exception as e:
print(path)
print(str(e))
training_data('cloudy',cloudy_dir)
training_data('rain',rain_dir)
training_data('shine',shine_dir)
training_data('sunrise',sunrise_dir) # 如果cv在读有损失的图片可能会报错

image-20210331105157531

构建X,y的张量

1
2
3
4
5
6
7
8
9
10
11
from sklearn.preprocessing import LabelEncoder
from keras.utils.np_utils import to_categorical
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y_label) # 标签编码
y = to_categorical(y,10) #将标签转化为One-hot编码
X = np.array(X)
X = X/255 # X进行归一化处理
print('X张量的形状:',X.shape)
print('X一个数据',X[0])
print('y张量的形状:',y.shape)
print('y一个数据',y[0])

image-20210331110059562

image-20210331110113696

随机显示图片

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt 
import random as rdm
fig, ax = plt.subplots(5,2)
fig.set_size_inches(15,15)
for i in range(5):
for j in range(2):
r = rdm.randint(0,len(X))
ax[i,j].imshow(X[r])
ax[i,j].set_title('tq:'+y_label[r])
plt.tight_layout()

image-20210331110836357

拆分数据集

1
2
from sklearn.model_selection import train_test_split
X_train, X_test, y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0)

构建简单的卷积网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential, load_model
cnn = tf.keras.Sequential() # 序贯模型
cnn.add(layers.Conv2D(32,(3,3),activation='relu',
input_shape=(100,100,3)))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(64,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Flatten())
cnn.add(layers.Dense(512,activation='relu'))
cnn.add(layers.Dense(10,activation='softmax'))
cnn.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['acc'])
history = cnn.fit(X_train,y_train,
epochs=10,
batch_size=256,
validation_data=(X_test,y_test)
)

image-20210331115256984

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def show_history(history): # 显示训练过程的学习曲线
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.figure(figsize=(12,4))
plt.subplot(1, 2, 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
acc = history.history['acc']
val_acc = history.history['val_acc']
plt.subplot(1, 2, 2)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
show_history(history)

image-20210331115356781

我们可以看到在图像识别的线性上面还是比较欠缺

优化学习

优化学习速率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential, load_model

from tensorflow.keras import optimizers
cnn = tf.keras.Sequential() # 序贯模型
cnn.add(layers.Conv2D(32,(3,3),activation='relu',
input_shape=(100,100,3)))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(64,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Flatten())
cnn.add(layers.Dense(512,activation='relu'))
cnn.add(layers.Dense(10,activation='softmax'))
cnn.compile(loss='categorical_crossentropy',
optimizer=optimizers.Adam(lr=0.02),
metrics=['acc'])

image-20210331121023016

其实还是不够完美,更多详情参考一下Adam

第二个添加Dropout层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras import optimizers
cnn = tf.keras.Sequential() # 序贯模型
cnn.add(layers.Conv2D(32,(3,3),activation='relu',
input_shape=(100,100,3)))
cnn.add(layers.Dropout(0.5))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(64,(3,3),activation='relu'))
cnn.add(layers.Dropout(0.5))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3),activation='relu'))
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Flatten())
cnn.add(layers.Dropout(0.5))
cnn.add(layers.Dense(512,activation='relu'))
cnn.add(layers.Dense(10,activation='softmax'))
cnn.compile(loss='categorical_crossentropy',
optimizer=optimizers.Adam(),
metrics=['acc'])

image-20210331130706617

从线性上面看,偏差还是不够

使用数据增强

在keras中,有一个imageData-Generator,通过对图像的平移,颠倒等一些列多种手段,增大数据集样本数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from keras.preprocessing.image import ImageDataGenerator
augs_gen = ImageDataGenerator(
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
rotation_range=10,
zoom_range = 0.1,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
vertical_flip=False)
augs_gen.fit(X_train) # 针对训练集拟合数据增强器
1
2
3
4
5
6
7
history = cnn.fit( # 使用fit_generator
augs_gen.flow(X_train,y_train,batch_size=16), # 增强后的训练集
validation_data = (X_test,y_test), # 指定验证集
validation_steps = 100, # 指定验证步长
steps_per_epoch = 100, # 指定每轮步长
epochs = 10, # 指定轮次
verbose = 1) # 指定是否显示训练过程中的信息

image-20210331132722608

总的来说,效果其实还是比较一般

保存模型

1
2
3
4
from tensorflow.keras.models import Sequential, load_model
cnn.save('tq.h5')
## del cnn
cnn = load_model('tq.h5')

可以保存模型

绘制特征通道

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt # 导入matplotlib
model = load_model('tq.h5')# 载入刚才保存的模型
# 绘制特征通道
layer_outputs = [layer.output for layer in model.layers[:16]]
image = X_train[0]
image = image.reshape(1, 100, 100, 3)
activation_model = Model(inputs=model.input, outputs=layer_outputs)
activations = activation_model.predict(image)
first_layer_activation = activations[0]
plt.matshow(first_layer_activation[0, :, :, 2], cmap='viridis')
plt.matshow(first_layer_activation[0, :, :, 3], cmap='viridis')

image-20210331133645175

1
2
3
4
5
pred = cnn.predict(X_test[2].reshape(1,100,100,3))
rs = pred.argmax
print(label_encoder.inverse_transform([pred.argmax(axis=1)[0]]))
import matplotlib.pyplot as plt
plt.imshow(X_test[2])

image-20210331140509790

2对应的是shine,可以看到当前结果还是很满意的

相关的一些案例参考

相关的一些小例子参考